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)
336 otrack->links[j] = this;
338 layout.create_blocks(*this);
340 signal_link_changed.emit(i, otrack);
341 otrack->signal_link_changed.emit(j, this);
350 bool Track::break_link(unsigned i)
353 throw out_of_range("Track::break_link");
355 Track *other = links[i];
360 if(!other->break_link(*this))
362 /* If the call doesn't succeed, it means that the other track already
363 broke the link and is calling us right now. Recreate blocks in the inner
364 call so it occurs before any signals are emitted. */
365 layout.create_blocks(*this);
368 signal_link_changed.emit(i, 0);
373 void Track::add_attachment(TrackAttachment &a)
375 if(find(attachments.begin(), attachments.end(), &a)!=attachments.end())
377 attachments.push_back(&a);
380 void Track::remove_attachment(TrackAttachment &a)
382 AttachmentList::iterator i = find(attachments.begin(), attachments.end(), &a);
383 if(i==attachments.end())
385 attachments.erase(i);
388 Track::AttachmentList Track::get_attachments_ordered(unsigned epi) const
390 AttachmentList result = attachments;
391 result.sort(AttachmentCompare(epi));
395 void Track::save(list<DataFile::Statement> &st) const
397 st.push_back((DataFile::Statement("position"), position.x, position.y, position.z));
398 st.push_back((DataFile::Statement("rotation"), rotation.radians()));
399 st.push_back((DataFile::Statement("tilt"), tilt.radians()));
401 st.push_back((DataFile::Statement("turnout_id"), turnout_id));
403 st.push_back((DataFile::Statement("sensor_id"), sensor_id));
405 st.push_back((DataFile::Statement("flex"), true));
408 void Track::turnout_event(unsigned addr, unsigned state)
413 path_changing = false;
414 signal_path_changed.emit(active_path);
419 Track::Loader::Loader(Track &t):
420 DataFile::ObjectLoader<Track>(t)
422 add("position", &Loader::position);
423 add("rotation", &Loader::rotation);
424 add("tilt", &Loader::tilt);
425 add("turnout_id", &Loader::turnout_id);
426 add("sensor_id", &Loader::sensor_id);
427 add("flex", &Track::flex);
430 add("slope", &Loader::slope);
433 void Track::Loader::position(float x, float y, float z)
435 obj.set_position(Vector(x, y, z));
438 void Track::Loader::rotation(float r)
440 obj.set_rotation(Angle::from_radians(r));
443 void Track::Loader::sensor_id(unsigned id)
445 obj.set_sensor_id(id);
448 void Track::Loader::slope(float s)
450 obj.set_tilt(Geometry::atan(s/obj.type.get_path_length(0)));
453 void Track::Loader::tilt(float t)
455 obj.set_tilt(Angle::from_radians(t));
458 void Track::Loader::turnout_id(unsigned id)
460 obj.set_turnout_id(id);