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