]> git.tdb.fi Git - r2c2.git/blob - source/designer/manipulator.cpp
b61d9a7ae5e28642344554f01b001f9887480c12
[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                 unsigned nsn = i->object->get_n_snap_nodes();
105                 for(unsigned j=0; j<nsn; ++j)
106                         z += i->object->get_snap_node(j).position.z/nsn;
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_tilt(Angle::zero());
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                         Angle tilt = Geometry::atan(cur_slope);
181
182                         set_slope(order.front(), start_z, tilt);
183                         start_z += order.front().track->get_type().get_path_length(0)*dir*cur_slope;
184                         total_len -= order.front().track->get_type().get_path_length(0);
185                         order.pop_front();
186
187                         end_z -= order.back().track->get_type().get_path_length(0)*dir*cur_slope;
188                         set_slope(order.back(), end_z, tilt);
189                         total_len -= order.back().track->get_type().get_path_length(0);
190                         order.pop_back();
191                 }
192         }
193
194         float cur_z = start_z;
195         Angle tilt = Geometry::atan((end_z-start_z)/total_len);
196         for(list<TrackOrder>::iterator i=order.begin(); i!=order.end(); ++i)
197         {
198                 set_slope(*i, cur_z, tilt);
199                 cur_z += i->track->get_type().get_path_length(0)*(end_z-start_z)/total_len;
200         }
201
202         update_objects();
203 }
204
205 void Manipulator::connect()
206 {
207         if(objects.size()!=2)
208         {
209                 signal_status.emit("Exactly two tracks must be selected");
210                 return;
211         }
212
213         Track *track1 = dynamic_cast<Track *>(objects.front().object);
214         Track *track2 = dynamic_cast<Track *>(objects.back().object);
215         if(!track1 || !track2)
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         Snap sn1;
224         bool ok = false;
225         float gap = 0;
226         for(unsigned i=0; i<track1->get_type().get_endpoints().size(); ++i)
227         {
228                 if(track1->get_link(i))
229                         continue;
230
231                 sn1 = track1->get_snap_node(i);
232
233                 for(unsigned j=0; j<track2->get_type().get_endpoints().size(); ++j)
234                 {
235                         if(track2->get_link(j))
236                                 continue;
237
238                         Snap sn2 = track2->get_snap_node(j);
239
240                         float dz = sn2.position.z-sn1.position.z;
241                         if(abs(dz)>0.02)
242                                 continue;
243
244                         Angle adiff = wrap_balanced(sn1.rotation+Angle::half_turn()-sn2.rotation);
245                         if(abs(adiff).radians()>0.01)
246                                 continue;
247
248                         Vector delta = rotated_vector(sn2.position-sn1.position, -sn1.rotation);
249                         if(abs(delta.y)>limit)
250                                 continue;
251
252                         gap = delta.x;
253                         if(gap<0)
254                                 continue;
255
256                         ok = true;
257                 }
258
259                 if(ok)
260                         break;
261         }
262
263         if(!ok)
264         {
265                 signal_status.emit("No aligned endpoints found");
266                 return;
267         }
268
269         vector<Track *> trks = create_straight(sn1.position, sn1.rotation, gap, limit);
270
271         if(trks.empty())
272         {
273                 signal_status.emit("No connection possible");
274                 return;
275         }
276
277         trks.front()->link_to(*track1);
278         trks.back()->link_to(*track2);
279
280         selection.replace(trks.begin(), trks.end());
281 }
282
283 void Manipulator::cancel()
284 {
285         if(!mode)
286                 return;
287         mode = NONE;
288
289         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
290         {
291                 i->object->set_position(center+i->pos);
292                 i->object->set_rotation(i->rot);
293         }
294
295         for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
296                 delete *i;
297         extend_tracks.clear();
298
299         angle = Angle::zero();
300
301         signal_done.emit(false);
302 }
303
304 void Manipulator::button_press(unsigned btn)
305 {
306         if(!mode)
307                 return;
308
309         if(btn==3)
310                 cancel();
311         else if(btn==1)
312         {
313                 Mode m = mode;
314                 mode = NONE;
315                 angle = Angle::zero();
316
317                 if(m!=EXTEND)
318                 {
319                         for(set<Object *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
320                                 for(vector<MObject>::iterator j=objects.begin(); j!=objects.end(); ++j)
321                                         j->object->break_link(**i);
322                 }
323
324                 const set<Track *> &ltracks = designer.get_layout().get_all<Track>();
325                 for(set<Track *>::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
326                 {
327                         bool ok = true;
328                         for(vector<MObject>::iterator j=objects.begin(); (j!=objects.end() && ok); ++j)
329                                 ok = (j->object!=*i);
330                         if(!ok) continue;
331
332                         for(vector<MObject>::iterator j=objects.begin(); j!=objects.end(); ++j)
333                                 j->object->link_to(**i);
334                 }
335
336                 if(m==EXTEND)
337                 {
338                         selection.replace(extend_tracks.begin(), extend_tracks.end());
339                         extend_tracks.clear();
340                 }
341                 else
342                 {
343                         update_objects();
344                         update_neighbors();
345                 }
346
347                 signal_done.emit(true);
348         }
349 }
350
351 void Manipulator::axis_motion(unsigned axis, float value, float)
352 {
353         if(axis==0)
354                 pointer.x = value;
355         else if(axis==1)
356                 pointer.y = value;
357         gpointer = designer.map_pointer_to_ground(pointer);
358
359         if(mode==MOVE)
360         {
361                 Vector offset = center+gpointer-move_origin;
362                 for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
363                 {
364                         i->object->set_position(offset+i->pos);
365                         i->object->set_rotation(i->rot);
366                 }
367
368                 const set<Track *> &ltracks = designer.get_layout().get_all<Track>();
369                 float limit = max(designer.get_layout().get_catalogue().get_gauge(),
370                         designer.get_camera_controller().get_view_scale()/100.0f);
371                 MObject *snapped = 0;
372                 for(set<Track *>::const_iterator i=ltracks.begin(); (i!=ltracks.end() && !snapped); ++i)
373                 {
374                         bool ok = true;
375                         for(vector<MObject>::iterator j=objects.begin(); (j!=objects.end() && ok); ++j)
376                                 ok = (j->object!=*i);
377                         if(!ok)
378                                 continue;
379
380                         for(vector<MObject>::iterator j=objects.begin(); (j!=objects.end() && !snapped); ++j)
381                                 if(j->object->snap_to(**i, limit))
382                                         snapped = &*j;
383                 }
384
385                 if(snapped)
386                 {
387                         Angle da = snapped->object->get_rotation()-snapped->rot;
388                         Transform trans = Transform::rotation(da, Vector(0, 0, 1));
389                         const Vector &sp = snapped->object->get_position();
390                         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
391                         {
392                                 if(&*i==snapped)
393                                         continue;
394
395                                 i->object->set_position(sp+trans.transform(i->pos-snapped->pos));
396                                 i->object->set_rotation(i->rot+da);
397                         }
398                 }
399         }
400         else if(mode==ROTATE)
401         {
402                 Angle a = Geometry::atan2(gpointer.y-center.y, gpointer.x-center.x);
403                 angle += a-rot_origin;
404                 rot_origin = a;
405
406                 Transform trans = Transform::rotation(angle, Vector(0, 0, 1));
407                 for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
408                 {
409                         i->object->set_position(center+trans.transform(i->pos));
410                         i->object->set_rotation(angle+i->rot);
411                 }
412         }
413         else if(mode==ELEVATE && axis==1)
414         {
415                 float dz = pointer.y-elev_origin;
416
417                 signal_status.emit(format("Elevation: %+.0fmm (%.0fmm)", dz*1000, (center.z+dz)*1000));
418
419                 for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
420                         i->object->set_position(center+i->pos+Vector(0, 0, dz));
421         }
422         else if(mode==EXTEND)
423         {
424                 Vector pos;
425                 Angle dir;
426                 float length = 0;
427                 for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
428                 {
429                         unsigned nls = i->object->get_n_link_slots();
430                         for(unsigned j=0; j<nls; ++j)
431                         {
432                                 if(i->object->get_link(j))
433                                         continue;
434
435                                 Snap sn = i->object->get_snap_node(j);
436                                 Vector delta = rotated_vector(gpointer-sn.position, -sn.rotation);
437
438                                 if(delta.x<length)
439                                         continue;
440
441                                 pos = sn.position;
442                                 dir = sn.rotation;
443                                 length = delta.x;
444                         }
445                 }
446
447                 if(length)
448                 {
449                         vector<Track *> trks = create_straight(pos, dir, length, max(length/500, 0.001f));
450
451                         if(!trks.empty())
452                         {
453                                 for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
454                                         delete *i;
455                                 extend_tracks = trks;
456
457                                 map<ArticleNumber, unsigned> counts;
458                                 length = 0;
459                                 for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
460                                 {
461                                         length += (*i)->get_type().get_total_length();
462                                         ++counts[(*i)->get_type().get_article_number()];
463                                 }
464
465                                 string detail;
466                                 for(map<ArticleNumber, unsigned>::const_iterator i=counts.begin(); i!=counts.end(); ++i)
467                                 {
468                                         if(!detail.empty())
469                                                 detail += ", ";
470                                         detail += format("%dx %s", i->second, i->first);
471                                 }
472
473                                 signal_status.emit(format("Extend: %.0fmm (%s)", length*1000, detail));
474                         }
475                 }
476         }
477 }
478
479 void Manipulator::selection_changed()
480 {
481         if(mode)
482                 cancel();
483
484         objects.clear();
485         set<Object *> pending = selection.get_objects();
486         while(!pending.empty())
487         {
488                 for(set<Object *>::iterator i=pending.begin(); i!=pending.end(); )
489                 {
490                         if((*i)->get_parent() && pending.count((*i)->get_parent()))
491                                 ++i;
492                         else
493                         {
494                                 objects.push_back(*i);
495                                 pending.erase(i++);
496                         }
497                 }
498         }
499
500         update_neighbors();
501         update_objects();
502 }
503
504 void Manipulator::update_objects()
505 {
506         Geometry::BoundingBox<float, 3> bbox;
507         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
508                 bbox = bbox|i->object->get_bounding_box();
509
510         const Vector &minp = bbox.get_minimum_point();
511         const Vector &maxp = bbox.get_maximum_point();
512
513         center = (minp+maxp)/2.0f;
514         center.z = minp.z;
515         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
516         {
517                 i->pos = i->object->get_position()-center;
518                 i->rot = i->object->get_rotation();
519         }
520 }
521
522 void Manipulator::update_neighbors()
523 {
524         neighbors.clear();
525         for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
526         {
527                 unsigned nls = i->object->get_n_link_slots();
528                 for(unsigned j=0; j<nls; ++j)
529                 {
530                         Object *linked = i->object->get_link(j);
531                         if(!linked)
532                                 continue;
533                         if(neighbors.count(linked))
534                                 continue;
535
536                         bool ok = true;
537                         for(vector<MObject>::iterator k=objects.begin(); (k!=objects.end() && ok); ++k)
538                                 ok = (k->object!=linked);
539
540                         if(ok)
541                                 neighbors.insert(linked);
542                 }
543         }
544 }
545
546 void Manipulator::set_slope(TrackOrder &track, float z, const Angle &tilt)
547 {
548         const Vector &p = track.track->get_position();
549         float dz = tan(tilt)*track.track->get_type().get_path_length(0);
550         if(track.rev)
551         {
552                 track.track->set_position(Vector(p.x, p.y, z+dz));
553                 track.track->set_tilt(-tilt);
554         }
555         else
556         {
557                 track.track->set_position(Vector(p.x, p.y, z));
558                 track.track->set_tilt(tilt);
559         }
560 }
561
562 vector<Track *> Manipulator::create_straight(const R2C2::Vector &start, const Angle &dir, float length, float limit)
563 {
564         const Catalogue::TrackMap &track_types = designer.get_catalogue().get_tracks();
565         std::map<float, const TrackType *> types_by_length;
566         unsigned preference = 0;
567         for(Catalogue::TrackMap::const_iterator i=track_types.begin(); i!=track_types.end(); ++i)
568         {
569                 const vector<TrackPart> &parts = i->second->get_parts();
570                 if(parts.size()!=1)
571                         continue;
572                 if(parts.front().is_curved() || parts.front().is_dead_end())
573                         continue;
574
575                 types_by_length[parts.front().get_length()] = i->second;
576                 preference = max(preference, i->second->get_autofit_preference());
577         }
578
579         vector<float> lengths;
580         float removed = 0;
581         while(length>limit)
582         {
583                 bool found = false;
584                 for(map<float, const TrackType *>::iterator i=types_by_length.end(); i!=types_by_length.begin(); )
585                 {
586                         --i;
587                         if(i->second->get_autofit_preference()<preference)
588                                 continue;
589                         if((!removed || i->first<removed) && i->first<length+limit)
590                         {
591                                 unsigned n = static_cast<unsigned>((length+limit)/i->first);
592                                 lengths.insert(lengths.end(), n, i->first);
593                                 length -= n*i->first;
594                                 found = true;
595                                 break;
596                         }
597                 }
598
599                 if(found)
600                         continue;
601
602                 if(lengths.empty())
603                 {
604                         if(preference>0)
605                         {
606                                 --preference;
607                                 removed = 0;
608                                 continue;
609                         }
610                         break;
611                 }
612
613                 length += lengths.back();
614                 removed = lengths.back();
615                 lengths.pop_back();
616         }
617
618         vector<Track *> trks;
619
620         if(!lengths.empty())
621         {
622                 Vector pos = start;
623                 Transform trans = Transform::rotation(dir, Vector(0, 0, 1));
624                 for(vector<float>::iterator i=lengths.begin(); i!=lengths.end(); ++i)
625                 {
626                         Track *track = new Track(designer.get_layout(), *get_item(types_by_length, *i));
627                         track->set_position(pos);
628                         track->set_rotation(dir);
629
630                         if(!trks.empty())
631                                 track->link_to(*trks.back());
632                         trks.push_back(track);
633
634                         pos += trans.transform(Vector(*i, 0, 0));
635                 }
636         }
637
638         return trks;
639 }
640
641
642 Manipulator::MObject::MObject(Object *o):
643         object(o),
644         pos(object->get_position()),
645         rot(object->get_rotation())
646 { }