]> git.tdb.fi Git - r2c2.git/blob - source/designer/extendtool.cpp
Split the Manipulator class into several Tools
[r2c2.git] / source / designer / extendtool.cpp
1 #include <msp/strings/format.h>
2 #include "libr2c2/articlenumber.h"
3 #include "designer.h"
4 #include "extendtool.h"
5
6 using namespace std;
7 using namespace Msp;
8 using namespace R2C2;
9
10 ExtendTool::ExtendTool(Designer &d, Input::Mouse &m, const set<Object *> &o):
11         Tool(d, m),
12         objects(o),
13         accepted(false)
14 {
15         bool ok = false;
16         for(set<Object *>::const_iterator i=objects.begin(); (!ok && i!=objects.end()); ++i)
17                 if(Track *track = dynamic_cast<Track *>(*i))
18                 {
19                         const vector<Track *> &links = track->get_links();
20                         for(vector<Track *>::const_iterator j=links.begin(); (!ok && j!=links.end()); ++j)
21                                 ok = !*j;
22                 }
23
24         if(!ok)
25         {
26                 done = true;
27                 set_status("No free endpoints");
28         }
29 }
30
31 ExtendTool::~ExtendTool()
32 {
33         if(!accepted)
34         {
35                 for(vector<R2C2::Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
36                         delete *i;
37         }
38 }
39
40 void ExtendTool::connect()
41 {
42         if(objects.size()!=2)
43         {
44                 signal_status.emit("Exactly two tracks must be selected");
45                 return;
46         }
47
48         Track *track1 = dynamic_cast<Track *>(*objects.begin());
49         Track *track2 = dynamic_cast<Track *>(*--objects.end());
50         if(!track1 || !track2)
51         {
52                 signal_status.emit("Exactly two tracks must be selected");
53                 return;
54         }
55
56         float limit = designer.get_layout().get_catalogue().get_gauge()/10;
57
58         Snap sn1;
59         bool ok = false;
60         float gap = 0;
61         unsigned nls1 = track1->get_n_link_slots();
62         unsigned nls2 = track2->get_n_link_slots();
63         for(unsigned i=0; i<nls1; ++i)
64         {
65                 if(track1->get_link(i))
66                         continue;
67
68                 sn1 = track1->get_snap_node(i);
69                 
70                 for(unsigned j=0; j<nls2; ++j)
71                 {
72                         if(track2->get_link(j))
73                                 continue;
74
75                         Snap sn2 = track2->get_snap_node(j);
76
77                         float dz = sn2.position.z-sn1.position.z;
78                         if(abs(dz)>0.02)
79                                 continue;
80
81                         Angle adiff = wrap_balanced(sn1.rotation+Angle::half_turn()-sn2.rotation);
82                         if(abs(adiff).radians()>0.01)
83                                 continue;
84
85                         Vector delta = rotated_vector(sn2.position-sn1.position, -sn1.rotation);
86                         if(abs(delta.y)>limit)
87                                 continue;
88
89                         gap = delta.x;
90                         if(gap<0)
91                                 continue;
92
93                         ok = true;
94                 }
95
96                 if(ok)
97                         break;
98         }
99
100         if(!ok)
101         {
102                 set_status("No aligned endpoints found");
103                 return;
104         }
105
106         extend_tracks = create_straight(sn1.position, sn1.rotation, gap, limit);
107
108         if(extend_tracks.empty())
109         {
110                 set_status("No connection possible");
111                 return;
112         }
113
114         extend_tracks.front()->link_to(*track1);
115         extend_tracks.back()->link_to(*track2);
116
117         accepted = true;
118         set_done();
119 }
120
121 void ExtendTool::button_press(unsigned btn)
122 {
123         if(btn==1)
124         {
125                 for(set<Object *>::const_iterator i=objects.begin(); i!=objects.end(); ++i)
126                         if(extend_tracks.front()->link_to(**i))
127                                 break;
128                 accepted = true;
129                 set_done();
130         }
131         else if(btn==3)
132                 set_done();
133 }
134
135 void ExtendTool::axis_motion(unsigned axis, float value, float rel)
136 {
137         Tool::axis_motion(axis, value, rel);
138
139         Vector pos;
140         Angle dir;
141         float length = 0;
142
143         for(set<Object *>::const_iterator i=objects.begin(); i!=objects.end(); ++i)
144         {
145                 unsigned nls = (*i)->get_n_link_slots();
146                 for(unsigned j=0; j<nls; ++j)
147                 {
148                         if((*i)->get_link(j))
149                                 continue;
150
151                         Snap sn = (*i)->get_snap_node(j);
152                         Vector delta = rotated_vector(ground_pointer-sn.position, -sn.rotation);
153
154                         if(delta.x<length)
155                                 continue;
156
157                         pos = sn.position;
158                         dir = sn.rotation;
159                         length = delta.x;
160                 }
161         }
162
163         if(length)
164         {
165                 vector<Track *> trks = create_straight(pos, dir, length, max(length/500, 0.001f));
166
167                 if(!trks.empty())
168                 {
169                         for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
170                                 delete *i;
171                         extend_tracks = trks;
172
173                         map<ArticleNumber, unsigned> counts;
174                         length = 0;
175                         for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
176                         {
177                                 length += (*i)->get_type().get_total_length();
178                                 ++counts[(*i)->get_type().get_article_number()];
179                         }
180
181                         string detail;
182                         for(map<ArticleNumber, unsigned>::const_iterator i=counts.begin(); i!=counts.end(); ++i)
183                         {
184                                 if(!detail.empty())
185                                         detail += ", ";
186                                 detail += format("%dx %s", i->second, i->first);
187                         }
188
189                         signal_status.emit(format("Extend: %.0fmm (%s)", length*1000, detail));
190                 }
191         }
192 }
193
194 vector<Track *> ExtendTool::create_straight(const Vector &start, const Angle &dir, float length, float limit)
195 {
196         const Catalogue::TrackMap &track_types = designer.get_catalogue().get_tracks();
197         map<float, const TrackType *> types_by_length;
198         unsigned preference = 0;
199         for(Catalogue::TrackMap::const_iterator i=track_types.begin(); i!=track_types.end(); ++i)
200         {
201                 const vector<TrackPart> &parts = i->second->get_parts();
202                 if(parts.size()!=1)
203                         continue;
204                 if(parts.front().is_curved() || parts.front().is_dead_end())
205                         continue;
206
207                 types_by_length[parts.front().get_length()] = i->second;
208                 preference = max(preference, i->second->get_autofit_preference());
209         }
210
211         vector<float> lengths;
212         float removed = 0;
213         while(length>limit)
214         {
215                 bool found = false;
216                 for(map<float, const TrackType *>::iterator i=types_by_length.end(); i!=types_by_length.begin(); )
217                 {
218                         --i;
219                         if(i->second->get_autofit_preference()<preference)
220                                 continue;
221                         if((!removed || i->first<removed) && i->first<length+limit)
222                         {
223                                 unsigned n = static_cast<unsigned>((length+limit)/i->first);
224                                 lengths.insert(lengths.end(), n, i->first);
225                                 length -= n*i->first;
226                                 found = true;
227                                 break;
228                         }
229                 }
230
231                 if(found)
232                         continue;
233
234                 if(lengths.empty())
235                 {
236                         if(preference>0)
237                         {
238                                 --preference;
239                                 removed = 0;
240                                 continue;
241                         }
242                         break;
243                 }
244
245                 length += lengths.back();
246                 removed = lengths.back();
247                 lengths.pop_back();
248         }
249
250         vector<Track *> trks;
251
252         if(!lengths.empty())
253         {
254                 Vector pos = start;
255                 Transform trans = Transform::rotation(dir, Vector(0, 0, 1));
256                 for(vector<float>::iterator i=lengths.begin(); i!=lengths.end(); ++i)
257                 {
258                         Track *track = new Track(designer.get_layout(), *get_item(types_by_length, *i));
259                         track->set_position(pos);
260                         track->set_rotation(dir);
261
262                         if(!trks.empty())
263                                 track->link_to(*trks.back());
264                         trks.push_back(track);
265
266                         pos += trans.transform(Vector(*i, 0, 0));
267                 }
268         }
269
270         return trks;
271 }
272
273 void ExtendTool::update_selection(Selection &sel) const
274 {
275         sel.replace(extend_tracks.begin(), extend_tracks.end());
276 }