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