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) ;
56 if(layout.has_driver() && turnout_id)
57 layout.get_driver().remove_turnout(turnout_id);
61 Track *Track::clone(Layout *to_layout) const
63 Track *track = new Track((to_layout ? *to_layout : layout), type);
64 track->set_position(position);
65 track->set_rotation(rotation);
69 void Track::set_block(Block *b)
71 if(b && !b->has_track(*this))
72 throw logic_error("track not in block");
73 if(!b && block && block->has_track(*this))
74 throw logic_error("track still in block");
79 Block &Track::get_block() const
82 throw logic_error("!block");
87 void Track::set_position(const Vector &p)
94 void Track::set_rotation(const Angle &r)
96 rotation = wrap_positive(r);
100 void Track::set_tilt(const Angle &t)
106 slope = tan(tilt)*type.get_path_length(0);
111 void Track::set_flex(bool f)
116 void Track::propagate_slope()
118 for(vector<Track *>::const_iterator i=links.begin(); i!=links.end(); ++i)
123 void Track::check_slope()
128 if(links[0] && links[1])
130 Vector epp0 = links[0]->get_snap_node(links[0]->get_link_slot(*this)).position;
131 Vector epp1 = links[1]->get_snap_node(links[1]->get_link_slot(*this)).position;
133 slope = epp1.z-position.z;
134 tilt = Geometry::atan(slope/type.get_path_length(0));
140 Vector epp = links[0]->get_snap_node(links[0]->get_link_slot(*this)).position;
145 Vector epp = links[1]->get_snap_node(links[1]->get_link_slot(*this)).position;
146 position.z = epp.z-slope;
153 void Track::set_turnout_id(unsigned i)
155 if(!type.is_turnout())
156 throw logic_error("not a turnout");
158 throw invalid_argument("Track::set_turnout_id");
160 Driver *driver = (layout.has_driver() ? &layout.get_driver() : 0);
162 if(driver && turnout_id)
163 driver->remove_turnout(turnout_id);
165 layout.create_blocks(*this);
166 layout.update_routes();
167 if(driver && turnout_id)
168 driver->add_turnout(turnout_id, type);
171 void Track::set_sensor_id(unsigned i)
173 if(type.is_turnout())
174 throw logic_error("is a turnout");
177 layout.create_blocks(*this);
180 void Track::set_active_path(unsigned p)
183 throw logic_error("not a turnout");
184 if(!(type.get_paths()&(1<<p)))
185 throw invalid_argument("Track::set_active_path");
187 signal_path_changing(p);
188 path_changing = true;
189 layout.get_driver().set_turnout(turnout_id, p);
192 float Track::get_path_length(int p) const
196 return type.get_path_length(p);
199 OrientedPoint Track::get_point(unsigned epi, unsigned path, float d) const
201 OrientedPoint p = type.get_point(epi, path, d);
203 p.position = position+rotated_vector(p.position, rotation);
204 p.rotation += rotation;
205 if(type.get_endpoints().size()==2)
207 float dz = tan(tilt)*d;
215 p.position.z += slope-dz;
223 OrientedPoint Track::get_point(unsigned epi, float d) const
225 return get_point(epi, active_path, d);
228 unsigned Track::get_n_snap_nodes() const
230 return type.get_endpoints().size();
233 Snap Track::get_snap_node(unsigned i) const
235 const vector<TrackType::Endpoint> &eps = type.get_endpoints();
237 throw out_of_range("Track::get_snap_node");
240 const TrackType::Endpoint &ep = eps[i];
242 result.position = position+rotated_vector(ep.pos, rotation);
243 if(eps.size()==2 && i==1)
244 result.position.z += slope;
246 result.rotation = rotation+ep.dir;
251 bool Track::snap(Snap &sn, float limit, SnapType what) const
253 if(Object::snap(sn, limit, what))
256 if(what&SNAP_SEGMENT)
258 Vector local = rotated_vector(sn.position-position, -rotation);
260 OrientedPoint np = type.get_nearest_point(local);
261 Vector span = local-np.position;
262 if(dot(span, span)<=limit*limit)
264 sn.position = position+rotated_vector(np.position, rotation);
265 sn.rotation = np.rotation+rotation;
273 SnapType Track::get_default_snap_type_to(const Object &other) const
275 if(dynamic_cast<const Track *>(&other))
281 unsigned Track::get_n_link_slots() const
286 Track *Track::get_link(unsigned i) const
289 throw out_of_range("Track::get_link");
294 int Track::get_link_slot(const Object &other) const
296 for(unsigned i=0; i<links.size(); ++i)
303 bool Track::link_to(Object &other)
305 Track *otrack = dynamic_cast<Track *>(&other);
309 float limit = layout.get_catalogue().get_gauge();
310 if(!flex && !otrack->get_flex())
314 unsigned nsn = get_n_snap_nodes();
315 unsigned other_nsn = other.get_n_snap_nodes();
316 for(unsigned i=0; i<nsn; ++i)
318 Snap sn = get_snap_node(i);
319 for(unsigned j=0; j<other_nsn; ++j)
321 Snap osn = other.get_snap_node(j);
322 Vector span = osn.position-sn.position;
323 Angle da = wrap_balanced(osn.rotation-sn.rotation-Angle::half_turn());
325 if(dot(span, span)<limit && abs(da).radians()<0.01)
329 otrack->links[j] = this;
331 layout.create_blocks(*this);
333 signal_link_changed.emit(i, otrack);
334 otrack->signal_link_changed.emit(j, this);
343 bool Track::break_link(unsigned i)
346 throw out_of_range("Track::break_link");
348 Track *other = links[i];
353 if(!other->break_link(*this))
355 /* If the call doesn't succeed, it means that the other track already
356 broke the link and is calling us right now. Recreate blocks in the inner
357 call so it occurs before any signals are emitted. */
358 layout.create_blocks(*this);
361 signal_link_changed.emit(i, 0);
366 void Track::add_attachment(TrackAttachment &a)
368 if(find(attachments.begin(), attachments.end(), &a)!=attachments.end())
370 attachments.push_back(&a);
373 void Track::remove_attachment(TrackAttachment &a)
375 AttachmentList::iterator i = find(attachments.begin(), attachments.end(), &a);
376 if(i==attachments.end())
378 attachments.erase(i);
381 Track::AttachmentList Track::get_attachments_ordered(unsigned epi) const
383 AttachmentList result = attachments;
384 result.sort(AttachmentCompare(epi));
388 void Track::save(list<DataFile::Statement> &st) const
390 st.push_back((DataFile::Statement("position"), position.x, position.y, position.z));
391 st.push_back((DataFile::Statement("rotation"), rotation.radians()));
392 st.push_back((DataFile::Statement("tilt"), tilt.radians()));
394 st.push_back((DataFile::Statement("turnout_id"), turnout_id));
396 st.push_back((DataFile::Statement("sensor_id"), sensor_id));
398 st.push_back((DataFile::Statement("flex"), true));
401 void Track::turnout_event(unsigned addr, unsigned state)
409 path_changing = false;
410 signal_path_changed.emit(active_path);
415 Track::Loader::Loader(Track &t):
416 DataFile::ObjectLoader<Track>(t)
418 add("position", &Loader::position);
419 add("rotation", &Loader::rotation);
420 add("tilt", &Loader::tilt);
421 add("turnout_id", &Loader::turnout_id);
422 add("sensor_id", &Loader::sensor_id);
423 add("flex", &Track::flex);
426 add("slope", &Loader::slope);
429 void Track::Loader::position(float x, float y, float z)
431 obj.set_position(Vector(x, y, z));
434 void Track::Loader::rotation(float r)
436 obj.set_rotation(Angle::from_radians(r));
439 void Track::Loader::sensor_id(unsigned id)
441 obj.set_sensor_id(id);
444 void Track::Loader::slope(float s)
446 obj.set_tilt(Geometry::atan(s/obj.type.get_path_length(0)));
449 void Track::Loader::tilt(float t)
451 obj.set_tilt(Angle::from_radians(t));
454 void Track::Loader::turnout_id(unsigned id)
456 obj.set_turnout_id(id);