]> git.tdb.fi Git - r2c2.git/blob - source/designer/designer.cpp
Convert designer to use mspgbase instead of sdl
[r2c2.git] / source / designer / designer.cpp
1 /* $Id$
2
3 This file is part of the MSP Märklin suite
4 Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6 */
7
8 #include <signal.h>
9 #include <cmath>
10 #include <iostream>
11 #include <GL/gl.h>
12 #include <msp/gl/rendermode.h>
13 #include <msp/gl/select.h>
14 #include <msp/gl/texture2d.h>
15 #include <msp/input/keys.h>
16 #include <msp/strings/codec.h>
17 #include <msp/strings/lexicalcast.h>
18 #include <msp/strings/utf8.h>
19 #include <msp/strings/utils.h>
20 #include <msp/time/units.h>
21 #include <msp/time/utils.h>
22 #include "libmarklin/tracktype.h"
23 #include "designer.h"
24 #include "input.h"
25 #include "manipulator.h"
26 #include "measure.h"
27 #include "selection.h"
28
29 using namespace std;
30 using namespace Marklin;
31 using namespace Msp;
32
33 Designer::Designer(int argc, char **argv):
34         screen_w(1280),
35         screen_h(960),
36         input(0),
37         mode(SELECT),
38         cam_yaw(M_PI/2),
39         cam_pitch(-M_PI/4),
40         cam_pos(0, -0.5, 0.5),
41         shift(false),
42         move_x(0),
43         move_y(0),
44         zoom(0),
45         rotate(0),
46         pitch(0)
47 {
48         catalogue.load("tracks.dat");
49
50         cat_layout=new Layout(catalogue);
51         cat_layout_3d=new Layout3D(*cat_layout);
52
53         const map<unsigned, TrackType *> &ctracks=catalogue.get_tracks();
54         unsigned n=0;
55         for(map<unsigned, TrackType *>::const_iterator i=ctracks.begin(); i!=ctracks.end(); ++i, ++n)
56         {
57                 Track *track=new Track(*i->second);
58                 track->set_position(Point((n%11)*0.1-0.5, 0.2-n/11*0.3, 0));
59                 track->set_rotation(M_PI/2);
60                 cat_layout->add_track(*track);
61         }
62
63         manipulator=new Manipulator(*this);
64         manipulator->signal_status.connect(sigc::mem_fun(this, &Designer::manipulation_status));
65         manipulator->signal_done.connect(sigc::mem_fun(this, &Designer::manipulation_done));
66
67         layout=new Layout(catalogue);
68         layout_3d=new Layout3D(*layout);
69
70         if(argc>1)
71         {
72                 layout->load(argv[1]);
73                 const list<Track3D *> &ltracks=layout_3d->get_tracks();
74                 for(list<Track3D *>::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
75                 {
76                         if((*i)->get_track().get_sensor_id())
77                                 (*i)->set_color(GL::Color(1, 1, 0.5));
78                         else if((*i)->get_track().get_turnout_id())
79                                 (*i)->set_color(GL::Color(0.5, 1, 1));
80                         else if((*i)->get_track().get_flex())
81                                 (*i)->set_color(GL::Color(1, 0.5, 1));
82                 }
83         }
84
85         selection=new Selection;
86         manipulator->set_selection(selection);
87
88         measure=new Measure(*this);
89         measure->signal_changed.connect(sigc::mem_fun(this, &Designer::measure_changed));
90         measure->signal_done.connect(sigc::mem_fun(this, &Designer::measure_done));
91 }
92
93 Designer::~Designer()
94 {
95         delete manipulator;
96         delete selection;
97         delete layout;
98         delete layout_3d;
99         delete cat_layout;
100         delete cat_layout_3d;
101         delete measure;
102 }
103
104 int Designer::main()
105 {
106         dpy=new Graphics::Display;
107         wnd=new Graphics::Window(*dpy, screen_w, screen_h);
108         glc=new Graphics::GLContext(*wnd);
109
110         wnd->signal_close.connect(sigc::bind(sigc::mem_fun(this, &Designer::exit), 0));
111         wnd->signal_key_press.connect(sigc::mem_fun(this, &Designer::key_press));
112         wnd->signal_key_release.connect(sigc::mem_fun(this, &Designer::key_release));
113         wnd->signal_button_press.connect(sigc::mem_fun(this, &Designer::button_press));
114         wnd->signal_pointer_motion.connect(sigc::mem_fun(this, &Designer::pointer_motion));
115
116         wnd->show();
117
118         glEnableClientState(GL_VERTEX_ARRAY);
119         glEnable(GL_DEPTH_TEST);
120         glEnable(GL_BLEND);
121         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
122         glEnable(GL_CULL_FACE);
123
124         GL::Texture2D *font_tex=new GL::Texture2D;
125         font_tex->set_min_filter(GL::LINEAR);
126         font_tex->load_image("dejavu-20.png");
127         font=new GL::Font();
128         font->set_texture(*font_tex);
129         DataFile::load(*font, "dejavu-20.font");
130
131         mode=SELECT;
132
133         Application::main();
134
135         delete font;
136         delete font_tex;
137         delete input;
138
139         delete glc;
140         delete wnd;
141         delete dpy;
142
143         return exit_code;
144 }
145
146 void Designer::map_pointer_coords(int x, int y, float &gx, float &gy)
147 {
148         float cos_pitch=cos(cam_pitch);
149         float sin_pitch=sin(cam_pitch);
150         float cos_yaw=cos(cam_yaw);
151         float sin_yaw=sin(cam_yaw);
152
153         float rx=sin_yaw*0.55228;
154         float ry=-cos_yaw*0.55228;
155
156         float ux=cos_yaw*-sin_pitch*0.41421;
157         float uy=sin_yaw*-sin_pitch*0.41421;
158         float uz=cos_pitch*0.41421;
159
160         float xf=static_cast<float>(x)*2/screen_w-1;
161         float yf=1-static_cast<float>(y)*2/screen_h;
162
163         float vx=cos_yaw*cos_pitch + xf*rx + yf*ux;
164         float vy=sin_yaw*cos_pitch + xf*ry + yf*uy;
165         float vz=sin_pitch + yf*uz;
166
167         gx=cam_pos.x-vx*cam_pos.z/vz;
168         gy=cam_pos.y-vy*cam_pos.z/vz;
169 }
170
171 void Designer::tick()
172 {
173         const Msp::Time::TimeStamp t=Msp::Time::now();
174         float dt=(t-last_tick)/Msp::Time::sec;
175         last_tick=t;
176
177         dpy->tick();
178
179         if(move_y)
180         {
181                 cam_pos.x+=cos(cam_yaw)*dt*move_y;
182                 cam_pos.y+=sin(cam_yaw)*dt*move_y;
183         }
184         if(move_x)
185         {
186                 cam_pos.x+=sin(cam_yaw)*dt*move_x;
187                 cam_pos.y+=-cos(cam_yaw)*dt*move_x;
188         }
189         if(zoom)
190         {
191                 cam_pos.x+=cos(cam_yaw)*cos(cam_pitch)*dt*zoom;
192                 cam_pos.y+=sin(cam_yaw)*cos(cam_pitch)*dt*zoom;
193                 cam_pos.z+=sin(cam_pitch)*dt*zoom;
194         }
195         if(rotate)
196         {
197                 float vx=cos(cam_yaw)*cos(cam_pitch);
198                 float vy=sin(cam_yaw)*cos(cam_pitch);
199                 float vz=sin(cam_pitch);
200
201                 float gx=cam_pos.x-vx*cam_pos.z/vz;
202                 float gy=cam_pos.y-vy*cam_pos.z/vz;
203                 float d=sqrt(vx*vx+vy*vy)*cam_pos.z/vz;
204
205                 cam_yaw+=M_PI*dt*rotate;
206                 if(cam_yaw>M_PI*2)
207                         cam_yaw-=M_PI*2;
208                 else if(cam_yaw<0)
209                         cam_yaw+=M_PI*2;
210
211                 cam_pos.x=gx+cos(cam_yaw)*d;
212                 cam_pos.y=gy+sin(cam_yaw)*d;
213         }
214         if(pitch)
215         {
216                 cam_pitch+=M_PI/2*dt*pitch;
217                 if(cam_pitch>M_PI/12)
218                         cam_pitch=M_PI/12;
219                 else if(cam_pitch<-M_PI/2)
220                         cam_pitch=-M_PI/2;
221         }
222
223         if(tooltip_timeout && t>tooltip_timeout)
224         {
225                 Track3D *t3d=0;
226
227                 if(mode==CATALOGUE)
228                         t3d=pick_track(pointer_x, pointer_y);
229                 else
230                         t3d=pick_track(pointer_x, pointer_y);
231
232                 if(t3d)
233                 {
234                         const Track &track=t3d->get_track();
235                         const TrackType &ttype=track.get_type();
236                         ostringstream ss;
237                         ss.precision(2);
238                         ss<<ttype.get_article_number()<<' '<<ttype.get_description();
239                         if(mode!=CATALOGUE)
240                                 ss<<" (slope "<<track.get_slope()/ttype.get_total_length()*100<<"%)";
241                         if(track.get_turnout_id())
242                                 ss<<" (turnout "<<track.get_turnout_id()<<')';
243                         else if(track.get_sensor_id())
244                                 ss<<" (sensor "<<track.get_sensor_id()<<')';
245                         tooltip=ss.str();
246
247                         move_tooltip(pointer_x, pointer_y);
248                 }
249                 else
250                         tooltip="";
251
252                 tooltip_timeout=Msp::Time::TimeStamp();
253         }
254
255         render();
256
257         glc->swap_buffers();
258 }
259
260 /*** private ***/
261
262 void Designer::key_press(unsigned code, unsigned mod, wchar_t ch)
263 {
264         unsigned key=Msp::Input::key_from_sys(code);
265
266         if(mode==INPUT)
267         {
268                 input->key_press(key, mod, ch);
269                 return;
270         }
271
272         if(key==Msp::Input::KEY_SHIFT_L || key==Msp::Input::KEY_SHIFT_R)
273                 shift=true;
274
275         if(key==Msp::Input::KEY_N)
276                 mode=CATALOGUE;
277         else if(key==Msp::Input::KEY_G)
278         {
279                 manipulator->start_move();
280                 mode=MANIPULATE;
281         }
282         else if(key==Msp::Input::KEY_R)
283         {
284                 manipulator->start_rotate();
285                 mode=MANIPULATE;
286         }
287         else if(key==Msp::Input::KEY_D)
288         {
289                 manipulator->duplicate();
290                 manipulator->start_move();
291                 mode=MANIPULATE;
292         }
293         else if(key==Msp::Input::KEY_W)
294         {
295                 input=new ::Input(*this, "Filename");
296                 input->signal_cancel.connect(sigc::mem_fun(this, &Designer::input_dismiss));
297                 input->signal_accept.connect(sigc::mem_fun(this, &Designer::save_accept));
298                 mode=INPUT;
299         }
300         else if(key==Msp::Input::KEY_PLUS)
301                 selection->select_more();
302         else if(key==Msp::Input::KEY_L && (mod&1))
303         {
304                 const set<Track *> &tracks=layout->get_tracks();
305                 float len=0;
306                 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
307                         len+=(*i)->get_type().get_total_length();
308                 cout<<"Total length: "<<len<<"m\n";
309         }
310         else if(key==Msp::Input::KEY_L)
311                 selection->select_linked();
312         else if(key==Msp::Input::KEY_M)
313         {
314                 measure->start();
315                 mode=MEASURE;
316         }
317         else if(key==Msp::Input::KEY_Z)
318         {
319                 manipulator->start_elevate();
320                 mode=MANIPULATE;
321         }
322         else if(key==Msp::Input::KEY_ESC)
323         {
324                 if(mode==MANIPULATE)
325                         manipulator->cancel();
326                 else if(mode==CATALOGUE)
327                         mode=SELECT;
328                 else
329                         selection->clear();
330         }
331         else if(key==Msp::Input::KEY_X)
332         {
333                 set<Track *> tracks=selection->get_tracks();
334                 selection->clear();
335                 for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
336                 {
337                         layout->remove_track(**i);
338                         delete *i;
339                 }
340         }
341         else if(key==Msp::Input::KEY_F && (mod&1))
342         {
343                 const set<Track *> &tracks=selection->get_tracks();
344                 const set<Track *> &ltracks=layout->get_tracks();
345                 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
346                 {
347                         (*i)->set_flex(!(*i)->get_flex());
348                         (*i)->break_links();
349                         for(set<Track *>::const_iterator j=ltracks.begin(); j!=ltracks.end(); ++j)
350                                 if(*j!=*i)
351                                         (*i)->snap_to(**j, true);
352
353                         Track3D &t3d=layout_3d->get_track(**i);
354                         if((*i)->get_flex())
355                                 t3d.set_color(GL::Color(1, 0.5, 1));
356                         else
357                                 t3d.set_color(GL::Color(1, 1, 1));
358                 }
359         }
360         else if(key==Msp::Input::KEY_F)
361                 manipulator->flatten();
362         else if(key==Msp::Input::KEY_E && (mod&1))
363                 manipulator->even_slope(true);
364         else if(key==Msp::Input::KEY_E)
365                 manipulator->even_slope();
366         else if(key==Msp::Input::KEY_T)
367         {
368                 Track *track=selection->get_track();
369                 if(selection->size()==1 && track->get_type().get_n_routes()>1)
370                 {
371                         ostringstream ss;
372                         ss<<track->get_turnout_id();
373                         input=new ::Input(*this, "Turnout ID", ss.str());
374                         input->signal_cancel.connect(sigc::mem_fun(this, &Designer::input_dismiss));
375                         input->signal_accept.connect(sigc::mem_fun(this, &Designer::turnout_id_accept));
376                         mode=INPUT;
377                 }
378         }
379         else if(key==Msp::Input::KEY_S)
380         {
381                 Track *track=selection->get_track();
382                 if(selection->size()==1 && track->get_type().get_n_routes()==1)
383                 {
384                         ostringstream ss;
385                         ss<<track->get_sensor_id();
386                         input=new ::Input(*this, "Sensor ID", ss.str());
387                         input->signal_cancel.connect(sigc::mem_fun(this, &Designer::input_dismiss));
388                         input->signal_accept.connect(sigc::mem_fun(this, &Designer::sensor_id_accept));
389                         mode=INPUT;
390                 }
391         }
392         else if(key==Msp::Input::KEY_RIGHT)
393                 rotate=-1;
394         else if(key==Msp::Input::KEY_LEFT)
395                 rotate=1;
396         else if(key==Msp::Input::KEY_UP)
397                 move_y=1;
398         else if(key==Msp::Input::KEY_DOWN)
399                 move_y=-1;
400         else if(key==Msp::Input::KEY_INSERT)
401                 zoom=-1;
402         else if(key==Msp::Input::KEY_PGUP)
403                 zoom=1;
404         else if(key==Msp::Input::KEY_HOME)
405                 pitch=1;
406         else if(key==Msp::Input::KEY_END)
407                 pitch=-1;
408         else if(key==Msp::Input::KEY_DELETE)
409                 move_x=-1;
410         else if(key==Msp::Input::KEY_PGDN)
411                 move_x=1;
412 }
413
414 void Designer::key_release(unsigned code, unsigned)
415 {
416         unsigned key=Msp::Input::key_from_sys(code);
417
418         if(mode==INPUT)
419                 return;
420
421         if(key==Msp::Input::KEY_SHIFT_L || key==Msp::Input::KEY_SHIFT_R)
422                 shift=false;
423         else if(key==Msp::Input::KEY_RIGHT || key==Msp::Input::KEY_LEFT)
424                 rotate=0;
425         else if(key==Msp::Input::KEY_UP || key==Msp::Input::KEY_DOWN)
426                 move_y=0;
427         else if(key==Msp::Input::KEY_INSERT || key==Msp::Input::KEY_PGUP)
428                 zoom=0;
429         else if(key==Msp::Input::KEY_HOME || key==Msp::Input::KEY_END)
430                 pitch=0;
431         else if(key==Msp::Input::KEY_DELETE || key==Msp::Input::KEY_PGDN)
432                 move_x=0;
433 }
434
435 void Designer::button_press(int x, int y, unsigned btn, unsigned)
436 {
437         y=screen_h-y-1;
438
439         float gx, gy;
440         map_pointer_coords(x, y, gx, gy);
441
442         if(mode==CATALOGUE)
443         {
444                 if(btn==1)
445                 {
446                         Track3D *ctrack=pick_track(x, y);
447                         if(ctrack)
448                         {
449                                 Track *track=ctrack->get_track().copy();
450                                 track->set_position(Point(gx, gy, 0));
451                                 layout->add_track(*track);
452
453                                 selection->clear();
454                                 selection->add_track(track);
455
456                                 mode=SELECT;
457                         }
458                 }
459                 else
460                         mode=SELECT;
461         }
462         else if(mode==SELECT)
463         {
464                 if(btn==1)
465                 {
466                         Track3D *track=pick_track(x, y);
467                         if(track)
468                         {
469                                 if(!shift)
470                                         selection->clear();
471                                 selection->toggle_track(&track->get_track());
472                         }
473                 }
474         }
475         else if(mode==MANIPULATE)
476                 manipulator->button_press(x, y, gx, gy, btn);
477         else if(mode==MEASURE)
478                 measure->button_press(x, y, gx, gy, btn);
479 }
480
481 void Designer::pointer_motion(int x, int y)
482 {
483         y=screen_h-y-1;
484
485         float gx, gy;
486         map_pointer_coords(x, y, gx, gy);
487
488         if(mode==SELECT || mode==CATALOGUE)
489         {
490                 pointer_x=x;
491                 pointer_y=y;
492                 tooltip_timeout=Msp::Time::now()+100*Msp::Time::msec;
493         }
494
495         if(mode!=INPUT)
496         {
497                 manipulator->pointer_motion(x, y, gx, gy);
498                 measure->pointer_motion(x, y, gx, gy);
499         }
500
501         if(mode==MEASURE || mode==MANIPULATE)
502                 move_tooltip(x, y);
503 }
504
505 void Designer::project_3d()
506 {
507         glMatrixMode(GL_PROJECTION);
508         glLoadIdentity();
509         glFrustum(-0.055228, 0.055228, -0.041421, 0.041421, 0.1, 10);
510         glMatrixMode(GL_MODELVIEW);
511 }
512
513 void Designer::apply_camera()
514 {
515         glLoadIdentity();
516         if(mode==CATALOGUE)
517                 glTranslatef(0, 0, -1);
518         else
519         {
520                 glRotatef(-cam_pitch*180/M_PI-90, 1, 0, 0);
521                 glRotatef(90-cam_yaw*180/M_PI, 0, 0, 1);
522                 glTranslatef(-cam_pos.x, -cam_pos.y, -cam_pos.z);
523         }
524 }
525
526 void Designer::render()
527 {
528         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
529         glEnable(GL_DEPTH_TEST);
530
531         project_3d();
532         apply_camera();
533         if(mode==CATALOGUE)
534                 cat_layout_3d->render();
535         else
536         {
537                 layout_3d->render(true);
538                 manipulator->render();
539                 if(mode==MEASURE)
540                         measure->render();
541         }
542
543         glMatrixMode(GL_PROJECTION);
544         glLoadIdentity();
545         glOrtho(0, screen_w, 0, screen_h, 0, 1);
546         glMatrixMode(GL_MODELVIEW);
547         glLoadIdentity();
548
549         glDisable(GL_DEPTH_TEST);
550
551         if(!tooltip.empty())
552         {
553                 glTranslatef(tooltip_x, tooltip_y, 0);
554                 glScalef(20, 20, 20);
555                 float width=font->get_string_width(tooltip);
556                 glColor4f(0, 0, 0, 0.5);
557                 glBegin(GL_QUADS);
558                 glVertex2f(0, 0);
559                 glVertex2f(width, 0);
560                 glVertex2f(width, 1);
561                 glVertex2f(0, 1);
562                 glEnd();
563                 glColor4f(1, 1, 1, 1);
564                 font->draw_string(tooltip);
565         }
566
567         if(mode==INPUT)
568                 input->render();
569 }
570
571 Track3D *Designer::pick_track(int x, int y)
572 {
573         Layout3D *l=layout_3d;
574         if(mode==CATALOGUE)
575                 l=cat_layout_3d;
576
577         float xx=(static_cast<float>(x-static_cast<int>(screen_w)/2)/screen_h)*0.82843;
578         float yy=(static_cast<float>(y)/screen_h-0.5)*0.82843;
579         float size=4.0/screen_h*0.82843;
580
581         project_3d();
582         apply_camera();
583
584         return l->pick_track(xx, yy, size);
585 }
586
587 void Designer::manipulation_status(const string &status)
588 {
589         tooltip=status;
590 }
591
592 void Designer::manipulation_done(bool)
593 {
594         mode=SELECT;
595 }
596
597 void Designer::measure_changed()
598 {
599         float pard=measure->get_parallel_distance()*1000;
600         float perpd=measure->get_perpendicular_distance()*1000;
601         float d=sqrt(pard*pard+perpd*perpd);
602         float adiff=measure->get_angle_difference()*180/M_PI;
603         ostringstream ss;
604         ss.precision(3);
605         ss<<"Par "<<pard<<"mm - Perp "<<perpd<<"mm - Total "<<d<<"mm - Angle "<<adiff<<"°";
606         tooltip=ss.str();
607 }
608
609 void Designer::measure_done()
610 {
611         mode=SELECT;
612 }
613
614 void Designer::move_tooltip(int x, int y)
615 {
616         int w=static_cast<int>(font->get_string_width(tooltip)*20);
617         tooltip_x=max(min(static_cast<int>(screen_w)-w, x), 0);
618         tooltip_y=max(min(static_cast<int>(screen_h)-20, y), 0);
619 }
620
621 void Designer::save_accept()
622 {
623         layout->save(input->get_text());
624
625         input_dismiss();
626 }
627
628 void Designer::turnout_id_accept()
629 {
630         Track *track=selection->get_track();
631         unsigned id=lexical_cast<unsigned>(input->get_text());
632         track->set_turnout_id(id);
633
634         Track3D &t3d=layout_3d->get_track(*track);
635         if(id)
636                 t3d.set_color(GL::Color(0.5, 1, 1));
637         else
638                 t3d.set_color(GL::Color(1, 1, 1));
639
640         input_dismiss();
641 }
642
643 void Designer::sensor_id_accept()
644 {
645         Track *track=selection->get_track();
646         unsigned id=lexical_cast<unsigned>(input->get_text());
647         track->set_sensor_id(id);
648
649         Track3D &t3d=layout_3d->get_track(*track);
650         if(id)
651                 t3d.set_color(GL::Color(1, 1, 0.5));
652         else
653                 t3d.set_color(GL::Color(1, 1, 1));
654
655         input_dismiss();
656 }
657
658 void Designer::input_dismiss()
659 {
660         delete input;
661         input=0;
662         mode=SELECT;
663 }
664
665 Application::RegApp<Designer> Designer::reg;