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