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