]> git.tdb.fi Git - r2c2.git/blob - source/designer/designer.cpp
f79ef7a9ae8daa4bfdb94a901ba3b6d170412b9c
[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 <GL/gl.h>
11 #include <msp/gl/blend.h>
12 #include <msp/gl/framebuffer.h>
13 #include <msp/gl/matrix.h>
14 #include <msp/gl/misc.h>
15 #include <msp/gl/projection.h>
16 #include <msp/gl/rendermode.h>
17 #include <msp/gl/select.h>
18 #include <msp/gl/tests.h>
19 #include <msp/gl/texture2d.h>
20 #include <msp/input/keys.h>
21 #include <msp/io/print.h>
22 #include <msp/strings/codec.h>
23 #include <msp/strings/lexicalcast.h>
24 #include <msp/strings/utf8.h>
25 #include <msp/strings/utils.h>
26 #include <msp/time/units.h>
27 #include <msp/time/utils.h>
28 #include "libmarklin/tracktype.h"
29 #include "designer.h"
30 #include "input.h"
31 #include "manipulator.h"
32 #include "measure.h"
33 #include "selection.h"
34 #include "toolbar.h"
35
36 using namespace std;
37 using namespace Marklin;
38 using namespace Msp;
39
40 Application::RegApp<Designer> Designer::reg;
41
42 Designer::Designer(int argc, char **argv):
43         window(1280, 960),
44         base_object(0),
45         cur_route(0),
46         mode(SELECT),
47         manipulator(*this, selection),
48         measure(*this),
49         input(0),
50         camera_ctl(window, camera),
51         shift(false)
52 {
53         window.set_title("Railway Designer");
54         window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &Designer::exit), 0));
55         window.signal_key_press.connect(sigc::mem_fun(this, &Designer::key_press));
56         window.signal_key_release.connect(sigc::mem_fun(this, &Designer::key_release));
57         window.signal_button_press.connect(sigc::mem_fun(this, &Designer::button_press));
58         window.signal_pointer_motion.connect(sigc::mem_fun(this, &Designer::pointer_motion));
59
60         manipulator.signal_status.connect(sigc::mem_fun(this, &Designer::manipulation_status));
61         manipulator.signal_done.connect(sigc::mem_fun(this, &Designer::manipulation_done));
62         measure.signal_changed.connect(sigc::mem_fun(this, &Designer::measure_changed));
63         measure.signal_done.connect(sigc::mem_fun(this, &Designer::measure_done));
64
65         // Setup catalogue and layout
66         DataFile::load(catalogue, "tracks.dat");
67
68         cat_layout_3d = new Layout3D(catalogue.get_layout());
69
70         layout = new Layout(catalogue);
71         layout_3d = new Layout3D(*layout);
72
73         if(argc>1)
74         {
75                 filename = argv[1];
76                 DataFile::load(*layout, argv[1]);
77                 const list<Track3D *> &ltracks = layout_3d->get_tracks();
78                 for(list<Track3D *>::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
79                         update_track_color(**i);
80
81                 if(!layout->get_base().empty())
82                 {
83                         base_object = new GL::Object;
84                         DataFile::load(*base_object, layout->get_base());
85                 }
86         }
87
88         // Setup OpenGL
89         GL::enable(GL::DEPTH_TEST);
90         GL::enable(GL::BLEND);
91         GL::blend_func(GL::SRC_ALPHA, GL::ONE_MINUS_SRC_ALPHA);
92         GL::enable(GL_CULL_FACE);
93
94         pipeline = new GL::Pipeline(window.get_width(), window.get_height(), false);
95         pipeline->set_camera(&camera);
96         pipeline->add_renderable(layout_3d->get_scene());
97         if(base_object)
98                 pipeline->add_renderable(*base_object);
99
100         light.set_position(0, -0.259, 0.966, 0);
101         lighting.attach(0, light);
102
103         GL::PipelinePass *pass = &pipeline->add_pass(GL::Tag());
104         pass->lighting = &lighting;
105
106         camera.set_up_direction(GL::Vector3(0, 0, 1));
107         view_all();
108
109         // Setup UI
110         DataFile::load(ui_res, "marklin.res");
111         root = new GLtk::Root(ui_res, window);
112
113         lbl_tooltip = new GLtk::Label(ui_res);
114         lbl_tooltip->set_style("tooltip");
115         root->add(*lbl_tooltip);
116         lbl_tooltip->set_visible(false);
117
118         toolbar = new Toolbar(*this);
119         root->add(*toolbar);
120         toolbar->set_position(0, window.get_height()-toolbar->get_geometry().h);
121         toolbar->set_visible(true);
122 }
123
124 Designer::~Designer()
125 {
126         delete root;
127         delete layout;
128         delete layout_3d;
129         delete cat_layout_3d;
130 }
131
132 int Designer::main()
133 {
134         window.show();
135
136         mode = SELECT;
137
138         return Application::main();
139 }
140
141 void Designer::save()
142 {
143         input = new ::Input(*this, "Save layout", filename);
144         input->signal_cancel.connect(sigc::mem_fun(this, &Designer::input_dismiss));
145         input->signal_accept.connect(sigc::mem_fun(this, &Designer::save_accept));
146         mode = INPUT;
147 }
148
149 void Designer::quit()
150 {
151         exit(0);
152 }
153
154 void Designer::edit_route(Route &r)
155 {
156         cur_route = &r;
157 }
158
159 void Designer::add_selection_to_route()
160 {
161         if(!cur_route)
162                 return;
163
164         try
165         {
166                 const set<Track *> &stracks = selection.get_tracks();
167                 set<const Track *> tracks(stracks.begin(), stracks.end());
168                 cur_route->add_tracks(tracks);
169         }
170         catch(const Exception &e)
171         {
172                 IO::print("%s\n", e.what());
173         }
174 }
175
176 Point Designer::map_pointer_coords(int x, int y)
177 {
178         float xf = x*2.0/window.get_width()-1.0;
179         float yf = y*2.0/window.get_height()-1.0;
180         GL::Vector4 vec = camera.unproject(GL::Vector4(xf, yf, 0, 0));
181         const GL::Vector3 &pos = camera.get_position();
182
183         return Point(pos.x-vec.x*pos.z/vec.z, pos.y-vec.y*pos.z/vec.z);
184 }
185
186 void Designer::tick()
187 {
188         const Msp::Time::TimeStamp t = Msp::Time::now();
189         float dt = (t-last_tick)/Msp::Time::sec;
190         last_tick = t;
191
192         window.get_display().tick();
193         camera_ctl.tick(dt);
194
195         if(tooltip_timeout && t>tooltip_timeout)
196         {
197                 Track3D *t3d = 0;
198
199                 if(mode==CATALOGUE)
200                         t3d = pick_track(pointer_x, pointer_y);
201                 else
202                         t3d = pick_track(pointer_x, pointer_y);
203
204                 if(t3d)
205                 {
206                         const Track &track = t3d->get_track();
207                         const TrackType &ttype = track.get_type();
208                         string info = format("%d %s", ttype.get_article_number(), ttype.get_description());
209                         if(mode!=CATALOGUE && abs(track.get_slope())>1e-4)
210                                 info += format(" (slope %.1f%%)", abs(track.get_slope()/ttype.get_total_length()*100));
211                         if(track.get_turnout_id())
212                                 info += format(" (turnout %d)", track.get_turnout_id());
213                         else if(track.get_sensor_id())
214                                 info += format(" (sensor %d)", track.get_sensor_id());
215
216                         set_tooltip(pointer_x, pointer_y, info);
217                 }
218                 else
219                         clear_tooltip();
220
221                 tooltip_timeout = Msp::Time::TimeStamp();
222         }
223
224         render();
225
226         window.swap_buffers();
227 }
228
229 void Designer::key_press(unsigned code, unsigned mod, wchar_t)
230 {
231         unsigned key = Msp::Input::key_from_sys(code);
232
233         if(mode==INPUT)
234                 return;
235
236         if(key==Msp::Input::KEY_SHIFT_L || key==Msp::Input::KEY_SHIFT_R)
237                 shift = true;
238
239         if(key==Msp::Input::KEY_N)
240                 mode = CATALOGUE;
241         else if(key==Msp::Input::KEY_G)
242         {
243                 manipulator.start_move();
244                 mode = MANIPULATE;
245         }
246         else if(key==Msp::Input::KEY_R)
247         {
248                 manipulator.start_rotate();
249                 mode = MANIPULATE;
250         }
251         else if(key==Msp::Input::KEY_D)
252         {
253                 manipulator.duplicate();
254                 manipulator.start_move();
255                 mode = MANIPULATE;
256         }
257         else if(key==Msp::Input::KEY_W)
258                 save();
259         else if(key==Msp::Input::KEY_PLUS)
260                 selection.select_more();
261         else if(key==Msp::Input::KEY_L && (mod&1))
262         {
263                 const set<Track *> &tracks = layout->get_tracks();
264                 float len = 0;
265                 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
266                         len += (*i)->get_type().get_total_length();
267                 IO::print("Total length: %.1fm\n", len);
268         }
269         else if(key==Msp::Input::KEY_L)
270                 selection.select_linked();
271         else if(key==Msp::Input::KEY_M)
272         {
273                 measure.start();
274                 mode = MEASURE;
275         }
276         else if(key==Msp::Input::KEY_Z)
277         {
278                 manipulator.start_elevate();
279                 mode = MANIPULATE;
280         }
281         else if(key==Msp::Input::KEY_ESC)
282         {
283                 if(mode==MANIPULATE)
284                         manipulator.cancel();
285                 else if(mode==CATALOGUE)
286                         mode = SELECT;
287                 else
288                         selection.clear();
289         }
290         else if(key==Msp::Input::KEY_X)
291         {
292                 set<Track *> tracks = selection.get_tracks();
293                 selection.clear();
294                 for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
295                 {
296                         layout->remove_track(**i);
297                         delete *i;
298                 }
299         }
300         else if(key==Msp::Input::KEY_F && (mod&1))
301         {
302                 const set<Track *> &tracks = selection.get_tracks();
303                 const set<Track *> &ltracks = layout->get_tracks();
304                 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
305                 {
306                         (*i)->set_flex(!(*i)->get_flex());
307                         (*i)->break_links();
308                         for(set<Track *>::const_iterator j=ltracks.begin(); j!=ltracks.end(); ++j)
309                                 if(*j!=*i)
310                                         (*i)->snap_to(**j, true);
311
312                         update_track_color(layout_3d->get_track(**i));
313                 }
314         }
315         else if(key==Msp::Input::KEY_F)
316                 manipulator.flatten();
317         else if(key==Msp::Input::KEY_E && (mod&1))
318                 manipulator.even_slope(true);
319         else if(key==Msp::Input::KEY_E)
320                 manipulator.even_slope();
321         else if(key==Msp::Input::KEY_T)
322         {
323                 Track *track = selection.get_track();
324                 if(selection.size()==1 && track->get_type().get_n_paths()>1)
325                 {
326                         input = new ::Input(*this, "Turnout ID", lexical_cast(track->get_turnout_id()));
327                         input->signal_cancel.connect(sigc::mem_fun(this, &Designer::input_dismiss));
328                         input->signal_accept.connect(sigc::mem_fun(this, &Designer::turnout_id_accept));
329                         mode = INPUT;
330                 }
331         }
332         else if(key==Msp::Input::KEY_S)
333         {
334                 const set<Track *> &tracks = selection.get_tracks();
335                 bool ok = false;
336                 int id = -1;
337                 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
338                 {
339                         if((*i)->get_type().get_n_paths()==1)
340                                 ok = true;
341                         if(static_cast<int>((*i)->get_sensor_id())!=id)
342                         {
343                                 if(id==-1)
344                                         id = (*i)->get_sensor_id();
345                                 else
346                                         id = -2;
347                         }
348                 }
349                 if(ok)
350                 {
351                         input = new ::Input(*this, "Sensor ID", (id>=0 ? lexical_cast(id) : string()));
352                         input->signal_cancel.connect(sigc::mem_fun(this, &Designer::input_dismiss));
353                         input->signal_accept.connect(sigc::mem_fun(this, &Designer::sensor_id_accept));
354                         mode = INPUT;
355                 }
356         }
357         else if(key==Msp::Input::KEY_A)
358                 add_selection_to_route();
359 }
360
361 void Designer::key_release(unsigned code, unsigned)
362 {
363         unsigned key = Msp::Input::key_from_sys(code);
364
365         if(mode==INPUT)
366                 return;
367
368         if(key==Msp::Input::KEY_SHIFT_L || key==Msp::Input::KEY_SHIFT_R)
369                 shift = false;
370 }
371
372 void Designer::button_press(int x, int y, unsigned btn, unsigned)
373 {
374         y = window.get_height()-y-1;
375
376         Point ground = map_pointer_coords(x, y);
377
378         if(mode==CATALOGUE)
379         {
380                 if(btn==1)
381                 {
382                         Track3D *ctrack = pick_track(x, y);
383                         if(ctrack)
384                         {
385                                 Track *track = ctrack->get_track().copy();
386                                 track->set_position(ground);
387                                 layout->add_track(*track);
388
389                                 selection.clear();
390                                 selection.add_track(track);
391
392                                 mode = SELECT;
393                         }
394                 }
395                 else
396                         mode = SELECT;
397         }
398         else if(mode==SELECT)
399         {
400                 if(btn==1)
401                 {
402                         Track3D *track = pick_track(x, y);
403                         if(track)
404                         {
405                                 if(!shift)
406                                         selection.clear();
407                                 selection.toggle_track(&track->get_track());
408                         }
409                 }
410         }
411         else if(mode==MANIPULATE)
412                 manipulator.button_press(x, y, ground.x, ground.y, btn);
413         else if(mode==MEASURE)
414                 measure.button_press(x, y, ground.x, ground.y, btn);
415 }
416
417 void Designer::pointer_motion(int x, int y)
418 {
419         y = window.get_height()-y-1;
420
421         pointer_x = x;
422         pointer_y = y;
423
424         if(mode==SELECT || mode==CATALOGUE)
425                 tooltip_timeout = Msp::Time::now()+100*Msp::Time::msec;
426
427         clear_tooltip();
428
429         if(mode!=INPUT)
430         {
431                 Point ground = map_pointer_coords(x, y);
432                 manipulator.pointer_motion(x, y, ground.x, ground.y);
433                 measure.pointer_motion(x, y, ground.x, ground.y);
434         }
435 }
436
437 void Designer::apply_camera()
438 {
439         if(mode==CATALOGUE)
440         {
441                 GL::matrix_mode(GL::PROJECTION);
442                 GL::load_identity();
443                 GL::frustum_centered(0.11046, 0.082843, 0.1, 10);
444                 GL::matrix_mode(GL::MODELVIEW);
445                 GL::load_identity();
446                 GL::translate(0, 0, -1);
447         }
448         else
449                 camera.apply();
450 }
451
452 void Designer::render()
453 {
454         GL::clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT);
455         GL::enable(GL::DEPTH_TEST);
456         GL::Texture::unbind();
457
458         if(mode==CATALOGUE)
459         {
460                 apply_camera();
461                 cat_layout_3d->get_scene().render();
462         }
463         else
464         {
465                 pipeline->render_all();
466                 layout_3d->get_endpoint_scene().render();
467                 GL::enable(GL_CULL_FACE);
468                 /*if(cur_route)
469                 {
470                         glColor4f(0.5, 0.8, 1.0, 1.0);
471                         const set<const Track *> &rtracks = cur_route->get_tracks();
472                         const map<unsigned, int> &turnouts = cur_route->get_turnouts();
473                         for(set<const Track *>::const_iterator i=rtracks.begin(); i!=rtracks.end(); ++i)
474                         {
475                                 unsigned path = 0;
476                                 if(unsigned tid=(*i)->get_turnout_id())
477                                 {
478                                         map<unsigned, int>::const_iterator j = turnouts.find(tid);
479                                         if(j!=turnouts.end())
480                                                 path = j->second;
481                                 }
482                                 layout_3d->get_track(**i).render_path(path);
483                         }
484                 }*/
485
486                 manipulator.render();
487                 if(mode==MEASURE)
488                         measure.render();
489         }
490
491         GL::matrix_mode(GL::PROJECTION);
492         GL::load_identity();
493         GL::ortho_bottomleft(window.get_width(), window.get_height());
494         GL::matrix_mode(GL::MODELVIEW);
495         GL::load_identity();
496
497         GL::disable(GL::DEPTH_TEST);
498
499         root->render();
500 }
501
502 Track3D *Designer::pick_track(int x, int y)
503 {
504         Layout3D *l = layout_3d;
505         if(mode==CATALOGUE)
506                 l = cat_layout_3d;
507
508         float xx = ((float(x)-window.get_width()/2)/window.get_height())*0.82843;
509         float yy = (float(y)/window.get_height()-0.5)*0.82843;
510         float size = 4.0/window.get_height()*0.82843;
511
512         apply_camera();
513
514         return l->pick_track(xx, yy, size);
515 }
516
517 void Designer::update_track_color(Track3D &track)
518 {
519         if(track.get_track().get_sensor_id())
520         {
521                 if(track.get_track().get_flex())
522                         track.set_color(GL::Color(1, 0.6, 1));
523                 else
524                         track.set_color(GL::Color(0.7, 0.7, 1));
525         }
526         else if(track.get_track().get_turnout_id())
527                 track.set_color(GL::Color(0.8, 1, 0.8));
528         else if(track.get_track().get_flex())
529                 track.set_color(GL::Color(1, 0.8, 0.8));
530         else
531                 track.set_color(GL::Color(1, 1, 1));
532 }
533
534 void Designer::manipulation_status(const string &status)
535 {
536         set_tooltip(pointer_x, pointer_y, status);
537 }
538
539 void Designer::manipulation_done(bool)
540 {
541         mode = SELECT;
542 }
543
544 void Designer::measure_changed()
545 {
546         float pard = measure.get_parallel_distance()*1000;
547         float perpd = measure.get_perpendicular_distance()*1000;
548         float d = sqrt(pard*pard+perpd*perpd);
549         float adiff = measure.get_angle_difference()*180/M_PI;
550         string info = format("Par %.1fmm - Perp %.1fmm - Total %.1fmm - Angle %.1f°", pard, perpd, d, adiff);
551         set_tooltip(pointer_x, pointer_y, info);
552 }
553
554 void Designer::measure_done()
555 {
556         mode = SELECT;
557 }
558
559 void Designer::set_tooltip(int x, int y, const std::string &text)
560 {
561         int fontsize = ui_res.get_default_font().get_default_size();
562         int h = fontsize+6;
563         int w = static_cast<int>(ui_res.get_default_font().get_string_width(text)*fontsize)+6;
564         x = max(min(static_cast<int>(window.get_width())-w, x), 0);
565         y = max(min(static_cast<int>(window.get_height())-h, y), 0);
566         lbl_tooltip->set_text(text);
567         lbl_tooltip->set_geometry(GLtk::Geometry(x, y, w, h));
568         lbl_tooltip->set_visible(true);
569 }
570
571 void Designer::clear_tooltip()
572 {
573         lbl_tooltip->set_visible(false);
574 }
575
576 void Designer::save_accept()
577 {
578         layout->save(input->get_text());
579
580         input_dismiss();
581 }
582
583 void Designer::turnout_id_accept()
584 {
585         Track *track = selection.get_track();
586         unsigned id = lexical_cast<unsigned>(input->get_text());
587         track->set_turnout_id(id);
588
589         update_track_color(layout_3d->get_track(*track));
590
591         input_dismiss();
592 }
593
594 void Designer::sensor_id_accept()
595 {
596         const set<Track *> &tracks = selection.get_tracks();
597         unsigned id = lexical_cast<unsigned>(input->get_text());
598         for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
599         {
600                 (*i)->set_sensor_id(id);
601
602                 update_track_color(layout_3d->get_track(**i));
603         }
604
605         input_dismiss();
606 }
607
608 void Designer::input_dismiss()
609 {
610         delete input;
611         input = 0;
612         mode = SELECT;
613 }
614
615 void Designer::view_all()
616 {
617         Point minp;
618         Point maxp;
619
620         const list<Track3D *> &tracks = layout_3d->get_tracks();
621         for(list<Track3D *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
622         {
623                 Point tmin;
624                 Point tmax;
625                 (*i)->get_bounds(0, tmin, tmax);
626                 minp.x = min(minp.x, tmin.x);
627                 minp.y = min(minp.y, tmin.y);
628                 maxp.x = max(maxp.x, tmax.x);
629                 maxp.y = max(maxp.y, tmax.y);
630         }
631
632         float t = tan(camera.get_field_of_view()/2)*2;
633         float size = max((maxp.y-minp.y+0.1), (maxp.x-minp.x+0.1)/camera.get_aspect());
634         float cam_dist = size/t+size*0.25;
635         Point center((minp.x+maxp.x)/2, (minp.y+maxp.y)/2);
636         camera.set_position(GL::Vector3(center.x, center.y-cam_dist*0.5, cam_dist*0.866));
637         camera.set_look_direction(GL::Vector3(0, 0.5, -0.866));
638 }