]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/track.cpp
Perform check_slope automatically with certain operations
[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         for(vector<Track *>::const_iterator i=links.begin(); i!=links.end(); ++i)
73                 if(*i)
74                         (*i)->check_slope();
75 }
76
77 void Track::set_rotation(float r)
78 {
79         rotation = r;
80         while(rotation<0)
81                 rotation += M_PI*2;
82         while(rotation>M_PI*2)
83                 rotation -= M_PI*2;
84 }
85
86 void Track::set_slope(float s)
87 {
88         if(links.size()!=2)
89                 return;
90
91         slope = s;
92 }
93
94 void Track::set_flex(bool f)
95 {
96         flex = f;
97 }
98
99 void Track::check_slope()
100 {
101         if(links.size()!=2)
102                 return;
103
104         if(links[0] && links[1])
105         {
106                 Vector epp0 = links[0]->get_snap_node(links[0]->get_link_slot(*this)).position;
107                 Vector epp1 = links[1]->get_snap_node(links[1]->get_link_slot(*this)).position;
108                 position.z = epp0.z;
109                 slope = epp1.z-position.z;
110         }
111         else
112         {
113                 slope = 0;
114                 if(links[0])
115                 {
116                         Vector epp = links[0]->get_snap_node(links[0]->get_link_slot(*this)).position;
117                         position.z = epp.z;
118                 }
119                 else if(links[1])
120                 {
121                         Vector epp = links[1]->get_snap_node(links[1]->get_link_slot(*this)).position;
122                         position.z = epp.z;
123                 }
124         }
125 }
126
127 void Track::set_turnout_id(unsigned i)
128 {
129         if(!type.is_turnout())
130                 throw logic_error("not a turnout");
131
132         turnout_id = i;
133         layout.create_blocks(*this);
134         layout.update_routes();
135         if(layout.has_driver() && turnout_id)
136                 layout.get_driver().add_turnout(turnout_id, type);
137 }
138
139 void Track::set_sensor_id(unsigned i)
140 {
141         if(type.is_turnout())
142                 throw logic_error("is a turnout");
143
144         sensor_id = i;
145         layout.create_blocks(*this);
146         if(layout.has_driver() && sensor_id)
147                 layout.get_driver().add_sensor(sensor_id);
148 }
149
150 void Track::set_active_path(unsigned p)
151 {
152         if(!turnout_id)
153                 throw logic_error("not a turnout");
154         if(!(type.get_paths()&(1<<p)))
155                 throw invalid_argument("Track::set_active_path");
156
157         path_changing = true;
158         layout.get_driver().set_turnout(turnout_id, p);
159 }
160
161 TrackPoint Track::get_point(unsigned epi, unsigned path, float d) const
162 {
163         TrackPoint p = type.get_point(epi, path, d);
164         float c = cos(rotation);
165         float s = sin(rotation);
166
167         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);
168         p.dir += rotation;
169         if(type.get_endpoints().size()==2)
170         {
171                 float len = type.get_path_length(path);
172                 float grade = slope/len;
173                 if(epi==0)
174                 {
175                         p.pos.z += grade*d;
176                         p.grade = grade;
177                 }
178                 else
179                 {
180                         p.pos.z += slope-grade*d;
181                         p.grade = -grade;
182                 }
183         }
184
185         return p;
186 }
187
188 TrackPoint Track::get_point(unsigned epi, float d) const
189 {
190         return get_point(epi, active_path, d);
191 }
192
193 unsigned Track::get_n_snap_nodes() const
194 {
195         return type.get_endpoints().size();
196 }
197
198 Snap Track::get_snap_node(unsigned i) const
199 {
200         const vector<TrackType::Endpoint> &eps = type.get_endpoints();
201         if(i>=eps.size())
202                 throw out_of_range("Track::get_snap_node");
203
204         Snap result;
205         const TrackType::Endpoint &ep = eps[i];
206
207         float c = cos(rotation);
208         float s = sin(rotation);
209
210         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);
211         if(eps.size()==2 && i==1)
212                 result.position.z += slope;
213
214         result.rotation = rotation+ep.dir;
215
216         return result;
217 }
218
219 bool Track::snap(Snap &sn, float limit, SnapType what) const
220 {
221         if(Object::snap(sn, limit, what))
222                 return true;
223
224         if(what&SNAP_SEGMENT)
225         {
226                 Vector local(sn.position.x-position.x, sn.position.y-position.y, sn.position.z-position.z);
227                 float c = cos(rotation);
228                 float s = sin(rotation);
229                 local = Vector(c*local.x+s*local.y, c*local.y-s*local.x, local.z);
230
231                 TrackPoint tp = type.get_nearest_point(local);
232                 Vector d(local.x-tp.pos.x, local.y-tp.pos.y, local.z-tp.pos.z);
233                 if(d.x*d.x+d.y*d.y+d.z*d.z<=limit*limit)
234                 {
235                         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);
236                         sn.rotation = tp.dir+rotation;
237                         return true;
238                 }
239         }
240
241         return false;
242 }
243
244 SnapType Track::get_default_snap_type_to(const Object &other) const
245 {
246         if(dynamic_cast<const Track *>(&other))
247                 return SNAP_NODE;
248
249         return NO_SNAP;
250 }
251
252 unsigned Track::get_n_link_slots() const
253 {
254         return links.size();
255 }
256
257 Track *Track::get_link(unsigned i) const
258 {
259         if(i>=links.size())
260                 throw out_of_range("Track::get_link");
261
262         return links[i];
263 }
264
265 int Track::get_link_slot(const Object &other) const
266 {
267         for(unsigned i=0; i<links.size(); ++i)
268                 if(links[i]==&other)
269                         return i;
270
271         return -1;
272 }
273
274 bool Track::link_to(Object &other)
275 {
276         Track *otrack = dynamic_cast<Track *>(&other);
277         if(!otrack)
278                 return false;
279
280         float limit = layout.get_catalogue().get_gauge();
281         if(!flex && !otrack->get_flex())
282                 limit /= 10;
283         limit *= limit;
284
285         unsigned nsn = get_n_snap_nodes();
286         unsigned other_nsn = other.get_n_snap_nodes();
287         for(unsigned i=0; i<nsn; ++i)
288         {
289                 Snap sn = get_snap_node(i);
290                 for(unsigned j=0; j<other_nsn; ++j)
291                 {
292                         Snap osn = other.get_snap_node(j);
293                         Vector d(osn.position.x-sn.position.x, osn.position.y-sn.position.y, osn.position.z-sn.position.z);
294                         float da = osn.rotation-sn.rotation-M_PI;
295                         while(da<-M_PI)
296                                 da += M_PI*2;
297                         while(da>M_PI)
298                                 da -= M_PI*2;
299
300                         if(d.x*d.x+d.y*d.y<limit && d.z*d.z<limit && da>-0.01 && da<0.01)
301                         {
302                                 break_link(i);
303                                 links[i] = otrack;
304                                 otrack->links[j] = this;
305                                 check_slope();
306                                 layout.create_blocks(*this);
307
308                                 signal_link_changed.emit(i, otrack);
309                                 otrack->signal_link_changed.emit(j, this);
310                                 return true;
311                         }
312                 }
313         }
314
315         return false;
316 }
317
318 bool Track::break_link(unsigned i)
319 {
320         if(i>=links.size())
321                 throw out_of_range("Track::break_link");
322
323         Track *other = links[i];
324         if(!other)
325                 return false;
326
327         links[i] = 0;
328         other->break_link(*this);
329         // XXX Creates the blocks twice, because the other track calls this too
330         layout.create_blocks(*this);
331         signal_link_changed.emit(i, 0);
332
333         return true;
334 }
335
336 bool Track::collide_ray(const Vector &start, const Vector &ray) const
337 {
338         Vector local_start(start.x-position.x, start.y-position.y, start.z-position.z);
339         float c = cos(rotation);
340         float s = sin(rotation);
341         local_start = Vector(c*local_start.x+s*local_start.y, c*local_start.y-s*local_start.x, local_start.z);
342         Vector local_ray(c*ray.x+s*ray.y, c*ray.y-s*ray.x, ray.z);
343
344         float width = layout.get_catalogue().get_ballast_profile().get_width();
345
346         return type.collide_ray(local_start, local_ray, width);
347 }
348
349 void Track::save(list<DataFile::Statement> &st) const
350 {
351         st.push_back((DataFile::Statement("position"), position.x, position.y, position.z));
352         st.push_back((DataFile::Statement("rotation"), rotation));
353         st.push_back((DataFile::Statement("slope"), slope));
354         if(turnout_id)
355                 st.push_back((DataFile::Statement("turnout_id"), turnout_id));
356         if(sensor_id)
357                 st.push_back((DataFile::Statement("sensor_id"), sensor_id));
358         if(flex)
359                 st.push_back((DataFile::Statement("flex"), true));
360 }
361
362 void Track::turnout_event(unsigned addr, unsigned state)
363 {
364         if(!turnout_id)
365                 return;
366
367         if(addr==turnout_id)
368         {
369                 active_path = state;
370                 path_changing = false;
371                 signal_path_changed.emit(active_path);
372         }
373 }
374
375
376 Track::Loader::Loader(Track &t):
377         DataFile::ObjectLoader<Track>(t)
378 {
379         add("position",   &Loader::position);
380         add("rotation",   &Loader::rotation);
381         add("slope",      &Track::slope);
382         add("turnout_id", &Loader::turnout_id);
383         add("sensor_id",  &Loader::sensor_id);
384         add("flex",       &Track::flex);
385 }
386
387 void Track::Loader::position(float x, float y, float z)
388 {
389         obj.position = Vector(x, y, z);
390 }
391
392 void Track::Loader::rotation(float r)
393 {
394         obj.rotation = r;
395 }
396
397 void Track::Loader::sensor_id(unsigned id)
398 {
399         obj.set_sensor_id(id);
400 }
401
402 void Track::Loader::turnout_id(unsigned id)
403 {
404         obj.set_turnout_id(id);
405 }
406
407 } // namespace R2C2