+void Track::set_sensor_id(unsigned i)
+{
+ if(type.is_turnout())
+ throw InvalidState("Can't set sensor on a turnout");
+
+ sensor_id = i;
+ layout.create_blocks(*this);
+ if(layout.has_driver() && sensor_id)
+ layout.get_driver().add_sensor(sensor_id);
+}
+
+void Track::set_active_path(unsigned p)
+{
+ if(!turnout_id)
+ throw InvalidState("Not a turnout");
+ if(!(type.get_paths()&(1<<p)))
+ throw InvalidParameterValue("Invalid path");
+
+ layout.get_driver().set_turnout(turnout_id, p&1);
+ if(type.get_n_paths()>2)
+ layout.get_driver().set_turnout(turnout_id+1, p&2);
+}
+
+int Track::get_endpoint_by_link(const Track &other) const
+{
+ for(unsigned i=0; i<links.size(); ++i)
+ if(links[i]==&other)
+ return i;
+
+ return -1;
+}
+
+Point Track::get_endpoint_position(unsigned epi) const
+{
+ const vector<Endpoint> &eps = type.get_endpoints();
+ if(epi>=eps.size())
+ throw InvalidParameterValue("Endpoint index out of range");
+
+ const Endpoint &ep = eps[epi];
+
+ float c = cos(rot);
+ float s = sin(rot);
+
+ Point p(pos.x+c*ep.pos.x-s*ep.pos.y, pos.y+s*ep.pos.x+c*ep.pos.y, pos.z);
+ if(eps.size()==2 && epi==1)
+ p.z += slope;
+ return p;
+}
+
+float Track::get_endpoint_direction(unsigned epi) const