2 #include <msp/core/maputils.h>
8 #include "trackattachment.h"
16 struct AttachmentCompare
20 AttachmentCompare(unsigned e): entry(e) { }
22 bool operator()(const R2C2::TrackAttachment *a1, const R2C2::TrackAttachment *a2) const
23 { return a1->get_offset_from_endpoint(entry)<a2->get_offset_from_endpoint(entry); }
30 Track::Track(Layout &l, const TrackType &t):
38 links(type.get_endpoints().size()),
44 turnout_id = layout.allocate_turnout_id();
46 if(layout.has_driver())
48 Driver &driver = layout.get_driver();
49 driver.add_turnout(turnout_id, type);
50 driver.signal_turnout.connect(sigc::mem_fun(this, &Track::turnout_event));
57 for(unsigned paths = type.get_paths(); !(paths&1); ++active_path, paths>>=1) ;
63 if(layout.has_driver() && turnout_id)
64 layout.get_driver().remove_turnout(turnout_id);
68 Track *Track::clone(Layout *to_layout) const
70 Track *track = new Track((to_layout ? *to_layout : layout), type);
71 track->set_position(position);
72 track->set_rotation(rotation);
76 void Track::set_block(Block *b)
78 if(b && !b->has_track(*this))
79 throw logic_error("track not in block");
80 if(!b && block && block->has_track(*this))
81 throw logic_error("track still in block");
86 Block &Track::get_block() const
89 throw logic_error("!block");
94 void Track::set_position(const Vector &p)
101 void Track::set_rotation(const Angle &r)
103 rotation = wrap_positive(r);
107 void Track::set_tilt(const Angle &t)
113 slope = tan(tilt)*type.get_path_length(0);
118 void Track::set_flex(bool f)
123 void Track::propagate_slope()
125 for(vector<Track *>::const_iterator i=links.begin(); i!=links.end(); ++i)
130 void Track::check_slope()
135 if(links[0] && links[1])
137 Vector epp0 = links[0]->get_snap_node(links[0]->get_link_slot(*this)).position;
138 Vector epp1 = links[1]->get_snap_node(links[1]->get_link_slot(*this)).position;
140 slope = epp1.z-position.z;
141 tilt = Geometry::atan(slope/type.get_path_length(0));
147 Vector epp = links[0]->get_snap_node(links[0]->get_link_slot(*this)).position;
152 Vector epp = links[1]->get_snap_node(links[1]->get_link_slot(*this)).position;
153 position.z = epp.z-slope;
160 void Track::set_turnout_id(unsigned i)
162 if(!type.is_turnout())
163 throw logic_error("not a turnout");
165 throw invalid_argument("Track::set_turnout_id");
167 Driver *driver = (layout.has_driver() ? &layout.get_driver() : 0);
169 if(driver && turnout_id)
170 driver->remove_turnout(turnout_id);
172 layout.create_blocks(*this);
173 layout.update_routes();
174 if(driver && turnout_id)
175 driver->add_turnout(turnout_id, type);
178 void Track::set_sensor_id(unsigned i)
180 if(type.is_turnout())
181 throw logic_error("is a turnout");
184 layout.create_blocks(*this);
187 void Track::set_active_path(unsigned p)
190 throw logic_error("not a turnout");
191 if(!(type.get_paths()&(1<<p)))
192 throw invalid_argument("Track::set_active_path");
194 signal_path_changing(p);
195 path_changing = true;
196 layout.get_driver().set_turnout(turnout_id, p);
199 float Track::get_path_length(int p) const
203 return type.get_path_length(p);
206 OrientedPoint Track::get_point(unsigned epi, unsigned path, float d) const
208 OrientedPoint p = type.get_point(epi, path, d);
210 p.position = position+rotated_vector(p.position, rotation);
211 p.rotation += rotation;
212 if(type.get_endpoints().size()==2)
214 float dz = tan(tilt)*d;
222 p.position.z += slope-dz;
230 OrientedPoint Track::get_point(unsigned epi, float d) const
232 return get_point(epi, active_path, d);
235 unsigned Track::get_n_snap_nodes() const
237 return type.get_endpoints().size();
240 Snap Track::get_snap_node(unsigned i) const
242 const vector<TrackType::Endpoint> &eps = type.get_endpoints();
244 throw out_of_range("Track::get_snap_node");
247 const TrackType::Endpoint &ep = eps[i];
249 result.position = position+rotated_vector(ep.pos, rotation);
250 if(eps.size()==2 && i==1)
251 result.position.z += slope;
253 result.rotation = rotation+ep.dir;
258 bool Track::snap(Snap &sn, float limit, SnapType what) const
260 if(Object::snap(sn, limit, what))
263 if(what&SNAP_SEGMENT)
265 Vector local = rotated_vector(sn.position-position, -rotation);
267 OrientedPoint np = type.get_nearest_point(local);
268 Vector span = local-np.position;
269 if(dot(span, span)<=limit*limit)
271 sn.position = position+rotated_vector(np.position, rotation);
272 sn.rotation = np.rotation+rotation;
280 SnapType Track::get_default_snap_type_to(const Object &other) const
282 if(dynamic_cast<const Track *>(&other))
288 unsigned Track::get_n_link_slots() const
293 Track *Track::get_link(unsigned i) const
296 throw out_of_range("Track::get_link");
301 int Track::get_link_slot(const Object &other) const
303 for(unsigned i=0; i<links.size(); ++i)
310 bool Track::link_to(Object &other)
312 Track *otrack = dynamic_cast<Track *>(&other);
316 float limit = layout.get_catalogue().get_gauge();
317 if(!flex && !otrack->get_flex())
321 unsigned nsn = get_n_snap_nodes();
322 unsigned other_nsn = other.get_n_snap_nodes();
323 for(unsigned i=0; i<nsn; ++i)
325 Snap sn = get_snap_node(i);
326 for(unsigned j=0; j<other_nsn; ++j)
328 Snap osn = other.get_snap_node(j);
329 Vector span = osn.position-sn.position;
330 Angle da = wrap_balanced(osn.rotation-sn.rotation-Angle::half_turn());
332 if(dot(span, span)<limit && abs(da).radians()<0.01)
335 otrack->break_link(j);
337 otrack->links[j] = this;
339 layout.create_blocks(*this);
341 signal_link_changed.emit(i, otrack);
342 otrack->signal_link_changed.emit(j, this);
351 bool Track::break_link(unsigned i)
354 throw out_of_range("Track::break_link");
356 Track *other = links[i];
361 if(!other->break_link(*this))
363 /* If the call doesn't succeed, it means that the other track already
364 broke the link and is calling us right now. Recreate blocks in the inner
365 call so it occurs before any signals are emitted. */
366 layout.create_blocks(*this);
369 signal_link_changed.emit(i, 0);
374 void Track::add_attachment(TrackAttachment &a)
376 if(find(attachments.begin(), attachments.end(), &a)!=attachments.end())
378 attachments.push_back(&a);
381 void Track::remove_attachment(TrackAttachment &a)
383 AttachmentList::iterator i = find(attachments.begin(), attachments.end(), &a);
384 if(i==attachments.end())
386 attachments.erase(i);
389 Track::AttachmentList Track::get_attachments_ordered(unsigned epi) const
391 AttachmentList result = attachments;
392 result.sort(AttachmentCompare(epi));
396 void Track::save(list<DataFile::Statement> &st) const
398 st.push_back((DataFile::Statement("position"), position.x, position.y, position.z));
399 st.push_back((DataFile::Statement("rotation"), rotation.radians()));
400 st.push_back((DataFile::Statement("tilt"), tilt.radians()));
402 st.push_back((DataFile::Statement("turnout_id"), turnout_id));
404 st.push_back((DataFile::Statement("sensor_id"), sensor_id));
406 st.push_back((DataFile::Statement("flex"), true));
409 void Track::turnout_event(unsigned addr, unsigned state)
414 path_changing = false;
415 signal_path_changed.emit(active_path);
420 Track::Loader::Loader(Track &t):
421 DataFile::ObjectLoader<Track>(t)
423 add("position", &Loader::position);
424 add("rotation", &Loader::rotation);
425 add("tilt", &Loader::tilt);
426 add("turnout_id", &Loader::turnout_id);
427 add("sensor_id", &Loader::sensor_id);
428 add("flex", &Track::flex);
431 add("slope", &Loader::slope);
434 void Track::Loader::position(float x, float y, float z)
436 obj.set_position(Vector(x, y, z));
439 void Track::Loader::rotation(float r)
441 obj.set_rotation(Angle::from_radians(r));
444 void Track::Loader::sensor_id(unsigned id)
446 obj.set_sensor_id(id);
449 void Track::Loader::slope(float s)
451 obj.set_tilt(Geometry::atan(s/obj.type.get_path_length(0)));
454 void Track::Loader::tilt(float t)
456 obj.set_tilt(Angle::from_radians(t));
459 void Track::Loader::turnout_id(unsigned id)
461 obj.set_turnout_id(id);