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