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