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