]> git.tdb.fi Git - r2c2.git/blob - source/designer/manipulator.cpp
Initial revision
[r2c2.git] / source / designer / manipulator.cpp
1 #include <algorithm>
2 #include <cmath>
3 #include <GL/gl.h>
4 #include "3d/layout.h"
5 #include "designer.h"
6 #include "manipulator.h"
7 #include "selection.h"
8
9 using namespace std;
10 using namespace Marklin;
11 using namespace Msp;
12
13 #include <iostream>
14
15 Manipulator::Manipulator(Designer &d):
16         designer(d),
17         selection(0),
18         wrap_rot(0),
19         mode(NONE),
20         angle(0)
21 { }
22
23 void Manipulator::set_selection(Selection *s)
24 {
25         selection_changed_conn.disconnect();
26
27         selection=s;
28         if(selection)
29                 selection_changed_conn=selection->signal_changed.connect(sigc::mem_fun(this, &Manipulator::selection_changed));
30
31         selection_changed();
32 }
33
34 void Manipulator::start_move()
35 {
36         if(mode)
37                 cancel();
38
39         move_origin=gpointer;
40
41         mode=MOVE;
42 }
43
44 void Manipulator::start_rotate()
45 {
46         if(mode)
47                 cancel();
48
49         rot_origin=atan2(gpointer.y-center.y, gpointer.x-center.x);
50
51         mode=ROTATE;
52 }
53
54 void Manipulator::start_elevate()
55 {
56         if(mode)
57                 cancel();
58
59         elev_origin=pointer_y;
60
61         mode=ELEVATE;
62 }
63
64 void Manipulator::duplicate()
65 {
66         if(mode)
67                 cancel();
68
69         TrackSeq new_tracks;
70         for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i)
71         {
72                 Track *track=i->track->copy();
73                 designer.get_layout()->add_track(track);
74                 new_tracks.push_back(track);
75         }
76
77         selection->clear();
78         for(TrackSeq::iterator i=new_tracks.begin(); i!=new_tracks.end(); ++i)
79         {
80                 selection->add_track(*i);
81                 for(TrackSeq::iterator j=i; j!=new_tracks.end(); ++j)
82                         if(j!=i)
83                                 (*i)->snap_to(**j, true);
84         }
85 }
86
87 void Manipulator::flatten()
88 {
89         if(mode)
90                 cancel();
91
92         if(tracks.empty()) return;
93
94         float z=0;
95         for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i)
96                 z+=i->track->get_position().z+i->track->get_slope()/2;
97         z/=tracks.size();
98
99         for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i)
100         {
101                 Point p=i->track->get_position();
102                 i->track->set_position(Point(p.x, p.y, z));
103                 i->track->set_slope(0);
104         }
105
106         for(TrackSeq::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
107                 (*i)->check_slope();
108
109         update_wrap();
110 }
111
112 void Manipulator::even_slope(bool smooth)
113 {
114         if(mode)
115                 cancel();
116
117         if(neighbors.size()!=2)
118                 return;
119
120         for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i)
121                 if(i->track->get_endpoints().size()!=2)
122                         return;
123
124         TrackSeq tracks2;
125         for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i)
126                 tracks2.push_back(i->track);
127
128         float total_len=0;
129
130         TrackOrderSeq order;
131         Track *cur=neighbors.front();
132         while(tracks2.size())
133         {
134                 bool rev=false;
135                 for(TrackSeq::iterator i=tracks2.begin(); i!=tracks2.end(); ++i)
136                 {
137                         const Track::EndpointSeq &epl=(*i)->get_endpoints();
138                         if(epl.front().link==cur)
139                         {
140                                 cur=*i;
141                                 tracks2.erase(i);
142                                 break;
143                         }
144                         else if(epl.back().link==cur)
145                         {
146                                 cur=*i;
147                                 rev=true;
148                                 tracks2.erase(i);
149                                 break;
150                         }
151                 }
152                 order.push_back(TrackOrder(cur, rev));
153                 total_len+=cur->get_length();
154         }
155
156         const Track::Endpoint *ep=neighbors.front()->get_endpoint_by_link(order.front().track);
157         float start_z=neighbors.front()->get_position().z+ep->pos.z;
158         ep=neighbors.back()->get_endpoint_by_link(order.back().track);
159         float end_z=neighbors.back()->get_position().z+ep->pos.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_length()*dir*cur_slope;
170                         set_slope(order.front(), start_z, dz);
171                         start_z+=dz;
172                         total_len-=order.front().track->get_length();
173                         order.erase(order.begin());
174
175                         dz=order.back().track->get_length()*dir*cur_slope;
176                         set_slope(order.back(), end_z-dz, dz);
177                         end_z-=dz;
178                         total_len-=order.back().track->get_length();
179                         order.erase(--order.end());
180                 }
181         }
182
183         float cur_z=start_z;
184         for(TrackOrderSeq::iterator i=order.begin(); i!=order.end(); ++i)
185         {
186                 float dz=i->track->get_length()*(end_z-start_z)/total_len;
187                 set_slope(*i, cur_z, dz);
188                 cur_z+=dz;
189         }
190
191         for(TrackSeq::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(MTrackSeq::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(TrackSeq::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
211                 (*i)->check_slope();
212
213         angle=0;
214         //snapped=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                 //snapped=0;
228
229                 for(TrackSeq::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
230                         for(MTrackSeq::iterator j=tracks.begin(); j!=tracks.end(); ++j)
231                                 j->track->break_link(**i);
232
233                 const TrackSeq &ltracks=designer.get_layout()->get_tracks();
234                 for(TrackSeq::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
235                 {
236                         bool ok=true;
237                         for(MTrackSeq::iterator j=tracks.begin(); (j!=tracks.end() && ok); ++j)
238                                 ok=(j->track!=*i);
239                         if(!ok) continue;
240
241                         for(MTrackSeq::iterator j=tracks.begin(); j!=tracks.end(); ++j)
242                                 j->track->snap_to(**i, true);
243                 }
244
245                 update_neighbors();
246
247                 signal_done.emit(true);
248         }
249 }
250
251 void Manipulator::pointer_motion(int, int y, float gx, float gy)
252 {
253         pointer_y=y;
254         gpointer=Point(gx, gy, 0);
255
256         if(mode==MOVE)
257         {
258                 Point delta(gpointer.x-move_origin.x, gpointer.y-move_origin.y, 0);
259
260                 wrap_pos=Point(center.x+delta.x, center.y+delta.y, center.z);
261                 for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i)
262                 {
263                         i->track->set_position(Point(wrap_pos.x+i->pos.x, wrap_pos.y+i->pos.y, wrap_pos.z+i->pos.z));
264                         i->track->set_rotation(i->rot);
265                 }
266
267                 const TrackSeq &ltracks=designer.get_layout()->get_tracks();
268                 MTrack *snapped=0;
269                 for(TrackSeq::const_iterator i=ltracks.begin(); (i!=ltracks.end() && !snapped); ++i)
270                 {
271                         bool ok=true;
272                         for(MTrackSeq::iterator j=tracks.begin(); (j!=tracks.end() && ok); ++j)
273                                 ok=(j->track!=*i);
274                         if(!ok) continue;
275
276                         for(MTrackSeq::iterator j=tracks.begin(); (j!=tracks.end() && !snapped); ++j)
277                                 if(j->track->snap_to(**i, false))
278                                         snapped=&*j;
279                 }
280
281                 if(snapped)
282                 {
283                         float da=snapped->track->get_rotation()-snapped->rot;
284                         float c=cos(da);
285                         float s=sin(da);
286                         const Point &sp=snapped->track->get_position();
287                         for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i)
288                         {
289                                 if(&*i==snapped)
290                                         continue;
291
292                                 Point dp(i->pos.x-snapped->pos.x, i->pos.y-snapped->pos.y, 0);
293                                 i->track->set_position(Point(sp.x+c*dp.x-s*dp.y, sp.y+s*dp.x+c*dp.y, sp.z));
294                                 i->track->set_rotation(i->rot+da);
295                         }
296                 }
297         }
298         else if(mode==ROTATE)
299         {
300                 float a=atan2(gpointer.y-center.y, gpointer.x-center.x);
301                 angle+=a-rot_origin;
302                 rot_origin=a;
303
304                 wrap_rot=angle;
305                 for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i)
306                 {
307                         float c=cos(angle);
308                         float s=sin(angle);
309                         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));
310                         i->track->set_rotation(angle+i->rot);
311                 }
312         }
313         else if(mode==ELEVATE)
314         {
315                 float dz=(y-elev_origin)/1000.;
316
317                 ostringstream ss;
318                 ss.precision(3);
319                 ss<<"Elevation: "<<dz*1000<<"mm ("<<(center.z+dz)*1000<<"mm)";
320                 signal_status.emit(ss.str());
321
322                 wrap_pos.z=center.z+dz;
323                 for(MTrackSeq::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(TrackSeq::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         if(selection)
367         {
368                 const Selection::TrackSet &stracks=selection->get_tracks();
369                 tracks.insert(tracks.end(), stracks.begin(), stracks.end());
370         }
371
372         update_neighbors();
373         update_wrap();
374 }
375
376 void Manipulator::update_wrap()
377 {
378         wrap.clear();
379         float min_x=0,max_x=0;
380         float min_y=0,max_y=0;
381         for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i)
382         {
383                 Track3D *t3d=designer.get_layout_3d()->get_track(i->track);
384
385                 TrackWrap tw;
386                 float min_area=100;
387                 for(float a=0; a<M_PI; a+=0.01)
388                 {
389                         Point minp,maxp;
390                         t3d->get_bounds(a, minp, maxp);
391                         float area=(maxp.x-minp.x)*(maxp.y-minp.y);
392                         if(area<min_area)
393                         {
394                                 float c=cos(a);
395                                 float s=sin(a);
396                                 float x=(minp.x+maxp.x)/2;
397                                 float y=(minp.y+maxp.y)/2;
398                                 tw.pos=Point(c*x-s*y, s*x+c*y, (minp.z+maxp.z)/2);
399                                 tw.rot=a;
400                                 tw.width=maxp.x-minp.x+0.01;
401                                 tw.height=maxp.y-minp.y+0.01;
402
403                                 min_area=area;
404                         }
405                 }
406
407                 if(i==tracks.begin())
408                 {
409                         min_x=max_x=tw.pos.x;
410                         min_y=max_y=tw.pos.y;
411                 }
412                 else
413                 {
414                         min_x=min(min_x, tw.pos.x);
415                         max_x=max(max_x, tw.pos.x);
416                         min_y=min(min_y, tw.pos.y);
417                         max_y=max(max_y, tw.pos.y);
418                 }
419                 wrap.push_back(tw);
420         }
421
422         center=Point((min_x+max_x)/2, (min_y+max_y)/2, 0);
423         wrap_pos=center;
424         wrap_rot=0;
425         for(MTrackSeq::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);
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         }
435 }
436
437 void Manipulator::update_neighbors()
438 {
439         neighbors.clear();
440         for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i)
441         {
442                 const Track::EndpointSeq &epl=i->track->get_endpoints();
443                 for(Track::EndpointSeq::const_iterator j=epl.begin(); j!=epl.end(); ++j)
444                 {
445                         if(!j->link)
446                                 continue;
447                         if(find(neighbors.begin(), neighbors.end(), j->link)!=neighbors.end())
448                                 continue;
449
450                         bool ok=true;
451                         for(MTrackSeq::iterator k=tracks.begin(); (k!=tracks.end() && ok); ++k)
452                                 ok=(k->track!=j->link);
453
454                         if(ok)
455                                 neighbors.push_back(j->link);
456                 }
457         }
458 }
459
460 void Manipulator::set_slope(TrackOrder &track, float z, float dz)
461 {
462         const Point &p=track.track->get_position();
463         if(track.rev)
464         {
465                 track.track->set_position(Point(p.x, p.y, z+dz));
466                 track.track->set_slope(-dz);
467         }
468         else
469         {
470                 track.track->set_position(Point(p.x, p.y, z));
471                 track.track->set_slope(dz);
472         }
473 }
474
475 Manipulator::MTrack::MTrack(Track *t):
476         track(t),
477         pos(track->get_position()),
478         rot(track->get_rotation())
479 { }