]> git.tdb.fi Git - r2c2.git/blob - source/designer/manipulator.cpp
5bf02e9760ab2765cc01e0842c111b760b89b429
[r2c2.git] / source / designer / manipulator.cpp
1 #include <algorithm>
2 #include <cmath>
3 #include <msp/strings/format.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, Input::Mouse &m, Selection &s):
14         designer(d),
15         mouse(m),
16         selection(s),
17         mode(NONE),
18         angle(0)
19 {
20         mouse.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &Manipulator::button_press), false));
21         mouse.signal_axis_motion.connect(sigc::bind_return(sigc::mem_fun(this, &Manipulator::axis_motion), false));
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         mode = ELEVATE;
51
52         elev_origin = pointer.y;
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_objects<Track>();
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<Object *> new_objs;
83         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
84         {
85                 Object *obj = i->object->clone(&designer.get_layout());
86                 for(list<Object *>::iterator j=new_objs.begin(); j!=new_objs.end(); ++j)
87                         obj->link_to(**j);
88                 new_objs.push_back(obj);
89         }
90
91         selection.replace(new_objs.begin(), new_objs.end());
92 }
93
94 void Manipulator::flatten()
95 {
96         if(mode)
97                 cancel();
98
99         if(objects.empty())
100                 return;
101
102         float z = 0;
103         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
104         {
105                 z += i->object->get_position().z;
106                 if(Track *track = dynamic_cast<Track *>(i->object))
107                         z += track->get_slope()/2;
108         }
109         z /= static_cast<int>(objects.size());
110
111         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
112         {
113                 Vector p = i->object->get_position();
114                 i->object->set_position(Vector(p.x, p.y, z));
115                 if(Track *track = dynamic_cast<Track *>(i->object))
116                         track->set_slope(0);
117         }
118
119         for(set<Track *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
120                 (*i)->check_slope();
121
122         update_objects();
123 }
124
125 void Manipulator::even_slope(bool smooth)
126 {
127         if(mode)
128                 cancel();
129
130         if(neighbors.size()!=2)
131                 return;
132
133         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
134                 if(i->object->get_n_link_slots()!=2)
135                         return;
136
137         list<Track *> tracks2;
138         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
139                 if(Track *track = dynamic_cast<Track *>(i->object))
140                         tracks2.push_back(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_link_slot(*order.front().track);
172         float start_z = (*nb)->get_snap_node(epi).position.z;
173         ++nb;
174         epi = (*nb)->get_link_slot(*order.back().track);
175         float end_z = (*nb)->get_snap_node(epi).position.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_objects();
211 }
212
213 void Manipulator::connect()
214 {
215         if(objects.size()!=2)
216         {
217                 signal_status.emit("Exactly two tracks must be selected");
218                 return;
219         }
220
221         Track *track1 = dynamic_cast<Track *>(objects.front().object);
222         Track *track2 = dynamic_cast<Track *>(objects.back().object);
223         if(!track1 || !track2)
224         {
225                 signal_status.emit("Exactly two tracks must be selected");
226                 return;
227         }
228
229         float limit = designer.get_layout().get_catalogue().get_gauge()/10;
230
231         Snap sn1;
232         bool ok = false;
233         float gap = 0;
234         for(unsigned i=0; i<track1->get_type().get_endpoints().size(); ++i)
235         {
236                 if(track1->get_link(i))
237                         continue;
238
239                 sn1 = track1->get_snap_node(i);
240
241                 for(unsigned j=0; j<track2->get_type().get_endpoints().size(); ++j)
242                 {
243                         if(track2->get_link(j))
244                                 continue;
245
246                         Snap sn2 = track2->get_snap_node(j);
247
248                         float dz = sn2.position.z-sn1.position.z;
249                         if(abs(dz)>0.02)
250                                 continue;
251
252                         float adiff = sn1.rotation+M_PI-sn2.rotation;
253                         while(adiff<-M_PI)
254                                 adiff += M_PI*2;
255                         while(adiff>M_PI)
256                                 adiff -= M_PI*2;
257                         if(abs(adiff)>0.01)
258                                 continue;
259
260                         float c = cos(sn1.rotation);
261                         float s = sin(sn1.rotation);
262                         float dx = sn2.position.x-sn1.position.x;
263                         float dy = sn2.position.y-sn1.position.y;
264                         if(abs(dx*s-dy*c)>limit)
265                                 continue;
266
267                         gap = dx*c+dy*s;
268                         if(gap<0)
269                                 continue;
270
271                         ok = true;
272                 }
273
274                 if(ok)
275                         break;
276         }
277
278         if(!ok)
279         {
280                 signal_status.emit("No aligned endpoints found");
281                 return;
282         }
283
284         vector<Track *> trks = create_straight(sn1.position, sn1.rotation, gap, limit);
285
286         if(trks.empty())
287         {
288                 signal_status.emit("No connection possible");
289                 return;
290         }
291
292         trks.front()->link_to(*track1);
293         trks.back()->link_to(*track2);
294
295         selection.replace(trks.begin(), trks.end());
296 }
297
298 void Manipulator::cancel()
299 {
300         if(!mode)
301                 return;
302         mode = NONE;
303
304         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
305         {
306                 i->object->set_position(Vector(center.x+i->pos.x, center.y+i->pos.y, center.z+i->pos.z));
307                 i->object->set_rotation(i->rot);
308         }
309
310         for(set<Track *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
311                 (*i)->check_slope();
312
313         for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
314                 delete *i;
315         extend_tracks.clear();
316
317         angle = 0;
318
319         signal_done.emit(false);
320 }
321
322 void Manipulator::button_press(unsigned btn)
323 {
324         if(!mode)
325                 return;
326
327         if(btn==3)
328                 cancel();
329         else if(btn==1)
330         {
331                 Mode m = mode;
332                 mode = NONE;
333                 angle = 0;
334
335                 if(m!=EXTEND)
336                 {
337                         for(set<Track *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
338                                 for(vector<MObject>::iterator j=objects.begin(); j!=objects.end(); ++j)
339                                         j->object->break_link(**i);
340                 }
341
342                 const set<Track *> &ltracks = designer.get_layout().get_tracks();
343                 for(set<Track *>::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
344                 {
345                         bool ok = true;
346                         for(vector<MObject>::iterator j=objects.begin(); (j!=objects.end() && ok); ++j)
347                                 ok = (j->object!=*i);
348                         if(!ok) continue;
349
350                         for(vector<MObject>::iterator j=objects.begin(); j!=objects.end(); ++j)
351                                 j->object->link_to(**i);
352                 }
353
354                 if(m==EXTEND)
355                 {
356                         selection.replace(extend_tracks.begin(), extend_tracks.end());
357                         extend_tracks.clear();
358                 }
359                 else
360                 {
361                         update_objects();
362                         update_neighbors();
363                 }
364
365                 signal_done.emit(true);
366         }
367 }
368
369 void Manipulator::axis_motion(unsigned axis, float value, float)
370 {
371         if(axis==0)
372                 pointer.x = value;
373         else if(axis==1)
374                 pointer.y = value;
375         gpointer = designer.map_pointer_to_ground(pointer);
376
377         if(mode==MOVE)
378         {
379                 Vector delta(gpointer.x-move_origin.x, gpointer.y-move_origin.y, 0);
380                 Vector offset(center.x+delta.x, center.y+delta.y, center.z);
381                 for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
382                 {
383                         i->object->set_position(Vector(offset.x+i->pos.x, offset.y+i->pos.y, offset.z+i->pos.z));
384                         i->object->set_rotation(i->rot);
385                 }
386
387                 const set<Track *> &ltracks = designer.get_layout().get_tracks();
388                 float limit = max(designer.get_layout().get_catalogue().get_gauge(),
389                         designer.get_camera_controller().get_view_scale()/100.0f);
390                 MObject *snapped = 0;
391                 for(set<Track *>::const_iterator i=ltracks.begin(); (i!=ltracks.end() && !snapped); ++i)
392                 {
393                         bool ok = true;
394                         for(vector<MObject>::iterator j=objects.begin(); (j!=objects.end() && ok); ++j)
395                                 ok = (j->object!=*i);
396                         if(!ok)
397                                 continue;
398
399                         for(vector<MObject>::iterator j=objects.begin(); (j!=objects.end() && !snapped); ++j)
400                                 if(j->object->snap_to(**i, limit))
401                                         snapped = &*j;
402                 }
403
404                 if(snapped)
405                 {
406                         float da = snapped->object->get_rotation()-snapped->rot;
407                         float c = cos(da);
408                         float s = sin(da);
409                         const Vector &sp = snapped->object->get_position();
410                         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
411                         {
412                                 if(&*i==snapped)
413                                         continue;
414
415                                 Vector dp(i->pos.x-snapped->pos.x, i->pos.y-snapped->pos.y, 0);
416                                 i->object->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));
417                                 i->object->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<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
428                 {
429                         float c = cos(angle);
430                         float s = sin(angle);
431                         i->object->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));
432                         i->object->set_rotation(angle+i->rot);
433                 }
434         }
435         else if(mode==ELEVATE && axis==1)
436         {
437                 float dz = pointer.y-elev_origin;
438
439                 signal_status.emit(format("Elevation: %+.0fmm (%.0fmm)", dz*1000, (center.z+dz)*1000));
440
441                 for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
442                         i->object->set_position(Vector(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                 Vector pos;
450                 float dir = 0;
451                 float length = 0;
452                 for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
453                 {
454                         unsigned nls = i->object->get_n_link_slots();
455                         for(unsigned j=0; j<nls; ++j)
456                         {
457                                 if(i->object->get_link(j))
458                                         continue;
459
460                                 Snap sn = i->object->get_snap_node(j);
461                                 float c = cos(sn.rotation);
462                                 float s = sin(sn.rotation);
463                                 float dx = gpointer.x-sn.position.x;
464                                 float dy = gpointer.y-sn.position.y;
465
466                                 float len = dx*c+dy*s;
467                                 if(len<length)
468                                         continue;
469
470                                 pos = sn.position;
471                                 dir = sn.rotation;
472                                 length = len;
473                         }
474                 }
475
476                 if(length)
477                 {
478                         vector<Track *> trks = create_straight(pos, dir, length, max(length/500, 0.001f));
479
480                         if(!trks.empty())
481                         {
482                                 for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
483                                         delete *i;
484                                 extend_tracks = trks;
485
486                                 map<ArticleNumber, unsigned> counts;
487                                 length = 0;
488                                 for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
489                                 {
490                                         length += (*i)->get_type().get_total_length();
491                                         ++counts[(*i)->get_type().get_article_number()];
492                                 }
493
494                                 string detail;
495                                 for(map<ArticleNumber, unsigned>::const_iterator i=counts.begin(); i!=counts.end(); ++i)
496                                 {
497                                         if(!detail.empty())
498                                                 detail += ", ";
499                                         detail += format("%dx %s", i->second, i->first);
500                                 }
501
502                                 signal_status.emit(format("Extend: %.0fmm (%s)", length*1000, detail));
503                         }
504                 }
505         }
506 }
507
508 void Manipulator::selection_changed()
509 {
510         if(mode)
511                 cancel();
512
513         objects.clear();
514         set<Object *> pending = selection.get_objects();
515         while(!pending.empty())
516         {
517                 for(set<Object *>::iterator i=pending.begin(); i!=pending.end(); )
518                 {
519                         if((*i)->get_parent() && pending.count((*i)->get_parent()))
520                                 ++i;
521                         else
522                         {
523                                 objects.push_back(*i);
524                                 pending.erase(i++);
525                         }
526                 }
527         }
528
529         update_neighbors();
530         update_objects();
531 }
532
533 void Manipulator::update_objects()
534 {
535         Vector minp, maxp;
536         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
537         {
538                 // XXX Use generic bounding box when it is implemented
539                 if(Track *track = dynamic_cast<Track *>(i->object))
540                 {
541                         unsigned n_endpoints = track->get_type().get_endpoints().size();
542                         for(unsigned j=0; j<n_endpoints; ++j)
543                         {
544                                 Vector p = track->get_snap_node(j).position;
545                                 if(i==objects.begin() && j==0)
546                                         minp = maxp = p;
547                                 else
548                                 {
549                                         minp.x = min(minp.x, p.x);
550                                         maxp.x = max(maxp.x, p.x);
551                                         minp.y = min(minp.y, p.y);
552                                         maxp.y = max(maxp.y, p.y);
553                                         minp.z = min(minp.z, p.z);
554                                 }
555                         }
556                 }
557                 else
558                 {
559                         const Vector &p = i->object->get_position();
560                         minp.x = min(minp.x, p.x);
561                         maxp.x = max(maxp.x, p.x);
562                         minp.y = min(minp.y, p.y);
563                         maxp.y = max(maxp.y, p.y);
564                         minp.z = min(minp.z, p.z);
565                 }
566         }
567
568         center = Vector((minp.x+maxp.x)/2, (minp.y+maxp.y)/2, minp.z);
569         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
570         {
571                 const Vector &p = i->object->get_position();
572                 i->pos = Vector(p.x-center.x, p.y-center.y, p.z-center.z);
573                 i->rot = i->object->get_rotation();
574         }
575 }
576
577 void Manipulator::update_neighbors()
578 {
579         neighbors.clear();
580         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
581         {
582                 Track *track = dynamic_cast<Track *>(i->object);
583                 if(!track)
584                         continue;
585
586                 const vector<Track *> &links = track->get_links();
587                 for(vector<Track *>::const_iterator j=links.begin(); j!=links.end(); ++j)
588                 {
589                         if(!*j)
590                                 continue;
591                         if(neighbors.count(*j))
592                                 continue;
593
594                         bool ok = true;
595                         for(vector<MObject>::iterator k=objects.begin(); (k!=objects.end() && ok); ++k)
596                                 ok = (k->object!=*j);
597
598                         if(ok)
599                                 neighbors.insert(*j);
600                 }
601         }
602 }
603
604 void Manipulator::set_slope(TrackOrder &track, float z, float dz)
605 {
606         const Vector &p = track.track->get_position();
607         if(track.rev)
608         {
609                 track.track->set_position(Vector(p.x, p.y, z+dz));
610                 track.track->set_slope(-dz);
611         }
612         else
613         {
614                 track.track->set_position(Vector(p.x, p.y, z));
615                 track.track->set_slope(dz);
616         }
617 }
618
619 vector<Track *> Manipulator::create_straight(const R2C2::Vector &start, float dir, float length, float limit)
620 {
621         const Catalogue::TrackMap &track_types = designer.get_catalogue().get_tracks();
622         std::map<float, const TrackType *> types_by_length;
623         unsigned preference = 0;
624         for(Catalogue::TrackMap::const_iterator i=track_types.begin(); i!=track_types.end(); ++i)
625         {
626                 const vector<TrackPart> &parts = i->second->get_parts();
627                 if(parts.size()!=1)
628                         continue;
629                 if(parts.front().is_curved() || parts.front().is_dead_end())
630                         continue;
631
632                 types_by_length[parts.front().get_length()] = i->second;
633                 preference = max(preference, i->second->get_autofit_preference());
634         }
635
636         vector<float> lengths;
637         float removed = 0;
638         while(length>limit)
639         {
640                 bool found = false;
641                 for(map<float, const TrackType *>::iterator i=types_by_length.end(); i!=types_by_length.begin(); )
642                 {
643                         --i;
644                         if(i->second->get_autofit_preference()<preference)
645                                 continue;
646                         if((!removed || i->first<removed) && i->first<length+limit)
647                         {
648                                 unsigned n = static_cast<unsigned>((length+limit)/i->first);
649                                 lengths.insert(lengths.end(), n, i->first);
650                                 length -= n*i->first;
651                                 found = true;
652                                 break;
653                         }
654                 }
655
656                 if(found)
657                         continue;
658
659                 if(lengths.empty())
660                 {
661                         if(preference>0)
662                         {
663                                 --preference;
664                                 removed = 0;
665                                 continue;
666                         }
667                         break;
668                 }
669
670                 length += lengths.back();
671                 removed = lengths.back();
672                 lengths.pop_back();
673         }
674
675         vector<Track *> trks;
676
677         if(!lengths.empty())
678         {
679                 Vector pos = start;
680                 float c = cos(dir);
681                 float s = sin(dir);
682                 for(vector<float>::iterator i=lengths.begin(); i!=lengths.end(); ++i)
683                 {
684                         Track *track = new Track(designer.get_layout(), *get_item(types_by_length, *i));
685                         track->set_position(pos);
686                         track->set_rotation(dir);
687
688                         if(!trks.empty())
689                                 track->link_to(*trks.back());
690                         trks.push_back(track);
691
692                         pos.x += c**i;
693                         pos.y += s**i;
694                 }
695         }
696
697         return trks;
698 }
699
700
701 Manipulator::MObject::MObject(Object *o):
702         object(o),
703         pos(object->get_position()),
704         rot(object->get_rotation())
705 { }