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");
196 signal_path_changing(p);
197 path_changing = true;
198 layout.get_driver().set_turnout(turnout_id, p);
201 float Track::get_path_length(int p) const
205 return type.get_path_length(p);
208 OrientedPoint Track::get_point(unsigned epi, unsigned path, float d) const
210 OrientedPoint p = type.get_point(epi, path, d);
212 p.position = position+rotated_vector(p.position, rotation);
213 p.rotation += rotation;
214 if(type.get_endpoints().size()==2)
216 float dz = tan(tilt)*d;
224 p.position.z += slope-dz;
232 OrientedPoint Track::get_point(unsigned epi, float d) const
234 return get_point(epi, active_path, d);
237 unsigned Track::get_n_snap_nodes() const
239 return type.get_endpoints().size();
242 Snap Track::get_snap_node(unsigned i) const
244 const vector<TrackType::Endpoint> &eps = type.get_endpoints();
246 throw out_of_range("Track::get_snap_node");
249 const TrackType::Endpoint &ep = eps[i];
251 result.position = position+rotated_vector(ep.pos, rotation);
252 if(eps.size()==2 && i==1)
253 result.position.z += slope;
255 result.rotation = rotation+ep.dir;
260 bool Track::snap(Snap &sn, float limit, SnapType what) const
262 if(Object::snap(sn, limit, what))
265 if(what&SNAP_SEGMENT)
267 Vector local = rotated_vector(sn.position-position, -rotation);
269 OrientedPoint np = type.get_nearest_point(local);
270 Vector span = local-np.position;
271 if(dot(span, span)<=limit*limit)
273 sn.position = position+rotated_vector(np.position, rotation);
274 sn.rotation = np.rotation+rotation;
282 SnapType Track::get_default_snap_type_to(const Object &other) const
284 if(dynamic_cast<const Track *>(&other))
290 unsigned Track::get_n_link_slots() const
295 Track *Track::get_link(unsigned i) const
298 throw out_of_range("Track::get_link");
303 int Track::get_link_slot(const Object &other) const
305 for(unsigned i=0; i<links.size(); ++i)
312 bool Track::link_to(Object &other)
314 Track *otrack = dynamic_cast<Track *>(&other);
318 float limit = layout.get_catalogue().get_gauge();
319 if(!flex && !otrack->get_flex())
323 unsigned nsn = get_n_snap_nodes();
324 unsigned other_nsn = other.get_n_snap_nodes();
325 for(unsigned i=0; i<nsn; ++i)
327 Snap sn = get_snap_node(i);
328 for(unsigned j=0; j<other_nsn; ++j)
330 Snap osn = other.get_snap_node(j);
331 Vector span = osn.position-sn.position;
332 Angle da = wrap_balanced(osn.rotation-sn.rotation-Angle::half_turn());
334 if(dot(span, span)<limit && abs(da).radians()<0.01)
337 otrack->break_link(j);
339 otrack->links[j] = this;
341 layout.create_blocks(*this);
343 signal_link_changed.emit(i, otrack);
344 otrack->signal_link_changed.emit(j, this);
353 bool Track::break_link(unsigned i)
356 throw out_of_range("Track::break_link");
358 Track *other = links[i];
363 if(!other->break_link(*this))
365 /* If the call doesn't succeed, it means that the other track already
366 broke the link and is calling us right now. Recreate blocks in the inner
367 call so it occurs before any signals are emitted. */
368 layout.create_blocks(*this);
371 signal_link_changed.emit(i, 0);
376 void Track::add_attachment(TrackAttachment &a)
378 if(find(attachments.begin(), attachments.end(), &a)!=attachments.end())
380 attachments.push_back(&a);
383 void Track::remove_attachment(TrackAttachment &a)
385 AttachmentList::iterator i = find(attachments.begin(), attachments.end(), &a);
386 if(i==attachments.end())
388 attachments.erase(i);
391 Track::AttachmentList Track::get_attachments_ordered(unsigned epi) const
393 AttachmentList result = attachments;
394 result.sort(AttachmentCompare(epi));
398 void Track::save(list<DataFile::Statement> &st) const
400 st.push_back((DataFile::Statement("position"), position.x, position.y, position.z));
401 st.push_back((DataFile::Statement("rotation"), rotation.radians()));
402 st.push_back((DataFile::Statement("tilt"), tilt.radians()));
404 st.push_back((DataFile::Statement("turnout_address"), turnout_addr));
406 st.push_back((DataFile::Statement("sensor_address"), sensor_addr));
408 st.push_back((DataFile::Statement("flex"), true));
411 void Track::turnout_event(unsigned id, unsigned state)
416 path_changing = false;
417 signal_path_changed.emit(active_path);
422 Track::Loader::Loader(Track &t):
423 DataFile::ObjectLoader<Track>(t)
425 add("position", &Loader::position);
426 add("rotation", &Loader::rotation);
427 add("tilt", &Loader::tilt);
428 add("turnout_id", &Loader::turnout_address);
429 add("turnout_address", &Loader::turnout_address);
430 add("sensor_id", &Loader::sensor_address);
431 add("sensor_address", &Loader::sensor_address);
432 add("flex", &Track::flex);
435 add("slope", &Loader::slope);
438 void Track::Loader::position(float x, float y, float z)
440 obj.set_position(Vector(x, y, z));
443 void Track::Loader::rotation(float r)
445 obj.set_rotation(Angle::from_radians(r));
448 void Track::Loader::sensor_address(unsigned addr)
450 obj.set_sensor_address(addr);
453 void Track::Loader::slope(float s)
455 obj.set_tilt(Geometry::atan(s/obj.type.get_path_length(0)));
458 void Track::Loader::tilt(float t)
460 obj.set_tilt(Angle::from_radians(t));
463 void Track::Loader::turnout_address(unsigned addr)
465 obj.set_turnout_address(addr);