]> git.tdb.fi Git - r2c2.git/blob - source/designer/manipulator.cpp
b41b29eeac9ac6e6562df825824f316a817fa3f7
[r2c2.git] / source / designer / manipulator.cpp
1 /* $Id$
2
3 This file is part of R²C²
4 Copyright © 2006-2010  Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6 */
7
8 #include <algorithm>
9 #include <cmath>
10 #include <msp/strings/formatter.h>
11 #include "libr2c2/tracktype.h"
12 #include "designer.h"
13 #include "manipulator.h"
14 #include "selection.h"
15
16 using namespace std;
17 using namespace R2C2;
18 using namespace Msp;
19
20 Manipulator::Manipulator(Designer &d, Graphics::EventSource &es, Selection &s):
21         designer(d),
22         event_source(es),
23         selection(s),
24         mode(NONE),
25         angle(0)
26 {
27         event_source.signal_button_press.connect(sigc::mem_fun(this, &Manipulator::button_press));
28         event_source.signal_pointer_motion.connect(sigc::mem_fun(this, &Manipulator::pointer_motion));
29         selection.signal_changed.connect(sigc::mem_fun(this, &Manipulator::selection_changed));
30 }
31
32 void Manipulator::start_move()
33 {
34         if(mode)
35                 cancel();
36
37         move_origin = gpointer;
38
39         mode = MOVE;
40 }
41
42 void Manipulator::start_rotate()
43 {
44         if(mode)
45                 cancel();
46
47         rot_origin = atan2(gpointer.y-center.y, gpointer.x-center.x);
48
49         mode = ROTATE;
50 }
51
52 void Manipulator::start_elevate()
53 {
54         if(mode)
55                 cancel();
56
57         elev_origin = pointer_y;
58
59         mode = ELEVATE;
60 }
61
62 bool Manipulator::start_extend()
63 {
64         if(mode)
65                 cancel();
66
67         bool ok = false;
68         const set<Track *> &stracks = selection.get_tracks();
69         for(set<Track *>::const_iterator i=stracks.begin(); (!ok && i!=stracks.end()); ++i)
70         {
71                 const vector<Track *> &links = (*i)->get_links();
72                 for(vector<Track *>::const_iterator j=links.begin(); (!ok && j!=links.end()); ++j)
73                         ok = !*j;
74         }
75
76         if(ok)
77                 mode = EXTEND;
78         else
79                 signal_status.emit("No free endpoints");
80
81         return ok;
82 }
83
84 void Manipulator::duplicate()
85 {
86         if(mode)
87                 cancel();
88
89         list<Track *> new_tracks;
90         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
91         {
92                 Track *track = new Track(designer.get_layout(), i->track->get_type());
93                 track->set_position(i->track->get_position());
94                 track->set_rotation(i->track->get_rotation());
95                 new_tracks.push_back(track);
96         }
97
98         selection.clear();
99         for(list<Track *>::iterator i=new_tracks.begin(); i!=new_tracks.end(); ++i)
100         {
101                 selection.add_track(*i);
102                 for(list<Track *>::iterator j=i; j!=new_tracks.end(); ++j)
103                         if(j!=i)
104                                 (*i)->snap_to(**j, true);
105         }
106 }
107
108 void Manipulator::flatten()
109 {
110         if(mode)
111                 cancel();
112
113         if(tracks.empty()) return;
114
115         float z = 0;
116         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
117                 z += i->track->get_position().z+i->track->get_slope()/2;
118         z /= tracks.size();
119
120         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
121         {
122                 Point p = i->track->get_position();
123                 i->track->set_position(Point(p.x, p.y, z));
124                 i->track->set_slope(0);
125         }
126
127         for(set<Track *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
128                 (*i)->check_slope();
129
130         update_tracks();
131 }
132
133 void Manipulator::even_slope(bool smooth)
134 {
135         if(mode)
136                 cancel();
137
138         if(neighbors.size()!=2)
139                 return;
140
141         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
142                 if(i->track->get_type().get_endpoints().size()!=2)
143                         return;
144
145         list<Track *> tracks2;
146         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
147                 tracks2.push_back(i->track);
148
149         float total_len = 0;
150
151         list<TrackOrder> order;
152         Track *cur = *neighbors.begin();
153         while(tracks2.size())
154         {
155                 bool rev = false;
156                 for(list<Track *>::iterator i=tracks2.begin(); i!=tracks2.end(); ++i)
157                 {
158                         const vector<Track *> &links = (*i)->get_links();
159                         if(links[0]==cur)
160                         {
161                                 cur = *i;
162                                 tracks2.erase(i);
163                                 break;
164                         }
165                         else if(links[1]==cur)
166                         {
167                                 cur = *i;
168                                 rev = true;
169                                 tracks2.erase(i);
170                                 break;
171                         }
172                 }
173                 order.push_back(TrackOrder(cur, rev));
174                 total_len += cur->get_type().get_total_length();
175         }
176
177         set<Track *>::iterator nb = neighbors.begin();
178         int epi = (*nb)->get_endpoint_by_link(*order.front().track);
179         float start_z = (*nb)->get_endpoint_position(epi).z;
180         ++nb;
181         epi = (*nb)->get_endpoint_by_link(*order.back().track);
182         float end_z = (*nb)->get_endpoint_position(epi).z;
183
184         if(smooth)
185         {
186                 float dir = (end_z>start_z)?1:-1;
187                 float cur_slope = 0;
188                 while((end_z-start_z)*dir/total_len>cur_slope+0.025 && order.size()>2)
189                 {
190                         cur_slope += 0.025;
191
192                         float dz = order.front().track->get_type().get_total_length()*dir*cur_slope;
193                         set_slope(order.front(), start_z, dz);
194                         start_z += dz;
195                         total_len -= order.front().track->get_type().get_total_length();
196                         order.erase(order.begin());
197
198                         dz = order.back().track->get_type().get_total_length()*dir*cur_slope;
199                         set_slope(order.back(), end_z-dz, dz);
200                         end_z -= dz;
201                         total_len -= order.back().track->get_type().get_total_length();
202                         order.erase(--order.end());
203                 }
204         }
205
206         float cur_z = start_z;
207         for(list<TrackOrder>::iterator i=order.begin(); i!=order.end(); ++i)
208         {
209                 float dz = i->track->get_type().get_total_length()*(end_z-start_z)/total_len;
210                 set_slope(*i, cur_z, dz);
211                 cur_z += dz;
212         }
213
214         for(set<Track *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
215                 (*i)->check_slope();
216
217         update_tracks();
218 }
219
220 void Manipulator::connect()
221 {
222         if(tracks.size()!=2)
223         {
224                 signal_status.emit("Exactly two tracks must be selected");
225                 return;
226         }
227
228         float limit = designer.get_layout().get_catalogue().get_gauge()/10;
229
230         Track *track1 = tracks.front().track;
231         Point pos1;
232         float dir1;
233         Track *track2 = tracks.back().track;
234         bool ok = false;
235         float gap = 0;
236         for(unsigned i=0; i<track1->get_type().get_endpoints().size(); ++i)
237         {
238                 if(track1->get_link(i))
239                         continue;
240
241                 pos1 = track1->get_endpoint_position(i);
242                 dir1 = track1->get_endpoint_direction(i);
243
244                 for(unsigned j=0; j<track2->get_type().get_endpoints().size(); ++j)
245                 {
246                         if(track2->get_link(j))
247                                 continue;
248
249                         Point pos2 = track2->get_endpoint_position(j);
250                         float dir2 = track2->get_endpoint_direction(j);
251
252                         float dz = pos2.z-pos1.z;
253                         if(abs(dz)>0.02)
254                                 continue;
255
256                         float adiff = dir1+M_PI-dir2;
257                         while(adiff<-M_PI)
258                                 adiff += M_PI*2;
259                         while(adiff>M_PI)
260                                 adiff -= M_PI*2;
261                         if(abs(adiff)>0.01)
262                                 continue;
263
264                         float c = cos(dir1);
265                         float s = sin(dir1);
266                         float dx = pos2.x-pos1.x;
267                         float dy = pos2.y-pos1.y;
268                         if(abs(dx*s-dy*c)>limit)
269                                 continue;
270
271                         gap = dx*c+dy*s;
272                         if(gap<0)
273                                 continue;
274
275                         ok = true;
276                 }
277
278                 if(ok)
279                         break;
280         }
281
282         if(!ok)
283         {
284                 signal_status.emit("No aligned endpoints found");
285                 return;
286         }
287
288         vector<Track *> trks = create_straight(pos1, dir1, gap, limit);
289
290         if(trks.empty())
291         {
292                 signal_status.emit("No connection possible");
293                 return;
294         }
295
296         trks.front()->snap_to(*track1, true);
297         trks.back()->snap_to(*track2, true);
298
299         selection.replace(trks.begin(), trks.end());
300 }
301
302 void Manipulator::cancel()
303 {
304         if(!mode)
305                 return;
306         mode = NONE;
307
308         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
309         {
310                 i->track->set_position(Point(center.x+i->pos.x, center.y+i->pos.y, center.z+i->pos.z));
311                 i->track->set_rotation(i->rot);
312         }
313
314         for(set<Track *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
315                 (*i)->check_slope();
316
317         for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
318                 delete *i;
319         extend_tracks.clear();
320
321         angle = 0;
322
323         signal_done.emit(false);
324 }
325
326 void Manipulator::button_press(int, int, unsigned btn, unsigned)
327 {
328         if(!mode)
329                 return;
330
331         if(btn==3)
332                 cancel();
333         else if(btn==1)
334         {
335                 Mode m = mode;
336                 mode = NONE;
337                 angle = 0;
338
339                 if(m!=EXTEND)
340                 {
341                         for(set<Track *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
342                                 for(vector<MTrack>::iterator j=tracks.begin(); j!=tracks.end(); ++j)
343                                         j->track->break_link(**i);
344                 }
345
346                 const set<Track *> &ltracks = designer.get_layout().get_tracks();
347                 for(set<Track *>::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
348                 {
349                         bool ok = true;
350                         for(vector<MTrack>::iterator j=tracks.begin(); (j!=tracks.end() && ok); ++j)
351                                 ok = (j->track!=*i);
352                         if(!ok) continue;
353
354                         for(vector<MTrack>::iterator j=tracks.begin(); j!=tracks.end(); ++j)
355                                 j->track->snap_to(**i, true);
356                 }
357
358                 if(m==EXTEND)
359                 {
360                         selection.replace(extend_tracks.begin(), extend_tracks.end());
361                         extend_tracks.clear();
362                 }
363                 else
364                 {
365                         update_tracks();
366                         update_neighbors();
367                 }
368
369                 signal_done.emit(true);
370         }
371 }
372
373 void Manipulator::pointer_motion(int x, int y)
374 {
375         pointer_y = event_source.get_height()-1-y;
376         gpointer = designer.map_pointer_to_ground(x, pointer_y);
377
378         if(mode==MOVE)
379         {
380                 Point delta(gpointer.x-move_origin.x, gpointer.y-move_origin.y, 0);
381                 Point offset(center.x+delta.x, center.y+delta.y, center.z);
382                 for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
383                 {
384                         i->track->set_position(Point(offset.x+i->pos.x, offset.y+i->pos.y, offset.z+i->pos.z));
385                         i->track->set_rotation(i->rot);
386                 }
387
388                 const set<Track *> &ltracks = designer.get_layout().get_tracks();
389                 float limit = max(designer.get_layout().get_catalogue().get_gauge(),
390                         designer.get_camera_controller().get_view_scale()*5/event_source.get_height());
391                 MTrack *snapped = 0;
392                 for(set<Track *>::const_iterator i=ltracks.begin(); (i!=ltracks.end() && !snapped); ++i)
393                 {
394                         bool ok = true;
395                         for(vector<MTrack>::iterator j=tracks.begin(); (j!=tracks.end() && ok); ++j)
396                                 ok = (j->track!=*i);
397                         if(!ok) continue;
398
399                         for(vector<MTrack>::iterator j=tracks.begin(); (j!=tracks.end() && !snapped); ++j)
400                                 if(j->track->snap_to(**i, false, limit))
401                                         snapped = &*j;
402                 }
403
404                 if(snapped)
405                 {
406                         float da = snapped->track->get_rotation()-snapped->rot;
407                         float c = cos(da);
408                         float s = sin(da);
409                         const Point &sp = snapped->track->get_position();
410                         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
411                         {
412                                 if(&*i==snapped)
413                                         continue;
414
415                                 Point dp(i->pos.x-snapped->pos.x, i->pos.y-snapped->pos.y, 0);
416                                 i->track->set_position(Point(sp.x+c*dp.x-s*dp.y, sp.y+s*dp.x+c*dp.y, sp.z+i->pos.z-snapped->pos.z));
417                                 i->track->set_rotation(i->rot+da);
418                         }
419                 }
420         }
421         else if(mode==ROTATE)
422         {
423                 float a = atan2(gpointer.y-center.y, gpointer.x-center.x);
424                 angle += a-rot_origin;
425                 rot_origin = a;
426
427                 for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
428                 {
429                         float c = cos(angle);
430                         float s = sin(angle);
431                         i->track->set_position(Point(center.x+c*i->pos.x-s*i->pos.y, center.y+s*i->pos.x+c*i->pos.y, center.z+i->pos.z));
432                         i->track->set_rotation(angle+i->rot);
433                 }
434         }
435         else if(mode==ELEVATE)
436         {
437                 float dz = (pointer_y-elev_origin)/1000.;
438
439                 signal_status.emit(format("Elevation: %+.0fmm (%.0fmm)", dz*1000, (center.z+dz)*1000));
440
441                 for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
442                         i->track->set_position(Point(center.x+i->pos.x, center.y+i->pos.y, center.z+i->pos.z+dz));
443
444                 for(set<Track *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
445                         (*i)->check_slope();
446         }
447         else if(mode==EXTEND)
448         {
449                 Point pos;
450                 float dir = 0;
451                 float length = 0;
452                 for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
453                 {
454                         unsigned n_endpoints = i->track->get_type().get_endpoints().size();
455                         for(unsigned j=0; j<n_endpoints; ++j)
456                         {
457                                 if(i->track->get_link(j))
458                                         continue;
459
460                                 Point ep_pos = i->track->get_endpoint_position(j);
461                                 float ep_dir = i->track->get_endpoint_direction(j);
462                                 float c = cos(ep_dir);
463                                 float s = sin(ep_dir);
464                                 float dx = gpointer.x-ep_pos.x;
465                                 float dy = gpointer.y-ep_pos.y;
466
467                                 float len = dx*c+dy*s;
468                                 if(len<length)
469                                         continue;
470
471                                 pos = ep_pos;
472                                 dir = ep_dir;
473                                 length = len;
474                         }
475                 }
476
477                 if(length)
478                 {
479                         vector<Track *> trks = create_straight(pos, dir, length, max(length/500, 0.001f));
480
481                         if(!trks.empty())
482                         {
483                                 for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
484                                         delete *i;
485                                 extend_tracks = trks;
486
487                                 length = 0;
488                                 for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
489                                         length += (*i)->get_type().get_total_length();
490
491                                 signal_status.emit(format("Extend: %.0fmm", length*1000));
492                         }
493                 }
494         }
495 }
496
497 void Manipulator::selection_changed()
498 {
499         if(mode)
500                 cancel();
501
502         tracks.clear();
503         const set<Track *> &stracks = selection.get_tracks();
504         tracks.insert(tracks.end(), stracks.begin(), stracks.end());
505
506         update_neighbors();
507         update_tracks();
508 }
509
510 void Manipulator::update_tracks()
511 {
512         Point minp, maxp;
513         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
514         {
515                 unsigned n_endpoints = i->track->get_type().get_endpoints().size();
516                 for(unsigned j=0; j<n_endpoints; ++j)
517                 {
518                         Point p = i->track->get_endpoint_position(j);
519                         if(i==tracks.begin() && j==0)
520                                 minp = maxp = p;
521                         else
522                         {
523                                 minp.x = min(minp.x, p.x);
524                                 maxp.x = max(maxp.x, p.x);
525                                 minp.y = min(minp.y, p.y);
526                                 maxp.y = max(maxp.y, p.y);
527                                 minp.z = min(minp.z, p.z);
528                         }
529                 }
530         }
531
532         center = Point((minp.x+maxp.x)/2, (minp.y+maxp.y)/2, minp.z);
533         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
534         {
535                 const Point &tp = i->track->get_position();
536                 i->pos = Point(tp.x-center.x, tp.y-center.y, tp.z-center.z);
537                 i->rot = i->track->get_rotation();
538         }
539 }
540
541 void Manipulator::update_neighbors()
542 {
543         neighbors.clear();
544         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
545         {
546                 const vector<Track *> &links = i->track->get_links();
547                 for(vector<Track *>::const_iterator j=links.begin(); j!=links.end(); ++j)
548                 {
549                         if(!*j)
550                                 continue;
551                         if(neighbors.count(*j))
552                                 continue;
553
554                         bool ok = true;
555                         for(vector<MTrack>::iterator k=tracks.begin(); (k!=tracks.end() && ok); ++k)
556                                 ok = (k->track!=*j);
557
558                         if(ok)
559                                 neighbors.insert(*j);
560                 }
561         }
562 }
563
564 void Manipulator::set_slope(TrackOrder &track, float z, float dz)
565 {
566         const Point &p = track.track->get_position();
567         if(track.rev)
568         {
569                 track.track->set_position(Point(p.x, p.y, z+dz));
570                 track.track->set_slope(-dz);
571         }
572         else
573         {
574                 track.track->set_position(Point(p.x, p.y, z));
575                 track.track->set_slope(dz);
576         }
577 }
578
579 vector<Track *> Manipulator::create_straight(const R2C2::Point &start, float dir, float length, float limit)
580 {
581         const Catalogue::TrackMap &track_types = designer.get_catalogue().get_tracks();
582         std::map<float, const TrackType *> types_by_length;
583         unsigned preference = 0;
584         for(Catalogue::TrackMap::const_iterator i=track_types.begin(); i!=track_types.end(); ++i)
585         {
586                 const vector<TrackPart> &parts = i->second->get_parts();
587                 if(parts.size()!=1)
588                         continue;
589                 if(parts.front().is_curved() || parts.front().is_dead_end())
590                         continue;
591
592                 types_by_length[parts.front().get_length()] = i->second;
593                 preference = max(preference, i->second->get_autofit_preference());
594         }
595
596         vector<float> lengths;
597         float removed = 0;
598         while(length>limit)
599         {
600                 bool found = false;
601                 for(map<float, const TrackType *>::iterator i=types_by_length.end(); i!=types_by_length.begin(); )
602                 {
603                         --i;
604                         if(i->second->get_autofit_preference()<preference)
605                                 continue;
606                         if((!removed || i->first<removed) && i->first<length+limit)
607                         {
608                                 unsigned n = static_cast<unsigned>((length+limit)/i->first);
609                                 lengths.insert(lengths.end(), n, i->first);
610                                 length -= n*i->first;
611                                 found = true;
612                                 break;
613                         }
614                 }
615
616                 if(found)
617                         continue;
618
619                 if(lengths.empty())
620                 {
621                         if(preference>0)
622                         {
623                                 --preference;
624                                 removed = 0;
625                                 continue;
626                         }
627                         break;
628                 }
629
630                 length += lengths.back();
631                 removed = lengths.back();
632                 lengths.pop_back();
633         }
634
635         vector<Track *> trks;
636
637         if(!lengths.empty())
638         {
639                 Point pos = start;
640                 float c = cos(dir);
641                 float s = sin(dir);
642                 for(vector<float>::iterator i=lengths.begin(); i!=lengths.end(); ++i)
643                 {
644                         map<float, const TrackType *>::iterator j = types_by_length.find(*i);
645                         if(j==types_by_length.end())
646                                 throw LogicError("Internal error");
647
648                         Track *track = new Track(designer.get_layout(), *j->second);
649                         track->set_position(pos);
650                         track->set_rotation(dir);
651
652                         if(!trks.empty())
653                                 track->snap_to(*trks.back(), true);
654                         trks.push_back(track);
655
656                         pos.x += c**i;
657                         pos.y += s**i;
658                 }
659         }
660
661         return trks;
662 }
663
664
665 Manipulator::MTrack::MTrack(Track *t):
666         track(t),
667         pos(track->get_position()),
668         rot(track->get_rotation())
669 { }