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()),
43 turnout_id = layout.allocate_turnout_id();
47 if(layout.has_driver())
48 layout.get_driver().signal_turnout.connect(sigc::mem_fun(this, &Track::turnout_event));
50 for(unsigned paths = type.get_paths(); !(paths&1); ++active_path, paths>>=1) ;
59 Track *Track::clone(Layout *to_layout) const
61 Track *track = new Track((to_layout ? *to_layout : layout), type);
62 track->set_position(position);
63 track->set_rotation(rotation);
67 void Track::set_block(Block *b)
69 if(b && !b->has_track(*this))
70 throw logic_error("track not in block");
71 if(!b && block && block->has_track(*this))
72 throw logic_error("track still in block");
77 Block &Track::get_block() const
80 throw logic_error("!block");
85 void Track::set_position(const Vector &p)
92 void Track::set_rotation(const Angle &r)
94 rotation = wrap_positive(r);
98 void Track::set_tilt(const Angle &t)
104 slope = tan(tilt)*type.get_path_length(0);
109 void Track::set_flex(bool f)
114 void Track::propagate_slope()
116 for(vector<Track *>::const_iterator i=links.begin(); i!=links.end(); ++i)
121 void Track::check_slope()
126 if(links[0] && links[1])
128 Vector epp0 = links[0]->get_snap_node(links[0]->get_link_slot(*this)).position;
129 Vector epp1 = links[1]->get_snap_node(links[1]->get_link_slot(*this)).position;
131 slope = epp1.z-position.z;
132 tilt = Geometry::atan(slope/type.get_path_length(0));
138 Vector epp = links[0]->get_snap_node(links[0]->get_link_slot(*this)).position;
143 Vector epp = links[1]->get_snap_node(links[1]->get_link_slot(*this)).position;
144 position.z = epp.z-slope;
151 void Track::set_turnout_id(unsigned i)
153 if(!type.is_turnout())
154 throw logic_error("not a turnout");
156 throw invalid_argument("Track::set_turnout_id");
159 layout.create_blocks(*this);
160 layout.update_routes();
161 if(layout.has_driver() && turnout_id)
162 layout.get_driver().add_turnout(turnout_id, type);
165 void Track::set_sensor_id(unsigned i)
167 if(type.is_turnout())
168 throw logic_error("is a turnout");
171 layout.create_blocks(*this);
174 void Track::set_active_path(unsigned p)
177 throw logic_error("not a turnout");
178 if(!(type.get_paths()&(1<<p)))
179 throw invalid_argument("Track::set_active_path");
181 signal_path_changing(p);
182 path_changing = true;
183 layout.get_driver().set_turnout(turnout_id, p);
186 float Track::get_path_length(int p) const
190 return type.get_path_length(p);
193 OrientedPoint Track::get_point(unsigned epi, unsigned path, float d) const
195 OrientedPoint p = type.get_point(epi, path, d);
197 p.position = position+rotated_vector(p.position, rotation);
198 p.rotation += rotation;
199 if(type.get_endpoints().size()==2)
201 float dz = tan(tilt)*d;
209 p.position.z += slope-dz;
217 OrientedPoint Track::get_point(unsigned epi, float d) const
219 return get_point(epi, active_path, d);
222 unsigned Track::get_n_snap_nodes() const
224 return type.get_endpoints().size();
227 Snap Track::get_snap_node(unsigned i) const
229 const vector<TrackType::Endpoint> &eps = type.get_endpoints();
231 throw out_of_range("Track::get_snap_node");
234 const TrackType::Endpoint &ep = eps[i];
236 result.position = position+rotated_vector(ep.pos, rotation);
237 if(eps.size()==2 && i==1)
238 result.position.z += slope;
240 result.rotation = rotation+ep.dir;
245 bool Track::snap(Snap &sn, float limit, SnapType what) const
247 if(Object::snap(sn, limit, what))
250 if(what&SNAP_SEGMENT)
252 Vector local = rotated_vector(sn.position-position, -rotation);
254 OrientedPoint np = type.get_nearest_point(local);
255 Vector span = local-np.position;
256 if(dot(span, span)<=limit*limit)
258 sn.position = position+rotated_vector(np.position, rotation);
259 sn.rotation = np.rotation+rotation;
267 SnapType Track::get_default_snap_type_to(const Object &other) const
269 if(dynamic_cast<const Track *>(&other))
275 unsigned Track::get_n_link_slots() const
280 Track *Track::get_link(unsigned i) const
283 throw out_of_range("Track::get_link");
288 int Track::get_link_slot(const Object &other) const
290 for(unsigned i=0; i<links.size(); ++i)
297 bool Track::link_to(Object &other)
299 Track *otrack = dynamic_cast<Track *>(&other);
303 float limit = layout.get_catalogue().get_gauge();
304 if(!flex && !otrack->get_flex())
308 unsigned nsn = get_n_snap_nodes();
309 unsigned other_nsn = other.get_n_snap_nodes();
310 for(unsigned i=0; i<nsn; ++i)
312 Snap sn = get_snap_node(i);
313 for(unsigned j=0; j<other_nsn; ++j)
315 Snap osn = other.get_snap_node(j);
316 Vector span = osn.position-sn.position;
317 Angle da = wrap_balanced(osn.rotation-sn.rotation-Angle::half_turn());
319 if(dot(span, span)<limit && abs(da).radians()<0.01)
323 otrack->links[j] = this;
325 layout.create_blocks(*this);
327 signal_link_changed.emit(i, otrack);
328 otrack->signal_link_changed.emit(j, this);
337 bool Track::break_link(unsigned i)
340 throw out_of_range("Track::break_link");
342 Track *other = links[i];
347 if(!other->break_link(*this))
349 /* If the call doesn't succeed, it means that the other track already
350 broke the link and is calling us right now. Recreate blocks in the inner
351 call so it occurs before any signals are emitted. */
352 layout.create_blocks(*this);
355 signal_link_changed.emit(i, 0);
360 void Track::add_attachment(TrackAttachment &a)
362 if(find(attachments.begin(), attachments.end(), &a)!=attachments.end())
364 attachments.push_back(&a);
367 void Track::remove_attachment(TrackAttachment &a)
369 AttachmentList::iterator i = find(attachments.begin(), attachments.end(), &a);
370 if(i==attachments.end())
372 attachments.erase(i);
375 Track::AttachmentList Track::get_attachments_ordered(unsigned epi) const
377 AttachmentList result = attachments;
378 result.sort(AttachmentCompare(epi));
382 void Track::save(list<DataFile::Statement> &st) const
384 st.push_back((DataFile::Statement("position"), position.x, position.y, position.z));
385 st.push_back((DataFile::Statement("rotation"), rotation.radians()));
386 st.push_back((DataFile::Statement("tilt"), tilt.radians()));
388 st.push_back((DataFile::Statement("turnout_id"), turnout_id));
390 st.push_back((DataFile::Statement("sensor_id"), sensor_id));
392 st.push_back((DataFile::Statement("flex"), true));
395 void Track::turnout_event(unsigned addr, unsigned state)
403 path_changing = false;
404 signal_path_changed.emit(active_path);
409 Track::Loader::Loader(Track &t):
410 DataFile::ObjectLoader<Track>(t)
412 add("position", &Loader::position);
413 add("rotation", &Loader::rotation);
414 add("tilt", &Loader::tilt);
415 add("turnout_id", &Loader::turnout_id);
416 add("sensor_id", &Loader::sensor_id);
417 add("flex", &Track::flex);
420 add("slope", &Loader::slope);
423 void Track::Loader::position(float x, float y, float z)
425 obj.set_position(Vector(x, y, z));
428 void Track::Loader::rotation(float r)
430 obj.set_rotation(Angle::from_radians(r));
433 void Track::Loader::sensor_id(unsigned id)
435 obj.set_sensor_id(id);
438 void Track::Loader::slope(float s)
440 obj.set_tilt(Geometry::atan(s/obj.type.get_path_length(0)));
443 void Track::Loader::tilt(float t)
445 obj.set_tilt(Angle::from_radians(t));
448 void Track::Loader::turnout_id(unsigned id)
450 obj.set_turnout_id(id);