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