]> git.tdb.fi Git - r2c2.git/blob - source/designer/manipulator.cpp
Add an internal layout to Catalogue for selecting tracks
[r2c2.git] / source / designer / manipulator.cpp
1 /* $Id$
2
3 This file is part of the MSP Märklin suite
4 Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6 */
7
8 #include <algorithm>
9 #include <cmath>
10 #include <GL/gl.h>
11 #include <msp/strings/formatter.h>
12 #include "3d/layout.h"
13 #include "libmarklin/tracktype.h"
14 #include "designer.h"
15 #include "manipulator.h"
16 #include "selection.h"
17
18 using namespace std;
19 using namespace Marklin;
20 using namespace Msp;
21
22 Manipulator::Manipulator(Designer &d, Selection &s):
23         designer(d),
24         selection(s),
25         wrap_rot(0),
26         mode(NONE),
27         angle(0)
28 {
29         selection.signal_changed.connect(sigc::mem_fun(this, &Manipulator::selection_changed));
30 }
31
32 void Manipulator::start_move()
33 {
34         if(mode)
35                 cancel();
36
37         move_origin = gpointer;
38
39         mode = MOVE;
40 }
41
42 void Manipulator::start_rotate()
43 {
44         if(mode)
45                 cancel();
46
47         rot_origin = atan2(gpointer.y-center.y, gpointer.x-center.x);
48
49         mode = ROTATE;
50 }
51
52 void Manipulator::start_elevate()
53 {
54         if(mode)
55                 cancel();
56
57         elev_origin = pointer_y;
58
59         mode = ELEVATE;
60 }
61
62 void Manipulator::duplicate()
63 {
64         if(mode)
65                 cancel();
66
67         list<Track *> new_tracks;
68         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
69         {
70                 Track *track = i->track->copy();
71                 designer.get_layout()->add_track(*track);
72                 new_tracks.push_back(track);
73         }
74
75         selection.clear();
76         for(list<Track *>::iterator i=new_tracks.begin(); i!=new_tracks.end(); ++i)
77         {
78                 selection.add_track(*i);
79                 for(list<Track *>::iterator j=i; j!=new_tracks.end(); ++j)
80                         if(j!=i)
81                                 (*i)->snap_to(**j, true);
82         }
83 }
84
85 void Manipulator::flatten()
86 {
87         if(mode)
88                 cancel();
89
90         if(tracks.empty()) return;
91
92         float z = 0;
93         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
94                 z += i->track->get_position().z+i->track->get_slope()/2;
95         z /= tracks.size();
96
97         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
98         {
99                 Point p = i->track->get_position();
100                 i->track->set_position(Point(p.x, p.y, z));
101                 i->track->set_slope(0);
102         }
103
104         for(set<Track *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
105                 (*i)->check_slope();
106
107         update_wrap();
108 }
109
110 void Manipulator::even_slope(bool smooth)
111 {
112         if(mode)
113                 cancel();
114
115         if(neighbors.size()!=2)
116                 return;
117
118         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
119                 if(i->track->get_type().get_endpoints().size()!=2)
120                         return;
121
122         list<Track *> tracks2;
123         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
124                 tracks2.push_back(i->track);
125
126         float total_len = 0;
127
128         list<TrackOrder> order;
129         Track *cur = *neighbors.begin();
130         while(tracks2.size())
131         {
132                 bool rev = false;
133                 for(list<Track *>::iterator i=tracks2.begin(); i!=tracks2.end(); ++i)
134                 {
135                         const vector<Track *> &links = (*i)->get_links();
136                         if(links[0]==cur)
137                         {
138                                 cur = *i;
139                                 tracks2.erase(i);
140                                 break;
141                         }
142                         else if(links[1]==cur)
143                         {
144                                 cur = *i;
145                                 rev = true;
146                                 tracks2.erase(i);
147                                 break;
148                         }
149                 }
150                 order.push_back(TrackOrder(cur, rev));
151                 total_len += cur->get_type().get_total_length();
152         }
153
154         set<Track *>::iterator nb = neighbors.begin();
155         int epi = (*nb)->get_endpoint_by_link(*order.front().track);
156         float start_z = (*nb)->get_endpoint_position(epi).z;
157         ++nb;
158         epi = (*nb)->get_endpoint_by_link(*order.back().track);
159         float end_z = (*nb)->get_endpoint_position(epi).z;
160
161         if(smooth)
162         {
163                 float dir = (end_z>start_z)?1:-1;
164                 float cur_slope = 0;
165                 while((end_z-start_z)*dir/total_len>cur_slope+0.025 && order.size()>2)
166                 {
167                         cur_slope += 0.025;
168
169                         float dz = order.front().track->get_type().get_total_length()*dir*cur_slope;
170                         set_slope(order.front(), start_z, dz);
171                         start_z += dz;
172                         total_len -= order.front().track->get_type().get_total_length();
173                         order.erase(order.begin());
174
175                         dz = order.back().track->get_type().get_total_length()*dir*cur_slope;
176                         set_slope(order.back(), end_z-dz, dz);
177                         end_z -= dz;
178                         total_len -= order.back().track->get_type().get_total_length();
179                         order.erase(--order.end());
180                 }
181         }
182
183         float cur_z = start_z;
184         for(list<TrackOrder>::iterator i=order.begin(); i!=order.end(); ++i)
185         {
186                 float dz = i->track->get_type().get_total_length()*(end_z-start_z)/total_len;
187                 set_slope(*i, cur_z, dz);
188                 cur_z += dz;
189         }
190
191         for(set<Track *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
192                 (*i)->check_slope();
193
194         update_wrap();
195 }
196
197 void Manipulator::cancel()
198 {
199         if(!mode)
200                 return;
201         mode = NONE;
202
203         wrap_pos = center;
204         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
205         {
206                 i->track->set_position(Point(center.x+i->pos.x, center.y+i->pos.y, center.z+i->pos.z));
207                 i->track->set_rotation(i->rot);
208         }
209
210         for(set<Track *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
211                 (*i)->check_slope();
212
213         angle = 0;
214         wrap_rot = 0;
215
216         signal_done.emit(false);
217 }
218
219 void Manipulator::button_press(int, int, float, float, unsigned btn)
220 {
221         if(btn==3)
222                 cancel();
223         else if(mode)
224         {
225                 mode = NONE;
226                 update_wrap();
227                 angle = 0;
228
229                 for(set<Track *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
230                         for(vector<MTrack>::iterator j=tracks.begin(); j!=tracks.end(); ++j)
231                                 j->track->break_link(**i);
232
233                 const set<Track *> &ltracks = designer.get_layout()->get_tracks();
234                 for(set<Track *>::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
235                 {
236                         bool ok = true;
237                         for(vector<MTrack>::iterator j=tracks.begin(); (j!=tracks.end() && ok); ++j)
238                                 ok = (j->track!=*i);
239                         if(!ok) continue;
240
241                         for(vector<MTrack>::iterator j=tracks.begin(); j!=tracks.end(); ++j)
242                                 j->track->snap_to(**i, true);
243                 }
244
245                 for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
246                         i->rot = i->track->get_rotation();
247
248                 update_neighbors();
249
250                 signal_done.emit(true);
251         }
252 }
253
254 void Manipulator::pointer_motion(int, int y, float gx, float gy)
255 {
256         pointer_y = y;
257         gpointer = Point(gx, gy, 0);
258
259         if(mode==MOVE)
260         {
261                 Point delta(gpointer.x-move_origin.x, gpointer.y-move_origin.y, 0);
262
263                 wrap_pos = Point(center.x+delta.x, center.y+delta.y, center.z);
264                 for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
265                 {
266                         i->track->set_position(Point(wrap_pos.x+i->pos.x, wrap_pos.y+i->pos.y, wrap_pos.z+i->pos.z));
267                         i->track->set_rotation(i->rot);
268                 }
269
270                 const set<Track *> &ltracks = designer.get_layout()->get_tracks();
271                 MTrack *snapped = 0;
272                 for(set<Track *>::const_iterator i=ltracks.begin(); (i!=ltracks.end() && !snapped); ++i)
273                 {
274                         bool ok = true;
275                         for(vector<MTrack>::iterator j=tracks.begin(); (j!=tracks.end() && ok); ++j)
276                                 ok = (j->track!=*i);
277                         if(!ok) continue;
278
279                         for(vector<MTrack>::iterator j=tracks.begin(); (j!=tracks.end() && !snapped); ++j)
280                                 if(j->track->snap_to(**i, false))
281                                         snapped = &*j;
282                 }
283
284                 if(snapped)
285                 {
286                         float da = snapped->track->get_rotation()-snapped->rot;
287                         float c = cos(da);
288                         float s = sin(da);
289                         const Point &sp = snapped->track->get_position();
290                         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
291                         {
292                                 if(&*i==snapped)
293                                         continue;
294
295                                 Point dp(i->pos.x-snapped->pos.x, i->pos.y-snapped->pos.y, 0);
296                                 i->track->set_position(Point(sp.x+c*dp.x-s*dp.y, sp.y+s*dp.x+c*dp.y, sp.z));
297                                 i->track->set_rotation(i->rot+da);
298                         }
299                 }
300         }
301         else if(mode==ROTATE)
302         {
303                 float a = atan2(gpointer.y-center.y, gpointer.x-center.x);
304                 angle += a-rot_origin;
305                 rot_origin = a;
306
307                 wrap_rot = angle;
308                 for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
309                 {
310                         float c = cos(angle);
311                         float s = sin(angle);
312                         i->track->set_position(Point(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));
313                         i->track->set_rotation(angle+i->rot);
314                 }
315         }
316         else if(mode==ELEVATE)
317         {
318                 float dz = (y-elev_origin)/1000.;
319
320                 signal_status.emit(format("Elevation: %+.0fmm (%.0fmm)", dz*1000, (center.z+dz)*1000));
321
322                 wrap_pos.z = center.z+dz;
323                 for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
324                         i->track->set_position(Point(center.x+i->pos.x, center.y+i->pos.y, center.z+i->pos.z+dz));
325
326                 for(set<Track *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
327                         (*i)->check_slope();
328         }
329 }
330
331 void Manipulator::render()
332 {
333         glPushMatrix();
334         glTranslatef(wrap_pos.x, wrap_pos.y, wrap_pos.z);
335         glRotatef(wrap_rot*180/M_PI, 0, 0, 1);
336
337         glLineWidth(2);
338         glColor4f(0, 1, 0, 0.5);
339         for(list<TrackWrap>::iterator i=wrap.begin(); i!=wrap.end(); ++i)
340         {
341                 glPushMatrix();
342                 glTranslatef(i->pos.x, i->pos.y, i->pos.z);
343                 glRotatef(i->rot*180/M_PI, 0, 0, 1);
344
345                 glBegin(GL_LINE_LOOP);
346                 glVertex2f(-i->width/2, -i->height/2);
347                 glVertex2f(i->width/2, -i->height/2);
348                 glVertex2f(i->width/2, i->height/2);
349                 glVertex2f(-i->width/2, i->height/2);
350                 glEnd();
351
352                 glPopMatrix();
353         }
354
355         glPopMatrix();
356 }
357
358 /*** private ***/
359
360 void Manipulator::selection_changed()
361 {
362         if(mode)
363                 cancel();
364
365         tracks.clear();
366         const set<Track *> &stracks = selection.get_tracks();
367         tracks.insert(tracks.end(), stracks.begin(), stracks.end());
368
369         update_neighbors();
370         update_wrap();
371 }
372
373 void Manipulator::update_wrap()
374 {
375         wrap.clear();
376         float min_x = 0, max_x = 0;
377         float min_y = 0, max_y = 0;
378         float min_z = 0;
379         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
380         {
381                 Track3D &t3d = designer.get_layout_3d()->get_track(*i->track);
382
383                 TrackWrap tw;
384                 float min_area = 100;
385                 for(float a=0; a<M_PI; a+=0.01)
386                 {
387                         Point minp, maxp;
388                         t3d.get_bounds(a, minp, maxp);
389                         float area = (maxp.x-minp.x)*(maxp.y-minp.y);
390                         if(area<min_area)
391                         {
392                                 float c = cos(a);
393                                 float s = sin(a);
394                                 float x = (minp.x+maxp.x)/2;
395                                 float y = (minp.y+maxp.y)/2;
396                                 tw.pos = Point(c*x-s*y, s*x+c*y, minp.z);
397                                 tw.rot = a;
398                                 tw.width = maxp.x-minp.x+0.01;
399                                 tw.height = maxp.y-minp.y+0.01;
400
401                                 min_area = area;
402                         }
403                 }
404
405                 if(i==tracks.begin())
406                 {
407                         min_x = max_x = tw.pos.x;
408                         min_y = max_y = tw.pos.y;
409                         min_z = tw.pos.z;
410                 }
411                 else
412                 {
413                         min_x = min(min_x, tw.pos.x);
414                         max_x = max(max_x, tw.pos.x);
415                         min_y = min(min_y, tw.pos.y);
416                         max_y = max(max_y, tw.pos.y);
417                         min_z = min(min_z, tw.pos.z);
418                 }
419                 wrap.push_back(tw);
420         }
421
422         center = Point((min_x+max_x)/2, (min_y+max_y)/2, min_z);
423         wrap_pos = center;
424         wrap_rot = 0;
425         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
426         {
427                 const Point &tp = i->track->get_position();
428                 i->pos = Point(tp.x-center.x, tp.y-center.y, tp.z-center.z);
429         }
430         for(list<TrackWrap>::iterator i=wrap.begin(); i!=wrap.end(); ++i)
431         {
432                 i->pos.x -= center.x;
433                 i->pos.y -= center.y;
434                 i->pos.z -= center.z;
435         }
436 }
437
438 void Manipulator::update_neighbors()
439 {
440         neighbors.clear();
441         for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
442         {
443                 const vector<Track *> &links = i->track->get_links();
444                 for(vector<Track *>::const_iterator j=links.begin(); j!=links.end(); ++j)
445                 {
446                         if(!*j)
447                                 continue;
448                         if(neighbors.count(*j))
449                                 continue;
450
451                         bool ok = true;
452                         for(vector<MTrack>::iterator k=tracks.begin(); (k!=tracks.end() && ok); ++k)
453                                 ok = (k->track!=*j);
454
455                         if(ok)
456                                 neighbors.insert(*j);
457                 }
458         }
459 }
460
461 void Manipulator::set_slope(TrackOrder &track, float z, float dz)
462 {
463         const Point &p = track.track->get_position();
464         if(track.rev)
465         {
466                 track.track->set_position(Point(p.x, p.y, z+dz));
467                 track.track->set_slope(-dz);
468         }
469         else
470         {
471                 track.track->set_position(Point(p.x, p.y, z));
472                 track.track->set_slope(dz);
473         }
474 }
475
476 Manipulator::MTrack::MTrack(Track *t):
477         track(t),
478         pos(track->get_position()),
479         rot(track->get_rotation())
480 { }