]> git.tdb.fi Git - r2c2.git/blob - source/designer/designer.cpp
Strip Id tags and copyright notices from files
[r2c2.git] / source / designer / designer.cpp
1 #include <signal.h>
2 #include <algorithm>
3 #include <cmath>
4 #include <msp/fs/utils.h>
5 #include <msp/gl/blend.h>
6 #include <msp/gl/framebuffer.h>
7 #include <msp/gl/matrix.h>
8 #include <msp/gl/misc.h>
9 #include <msp/gl/projection.h>
10 #include <msp/gl/tests.h>
11 #include <msp/gl/texture2d.h>
12 #include <msp/input/keys.h>
13 #include <msp/io/print.h>
14 #include <msp/strings/codec.h>
15 #include <msp/strings/lexicalcast.h>
16 #include <msp/strings/utf8.h>
17 #include <msp/strings/utils.h>
18 #include <msp/time/units.h>
19 #include <msp/time/utils.h>
20 #include "libr2c2/route.h"
21 #include "libr2c2/tracktype.h"
22 #include "libr2c2/zone.h"
23 #include "3d/path.h"
24 #include "designer.h"
25 #include "input.h"
26 #include "layoutbar.h"
27 #include "manipulator.h"
28 #include "measure.h"
29 #include "routebar.h"
30 #include "selection.h"
31 #include "svgexporter.h"
32 #include "trackbar.h"
33 #include "trackproperties.h"
34 #include "zonebar.h"
35 #include "zoneproperties.h"
36
37 using namespace std;
38 using namespace R2C2;
39 using namespace Msp;
40
41 Application::RegApp<Designer> Designer::reg;
42
43 Designer::Designer(int argc, char **argv):
44         window(1280, 960),
45         ui_res("r2c2.res"),
46         root(ui_res, window),
47         base_object(0),
48         cur_route(0),
49         cur_zone(0),
50         mode(SELECT),
51         manipulator(*this, root, selection),
52         measure(*this)
53 {
54         window.set_title("Railway Designer");
55         window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &Designer::exit), 0));
56
57         selection.signal_changed.connect(sigc::mem_fun(this, &Designer::selection_changed));
58         manipulator.signal_status.connect(sigc::mem_fun(this, &Designer::manipulation_status));
59         manipulator.signal_done.connect(sigc::mem_fun(this, &Designer::manipulation_done));
60         measure.signal_changed.connect(sigc::mem_fun(this, &Designer::measure_changed));
61         measure.signal_done.connect(sigc::mem_fun(this, &Designer::measure_done));
62
63         // Setup catalogue and layout
64         DataFile::load(catalogue, "tracks.dat");
65
66         cat_layout_3d = new Layout3D(catalogue.get_layout());
67
68         layout = new Layout(catalogue);
69         layout_3d = new Layout3D(*layout);
70
71         layout->signal_track_added.connect(sigc::mem_fun(this, &Designer::track_added));
72         layout->signal_track_removed.connect(sigc::mem_fun(this, &Designer::track_removed));
73
74         if(argc>1)
75         {
76                 filename = argv[1];
77                 DataFile::load(*layout, argv[1]);
78
79                 if(!layout->get_base().empty())
80                 {
81                         base_object = new GL::Object;
82                         DataFile::load(*base_object, layout->get_base());
83                 }
84         }
85
86         // Setup OpenGL
87         cat_view = new View3D(*cat_layout_3d, window.get_width(), window.get_height());
88
89         main_view = new View3D(*layout_3d, window.get_width(), window.get_height());
90         GL::Pipeline *pipeline = &main_view->get_pipeline();
91
92         GL::PipelinePass *pass = &pipeline->add_pass("unlit");
93         pass->depth_test = &GL::DepthTest::lequal();
94         pass->blend = &GL::Blend::alpha();
95
96         pass = &pipeline->add_pass("blended");
97         pass->lighting = &layout_3d->get_lighting();
98         pass->depth_test = &GL::DepthTest::lequal();
99         pass->blend = &GL::Blend::alpha();
100
101         pass = &pipeline->add_pass("overlay");
102         pass->blend = &GL::Blend::alpha();
103
104         if(base_object)
105                 pipeline->add_renderable(*base_object);
106         pipeline->add_renderable_for_pass(layout_3d->get_path_scene(), "unlit");
107         pipeline->add_renderable_for_pass(layout_3d->get_endpoint_scene(), "unlit");
108
109         // Setup UI
110         root.signal_key_press.connect(sigc::mem_fun(this, &Designer::key_press));
111         root.signal_button_press.connect(sigc::mem_fun(this, &Designer::button_press));
112         root.signal_pointer_motion.connect(sigc::mem_fun(this, &Designer::pointer_motion));
113         root.signal_tooltip.connect(sigc::mem_fun(this, &Designer::tooltip));
114
115         toolbars.push_back(new Layoutbar(*this));
116         toolbars.push_back(new Trackbar(*this));
117         toolbars.push_back(new Routebar(*this));
118         toolbars.push_back(new Zonebar(*this));
119         for(vector<Toolbar *>::iterator i=toolbars.begin(); i!=toolbars.end(); ++i)
120         {
121                 root.add(**i);
122                 if(i!=toolbars.begin())
123                         (*i)->expand(false);
124                 (*i)->signal_expanded.connect(sigc::hide(sigc::mem_fun(this, &Designer::arrange_toolbars)));
125         }
126         arrange_toolbars();
127
128         GLtk::Panel *statusbar = new GLtk::Panel;
129         root.add(*statusbar);
130         statusbar->set_size(window.get_width(), 20);
131         statusbar->set_focusable(false);
132
133         lbl_status = new GLtk::Label;
134         statusbar->add(*lbl_status);
135         lbl_status->set_geometry(GLtk::Geometry(20, 2, 300, 16));
136
137         track_wrap = new TrackWrap(*layout_3d, selection);
138         pipeline->add_renderable_for_pass(*track_wrap, "unlit");
139         overlay = new Overlay3D(ui_res.get_default_font());
140         pipeline->add_renderable_for_pass(*overlay, "overlay");
141
142         camera_ctl = new CameraController(*main_view, root);
143         cat_view->get_camera().set_look_direction(GL::Vector3(0, 0.13053, -0.99144));
144         cat_view->view_all(true);
145         main_view->view_all();
146
147         const Layout3D::TrackMap &tracks = layout_3d->get_tracks();
148         for(Layout3D::TrackMap::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
149                 update_track_icon(*i->second);
150 }
151
152 Designer::~Designer()
153 {
154         for(vector<Toolbar *>::iterator i=toolbars.begin(); i!=toolbars.end(); ++i)
155                 delete *i;
156         delete camera_ctl;
157         delete track_wrap;
158         delete overlay;
159         delete main_view;
160         delete base_object;
161         delete layout_3d;
162         delete layout;
163         delete cat_view;
164         delete cat_layout_3d;
165 }
166
167 int Designer::main()
168 {
169         window.show();
170
171         mode = SELECT;
172
173         return Application::main();
174 }
175
176 void Designer::save()
177 {
178         InputDialog *input = new InputDialog(*this, "Save layout", filename);
179         input->signal_accept.connect(sigc::mem_fun(layout, &Layout::save));
180 }
181
182 void Designer::quit()
183 {
184         exit(0);
185 }
186
187 void Designer::new_track()
188 {
189         if(mode!=SELECT)
190                 return;
191
192         mode = CATALOGUE;
193         lbl_status->set_text("Select new track or press Esc to cancel");
194 }
195
196 void Designer::erase_tracks()
197 {
198         if(mode!=SELECT)
199                 return;
200
201         set<Track *> tracks = selection.get_tracks();
202         selection.clear();
203         for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
204         {
205                 overlay->clear(layout_3d->get_track(**i));
206                 delete *i;
207         }
208 }
209
210 void Designer::track_properties()
211 {
212         if(mode!=SELECT || selection.empty())
213                 return;
214
215         TrackProperties *track_prop = new TrackProperties(selection);
216         root.add(*track_prop);
217         const GLtk::Geometry &root_geom = root.get_geometry();
218         const GLtk::Geometry &dlg_geom = track_prop->get_geometry();
219         track_prop->set_position((root_geom.w-dlg_geom.w)/2, (root_geom.h-dlg_geom.h)/2);
220
221         track_prop->signal_response.connect(sigc::mem_fun(this, &Designer::track_properties_response));
222 }
223
224 void Designer::extend_track()
225 {
226         if(mode!=SELECT)
227                 return;
228
229         if(manipulator.start_extend())
230                 mode = MANIPULATE;
231 }
232
233 void Designer::connect_tracks()
234 {
235         if(mode!=SELECT)
236                 return;
237
238         manipulator.connect();
239 }
240
241 void Designer::flatten_tracks()
242 {
243         if(mode!=SELECT)
244                 return;
245
246         manipulator.flatten();
247 }
248
249 void Designer::svg_export()
250 {
251         InputDialog *input = new InputDialog(*this, "SVG export", FS::basepart(filename)+".svg");
252         input->signal_accept.connect(sigc::mem_fun(this, &Designer::svg_export_accept));
253 }
254
255 void Designer::edit_route(Route *r)
256 {
257         cur_route = r;
258         cur_zone = 0;
259         if(cur_route)
260                 show_route(*cur_route);
261         else
262                 clear_paths();
263 }
264
265 void Designer::rename_route()
266 {
267         if(mode!=SELECT || !cur_route)
268                 return;
269
270         InputDialog *input = new InputDialog(*this, "Route name", cur_route->get_name());
271         input->signal_accept.connect(sigc::mem_fun(this, &Designer::route_name_accept));
272 }
273
274 void Designer::add_selection_to_route()
275 {
276         if(!cur_route)
277                 return;
278
279         try
280         {
281                 cur_route->add_tracks(selection.get_tracks());
282         }
283         catch(const Exception &e)
284         {
285                 lbl_status->set_text(e.what());
286         }
287
288         show_route(*cur_route);
289 }
290
291 void Designer::edit_zone(Zone *z)
292 {
293         cur_zone = z;
294         cur_route = 0;
295         if(cur_zone)
296                 show_zone(*cur_zone);
297         else
298                 clear_paths();
299 }
300
301 void Designer::zone_properties()
302 {
303         if(!cur_zone)
304                 return;
305
306         ZoneProperties *zone_prop = new ZoneProperties(*cur_zone);
307         root.add(*zone_prop);
308         const GLtk::Geometry &root_geom = root.get_geometry();
309         const GLtk::Geometry &dlg_geom = zone_prop->get_geometry();
310         zone_prop->set_position((root_geom.w-dlg_geom.w)/2, (root_geom.h-dlg_geom.h)/2);
311 }
312
313 void Designer::add_selection_to_zone()
314 {
315         if(!cur_zone)
316                 return;
317
318         try
319         {
320                 cur_zone->add_tracks(selection.get_tracks());
321         }
322         catch(const Exception &e)
323         {
324                 lbl_status->set_text(e.what());
325         }
326
327         show_zone(*cur_zone);
328 }
329
330 Vector Designer::map_pointer_to_ground(int x, int y)
331 {
332         float xf = x*2.0/window.get_width()-1.0;
333         float yf = y*2.0/window.get_height()-1.0;
334         GL::Vector4 vec = main_view->get_camera().unproject(GL::Vector4(xf, yf, 0, 0));
335         const GL::Vector3 &pos = main_view->get_camera().get_position();
336
337         return Vector(pos.x-vec.x*pos.z/vec.z, pos.y-vec.y*pos.z/vec.z);
338 }
339
340 void Designer::tick()
341 {
342         const Msp::Time::TimeStamp t = Msp::Time::now();
343         float dt = (t-last_tick)/Msp::Time::sec;
344         last_tick = t;
345
346         if(mode==MANIPULATE_DONE)
347                 mode = SELECT;
348
349         window.tick();
350         root.tick();
351         camera_ctl->tick(dt);
352
353         for(list<Track *>::iterator i=new_tracks.begin(); i!=new_tracks.end(); ++i)
354                 layout_3d->get_track(**i).get_path().set_mask(0);
355         new_tracks.clear();
356
357         render();
358
359         window.swap_buffers();
360 }
361
362 void Designer::key_press(unsigned key, unsigned mod, wchar_t)
363 {
364         key = Input::key_from_sys(key);
365         mod = Input::mod_from_sys(mod);
366
367         if(key==Msp::Input::KEY_N && (mod&Input::MOD_SHIFT))
368                 extend_track();
369         else if(key==Msp::Input::KEY_N)
370                 new_track();
371         else if(key==Msp::Input::KEY_G)
372         {
373                 manipulator.start_move();
374                 mode = MANIPULATE;
375         }
376         else if(key==Msp::Input::KEY_R)
377         {
378                 manipulator.start_rotate();
379                 mode = MANIPULATE;
380         }
381         else if(key==Msp::Input::KEY_D)
382         {
383                 manipulator.duplicate();
384                 manipulator.start_move();
385                 mode = MANIPULATE;
386         }
387         else if(key==Msp::Input::KEY_W)
388                 save();
389         else if(key==Msp::Input::KEY_PLUS)
390                 selection.select_more();
391         else if(key==Msp::Input::KEY_L && (mod&Input::MOD_SHIFT))
392                 selection.select_blocks();
393         else if(key==Msp::Input::KEY_L)
394                 selection.select_linked();
395         else if(key==Msp::Input::KEY_M)
396         {
397                 measure.start();
398                 mode = MEASURE;
399         }
400         else if(key==Msp::Input::KEY_Z)
401         {
402                 manipulator.start_elevate();
403                 mode = MANIPULATE;
404         }
405         else if(key==Msp::Input::KEY_ESC)
406         {
407                 if(mode==MANIPULATE)
408                         manipulator.cancel();
409                 else if(mode==CATALOGUE)
410                         mode = SELECT;
411                 else
412                         selection.clear();
413         }
414         else if(key==Msp::Input::KEY_X)
415                 erase_tracks();
416         else if(key==Msp::Input::KEY_F && (mod&Input::MOD_SHIFT))
417         {
418                 const set<Track *> &tracks = selection.get_tracks();
419                 const set<Track *> &ltracks = layout->get_tracks();
420                 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
421                 {
422                         (*i)->set_flex(!(*i)->get_flex());
423                         (*i)->break_links();
424                         for(set<Track *>::const_iterator j=ltracks.begin(); j!=ltracks.end(); ++j)
425                                 if(*j!=*i)
426                                         (*i)->snap_to(**j, true);
427
428                         update_track_icon(layout_3d->get_track(**i));
429                 }
430         }
431         else if(key==Msp::Input::KEY_F)
432                 flatten_tracks();
433         else if(key==Msp::Input::KEY_E && (mod&Input::MOD_SHIFT))
434                 manipulator.even_slope(true);
435         else if(key==Msp::Input::KEY_E)
436                 manipulator.even_slope();
437         else if(key==Msp::Input::KEY_A)
438         {
439                 if(cur_route)
440                         add_selection_to_route();
441                 else if(cur_zone)
442                         add_selection_to_zone();
443         }
444         else if(key==Msp::Input::KEY_C)
445                 connect_tracks();
446         else if(key==Msp::Input::KEY_V)
447                 svg_export();
448         else if(key==Msp::Input::KEY_P)
449                 track_properties();
450 }
451
452 void Designer::button_press(int x, int y, unsigned btn, unsigned mod)
453 {
454         y = window.get_height()-y-1;
455         mod = Input::mod_from_sys(mod);
456
457         Vector ground = map_pointer_to_ground(x, y);
458
459         if(mode==CATALOGUE)
460         {
461                 if(btn==1)
462                 {
463                         Track *ctrack = pick_track(x, y);
464                         if(ctrack)
465                         {
466                                 Track *track = new Track(*layout, ctrack->get_type());
467                                 track->set_position(ground);
468
469                                 selection.clear();
470                                 selection.add_track(track);
471
472                                 mode = SELECT;
473                         }
474                 }
475                 else
476                         mode = SELECT;
477         }
478         else if(mode==SELECT)
479         {
480                 if(btn==1)
481                 {
482                         Track *track = pick_track(x, y);
483                         if(track)
484                         {
485                                 if(!(mod&Input::MOD_SHIFT))
486                                         selection.clear();
487                                 selection.toggle_track(track);
488                         }
489                 }
490         }
491         else if(mode==MEASURE)
492                 measure.button_press(x, y, ground.x, ground.y, btn);
493 }
494
495 void Designer::pointer_motion(int x, int y)
496 {
497         y = window.get_height()-y-1;
498
499         if(!root.get_child_at(x, y))
500         {
501                 Vector ground = map_pointer_to_ground(x, y);
502                 measure.pointer_motion(x, y, ground.x, ground.y);
503         }
504 }
505
506 void Designer::render()
507 {
508         GL::Framebuffer::system().clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT);
509
510         View3D *view = (mode==CATALOGUE ? cat_view : main_view);
511         view->render();
512         if(mode==MEASURE)
513         {
514                 GL::Bind bind_depth(GL::DepthTest::lequal());
515                 measure.render();
516         }
517
518         GL::matrix_mode(GL::PROJECTION);
519         GL::load_identity();
520         GL::ortho_bottomleft(window.get_width(), window.get_height());
521         GL::matrix_mode(GL::MODELVIEW);
522         GL::load_identity();
523
524         GL::Bind bind_blend(GL::Blend::alpha());
525         root.render();
526         // XXX Should fix GLtk so these would not be needed
527         GL::Texture::unbind();
528         glColor3f(1.0, 1.0, 1.0);
529 }
530
531 void Designer::track_added(Track &trk)
532 {
533         new_tracks.push_back(&trk);
534 }
535
536 void Designer::track_removed(Track &trk)
537 {
538         list<Track *>::iterator i = find(new_tracks.begin(), new_tracks.end(), &trk);
539         if(i!=new_tracks.end())
540                 new_tracks.erase(i);
541 }
542
543 Track *Designer::pick_track(int x, int y)
544 {
545         float xx = x*2.0/window.get_width()-1.0;
546         float yy = y*2.0/window.get_height()-1.0;
547
548         View3D &view = *(mode==CATALOGUE ? cat_view : main_view);
549         const GL::Vector3 &cpos = view.get_camera().get_position();
550         GL::Vector4 cray = view.get_camera().unproject(GL::Vector4(xx, yy, 0, 0));
551
552         return view.get_layout().get_layout().pick_track(Vector(cpos.x, cpos.y, cpos.z), Vector(cray.x, cray.y, cray.z));
553 }
554
555 void Designer::update_track_icon(Track3D &track)
556 {
557         overlay->clear(track);
558
559         if(track.get_track().get_flex())
560                 overlay->add_graphic(track, "flex");
561
562         if(unsigned sid = track.get_track().get_sensor_id())
563         {
564                 overlay->add_graphic(track, "sensor");
565                 overlay->set_label(track, lexical_cast(sid));
566         }
567         else if(unsigned tid = track.get_track().get_turnout_id())
568         {
569                 if(tid<0x800)
570                 {
571                         overlay->add_graphic(track, "turnout");
572                         overlay->set_label(track, lexical_cast(tid));
573                 }
574         }
575 }
576
577 void Designer::selection_changed()
578 {
579         const set<Track *> &tracks = selection.get_tracks();
580         if(tracks.empty())
581                 lbl_status->set_text(string());
582         else
583         {
584                 float len = 0;
585                 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
586                         len += (*i)->get_type().get_total_length();
587                 lbl_status->set_text(format("%.2fm of track selected\n", len));
588         }
589 }
590
591 void Designer::manipulation_status(const string &status)
592 {
593         lbl_status->set_text(status);
594 }
595
596 void Designer::manipulation_done(bool)
597 {
598         mode = MANIPULATE_DONE;
599         selection_changed();
600 }
601
602 void Designer::measure_changed()
603 {
604         float pard = measure.get_parallel_distance()*1000;
605         float perpd = measure.get_perpendicular_distance()*1000;
606         float d = sqrt(pard*pard+perpd*perpd);
607         float adiff = measure.get_angle_difference()*180/M_PI;
608         string info = format("Par %.1fmm - Perp %.1fmm - Total %.1fmm - Angle %.1f°", pard, perpd, d, adiff);
609         lbl_status->set_text(info);
610 }
611
612 void Designer::measure_done()
613 {
614         mode = SELECT;
615         selection_changed();
616 }
617
618 void Designer::arrange_toolbars()
619 {
620         unsigned x = 0;
621         for(vector<Toolbar *>::iterator i=toolbars.begin(); i!=toolbars.end(); ++i)
622         {
623                 const GLtk::Geometry &geom = (*i)->get_geometry();
624                 (*i)->set_position(x, window.get_height()-geom.h);
625                 x += geom.w;
626         }
627 }
628
629 void Designer::track_properties_response(int)
630 {
631         const set<Track *> &tracks = selection.get_tracks();
632         for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
633                 update_track_icon(layout_3d->get_track(**i));
634 }
635
636 void Designer::route_name_accept(const string &text)
637 {
638         if(cur_route)
639                 cur_route->set_name(text);
640 }
641
642 void Designer::svg_export_accept(const string &text)
643 {
644         SvgExporter svg_exp(*layout);
645         svg_exp.save(text);
646 }
647
648 string Designer::tooltip(int x, int y)
649 {
650         if(Track *track = pick_track(x, y))
651         {
652                 const TrackType &ttype = track->get_type();
653                 string info = format("%d %s", ttype.get_article_number(), ttype.get_description());
654                 if(mode!=CATALOGUE && abs(track->get_slope())>1e-4)
655                         info += format(" (slope %.1f%%)", abs(track->get_slope()/ttype.get_total_length()*100));
656                 if(track->get_turnout_id())
657                         info += format(" (turnout %d)", track->get_turnout_id());
658                 else if(track->get_sensor_id())
659                         info += format(" (sensor %d)", track->get_sensor_id());
660
661                 return info;
662         }
663
664         return string();
665 }
666
667 void Designer::clear_paths()
668 {
669         const set<Track *> &ltracks = layout->get_tracks();
670         for(set<Track *>::iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
671         {
672                 Track3D &t3d = layout_3d->get_track(**i);
673                 t3d.get_path().set_mask(0);
674         }
675 }
676
677 void Designer::show_route(const Route &route)
678 {
679         clear_paths();
680
681         const set<Track *> &rtracks = route.get_tracks();
682         for(set<Track *>::iterator i=rtracks.begin(); i!=rtracks.end(); ++i)
683         {
684                 Track3D &t3d = layout_3d->get_track(**i);
685                 t3d.get_path().set_color(GL::Color(0.5, 0.8, 1.0));
686                 int path = -1;
687                 if(unsigned tid = (*i)->get_turnout_id())
688                         path = route.get_turnout(tid);
689                 if(path>=0)
690                         t3d.get_path().set_path(path);
691                 else
692                         t3d.get_path().set_mask((*i)->get_type().get_paths());
693         }
694 }
695
696 void Designer::show_zone(const Zone &zone)
697 {
698         clear_paths();
699
700         const Zone::TrackSet &ztracks = zone.get_tracks();
701         for(Zone::TrackSet::const_iterator i=ztracks.begin(); i!=ztracks.end(); ++i)
702         {
703                 Track3D &t3d = layout_3d->get_track(**i);
704                 t3d.get_path().set_color(GL::Color(0.8, 1.0, 0.5));
705                 t3d.get_path().set_mask((*i)->get_type().get_paths());
706         }
707 }