{
if(tag=="unlit")
{
- Vector p = track.get_track().get_endpoint_position(index);
- float a = track.get_track().get_endpoint_direction(index)+M_PI;
+ Snap sn = track.get_track().get_snap_node(index);
GL::MatrixStack::Push push_mtx(renderer.matrix_stack());
GL::Matrix matrix;
- matrix.translate(p.x, p.y, p.z);
- matrix.rotate(a, 0, 0, 1);
+ matrix.translate(sn.position.x, sn.position.y, sn.position.z);
+ matrix.rotate(sn.rotation+M_PI, 0, 0, 1);
renderer.matrix_stack() *= matrix;
if(track.get_track().get_link(index))
set<Track *>::iterator nb = neighbors.begin();
int epi = (*nb)->get_endpoint_by_link(*order.front().track);
- float start_z = (*nb)->get_endpoint_position(epi).z;
+ float start_z = (*nb)->get_snap_node(epi).position.z;
++nb;
epi = (*nb)->get_endpoint_by_link(*order.back().track);
- float end_z = (*nb)->get_endpoint_position(epi).z;
+ float end_z = (*nb)->get_snap_node(epi).position.z;
if(smooth)
{
float limit = designer.get_layout().get_catalogue().get_gauge()/10;
- Vector pos1;
- float dir1;
+ Snap sn1;
bool ok = false;
float gap = 0;
for(unsigned i=0; i<track1->get_type().get_endpoints().size(); ++i)
if(track1->get_link(i))
continue;
- pos1 = track1->get_endpoint_position(i);
- dir1 = track1->get_endpoint_direction(i);
+ sn1 = track1->get_snap_node(i);
for(unsigned j=0; j<track2->get_type().get_endpoints().size(); ++j)
{
if(track2->get_link(j))
continue;
- Vector pos2 = track2->get_endpoint_position(j);
- float dir2 = track2->get_endpoint_direction(j);
+ Snap sn2 = track2->get_snap_node(j);
- float dz = pos2.z-pos1.z;
+ float dz = sn2.position.z-sn1.position.z;
if(abs(dz)>0.02)
continue;
- float adiff = dir1+M_PI-dir2;
+ float adiff = sn1.rotation+M_PI-sn2.rotation;
while(adiff<-M_PI)
adiff += M_PI*2;
while(adiff>M_PI)
if(abs(adiff)>0.01)
continue;
- float c = cos(dir1);
- float s = sin(dir1);
- float dx = pos2.x-pos1.x;
- float dy = pos2.y-pos1.y;
+ float c = cos(sn1.rotation);
+ float s = sin(sn1.rotation);
+ float dx = sn2.position.x-sn1.position.x;
+ float dy = sn2.position.y-sn1.position.y;
if(abs(dx*s-dy*c)>limit)
continue;
return;
}
- vector<Track *> trks = create_straight(pos1, dir1, gap, limit);
+ vector<Track *> trks = create_straight(sn1.position, sn1.rotation, gap, limit);
if(trks.empty())
{
if(track->get_link(j))
continue;
- Vector ep_pos = track->get_endpoint_position(j);
- float ep_dir = track->get_endpoint_direction(j);
- float c = cos(ep_dir);
- float s = sin(ep_dir);
- float dx = gpointer.x-ep_pos.x;
- float dy = gpointer.y-ep_pos.y;
+ Snap sn = track->get_snap_node(j);
+ float c = cos(sn.rotation);
+ float s = sin(sn.rotation);
+ float dx = gpointer.x-sn.position.x;
+ float dy = gpointer.y-sn.position.y;
float len = dx*c+dy*s;
if(len<length)
continue;
- pos = ep_pos;
- dir = ep_dir;
+ pos = sn.position;
+ dir = sn.rotation;
length = len;
}
}
unsigned n_endpoints = track->get_type().get_endpoints().size();
for(unsigned j=0; j<n_endpoints; ++j)
{
- Vector p = track->get_endpoint_position(j);
+ Vector p = track->get_snap_node(j).position;
if(i==objects.begin() && j==0)
minp = maxp = p;
else
if(btn==1)
{
- spoint = Vector(gx, gy, 0);
- sdir = 0;
- snap_to_tracks(spoint, sdir);
+ ssnap.position = Vector(gx, gy, 0);
+ ssnap.rotation = 0;
+ snap_to_tracks(ssnap);
state = ACTIVE;
}
if(!state)
return;
- pointer = Vector(gx, gy, 0);
- float dir = sdir;
- snap_to_tracks(pointer, dir);
+ Snap sn = ssnap;
+ sn.position = Vector(gx, gy, 0);
+ snap_to_tracks(sn);
+ pointer = sn.position;
if(state!=STARTING)
{
- Vector delta(pointer.x-spoint.x, pointer.y-spoint.y, 0);
- float c = cos(sdir);
- float s = sin(sdir);
+ Vector delta(pointer.x-ssnap.position.x, pointer.y-ssnap.position.y, 0);
+ float c = cos(ssnap.rotation);
+ float s = sin(ssnap.rotation);
par_dist = delta.x*c+delta.y*s;
perp_dist = delta.x*s-delta.y*c;
- adiff = dir-sdir+M_PI;
+ adiff = sn.rotation-ssnap.rotation+M_PI;
while(adiff<-M_PI)
adiff += M_PI*2;
while(adiff>M_PI)
return;
GL::Renderer::Push push(renderer);
- const Vector &pos = (state==ACTIVE ? spoint : pointer);
+ const Vector &pos = (state==ACTIVE ? ssnap.position : pointer);
renderer.matrix_stack() *= GL::Matrix::translation(pos.x, pos.y, pos.z);
mesh.draw(renderer);
if(state==ACTIVE)
{
- float c = cos(sdir);
- float s = sin(sdir);
+ float c = cos(ssnap.rotation);
+ float s = sin(ssnap.rotation);
bld.begin(GL::QUAD_STRIP);
bld.vertex(0, 0, 0);
bld.vertex(0, 0, 0.01);
bld.vertex(c*par_dist, s*par_dist, 0);
bld.vertex(c*par_dist, s*par_dist, 0.01);
- bld.vertex(pointer.x-spoint.x, pointer.y-spoint.y, 0);
- bld.vertex(pointer.x-spoint.x, pointer.y-spoint.y, 0.01);
+ bld.vertex(pointer.x-ssnap.position.x, pointer.y-ssnap.position.y, 0);
+ bld.vertex(pointer.x-ssnap.position.x, pointer.y-ssnap.position.y, 0.01);
bld.vertex(0, 0, 0);
bld.vertex(0, 0, 0.01);
bld.end();
}
}
-void Measure::snap_to_tracks(Vector &pt, float &dir)
+void Measure::snap_to_tracks(Snap &sn)
{
const set<Track *> <racks = designer.get_layout().get_tracks();
for(set<Track *>::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
- if((*i)->snap(pt, dir))
+ if((*i)->snap(sn, 0.01, SNAP_NODE))
return;
}
private:
Designer &designer;
R2C2::Vector pointer;
- R2C2::Vector spoint;
- float sdir;
+ R2C2::Snap ssnap;
float par_dist;
float perp_dist;
float adiff;
virtual void render(Msp::GL::Renderer &, const Msp::GL::Tag &) const;
private:
void update_mesh();
- void snap_to_tracks(R2C2::Vector &, float &);
+ void snap_to_tracks(R2C2::Snap &);
};
#endif
unsigned n_endpoints = (*i)->get_type().get_endpoints().size();
for(unsigned j=0; j<n_endpoints; ++j)
{
- Vector pos = (*i)->get_endpoint_position(j);
+ Vector pos = (*i)->get_snap_node(j).position;
if(i==tracks.begin() && j==0)
minp = maxp = pos;
else
camera.apply();
GL::MatrixStack::Push push_mat(GL::MatrixStack::modelview());
- float rot = picking_track->get_endpoint_direction(picking_entry);
- Vector pos = picking_track->get_endpoint_position(picking_entry);
+ Snap sn = picking_track->get_snap_node(picking_entry);
- GL::MatrixStack::modelview() *= GL::Matrix::translation(pos.x, pos.y, pos.z+0.03);
- GL::MatrixStack::modelview() *= GL::Matrix::rotation(rot+M_PI, 0, 0, 1);
+ GL::MatrixStack::modelview() *= GL::Matrix::translation(sn.position.x, sn.position.y, sn.position.z+0.03);
+ GL::MatrixStack::modelview() *= GL::Matrix::rotation(sn.rotation+M_PI, 0, 0, 1);
arrow_mesh.draw();
}
--- /dev/null
+#include <stdexcept>
+#include "object.h"
+
+using namespace std;
+
+namespace R2C2 {
+
+Snap Object::get_snap_node(unsigned) const
+{
+ throw out_of_range("Object::get_snap_node");
+}
+
+bool Object::snap(Snap &sn, float limit, SnapType what) const
+{
+ if(what&SNAP_NODE)
+ {
+ unsigned nsn = get_n_snap_nodes();
+ for(unsigned i=0; i<nsn; ++i)
+ {
+ Snap node = get_snap_node(i);
+ Vector d(sn.position.x-node.position.x, sn.position.y-node.position.y, sn.position.z-node.position.z);
+ if(d.x*d.x+d.y*d.y<limit*limit)
+ {
+ sn = node;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool Object::snap_to(const Object &other, float limit, SnapType what)
+{
+ if(what==SNAP_DEFAULT)
+ what = get_default_snap_type_to(other);
+
+ if(!what)
+ return false;
+
+ unsigned nsn = get_n_snap_nodes();
+ for(unsigned i=0; i<nsn; ++i)
+ {
+ Snap sn = get_snap_node(i);
+ Snap ssn = sn;
+ if(other.snap(ssn, limit, what))
+ {
+ set_rotation(rotation+ssn.rotation-sn.rotation-M_PI);
+ sn = get_snap_node(i);
+ position.x += ssn.position.x-sn.position.x;
+ position.y += ssn.position.y-sn.position.y;
+ position.z += ssn.position.z-sn.position.z;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace R2C2
#include "geometry.h"
#include "objecttype.h"
+#include "snap.h"
namespace R2C2 {
const Vector &get_position() const { return position; }
float get_rotation() const { return rotation; }
virtual Object *get_parent() const { return 0; }
+
+ virtual unsigned get_n_snap_nodes() const { return 0; }
+ virtual Snap get_snap_node(unsigned) const;
+ virtual bool snap(Snap &, float, SnapType = SNAP_DEFAULT) const;
+ virtual bool snap_to(const Object &, float, SnapType = SNAP_DEFAULT);
+protected:
+ virtual SnapType get_default_snap_type_to(const Object &) const { return NO_SNAP; }
+
+public:
virtual bool collide_ray(const Vector &, const Vector &) const = 0;
};
for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
if(!(*i)->get_type().is_turnout())
{
- TrackPoint n = (*i)->get_nearest_point(p);
- float d = distance(p, n.pos);
+ Snap sn;
+ sn.position = p;
+ sn.rotation = rotation;
+ (*i)->snap(sn, 1000, SNAP_SEGMENT);
+ float d = distance(p, sn.position);
if(d<dist || dist<0)
{
- position = n.pos;
- rotation = n.dir;
+ position = sn.position;
+ rotation = sn.rotation;
track = *i;
dist = d;
}
unsigned n_endpoints = track->get_type().get_endpoints().size();
for(unsigned j=0; j<n_endpoints; ++j)
{
- float a = track->get_endpoint_direction(j)-rotation;
+ float a = track->get_snap_node(j).rotation-rotation;
while(a<-M_PI/2)
a += M_PI*2;
while(a>M_PI*3/2)
normalize_location();
}
+unsigned Signal::get_n_snap_nodes() const
+{
+ return 1;
+}
+
+Snap Signal::get_snap_node(unsigned i) const
+{
+ if(i>=1)
+ throw out_of_range("Signal::get_snap_node");
+
+ Snap sn;
+ sn.position = position;
+ sn.rotation = rotation;
+ return sn;
+}
+
+SnapType Signal::get_default_snap_type_to(const Object &other) const
+{
+ if(dynamic_cast<const Track *>(&other))
+ return SNAP_SEGMENT;
+
+ return NO_SNAP;
+}
+
bool Signal::collide_ray(const Vector &start, const Vector &ray) const
{
// XXX Totally hardcoded stuff, should be replaced with a geometry system
public:
virtual Track *get_parent() const { return track; }
+ virtual unsigned get_n_snap_nodes() const;
+ virtual Snap get_snap_node(unsigned) const;
+private:
+ virtual SnapType get_default_snap_type_to(const Object &) const;
+
+public:
virtual bool collide_ray(const Vector &, const Vector &) const;
void tick(const Msp::Time::TimeDelta &);
--- /dev/null
+#ifndef LIBR2C2_SNAP_H_
+#define LIBR2C2_SNAP_H_
+
+namespace R2C2 {
+
+enum SnapType
+{
+ SNAP_DEFAULT = 0,
+ NO_SNAP = 0,
+ SNAP_NODE = 1,
+ SNAP_SEGMENT = 2
+};
+
+struct Snap
+{
+ Vector position;
+ float rotation;
+};
+
+inline SnapType operator|(SnapType s1, SnapType s2)
+{ return static_cast<SnapType>(static_cast<int>(s1)|static_cast<int>(s2)); }
+
+} // namespace R2C2
+
+#endif
if(links[0] && links[1])
{
- Vector epp0 = links[0]->get_endpoint_position(links[0]->get_endpoint_by_link(*this));
- Vector epp1 = links[1]->get_endpoint_position(links[1]->get_endpoint_by_link(*this));
+ Vector epp0 = links[0]->get_snap_node(links[0]->get_endpoint_by_link(*this)).position;
+ Vector epp1 = links[1]->get_snap_node(links[1]->get_endpoint_by_link(*this)).position;
position.z = epp0.z;
slope = epp1.z-position.z;
}
slope = 0;
if(links[0])
{
- Vector epp = links[0]->get_endpoint_position(links[0]->get_endpoint_by_link(*this));
+ Vector epp = links[0]->get_snap_node(links[0]->get_endpoint_by_link(*this)).position;
position.z = epp.z;
}
else if(links[1])
{
- Vector epp = links[1]->get_endpoint_position(links[1]->get_endpoint_by_link(*this));
+ Vector epp = links[1]->get_snap_node(links[1]->get_endpoint_by_link(*this)).position;
position.z = epp.z;
}
}
return -1;
}
-Vector Track::get_endpoint_position(unsigned epi) const
-{
- const vector<TrackType::Endpoint> &eps = type.get_endpoints();
- if(epi>=eps.size())
- throw out_of_range("Track::get_endpoint_position");
-
- const TrackType::Endpoint &ep = eps[epi];
-
- float c = cos(rotation);
- float s = sin(rotation);
-
- Vector p(position.x+c*ep.pos.x-s*ep.pos.y, position.y+s*ep.pos.x+c*ep.pos.y, position.z);
- if(eps.size()==2 && epi==1)
- p.z += slope;
- return p;
-}
-
-float Track::get_endpoint_direction(unsigned epi) const
-{
- const vector<TrackType::Endpoint> &eps = type.get_endpoints();
- if(epi>=eps.size())
- throw out_of_range("Track::get_endpoint_direction");
-
- const TrackType::Endpoint &ep = eps[epi];
-
- return rotation+ep.dir;
-}
-
bool Track::snap_to(Track &other, bool link, float limit)
{
if(!limit || link)
for(unsigned i=0; i<eps.size(); ++i)
{
- Vector epp = get_endpoint_position(i);
+ Vector epp = get_snap_node(i).position;
for(unsigned j=0; j<other_eps.size(); ++j)
{
if(other.get_link(j))
continue;
- Vector epp2 = other.get_endpoint_position(j);
+ Vector epp2 = other.get_snap_node(j).position;
float dx = epp2.x-epp.x;
float dy = epp2.y-epp.y;
float dz = epp2.z-epp.z;
return false;
}
-bool Track::snap(Vector &pt, float &d) const
-{
- const vector<TrackType::Endpoint> &eps = type.get_endpoints();
-
- for(unsigned i=0; i<eps.size(); ++i)
- {
- Vector epp = get_endpoint_position(i);
- float dx = pt.x-epp.x;
- float dy = pt.y-epp.y;
- if(dx*dx+dy*dy<1e-4)
- {
- pt = epp;
- d = rotation+eps[i].dir;
- return true;
- }
- }
-
- return false;
-}
-
void Track::break_link(Track &trk)
{
for(vector<Track *>::iterator i=links.begin(); i!=links.end(); ++i)
return get_point(epi, active_path, d);
}
-TrackPoint Track::get_nearest_point(const Vector &p) const
+unsigned Track::get_n_snap_nodes() const
{
- Vector local(p.x-position.x, p.y-position.y, p.z-position.z);
+ return type.get_endpoints().size();
+}
+
+Snap Track::get_snap_node(unsigned i) const
+{
+ const vector<TrackType::Endpoint> &eps = type.get_endpoints();
+ if(i>=eps.size())
+ throw out_of_range("Track::get_snap_node");
+
+ Snap result;
+ const TrackType::Endpoint &ep = eps[i];
+
float c = cos(rotation);
float s = sin(rotation);
- local = Vector(c*local.x+s*local.y, c*local.y-s*local.x, local.z);
- TrackPoint tp = type.get_nearest_point(local);
- tp.pos = Vector(position.x+tp.pos.x*c-tp.pos.y*s, position.y+tp.pos.y*c+tp.pos.x*s, position.z+tp.pos.z);
- tp.dir += rotation;
- return tp;
+ result.position = Vector(position.x+c*ep.pos.x-s*ep.pos.y, position.y+s*ep.pos.x+c*ep.pos.y, position.z);
+ if(eps.size()==2 && i==1)
+ result.position.z += slope;
+
+ result.rotation = rotation+ep.dir;
+
+ return result;
+}
+
+bool Track::snap(Snap &sn, float limit, SnapType what) const
+{
+ if(Object::snap(sn, limit, what))
+ return true;
+
+ if(what&SNAP_SEGMENT)
+ {
+ Vector local(sn.position.x-position.x, sn.position.y-position.y, sn.position.z-position.z);
+ float c = cos(rotation);
+ float s = sin(rotation);
+ local = Vector(c*local.x+s*local.y, c*local.y-s*local.x, local.z);
+
+ TrackPoint tp = type.get_nearest_point(local);
+ Vector d(local.x-tp.pos.x, local.y-tp.pos.y, local.z-tp.pos.z);
+ if(d.x*d.x+d.y*d.y+d.z*d.z<=limit*limit)
+ {
+ sn.position = Vector(position.x+tp.pos.x*c-tp.pos.y*s, position.y+tp.pos.y*c+tp.pos.x*s, position.z+tp.pos.z);
+ sn.rotation = tp.dir+rotation;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+SnapType Track::get_default_snap_type_to(const Object &other) const
+{
+ if(dynamic_cast<const Track *>(&other))
+ return SNAP_NODE;
+
+ return NO_SNAP;
}
bool Track::collide_ray(const Vector &start, const Vector &ray) const
bool is_path_changing() const { return path_changing; }
int get_endpoint_by_link(Track &) const;
- Vector get_endpoint_position(unsigned) const;
- float get_endpoint_direction(unsigned) const;
bool snap_to(Track &, bool, float = 0);
- bool snap(Vector &, float &) const;
void break_link(Track &);
void break_links();
const std::vector<Track *> &get_links() const { return links; }
Track *get_link(unsigned) const;
TrackPoint get_point(unsigned, unsigned, float) const;
TrackPoint get_point(unsigned, float) const;
- TrackPoint get_nearest_point(const Vector &) const;
+ virtual unsigned get_n_snap_nodes() const;
+ virtual Snap get_snap_node(unsigned) const;
+ virtual bool snap(Snap &, float, SnapType = SNAP_DEFAULT) const;
+private:
+ virtual SnapType get_default_snap_type_to(const Object &) const;
+
+public:
virtual bool collide_ray(const Vector &, const Vector &) const;
void save(std::list<Msp::DataFile::Statement> &) const;