]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/vehicle.cpp
Strip Id tags and copyright notices from files
[r2c2.git] / source / libr2c2 / vehicle.cpp
1 #include <cmath>
2 #include "catalogue.h"
3 #include "driver.h"
4 #include "layout.h"
5 #include "track.h"
6 #include "trackiter.h"
7 #include "tracktype.h"
8 #include "vehicle.h"
9 #include "vehicletype.h"
10
11 using namespace std;
12 using namespace Msp;
13
14 namespace R2C2 {
15
16 Vehicle::Vehicle(Layout &l, const VehicleType &t):
17         layout(l),
18         type(t),
19         next(0),
20         prev(0),
21         direction(0),
22         front_sensor(0),
23         back_sensor(0)
24 {
25         axles.assign(type.get_fixed_axles().begin(), type.get_fixed_axles().end());
26         bogies.assign(type.get_bogies().begin(), type.get_bogies().end());
27         rods.assign(type.get_rods().begin(), type.get_rods().end());
28
29         layout.add_vehicle(*this);
30 }
31
32 Vehicle::~Vehicle()
33 {
34         if(next)
35                 detach_back();
36         if(prev)
37                 detach_front();
38         layout.remove_vehicle(*this);
39 }
40
41 void Vehicle::attach_back(Vehicle &veh)
42 {
43         if(next || veh.prev)
44                 throw InvalidState("Already attached");
45
46         next = &veh;
47         veh.prev = this;
48
49         if(track_pos.track)
50                 propagate_backward();
51 }
52
53 void Vehicle::attach_front(Vehicle &veh)
54 {
55         if(prev || veh.next)
56                 throw InvalidState("Already attached");
57
58         prev = &veh;
59         veh.next = this;
60
61         if(prev->get_track())
62                 prev->propagate_backward();
63 }
64
65 void Vehicle::detach_back()
66 {
67         if(!next)
68                 throw InvalidState("Not attached");
69
70         next->prev = 0;
71         next = 0;
72 }
73
74 void Vehicle::detach_front()
75 {
76         if(!prev)
77                 throw InvalidState("Not attached");
78
79         prev->next = 0;
80         prev = 0;
81 }
82
83 void Vehicle::place(Track &t, unsigned e, float o, PlaceMode m)
84 {
85         track_pos = TrackPosition(&t, e, o);
86
87         if(m==FRONT_AXLE)
88                 track_pos.advance(-type.get_front_axle_offset());
89         else if(m==FRONT_BUFFER)
90                 track_pos.advance(-type.get_length()/2);
91         else if(m==BACK_AXLE)
92                 track_pos.advance(-type.get_back_axle_offset());
93         else if(m==BACK_BUFFER)
94                 track_pos.advance(type.get_length()/2);
95
96         update_position();
97         propagate_position();
98 }
99
100 void Vehicle::unplace()
101 {
102         if(!track_pos.track)
103                 return;
104
105         track_pos = TrackPosition();
106
107         if(prev)
108                 prev->unplace();
109         if(next)
110                 next->unplace();
111 }
112
113 void Vehicle::advance(float d)
114 {
115         track_pos.advance(d);
116         update_position();
117         turn_axles(d);
118         propagate_position();
119 }
120
121 const Vehicle::Axle &Vehicle::get_fixed_axle(unsigned i) const
122 {
123         if(i>=axles.size())
124                 throw InvalidParameterValue("Axle index out of range");
125         return axles[i];
126 }
127
128 const Vehicle::Bogie &Vehicle::get_bogie(unsigned i) const
129 {
130         if(i>=bogies.size())
131                 throw InvalidParameterValue("Bogie index out of range");
132         return bogies[i];
133 }
134
135 const Vehicle::Axle &Vehicle::get_bogie_axle(unsigned i, unsigned j) const
136 {
137         if(i>=bogies.size())
138                 throw InvalidParameterValue("Bogie index out of range");
139         if(j>=bogies[i].axles.size())
140                 throw InvalidParameterValue("Axle index out of range");
141         return bogies[i].axles[j];
142 }
143
144 const Vehicle::Rod &Vehicle::get_rod(unsigned i) const
145 {
146         if(i>=rods.size())
147                 throw InvalidParameterValue("Rod index out of range");
148         return rods[i];
149 }
150
151 void Vehicle::update_position()
152 {
153         TrackPoint tp;
154
155         if(axles.size()>=2)
156         {
157                 float wheelbase = axles.front().type->position-axles.back().type->position;
158                 tp = get_point(track_pos, wheelbase, -axles.back().type->position/wheelbase);
159         }
160         else if(bogies.size()>=2)
161         {
162                 TrackPosition front = track_pos;
163                 front.advance(bogies.front().type->position);
164                 TrackPosition back = track_pos;
165                 back.advance(bogies.back().type->position);
166                 float bogie_spacing = bogies.front().type->position-bogies.back().type->position;
167                 adjust_for_distance(front, back, bogie_spacing);
168
169                 const vector<Axle> &front_axles = bogies.front().axles;
170                 float wheelbase = front_axles.front().type->position-front_axles.back().type->position;
171                 TrackPoint front_point = get_point(front, wheelbase, -front_axles.back().type->position/wheelbase);
172
173                 const vector<Axle> &back_axles = bogies.back().axles;
174                 wheelbase = back_axles.front().type->position-back_axles.back().type->position;
175                 TrackPoint back_point = get_point(back, wheelbase, -back_axles.back().type->position/wheelbase);
176
177                 tp = get_point(front_point.pos, back_point.pos, -bogies.back().type->position/bogie_spacing);
178
179                 bogies.front().direction = front_point.dir-tp.dir;
180                 bogies.back().direction = back_point.dir-tp.dir;
181         }
182         else
183                 tp = track_pos.get_point();
184
185         if(!prev)
186                 check_sensor(type.get_front_axle_offset(), front_sensor);
187         if(!next)
188                 check_sensor(type.get_back_axle_offset(), back_sensor);
189
190         position = tp.pos;
191         position.z += layout.get_catalogue().get_rail_elevation();
192         direction = tp.dir;
193 }
194
195 void Vehicle::update_position_from(const Vehicle &veh)
196 {
197         int sign = (&veh==prev ? -1 : 1);
198
199         float tdist = (type.get_length()+veh.type.get_length())/2;
200         float margin = layout.get_catalogue().get_scale();
201
202         float dist = distance(veh.position, position);
203         if(dist<tdist-margin || dist>tdist+margin)
204         {
205                 track_pos = veh.track_pos;
206                 track_pos.advance(sign*tdist);
207                 update_position();
208
209                 dist = distance(veh.position, position);
210         }
211
212         track_pos.advance(sign*(tdist-dist));
213         update_position();
214         turn_axles(sign*(tdist-dist));
215 }
216
217 void Vehicle::propagate_position()
218 {
219         if(prev)
220                 propagate_forward();
221         if(next)
222                 propagate_backward();
223 }
224
225 void Vehicle::propagate_forward()
226 {
227         prev->update_position_from(*this);
228
229         if(prev->prev)
230                 prev->propagate_forward();
231 }
232
233 void Vehicle::propagate_backward()
234 {
235         next->update_position_from(*this);
236
237         if(next->next)
238                 next->propagate_backward();
239 }
240
241 void Vehicle::check_sensor(float offset, unsigned &sensor)
242 {
243         TrackPosition pos = track_pos;
244         pos.advance(offset);
245         unsigned s = pos.track->get_sensor_id();
246         if(s!=sensor)
247         {
248                 /* Sensor ID under axle has changed.  Deduce movement direction by using
249                 the sensor ID under the midpoint of the vehicle. */
250                 /* XXX This depends on the simulation running fast enough.  Something
251                 more robust would be preferable. */
252                 unsigned old = sensor;
253                 sensor = s;
254                 unsigned mid = track_pos.track->get_sensor_id();
255
256                 if(s && s!=mid)
257                         /* There's a sensor and it's different from mid.  We've just entered
258                         that sensor. */
259                         layout.get_driver().set_sensor(sensor, true);
260                 if(old && old!=mid)
261                         /* A sensor was under the axle and it was different from mid.  We've
262                         just left that sensor. */
263                         layout.get_driver().set_sensor(old, false);
264         }
265 }
266
267 void Vehicle::turn_axles(float d)
268 {
269         for(vector<Axle>::iterator i=axles.begin(); i!=axles.end(); ++i)
270                 i->angle += d*2/i->type->wheel_dia;
271         for(vector<Bogie>::iterator i=bogies.begin(); i!=bogies.end(); ++i)
272                 for(vector<Axle>::iterator j=i->axles.begin(); j!=i->axles.end(); ++j)
273                         j->angle += d*2/j->type->wheel_dia;
274
275         update_rods();
276 }
277
278 void Vehicle::update_rods()
279 {
280         for(vector<Rod>::iterator i=rods.begin(); i!=rods.end(); ++i)
281         {
282                 if(i->type->pivot==VehicleType::Rod::BODY)
283                         i->position = i->type->pivot_point;
284                 else if(i->type->pivot==VehicleType::Rod::AXLE)
285                 {
286                         const Axle &axle = get_fixed_axle(i->type->pivot_index);
287                         float c = cos(axle.angle);
288                         float s = sin(axle.angle);
289                         const Vector &pp = i->type->pivot_point;
290                         i->position = Vector(axle.type->position+pp.x*c+pp.z*s, pp.y, axle.type->wheel_dia/2+pp.z*c-pp.x*s);
291                 }
292                 else if(i->type->pivot==VehicleType::Rod::ROD)
293                 {
294                         const Rod &prod = get_rod(i->type->pivot_index);
295                         float c = cos(prod.angle);
296                         float s = sin(prod.angle);
297                         const Vector &pos = prod.position;
298                         const Vector &off = i->type->pivot_point;
299                         i->position = Vector(pos.x+off.x*c-off.z*s, pos.y+off.y, pos.z+off.z*c+off.x*s);
300                 }
301
302                 if(i->type->connect_index>=0)
303                 {
304                         Rod &crod = rods[i->type->connect_index];
305                         if(i->type->limit==VehicleType::Rod::ROTATE && crod.type->limit==VehicleType::Rod::SLIDE_X)
306                         {
307                                 float dx = (crod.position.x+i->type->connect_offset.x)-i->position.x;
308                                 float dz = (crod.position.z+i->type->connect_offset.z)-i->position.z;
309                                 float cd = sqrt(i->type->connect_point.x*i->type->connect_point.x+i->type->connect_point.z*i->type->connect_point.z);
310                                 float ca = atan2(i->type->connect_point.z, i->type->connect_point.x);
311                                 dx = sqrt(cd*cd-dz*dz)*(dx>0 ? 1 : -1);
312                                 i->angle = atan2(dz, dx)-ca;
313                                 crod.position.x = i->position.x+dx-i->type->connect_offset.x;
314                         }
315                         else if(i->type->limit==VehicleType::Rod::ROTATE && crod.type->limit==VehicleType::Rod::ROTATE)
316                         {
317                                 float dx = crod.position.x-i->position.x;
318                                 float dz = crod.position.z-i->position.z;
319                                 float d = sqrt(dx*dx+dz*dz);
320                                 float cd1 = sqrt(i->type->connect_point.x*i->type->connect_point.x+i->type->connect_point.z*i->type->connect_point.z);
321                                 float cd2 = sqrt(i->type->connect_offset.x*i->type->connect_offset.x+i->type->connect_offset.z*i->type->connect_offset.z);
322                                 float a = (d*d+cd1*cd1-cd2*cd2)/(2*d);
323                                 float b = sqrt(cd1*cd1-a*a);
324                                 float sign = (dx*i->type->connect_point.z-dz*i->type->connect_point.x>0 ? 1 : -1);
325                                 float cx = (dx*a-dz*b*sign)/d;
326                                 float cz = (dz*a+dx*b*sign)/d;
327                                 float ca1 = atan2(i->type->connect_point.z, i->type->connect_point.x);
328                                 float ca2 = atan2(i->type->connect_offset.z, i->type->connect_offset.x);
329                                 i->angle = atan2(cz, cx)-ca1;
330                                 crod.angle = atan2(cz-dz, cx-dx)-ca2;
331                         }
332                 }
333         }
334 }
335
336 void Vehicle::adjust_for_distance(TrackPosition &front, TrackPosition &back, float tdist, float ratio) const
337 {
338         float margin = 0.01*layout.get_catalogue().get_scale();
339         int adjust_dir = 0;
340         while(1)
341         {
342                 Vector front_point = front.get_point().pos;
343                 Vector back_point = back.get_point().pos;
344
345                 float dx = front_point.x-back_point.x;
346                 float dy = front_point.y-back_point.y;
347                 float dz = front_point.z-back_point.z;
348                 float dist = sqrt(dx*dx+dy*dy+dz*dz);
349
350                 float diff = tdist-dist;
351                 if(diff<-margin && adjust_dir<=0)
352                 {
353                         diff -= margin;
354                         adjust_dir = -1;
355                 }
356                 else if(diff>margin && adjust_dir>=0)
357                 {
358                         diff += margin;
359                         adjust_dir = 1;
360                 }
361                 else
362                         return;
363
364                 front.advance(diff*(1-ratio));
365                 back.advance(-diff*ratio);
366         }
367 }
368
369 TrackPoint Vehicle::get_point(const Vector &front, const Vector &back, float ratio) const
370 {
371         float dx = front.x-back.x;
372         float dy = front.y-back.y;
373         float dz = front.z-back.z;
374
375         TrackPoint tp;
376         tp.pos = Vector(back.x+dx*ratio, back.y+dy*ratio, back.z+dz*ratio);
377         tp.dir = atan2(dy, dx);
378
379         return tp;
380 }
381
382 TrackPoint Vehicle::get_point(const TrackPosition &pos, float tdist, float ratio) const
383 {
384         TrackPosition front = pos;
385         front.advance(tdist*(1-ratio));
386
387         TrackPosition back = pos;
388         back.advance(-tdist*ratio);
389
390         adjust_for_distance(front, back, tdist, ratio);
391         return get_point(front.get_point().pos, back.get_point().pos, ratio);
392 }
393
394
395 Vehicle::Axle::Axle(const VehicleType::Axle &t):
396         type(&t),
397         angle(0)
398 { }
399
400
401 Vehicle::Bogie::Bogie(const VehicleType::Bogie &t):
402         type(&t),
403         direction(0)
404 {
405         for(VehicleType::AxleArray::const_iterator i=type->axles.begin(); i!=type->axles.end(); ++i)
406                 axles.push_back(*i);
407 }
408
409
410 Vehicle::Rod::Rod(const VehicleType::Rod &t):
411         type(&t),
412         angle(0)
413 { }
414
415
416 Vehicle::TrackPosition::TrackPosition():
417         track(0),
418         ep(0),
419         offs(0)
420 { }
421
422 Vehicle::TrackPosition::TrackPosition(Track *t, unsigned e, float o):
423         track(t),
424         ep(e),
425         offs(o)
426 { }
427
428 void Vehicle::TrackPosition::advance(float d)
429 {
430         if(!track)
431                 return;
432
433         offs += d;
434         TrackIter iter(track, ep);
435         while(iter)
436         {
437                 float path_len = iter->get_type().get_path_length(iter->get_active_path());
438
439                 if(offs>path_len)
440                 {
441                         offs -= path_len;
442                         iter = iter.next();
443                 }
444                 else
445                         break;
446         }
447
448         while(iter && offs<0)
449         {
450                 iter = iter.flip().reverse();
451
452                 if(iter)
453                 {
454                         float path_len = iter->get_type().get_path_length(iter->get_active_path());
455                         offs += path_len;
456                 }
457         }
458
459         track = iter.track();
460         ep = iter.entry();
461         if(!track)
462                 offs = 0;
463 }
464
465 TrackPoint Vehicle::TrackPosition::get_point() const
466 {
467         if(track)
468                 return track->get_point(ep, offs);
469         else
470                 return TrackPoint();
471 }
472
473 } // namespace R2C2