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