]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/track.cpp
Add a common base class for tangible objects
[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_endpoint_position(links[0]->get_endpoint_by_link(*this));
104                 Vector epp1 = links[1]->get_endpoint_position(links[1]->get_endpoint_by_link(*this));
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_endpoint_position(links[0]->get_endpoint_by_link(*this));
114                         position.z = epp.z;
115                 }
116                 else if(links[1])
117                 {
118                         Vector epp = links[1]->get_endpoint_position(links[1]->get_endpoint_by_link(*this));
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 Vector Track::get_endpoint_position(unsigned epi) const
168 {
169         const vector<TrackType::Endpoint> &eps = type.get_endpoints();
170         if(epi>=eps.size())
171                 throw out_of_range("Track::get_endpoint_position");
172
173         const TrackType::Endpoint &ep = eps[epi];
174
175         float c = cos(rotation);
176         float s = sin(rotation);
177
178         Vector p(position.x+c*ep.pos.x-s*ep.pos.y, position.y+s*ep.pos.x+c*ep.pos.y, position.z);
179         if(eps.size()==2 && epi==1)
180                 p.z += slope;
181         return p;
182 }
183
184 float Track::get_endpoint_direction(unsigned epi) const
185 {
186         const vector<TrackType::Endpoint> &eps = type.get_endpoints();
187         if(epi>=eps.size())
188                 throw out_of_range("Track::get_endpoint_direction");
189
190         const TrackType::Endpoint &ep = eps[epi];
191
192         return rotation+ep.dir;
193 }
194
195 bool Track::snap_to(Track &other, bool link, float limit)
196 {
197         if(!limit || link)
198         {
199                 limit = layout.get_catalogue().get_gauge();
200                 if(link && !flex && !other.get_flex())
201                         limit /= 10;
202         }
203         limit *= limit;
204
205         const vector<TrackType::Endpoint> &eps = type.get_endpoints();
206         const vector<TrackType::Endpoint> &other_eps = other.get_type().get_endpoints();
207
208         for(unsigned i=0; i<eps.size(); ++i)
209         {
210                 Vector epp = get_endpoint_position(i);
211
212                 for(unsigned j=0; j<other_eps.size(); ++j)
213                 {
214                         if(other.get_link(j))
215                                 continue;
216
217                         Vector epp2 = other.get_endpoint_position(j);
218                         float dx = epp2.x-epp.x;
219                         float dy = epp2.y-epp.y;
220                         float dz = epp2.z-epp.z;
221                         if(dx*dx+dy*dy<limit && dz*dz<limit)
222                         {
223                                 if(!link || (!flex && !other.get_flex()))
224                                 {
225                                         set_rotation(other.rotation+other_eps[j].dir-eps[i].dir+M_PI);
226                                         Vector p(epp2.x-(eps[i].pos.x*cos(rotation)-eps[i].pos.y*sin(rotation)),
227                                                 epp2.y-(eps[i].pos.y*cos(rotation)+eps[i].pos.x*sin(rotation)),
228                                                 epp2.z);
229                                         if(eps.size()==2 && i==1)
230                                                 p.z -= slope;
231                                         set_position(p);
232                                 }
233
234                                 if(link)
235                                 {
236                                         if(links[i])
237                                                 break_link(*links[i]);
238                                         links[i] = &other;
239                                         other.links[j] = this;
240                                         layout.create_blocks(*this);
241
242                                         signal_link_changed.emit(i, &other);
243                                         other.signal_link_changed.emit(j, this);
244                                 }
245
246                                 return true;
247                         }
248                 }
249         }
250
251         return false;
252 }
253
254 bool Track::snap(Vector &pt, float &d) const
255 {
256         const vector<TrackType::Endpoint> &eps = type.get_endpoints();
257
258         for(unsigned i=0; i<eps.size(); ++i)
259         {
260                 Vector epp = get_endpoint_position(i);
261                 float dx = pt.x-epp.x;
262                 float dy = pt.y-epp.y;
263                 if(dx*dx+dy*dy<1e-4)
264                 {
265                         pt = epp;
266                         d = rotation+eps[i].dir;
267                         return true;
268                 }
269         }
270
271         return false;
272 }
273
274 void Track::break_link(Track &trk)
275 {
276         for(vector<Track *>::iterator i=links.begin(); i!=links.end(); ++i)
277                 if(*i==&trk)
278                 {
279                         *i = 0;
280                         trk.break_link(*this);
281                         // XXX Creates the blocks twice
282                         layout.create_blocks(*this);
283                         signal_link_changed.emit(i-links.begin(), 0);
284                         return;
285                 }
286 }
287
288 void Track::break_links()
289 {
290         for(vector<Track *>::iterator i=links.begin(); i!=links.end(); ++i)
291                 if(Track *trk=*i)
292                 {
293                         *i = 0;
294                         trk->break_link(*this);
295                 }
296 }
297
298 Track *Track::get_link(unsigned i) const
299 {
300         if(i>=links.size())
301                 throw out_of_range("Track::get_link");
302
303         return links[i];
304 }
305
306 TrackPoint Track::get_point(unsigned epi, unsigned path, float d) const
307 {
308         TrackPoint p = type.get_point(epi, path, d);
309         float c = cos(rotation);
310         float s = sin(rotation);
311
312         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);
313         p.dir += rotation;
314         if(type.get_endpoints().size()==2)
315         {
316                 float len = type.get_path_length(path);
317                 float grade = slope/len;
318                 if(epi==0)
319                 {
320                         p.pos.z += grade*d;
321                         p.grade = grade;
322                 }
323                 else
324                 {
325                         p.pos.z += slope-grade*d;
326                         p.grade = -grade;
327                 }
328         }
329
330         return p;
331 }
332
333 TrackPoint Track::get_point(unsigned epi, float d) const
334 {
335         return get_point(epi, active_path, d);
336 }
337
338 TrackPoint Track::get_nearest_point(const Vector &p) const
339 {
340         Vector local(p.x-position.x, p.y-position.y, p.z-position.z);
341         float c = cos(rotation);
342         float s = sin(rotation);
343         local = Vector(c*local.x+s*local.y, c*local.y-s*local.x, local.z);
344
345         TrackPoint tp = type.get_nearest_point(local);
346         tp.pos = 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);
347         tp.dir += rotation;
348         return tp;
349 }
350
351 bool Track::collide_ray(const Vector &start, const Vector &ray) const
352 {
353         Vector local_start(start.x-position.x, start.y-position.y, start.z-position.z);
354         float c = cos(rotation);
355         float s = sin(rotation);
356         local_start = Vector(c*local_start.x+s*local_start.y, c*local_start.y-s*local_start.x, local_start.z);
357         Vector local_ray(c*ray.x+s*ray.y, c*ray.y-s*ray.x, ray.z);
358
359         float width = layout.get_catalogue().get_ballast_profile().get_width();
360
361         return type.collide_ray(local_start, local_ray, width);
362 }
363
364 void Track::save(list<DataFile::Statement> &st) const
365 {
366         st.push_back((DataFile::Statement("position"), position.x, position.y, position.z));
367         st.push_back((DataFile::Statement("rotation"), rotation));
368         st.push_back((DataFile::Statement("slope"), slope));
369         if(turnout_id)
370                 st.push_back((DataFile::Statement("turnout_id"), turnout_id));
371         if(sensor_id)
372                 st.push_back((DataFile::Statement("sensor_id"), sensor_id));
373         if(flex)
374                 st.push_back((DataFile::Statement("flex"), true));
375 }
376
377 void Track::turnout_event(unsigned addr, unsigned state)
378 {
379         if(!turnout_id)
380                 return;
381
382         if(addr==turnout_id)
383         {
384                 active_path = state;
385                 path_changing = false;
386                 signal_path_changed.emit(active_path);
387         }
388 }
389
390
391 Track::Loader::Loader(Track &t):
392         DataFile::ObjectLoader<Track>(t)
393 {
394         add("position",   &Loader::position);
395         add("rotation",   &Loader::rotation);
396         add("slope",      &Track::slope);
397         add("turnout_id", &Loader::turnout_id);
398         add("sensor_id",  &Loader::sensor_id);
399         add("flex",       &Track::flex);
400 }
401
402 void Track::Loader::position(float x, float y, float z)
403 {
404         obj.position = Vector(x, y, z);
405 }
406
407 void Track::Loader::rotation(float r)
408 {
409         obj.rotation = r;
410 }
411
412 void Track::Loader::sensor_id(unsigned id)
413 {
414         obj.set_sensor_id(id);
415 }
416
417 void Track::Loader::turnout_id(unsigned id)
418 {
419         obj.set_turnout_id(id);
420 }
421
422 } // namespace R2C2