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