2 #include <msp/core/maputils.h>
3 #include <msp/strings/format.h>
9 #include "trackattachment.h"
10 #include "tracktype.h"
17 struct AttachmentCompare
21 AttachmentCompare(unsigned e): entry(e) { }
23 bool operator()(const R2C2::TrackAttachment *a1, const R2C2::TrackAttachment *a2) const
24 { return a1->get_offset_from_endpoint(entry)<a2->get_offset_from_endpoint(entry); }
31 Track::Track(Layout &l, const TrackType &t):
39 links(type.get_endpoints().size()),
46 turnout_addr = layout.allocate_turnout_address();
48 if(layout.has_driver())
50 Driver &driver = layout.get_driver();
51 turnout_id = driver.add_turnout(turnout_addr, type);
52 driver.signal_turnout.connect(sigc::mem_fun(this, &Track::turnout_event));
53 driver.signal_turnout_failed.connect(sigc::mem_fun(this, &Track::turnout_failed));
59 for(unsigned paths = type.get_paths(); !(paths&1); ++active_path, paths>>=1) ;
65 if(layout.has_driver() && turnout_id)
66 layout.get_driver().remove_turnout(turnout_id);
70 Track *Track::clone(Layout *to_layout) const
72 Track *track = new Track((to_layout ? *to_layout : layout), type);
73 track->set_position(position);
74 track->set_rotation(rotation);
78 void Track::set_block(Block *b)
80 if(b && !b->has_track(*this))
81 throw logic_error("track not in block");
82 if(!b && block && block->has_track(*this))
83 throw logic_error("track still in block");
88 Block &Track::get_block() const
91 throw logic_error("!block");
96 void Track::set_position(const Vector &p)
103 void Track::set_rotation(const Angle &r)
105 rotation = wrap_positive(r);
109 void Track::set_tilt(const Angle &t)
115 slope = tan(tilt)*type.get_path_length(0);
120 void Track::set_flex(bool f)
125 void Track::propagate_slope()
127 for(vector<Track *>::const_iterator i=links.begin(); i!=links.end(); ++i)
132 void Track::check_slope()
137 if(links[0] && links[1])
139 Vector epp0 = links[0]->get_snap_node(links[0]->get_link_slot(*this)).position;
140 Vector epp1 = links[1]->get_snap_node(links[1]->get_link_slot(*this)).position;
142 slope = epp1.z-position.z;
143 tilt = Geometry::atan(slope/type.get_path_length(0));
149 Vector epp = links[0]->get_snap_node(links[0]->get_link_slot(*this)).position;
154 Vector epp = links[1]->get_snap_node(links[1]->get_link_slot(*this)).position;
155 position.z = epp.z-slope;
162 void Track::set_turnout_address(unsigned a)
164 if(!type.is_turnout())
165 throw logic_error("not a turnout");
167 throw invalid_argument("Track::set_turnout_id");
169 Driver *driver = (layout.has_driver() ? &layout.get_driver() : 0);
171 if(driver && turnout_id)
172 driver->remove_turnout(turnout_id);
174 layout.create_blocks(*this);
175 layout.update_routes();
176 if(driver && turnout_addr)
177 turnout_id = driver->add_turnout(turnout_addr, type);
182 void Track::set_sensor_address(unsigned a)
184 if(type.is_turnout())
185 throw logic_error("is a turnout");
188 layout.create_blocks(*this);
191 void Track::set_preferred_exit(int e)
196 void Track::set_active_path(unsigned p)
198 if(!type.is_turnout())
199 throw logic_error("not a turnout");
200 if(!(type.get_paths()&(1<<p)))
201 throw invalid_argument("Track::set_active_path");
206 signal_path_changing(p);
207 path_changing = true;
208 layout.get_driver().set_turnout(turnout_id, p);
211 float Track::get_path_length(int p) const
215 return type.get_path_length(p);
218 OrientedPoint Track::get_point(unsigned epi, unsigned path, float d) const
220 OrientedPoint p = type.get_point(epi, path, d);
222 p.position = position+rotated_vector(p.position, rotation);
223 p.rotation += rotation;
224 if(type.get_endpoints().size()==2)
226 float dz = tan(tilt)*d;
234 p.position.z += slope-dz;
242 OrientedPoint Track::get_point(unsigned epi, float d) const
244 return get_point(epi, active_path, d);
247 unsigned Track::get_n_snap_nodes() const
249 return type.get_endpoints().size();
252 Snap Track::get_snap_node(unsigned i) const
254 const vector<TrackType::Endpoint> &eps = type.get_endpoints();
256 throw out_of_range("Track::get_snap_node");
259 const TrackType::Endpoint &ep = eps[i];
261 result.position = position+rotated_vector(ep.pos, rotation);
262 if(eps.size()==2 && i==1)
263 result.position.z += slope;
265 result.rotation = rotation+ep.dir;
270 bool Track::snap(Snap &sn, float limit, SnapType what) const
272 if(Object::snap(sn, limit, what))
275 if(what&SNAP_SEGMENT)
277 Vector local = rotated_vector(sn.position-position, -rotation);
279 OrientedPoint np = type.get_nearest_point(local);
280 Vector span = local-np.position;
281 if(dot(span, span)<=limit*limit)
283 sn.position = position+rotated_vector(np.position, rotation);
284 sn.rotation = np.rotation+rotation;
292 SnapType Track::get_default_snap_type_to(const Object &other) const
294 if(dynamic_cast<const Track *>(&other))
300 unsigned Track::get_n_link_slots() const
305 Track *Track::get_link(unsigned i) const
308 throw out_of_range("Track::get_link");
313 int Track::get_link_slot(const Object &other) const
315 for(unsigned i=0; i<links.size(); ++i)
322 bool Track::link_to(Object &other)
324 Track *otrack = dynamic_cast<Track *>(&other);
328 float gauge_ratio = otrack->get_type().get_gauge()/type.get_gauge();
329 if(gauge_ratio<0.99 || gauge_ratio>1.01)
332 float limit = type.get_gauge();
333 if(!flex && !otrack->get_flex())
337 unsigned nsn = get_n_snap_nodes();
338 unsigned other_nsn = other.get_n_snap_nodes();
339 for(unsigned i=0; i<nsn; ++i)
341 Snap sn = get_snap_node(i);
342 for(unsigned j=0; j<other_nsn; ++j)
344 Snap osn = other.get_snap_node(j);
345 Vector span = osn.position-sn.position;
346 Angle da = wrap_balanced(osn.rotation-sn.rotation-Angle::half_turn());
348 if(dot(span, span)<limit && abs(da).radians()<0.01)
351 otrack->break_link(j);
353 otrack->links[j] = this;
355 layout.create_blocks(*this);
357 signal_link_changed.emit(i, otrack);
358 otrack->signal_link_changed.emit(j, this);
367 bool Track::break_link(unsigned i)
370 throw out_of_range("Track::break_link");
372 Track *other = links[i];
377 if(!other->break_link(*this))
379 /* If the call doesn't succeed, it means that the other track already
380 broke the link and is calling us right now. Recreate blocks in the inner
381 call so it occurs before any signals are emitted. */
382 layout.create_blocks(*this);
385 signal_link_changed.emit(i, 0);
390 void Track::add_attachment(TrackAttachment &a)
392 if(find(attachments.begin(), attachments.end(), &a)!=attachments.end())
394 attachments.push_back(&a);
397 void Track::remove_attachment(TrackAttachment &a)
399 AttachmentList::iterator i = find(attachments.begin(), attachments.end(), &a);
400 if(i==attachments.end())
402 attachments.erase(i);
405 Track::AttachmentList Track::get_attachments_ordered(unsigned epi) const
407 AttachmentList result = attachments;
408 result.sort(AttachmentCompare(epi));
412 void Track::save(list<DataFile::Statement> &st) const
414 st.push_back((DataFile::Statement("position"), position.x, position.y, position.z));
415 st.push_back((DataFile::Statement("rotation"), rotation.radians()));
416 st.push_back((DataFile::Statement("tilt"), tilt.radians()));
418 st.push_back((DataFile::Statement("turnout_address"), turnout_addr));
420 st.push_back((DataFile::Statement("sensor_address"), sensor_addr));
422 st.push_back((DataFile::Statement("flex"), true));
425 void Track::save_dynamic(list<DataFile::Statement> &st) const
428 st.push_back((DataFile::Statement("path"), active_path));
431 void Track::turnout_event(unsigned id, unsigned state)
436 path_changing = false;
437 signal_path_changed.emit(active_path);
441 void Track::turnout_failed(unsigned id)
445 path_changing = false;
446 layout.emergency(block, "Turnout failed");
451 Track::Loader::Loader(Track &t):
452 DataFile::ObjectLoader<Track>(t)
454 add("path", &Loader::path);
455 add("position", &Loader::position);
456 add("rotation", &Loader::rotation);
457 add("tilt", &Loader::tilt);
458 add("turnout_id", &Loader::turnout_address);
459 add("turnout_address", &Loader::turnout_address);
460 add("sensor_id", &Loader::sensor_address);
461 add("sensor_address", &Loader::sensor_address);
462 add("flex", &Track::flex);
465 add("slope", &Loader::slope);
468 void Track::Loader::path(unsigned p)
470 obj.set_active_path(p);
471 if(obj.path_changing)
474 obj.signal_path_changed.emit(p);
478 void Track::Loader::position(float x, float y, float z)
480 obj.set_position(Vector(x, y, z));
483 void Track::Loader::rotation(float r)
485 obj.set_rotation(Angle::from_radians(r));
488 void Track::Loader::sensor_address(unsigned addr)
490 obj.set_sensor_address(addr);
493 void Track::Loader::slope(float s)
495 obj.set_tilt(Geometry::atan(s/obj.type.get_path_length(0)));
498 void Track::Loader::tilt(float t)
500 obj.set_tilt(Angle::from_radians(t));
503 void Track::Loader::turnout_address(unsigned addr)
505 obj.set_turnout_address(addr);