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_addr = layout.allocate_turnout_address();
46 if(layout.has_driver())
48 Driver &driver = layout.get_driver();
49 turnout_id = driver.add_turnout(turnout_addr, 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_address(unsigned a)
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_addr)
175 turnout_id = driver->add_turnout(turnout_addr, type);
180 void Track::set_sensor_address(unsigned a)
182 if(type.is_turnout())
183 throw logic_error("is a turnout");
186 layout.create_blocks(*this);
189 void Track::set_active_path(unsigned p)
191 if(!type.is_turnout())
192 throw logic_error("not a turnout");
193 if(!(type.get_paths()&(1<<p)))
194 throw invalid_argument("Track::set_active_path");
199 signal_path_changing(p);
200 path_changing = true;
201 layout.get_driver().set_turnout(turnout_id, p);
204 float Track::get_path_length(int p) const
208 return type.get_path_length(p);
211 OrientedPoint Track::get_point(unsigned epi, unsigned path, float d) const
213 OrientedPoint p = type.get_point(epi, path, d);
215 p.position = position+rotated_vector(p.position, rotation);
216 p.rotation += rotation;
217 if(type.get_endpoints().size()==2)
219 float dz = tan(tilt)*d;
227 p.position.z += slope-dz;
235 OrientedPoint Track::get_point(unsigned epi, float d) const
237 return get_point(epi, active_path, d);
240 unsigned Track::get_n_snap_nodes() const
242 return type.get_endpoints().size();
245 Snap Track::get_snap_node(unsigned i) const
247 const vector<TrackType::Endpoint> &eps = type.get_endpoints();
249 throw out_of_range("Track::get_snap_node");
252 const TrackType::Endpoint &ep = eps[i];
254 result.position = position+rotated_vector(ep.pos, rotation);
255 if(eps.size()==2 && i==1)
256 result.position.z += slope;
258 result.rotation = rotation+ep.dir;
263 bool Track::snap(Snap &sn, float limit, SnapType what) const
265 if(Object::snap(sn, limit, what))
268 if(what&SNAP_SEGMENT)
270 Vector local = rotated_vector(sn.position-position, -rotation);
272 OrientedPoint np = type.get_nearest_point(local);
273 Vector span = local-np.position;
274 if(dot(span, span)<=limit*limit)
276 sn.position = position+rotated_vector(np.position, rotation);
277 sn.rotation = np.rotation+rotation;
285 SnapType Track::get_default_snap_type_to(const Object &other) const
287 if(dynamic_cast<const Track *>(&other))
293 unsigned Track::get_n_link_slots() const
298 Track *Track::get_link(unsigned i) const
301 throw out_of_range("Track::get_link");
306 int Track::get_link_slot(const Object &other) const
308 for(unsigned i=0; i<links.size(); ++i)
315 bool Track::link_to(Object &other)
317 Track *otrack = dynamic_cast<Track *>(&other);
321 float limit = layout.get_catalogue().get_gauge();
322 if(!flex && !otrack->get_flex())
326 unsigned nsn = get_n_snap_nodes();
327 unsigned other_nsn = other.get_n_snap_nodes();
328 for(unsigned i=0; i<nsn; ++i)
330 Snap sn = get_snap_node(i);
331 for(unsigned j=0; j<other_nsn; ++j)
333 Snap osn = other.get_snap_node(j);
334 Vector span = osn.position-sn.position;
335 Angle da = wrap_balanced(osn.rotation-sn.rotation-Angle::half_turn());
337 if(dot(span, span)<limit && abs(da).radians()<0.01)
340 otrack->break_link(j);
342 otrack->links[j] = this;
344 layout.create_blocks(*this);
346 signal_link_changed.emit(i, otrack);
347 otrack->signal_link_changed.emit(j, this);
356 bool Track::break_link(unsigned i)
359 throw out_of_range("Track::break_link");
361 Track *other = links[i];
366 if(!other->break_link(*this))
368 /* If the call doesn't succeed, it means that the other track already
369 broke the link and is calling us right now. Recreate blocks in the inner
370 call so it occurs before any signals are emitted. */
371 layout.create_blocks(*this);
374 signal_link_changed.emit(i, 0);
379 void Track::add_attachment(TrackAttachment &a)
381 if(find(attachments.begin(), attachments.end(), &a)!=attachments.end())
383 attachments.push_back(&a);
386 void Track::remove_attachment(TrackAttachment &a)
388 AttachmentList::iterator i = find(attachments.begin(), attachments.end(), &a);
389 if(i==attachments.end())
391 attachments.erase(i);
394 Track::AttachmentList Track::get_attachments_ordered(unsigned epi) const
396 AttachmentList result = attachments;
397 result.sort(AttachmentCompare(epi));
401 void Track::save(list<DataFile::Statement> &st) const
403 st.push_back((DataFile::Statement("position"), position.x, position.y, position.z));
404 st.push_back((DataFile::Statement("rotation"), rotation.radians()));
405 st.push_back((DataFile::Statement("tilt"), tilt.radians()));
407 st.push_back((DataFile::Statement("turnout_address"), turnout_addr));
409 st.push_back((DataFile::Statement("sensor_address"), sensor_addr));
411 st.push_back((DataFile::Statement("flex"), true));
414 void Track::turnout_event(unsigned id, unsigned state)
419 path_changing = false;
420 signal_path_changed.emit(active_path);
425 Track::Loader::Loader(Track &t):
426 DataFile::ObjectLoader<Track>(t)
428 add("position", &Loader::position);
429 add("rotation", &Loader::rotation);
430 add("tilt", &Loader::tilt);
431 add("turnout_id", &Loader::turnout_address);
432 add("turnout_address", &Loader::turnout_address);
433 add("sensor_id", &Loader::sensor_address);
434 add("sensor_address", &Loader::sensor_address);
435 add("flex", &Track::flex);
438 add("slope", &Loader::slope);
441 void Track::Loader::position(float x, float y, float z)
443 obj.set_position(Vector(x, y, z));
446 void Track::Loader::rotation(float r)
448 obj.set_rotation(Angle::from_radians(r));
451 void Track::Loader::sensor_address(unsigned addr)
453 obj.set_sensor_address(addr);
456 void Track::Loader::slope(float s)
458 obj.set_tilt(Geometry::atan(s/obj.type.get_path_length(0)));
461 void Track::Loader::tilt(float t)
463 obj.set_tilt(Angle::from_radians(t));
466 void Track::Loader::turnout_address(unsigned addr)
468 obj.set_turnout_address(addr);