3 This file is part of R²C²
4 Copyright © 2006-2011 Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
11 #include <msp/fs/utils.h>
12 #include <msp/gl/blend.h>
13 #include <msp/gl/framebuffer.h>
14 #include <msp/gl/matrix.h>
15 #include <msp/gl/misc.h>
16 #include <msp/gl/projection.h>
17 #include <msp/gl/tests.h>
18 #include <msp/gl/texture2d.h>
19 #include <msp/input/keys.h>
20 #include <msp/io/print.h>
21 #include <msp/strings/codec.h>
22 #include <msp/strings/lexicalcast.h>
23 #include <msp/strings/utf8.h>
24 #include <msp/strings/utils.h>
25 #include <msp/time/units.h>
26 #include <msp/time/utils.h>
27 #include "libr2c2/route.h"
28 #include "libr2c2/tracktype.h"
29 #include "libr2c2/zone.h"
33 #include "layoutbar.h"
34 #include "manipulator.h"
37 #include "selection.h"
38 #include "svgexporter.h"
40 #include "trackproperties.h"
42 #include "zoneproperties.h"
48 Application::RegApp<Designer> Designer::reg;
50 Designer::Designer(int argc, char **argv):
58 manipulator(*this, root, selection),
61 window.set_title("Railway Designer");
62 window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &Designer::exit), 0));
64 selection.signal_changed.connect(sigc::mem_fun(this, &Designer::selection_changed));
65 manipulator.signal_status.connect(sigc::mem_fun(this, &Designer::manipulation_status));
66 manipulator.signal_done.connect(sigc::mem_fun(this, &Designer::manipulation_done));
67 measure.signal_changed.connect(sigc::mem_fun(this, &Designer::measure_changed));
68 measure.signal_done.connect(sigc::mem_fun(this, &Designer::measure_done));
70 // Setup catalogue and layout
71 DataFile::load(catalogue, "tracks.dat");
73 cat_layout_3d = new Layout3D(catalogue.get_layout());
75 layout = new Layout(catalogue);
76 layout_3d = new Layout3D(*layout);
78 layout->signal_track_added.connect(sigc::mem_fun(this, &Designer::track_added));
79 layout->signal_track_removed.connect(sigc::mem_fun(this, &Designer::track_removed));
84 DataFile::load(*layout, argv[1]);
86 if(!layout->get_base().empty())
88 base_object = new GL::Object;
89 DataFile::load(*base_object, layout->get_base());
94 cat_view = new View3D(*cat_layout_3d, window.get_width(), window.get_height());
96 main_view = new View3D(*layout_3d, window.get_width(), window.get_height());
97 GL::Pipeline *pipeline = &main_view->get_pipeline();
99 GL::PipelinePass *pass = &pipeline->add_pass("unlit");
100 pass->depth_test = &GL::DepthTest::lequal();
101 pass->blend = &GL::Blend::alpha();
103 pass = &pipeline->add_pass("blended");
104 pass->lighting = &layout_3d->get_lighting();
105 pass->depth_test = &GL::DepthTest::lequal();
106 pass->blend = &GL::Blend::alpha();
108 pass = &pipeline->add_pass("overlay");
109 pass->blend = &GL::Blend::alpha();
112 pipeline->add_renderable(*base_object);
113 pipeline->add_renderable_for_pass(layout_3d->get_path_scene(), "unlit");
114 pipeline->add_renderable_for_pass(layout_3d->get_endpoint_scene(), "unlit");
117 root.signal_key_press.connect(sigc::mem_fun(this, &Designer::key_press));
118 root.signal_button_press.connect(sigc::mem_fun(this, &Designer::button_press));
119 root.signal_pointer_motion.connect(sigc::mem_fun(this, &Designer::pointer_motion));
120 root.signal_tooltip.connect(sigc::mem_fun(this, &Designer::tooltip));
122 toolbars.push_back(new Layoutbar(*this));
123 toolbars.push_back(new Trackbar(*this));
124 toolbars.push_back(new Routebar(*this));
125 toolbars.push_back(new Zonebar(*this));
126 for(vector<Toolbar *>::iterator i=toolbars.begin(); i!=toolbars.end(); ++i)
129 if(i!=toolbars.begin())
131 (*i)->signal_expanded.connect(sigc::hide(sigc::mem_fun(this, &Designer::arrange_toolbars)));
135 GLtk::Panel *statusbar = new GLtk::Panel;
136 root.add(*statusbar);
137 statusbar->set_size(window.get_width(), 20);
138 statusbar->set_focusable(false);
140 lbl_status = new GLtk::Label;
141 statusbar->add(*lbl_status);
142 lbl_status->set_geometry(GLtk::Geometry(20, 2, 300, 16));
144 track_wrap = new TrackWrap(*layout_3d, selection);
145 pipeline->add_renderable_for_pass(*track_wrap, "unlit");
146 overlay = new Overlay3D(ui_res.get_default_font());
147 pipeline->add_renderable_for_pass(*overlay, "overlay");
149 camera_ctl = new CameraController(*main_view, root);
150 cat_view->get_camera().set_look_direction(GL::Vector3(0, 0.13053, -0.99144));
151 cat_view->view_all(true);
152 main_view->view_all();
154 const Layout3D::TrackMap &tracks = layout_3d->get_tracks();
155 for(Layout3D::TrackMap::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
156 update_track_icon(*i->second);
159 Designer::~Designer()
161 for(vector<Toolbar *>::iterator i=toolbars.begin(); i!=toolbars.end(); ++i)
171 delete cat_layout_3d;
180 return Application::main();
183 void Designer::save()
185 InputDialog *input = new InputDialog(*this, "Save layout", filename);
186 input->signal_accept.connect(sigc::mem_fun(layout, &Layout::save));
189 void Designer::quit()
194 void Designer::new_track()
200 lbl_status->set_text("Select new track or press Esc to cancel");
203 void Designer::erase_tracks()
208 set<Track *> tracks = selection.get_tracks();
210 for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
212 overlay->clear(layout_3d->get_track(**i));
217 void Designer::track_properties()
219 if(mode!=SELECT || selection.empty())
222 TrackProperties *track_prop = new TrackProperties(selection);
223 root.add(*track_prop);
224 const GLtk::Geometry &root_geom = root.get_geometry();
225 const GLtk::Geometry &dlg_geom = track_prop->get_geometry();
226 track_prop->set_position((root_geom.w-dlg_geom.w)/2, (root_geom.h-dlg_geom.h)/2);
228 track_prop->signal_response.connect(sigc::mem_fun(this, &Designer::track_properties_response));
231 void Designer::extend_track()
236 if(manipulator.start_extend())
240 void Designer::connect_tracks()
245 manipulator.connect();
248 void Designer::flatten_tracks()
253 manipulator.flatten();
256 void Designer::svg_export()
258 InputDialog *input = new InputDialog(*this, "SVG export", FS::basepart(filename)+".svg");
259 input->signal_accept.connect(sigc::mem_fun(this, &Designer::svg_export_accept));
262 void Designer::edit_route(Route *r)
267 show_route(*cur_route);
272 void Designer::rename_route()
274 if(mode!=SELECT || !cur_route)
277 InputDialog *input = new InputDialog(*this, "Route name", cur_route->get_name());
278 input->signal_accept.connect(sigc::mem_fun(this, &Designer::route_name_accept));
281 void Designer::add_selection_to_route()
288 cur_route->add_tracks(selection.get_tracks());
290 catch(const Exception &e)
292 lbl_status->set_text(e.what());
295 show_route(*cur_route);
298 void Designer::edit_zone(Zone *z)
303 show_zone(*cur_zone);
308 void Designer::zone_properties()
313 ZoneProperties *zone_prop = new ZoneProperties(*cur_zone);
314 root.add(*zone_prop);
315 const GLtk::Geometry &root_geom = root.get_geometry();
316 const GLtk::Geometry &dlg_geom = zone_prop->get_geometry();
317 zone_prop->set_position((root_geom.w-dlg_geom.w)/2, (root_geom.h-dlg_geom.h)/2);
320 void Designer::add_selection_to_zone()
327 cur_zone->add_tracks(selection.get_tracks());
329 catch(const Exception &e)
331 lbl_status->set_text(e.what());
334 show_zone(*cur_zone);
337 Vector Designer::map_pointer_to_ground(int x, int y)
339 float xf = x*2.0/window.get_width()-1.0;
340 float yf = y*2.0/window.get_height()-1.0;
341 GL::Vector4 vec = main_view->get_camera().unproject(GL::Vector4(xf, yf, 0, 0));
342 const GL::Vector3 &pos = main_view->get_camera().get_position();
344 return Vector(pos.x-vec.x*pos.z/vec.z, pos.y-vec.y*pos.z/vec.z);
347 void Designer::tick()
349 const Msp::Time::TimeStamp t = Msp::Time::now();
350 float dt = (t-last_tick)/Msp::Time::sec;
353 if(mode==MANIPULATE_DONE)
358 camera_ctl->tick(dt);
360 for(list<Track *>::iterator i=new_tracks.begin(); i!=new_tracks.end(); ++i)
361 layout_3d->get_track(**i).get_path().set_mask(0);
366 window.swap_buffers();
369 void Designer::key_press(unsigned key, unsigned mod, wchar_t)
371 key = Input::key_from_sys(key);
372 mod = Input::mod_from_sys(mod);
374 if(key==Msp::Input::KEY_N && (mod&Input::MOD_SHIFT))
376 else if(key==Msp::Input::KEY_N)
378 else if(key==Msp::Input::KEY_G)
380 manipulator.start_move();
383 else if(key==Msp::Input::KEY_R)
385 manipulator.start_rotate();
388 else if(key==Msp::Input::KEY_D)
390 manipulator.duplicate();
391 manipulator.start_move();
394 else if(key==Msp::Input::KEY_W)
396 else if(key==Msp::Input::KEY_PLUS)
397 selection.select_more();
398 else if(key==Msp::Input::KEY_L && (mod&Input::MOD_SHIFT))
399 selection.select_blocks();
400 else if(key==Msp::Input::KEY_L)
401 selection.select_linked();
402 else if(key==Msp::Input::KEY_M)
407 else if(key==Msp::Input::KEY_Z)
409 manipulator.start_elevate();
412 else if(key==Msp::Input::KEY_ESC)
415 manipulator.cancel();
416 else if(mode==CATALOGUE)
421 else if(key==Msp::Input::KEY_X)
423 else if(key==Msp::Input::KEY_F && (mod&Input::MOD_SHIFT))
425 const set<Track *> &tracks = selection.get_tracks();
426 const set<Track *> <racks = layout->get_tracks();
427 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
429 (*i)->set_flex(!(*i)->get_flex());
431 for(set<Track *>::const_iterator j=ltracks.begin(); j!=ltracks.end(); ++j)
433 (*i)->snap_to(**j, true);
435 update_track_icon(layout_3d->get_track(**i));
438 else if(key==Msp::Input::KEY_F)
440 else if(key==Msp::Input::KEY_E && (mod&Input::MOD_SHIFT))
441 manipulator.even_slope(true);
442 else if(key==Msp::Input::KEY_E)
443 manipulator.even_slope();
444 else if(key==Msp::Input::KEY_A)
447 add_selection_to_route();
449 add_selection_to_zone();
451 else if(key==Msp::Input::KEY_C)
453 else if(key==Msp::Input::KEY_V)
455 else if(key==Msp::Input::KEY_P)
459 void Designer::button_press(int x, int y, unsigned btn, unsigned mod)
461 y = window.get_height()-y-1;
462 mod = Input::mod_from_sys(mod);
464 Vector ground = map_pointer_to_ground(x, y);
470 Track *ctrack = pick_track(x, y);
473 Track *track = new Track(*layout, ctrack->get_type());
474 track->set_position(ground);
477 selection.add_track(track);
485 else if(mode==SELECT)
489 Track *track = pick_track(x, y);
492 if(!(mod&Input::MOD_SHIFT))
494 selection.toggle_track(track);
498 else if(mode==MEASURE)
499 measure.button_press(x, y, ground.x, ground.y, btn);
502 void Designer::pointer_motion(int x, int y)
504 y = window.get_height()-y-1;
506 if(!root.get_child_at(x, y))
508 Vector ground = map_pointer_to_ground(x, y);
509 measure.pointer_motion(x, y, ground.x, ground.y);
513 void Designer::render()
515 GL::Framebuffer::system().clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT);
517 View3D *view = (mode==CATALOGUE ? cat_view : main_view);
521 GL::Bind bind_depth(GL::DepthTest::lequal());
525 GL::matrix_mode(GL::PROJECTION);
527 GL::ortho_bottomleft(window.get_width(), window.get_height());
528 GL::matrix_mode(GL::MODELVIEW);
531 GL::Bind bind_blend(GL::Blend::alpha());
533 // XXX Should fix GLtk so these would not be needed
534 GL::Texture::unbind();
535 glColor3f(1.0, 1.0, 1.0);
538 void Designer::track_added(Track &trk)
540 new_tracks.push_back(&trk);
543 void Designer::track_removed(Track &trk)
545 list<Track *>::iterator i = find(new_tracks.begin(), new_tracks.end(), &trk);
546 if(i!=new_tracks.end())
550 Track *Designer::pick_track(int x, int y)
552 float xx = x*2.0/window.get_width()-1.0;
553 float yy = y*2.0/window.get_height()-1.0;
555 View3D &view = *(mode==CATALOGUE ? cat_view : main_view);
556 const GL::Vector3 &cpos = view.get_camera().get_position();
557 GL::Vector4 cray = view.get_camera().unproject(GL::Vector4(xx, yy, 0, 0));
559 return view.get_layout().get_layout().pick_track(Vector(cpos.x, cpos.y, cpos.z), Vector(cray.x, cray.y, cray.z));
562 void Designer::update_track_icon(Track3D &track)
564 overlay->clear(track);
566 if(track.get_track().get_flex())
567 overlay->add_graphic(track, "flex");
569 if(unsigned sid = track.get_track().get_sensor_id())
571 overlay->add_graphic(track, "sensor");
572 overlay->set_label(track, lexical_cast(sid));
574 else if(unsigned tid = track.get_track().get_turnout_id())
578 overlay->add_graphic(track, "turnout");
579 overlay->set_label(track, lexical_cast(tid));
584 void Designer::selection_changed()
586 const set<Track *> &tracks = selection.get_tracks();
588 lbl_status->set_text(string());
592 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
593 len += (*i)->get_type().get_total_length();
594 lbl_status->set_text(format("%.2fm of track selected\n", len));
598 void Designer::manipulation_status(const string &status)
600 lbl_status->set_text(status);
603 void Designer::manipulation_done(bool)
605 mode = MANIPULATE_DONE;
609 void Designer::measure_changed()
611 float pard = measure.get_parallel_distance()*1000;
612 float perpd = measure.get_perpendicular_distance()*1000;
613 float d = sqrt(pard*pard+perpd*perpd);
614 float adiff = measure.get_angle_difference()*180/M_PI;
615 string info = format("Par %.1fmm - Perp %.1fmm - Total %.1fmm - Angle %.1f°", pard, perpd, d, adiff);
616 lbl_status->set_text(info);
619 void Designer::measure_done()
625 void Designer::arrange_toolbars()
628 for(vector<Toolbar *>::iterator i=toolbars.begin(); i!=toolbars.end(); ++i)
630 const GLtk::Geometry &geom = (*i)->get_geometry();
631 (*i)->set_position(x, window.get_height()-geom.h);
636 void Designer::track_properties_response(int)
638 const set<Track *> &tracks = selection.get_tracks();
639 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
640 update_track_icon(layout_3d->get_track(**i));
643 void Designer::route_name_accept(const string &text)
646 cur_route->set_name(text);
649 void Designer::svg_export_accept(const string &text)
651 SvgExporter svg_exp(*layout);
655 string Designer::tooltip(int x, int y)
657 if(Track *track = pick_track(x, y))
659 const TrackType &ttype = track->get_type();
660 string info = format("%d %s", ttype.get_article_number(), ttype.get_description());
661 if(mode!=CATALOGUE && abs(track->get_slope())>1e-4)
662 info += format(" (slope %.1f%%)", abs(track->get_slope()/ttype.get_total_length()*100));
663 if(track->get_turnout_id())
664 info += format(" (turnout %d)", track->get_turnout_id());
665 else if(track->get_sensor_id())
666 info += format(" (sensor %d)", track->get_sensor_id());
674 void Designer::clear_paths()
676 const set<Track *> <racks = layout->get_tracks();
677 for(set<Track *>::iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
679 Track3D &t3d = layout_3d->get_track(**i);
680 t3d.get_path().set_mask(0);
684 void Designer::show_route(const Route &route)
688 const set<Track *> &rtracks = route.get_tracks();
689 for(set<Track *>::iterator i=rtracks.begin(); i!=rtracks.end(); ++i)
691 Track3D &t3d = layout_3d->get_track(**i);
692 t3d.get_path().set_color(GL::Color(0.5, 0.8, 1.0));
694 if(unsigned tid = (*i)->get_turnout_id())
695 path = route.get_turnout(tid);
697 t3d.get_path().set_path(path);
699 t3d.get_path().set_mask((*i)->get_type().get_paths());
703 void Designer::show_zone(const Zone &zone)
707 const Zone::TrackSet &ztracks = zone.get_tracks();
708 for(Zone::TrackSet::const_iterator i=ztracks.begin(); i!=ztracks.end(); ++i)
710 Track3D &t3d = layout_3d->get_track(**i);
711 t3d.get_path().set_color(GL::Color(0.8, 1.0, 0.5));
712 t3d.get_path().set_mask((*i)->get_type().get_paths());