]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/track.cpp
Add a generic snapping interface in Object
[r2c2.git] / source / libr2c2 / track.cpp
1 #include <cmath>
2 #include "block.h"
3 #include "catalogue.h"
4 #include "driver.h"
5 #include "layout.h"
6 #include "track.h"
7 #include "tracktype.h"
8
9 using namespace std;
10 using namespace Msp;
11
12 namespace R2C2 {
13
14 Track::Track(Layout &l, const TrackType &t):
15         Object(l),
16         type(t),
17         block(0),
18         slope(0),
19         flex(false),
20         turnout_id(0),
21         sensor_id(0),
22         links(type.get_endpoints().size()),
23         active_path(0),
24         path_changing(false)
25 {
26         if(type.is_turnout())
27                 turnout_id = layout.allocate_turnout_id();
28
29         layout.add_track(*this);
30
31         if(layout.has_driver())
32                 layout.get_driver().signal_turnout.connect(sigc::mem_fun(this, &Track::turnout_event));
33
34         for(unsigned paths = type.get_paths(); !(paths&1); ++active_path, paths>>=1) ;
35 }
36
37 Track::~Track()
38 {
39         break_links();
40         layout.remove_track(*this);
41 }
42
43 Track *Track::clone(Layout *to_layout) const
44 {
45         Track *track = new Track((to_layout ? *to_layout : layout), type);
46         track->set_position(position);
47         track->set_rotation(rotation);
48         return track;
49 }
50
51 void Track::set_block(Block *b)
52 {
53         if(b && !b->has_track(*this))
54                 throw logic_error("track not in block");
55         if(!b && block && block->has_track(*this))
56                 throw logic_error("track still in block");
57
58         block = b;
59 }
60
61 Block &Track::get_block() const
62 {
63         if(!block)
64                 throw logic_error("!block");
65
66         return *block;
67 }
68
69 void Track::set_position(const Vector &p)
70 {
71         position = p;
72 }
73
74 void Track::set_rotation(float r)
75 {
76         rotation = r;
77         while(rotation<0)
78                 rotation += M_PI*2;
79         while(rotation>M_PI*2)
80                 rotation -= M_PI*2;
81 }
82
83 void Track::set_slope(float s)
84 {
85         if(links.size()!=2)
86                 return;
87
88         slope = s;
89 }
90
91 void Track::set_flex(bool f)
92 {
93         flex = f;
94 }
95
96 void Track::check_slope()
97 {
98         if(links.size()!=2)
99                 return;
100
101         if(links[0] && links[1])
102         {
103                 Vector epp0 = links[0]->get_snap_node(links[0]->get_endpoint_by_link(*this)).position;
104                 Vector epp1 = links[1]->get_snap_node(links[1]->get_endpoint_by_link(*this)).position;
105                 position.z = epp0.z;
106                 slope = epp1.z-position.z;
107         }
108         else
109         {
110                 slope = 0;
111                 if(links[0])
112                 {
113                         Vector epp = links[0]->get_snap_node(links[0]->get_endpoint_by_link(*this)).position;
114                         position.z = epp.z;
115                 }
116                 else if(links[1])
117                 {
118                         Vector epp = links[1]->get_snap_node(links[1]->get_endpoint_by_link(*this)).position;
119                         position.z = epp.z;
120                 }
121         }
122 }
123
124 void Track::set_turnout_id(unsigned i)
125 {
126         if(!type.is_turnout())
127                 throw logic_error("not a turnout");
128
129         turnout_id = i;
130         layout.create_blocks(*this);
131         layout.update_routes();
132         if(layout.has_driver() && turnout_id)
133                 layout.get_driver().add_turnout(turnout_id, type);
134 }
135
136 void Track::set_sensor_id(unsigned i)
137 {
138         if(type.is_turnout())
139                 throw logic_error("is a turnout");
140
141         sensor_id = i;
142         layout.create_blocks(*this);
143         if(layout.has_driver() && sensor_id)
144                 layout.get_driver().add_sensor(sensor_id);
145 }
146
147 void Track::set_active_path(unsigned p)
148 {
149         if(!turnout_id)
150                 throw logic_error("not a turnout");
151         if(!(type.get_paths()&(1<<p)))
152                 throw invalid_argument("Track::set_active_path");
153
154         path_changing = true;
155         layout.get_driver().set_turnout(turnout_id, p);
156 }
157
158 int Track::get_endpoint_by_link(Track &other) const
159 {
160         for(unsigned i=0; i<links.size(); ++i)
161                 if(links[i]==&other)
162                         return i;
163
164         return -1;
165 }
166
167 bool Track::snap_to(Track &other, bool link, float limit)
168 {
169         if(!limit || link)
170         {
171                 limit = layout.get_catalogue().get_gauge();
172                 if(link && !flex && !other.get_flex())
173                         limit /= 10;
174         }
175         limit *= limit;
176
177         const vector<TrackType::Endpoint> &eps = type.get_endpoints();
178         const vector<TrackType::Endpoint> &other_eps = other.get_type().get_endpoints();
179
180         for(unsigned i=0; i<eps.size(); ++i)
181         {
182                 Vector epp = get_snap_node(i).position;
183
184                 for(unsigned j=0; j<other_eps.size(); ++j)
185                 {
186                         if(other.get_link(j))
187                                 continue;
188
189                         Vector epp2 = other.get_snap_node(j).position;
190                         float dx = epp2.x-epp.x;
191                         float dy = epp2.y-epp.y;
192                         float dz = epp2.z-epp.z;
193                         if(dx*dx+dy*dy<limit && dz*dz<limit)
194                         {
195                                 if(!link || (!flex && !other.get_flex()))
196                                 {
197                                         set_rotation(other.rotation+other_eps[j].dir-eps[i].dir+M_PI);
198                                         Vector p(epp2.x-(eps[i].pos.x*cos(rotation)-eps[i].pos.y*sin(rotation)),
199                                                 epp2.y-(eps[i].pos.y*cos(rotation)+eps[i].pos.x*sin(rotation)),
200                                                 epp2.z);
201                                         if(eps.size()==2 && i==1)
202                                                 p.z -= slope;
203                                         set_position(p);
204                                 }
205
206                                 if(link)
207                                 {
208                                         if(links[i])
209                                                 break_link(*links[i]);
210                                         links[i] = &other;
211                                         other.links[j] = this;
212                                         layout.create_blocks(*this);
213
214                                         signal_link_changed.emit(i, &other);
215                                         other.signal_link_changed.emit(j, this);
216                                 }
217
218                                 return true;
219                         }
220                 }
221         }
222
223         return false;
224 }
225
226 void Track::break_link(Track &trk)
227 {
228         for(vector<Track *>::iterator i=links.begin(); i!=links.end(); ++i)
229                 if(*i==&trk)
230                 {
231                         *i = 0;
232                         trk.break_link(*this);
233                         // XXX Creates the blocks twice
234                         layout.create_blocks(*this);
235                         signal_link_changed.emit(i-links.begin(), 0);
236                         return;
237                 }
238 }
239
240 void Track::break_links()
241 {
242         for(vector<Track *>::iterator i=links.begin(); i!=links.end(); ++i)
243                 if(Track *trk=*i)
244                 {
245                         *i = 0;
246                         trk->break_link(*this);
247                 }
248 }
249
250 Track *Track::get_link(unsigned i) const
251 {
252         if(i>=links.size())
253                 throw out_of_range("Track::get_link");
254
255         return links[i];
256 }
257
258 TrackPoint Track::get_point(unsigned epi, unsigned path, float d) const
259 {
260         TrackPoint p = type.get_point(epi, path, d);
261         float c = cos(rotation);
262         float s = sin(rotation);
263
264         p.pos = Vector(position.x+c*p.pos.x-s*p.pos.y, position.y+s*p.pos.x+c*p.pos.y, position.z);
265         p.dir += rotation;
266         if(type.get_endpoints().size()==2)
267         {
268                 float len = type.get_path_length(path);
269                 float grade = slope/len;
270                 if(epi==0)
271                 {
272                         p.pos.z += grade*d;
273                         p.grade = grade;
274                 }
275                 else
276                 {
277                         p.pos.z += slope-grade*d;
278                         p.grade = -grade;
279                 }
280         }
281
282         return p;
283 }
284
285 TrackPoint Track::get_point(unsigned epi, float d) const
286 {
287         return get_point(epi, active_path, d);
288 }
289
290 unsigned Track::get_n_snap_nodes() const
291 {
292         return type.get_endpoints().size();
293 }
294
295 Snap Track::get_snap_node(unsigned i) const
296 {
297         const vector<TrackType::Endpoint> &eps = type.get_endpoints();
298         if(i>=eps.size())
299                 throw out_of_range("Track::get_snap_node");
300
301         Snap result;
302         const TrackType::Endpoint &ep = eps[i];
303
304         float c = cos(rotation);
305         float s = sin(rotation);
306
307         result.position = Vector(position.x+c*ep.pos.x-s*ep.pos.y, position.y+s*ep.pos.x+c*ep.pos.y, position.z);
308         if(eps.size()==2 && i==1)
309                 result.position.z += slope;
310
311         result.rotation = rotation+ep.dir;
312
313         return result;
314 }
315
316 bool Track::snap(Snap &sn, float limit, SnapType what) const
317 {
318         if(Object::snap(sn, limit, what))
319                 return true;
320
321         if(what&SNAP_SEGMENT)
322         {
323                 Vector local(sn.position.x-position.x, sn.position.y-position.y, sn.position.z-position.z);
324                 float c = cos(rotation);
325                 float s = sin(rotation);
326                 local = Vector(c*local.x+s*local.y, c*local.y-s*local.x, local.z);
327
328                 TrackPoint tp = type.get_nearest_point(local);
329                 Vector d(local.x-tp.pos.x, local.y-tp.pos.y, local.z-tp.pos.z);
330                 if(d.x*d.x+d.y*d.y+d.z*d.z<=limit*limit)
331                 {
332                         sn.position = Vector(position.x+tp.pos.x*c-tp.pos.y*s, position.y+tp.pos.y*c+tp.pos.x*s, position.z+tp.pos.z);
333                         sn.rotation = tp.dir+rotation;
334                         return true;
335                 }
336         }
337
338         return false;
339 }
340
341 SnapType Track::get_default_snap_type_to(const Object &other) const
342 {
343         if(dynamic_cast<const Track *>(&other))
344                 return SNAP_NODE;
345
346         return NO_SNAP;
347 }
348
349 bool Track::collide_ray(const Vector &start, const Vector &ray) const
350 {
351         Vector local_start(start.x-position.x, start.y-position.y, start.z-position.z);
352         float c = cos(rotation);
353         float s = sin(rotation);
354         local_start = Vector(c*local_start.x+s*local_start.y, c*local_start.y-s*local_start.x, local_start.z);
355         Vector local_ray(c*ray.x+s*ray.y, c*ray.y-s*ray.x, ray.z);
356
357         float width = layout.get_catalogue().get_ballast_profile().get_width();
358
359         return type.collide_ray(local_start, local_ray, width);
360 }
361
362 void Track::save(list<DataFile::Statement> &st) const
363 {
364         st.push_back((DataFile::Statement("position"), position.x, position.y, position.z));
365         st.push_back((DataFile::Statement("rotation"), rotation));
366         st.push_back((DataFile::Statement("slope"), slope));
367         if(turnout_id)
368                 st.push_back((DataFile::Statement("turnout_id"), turnout_id));
369         if(sensor_id)
370                 st.push_back((DataFile::Statement("sensor_id"), sensor_id));
371         if(flex)
372                 st.push_back((DataFile::Statement("flex"), true));
373 }
374
375 void Track::turnout_event(unsigned addr, unsigned state)
376 {
377         if(!turnout_id)
378                 return;
379
380         if(addr==turnout_id)
381         {
382                 active_path = state;
383                 path_changing = false;
384                 signal_path_changed.emit(active_path);
385         }
386 }
387
388
389 Track::Loader::Loader(Track &t):
390         DataFile::ObjectLoader<Track>(t)
391 {
392         add("position",   &Loader::position);
393         add("rotation",   &Loader::rotation);
394         add("slope",      &Track::slope);
395         add("turnout_id", &Loader::turnout_id);
396         add("sensor_id",  &Loader::sensor_id);
397         add("flex",       &Track::flex);
398 }
399
400 void Track::Loader::position(float x, float y, float z)
401 {
402         obj.position = Vector(x, y, z);
403 }
404
405 void Track::Loader::rotation(float r)
406 {
407         obj.rotation = r;
408 }
409
410 void Track::Loader::sensor_id(unsigned id)
411 {
412         obj.set_sensor_id(id);
413 }
414
415 void Track::Loader::turnout_id(unsigned id)
416 {
417         obj.set_turnout_id(id);
418 }
419
420 } // namespace R2C2