void Layout3D::get_bounds(Vector &minp, Vector &maxp) const
{
- minp = maxp = Vector();
+ Geometry::BoundingBox<float, 3> bbox;
for(TrackMap::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
- {
- Vector tmin;
- Vector tmax;
- i->second->get_bounds(Angle::zero(), tmin, tmax);
- minp.x = min(minp.x, tmin.x);
- minp.y = min(minp.y, tmin.y);
- maxp.x = max(maxp.x, tmax.x);
- maxp.y = max(maxp.y, tmax.y);
- }
+ bbox = bbox|i->second->get_track().get_type().get_shape()->get_axis_aligned_bounding_box();
+
+ minp = bbox.get_minimum_point();
+ maxp = bbox.get_maximum_point();
}
void Layout3D::add_track(Track3D &t)
delete *i;
}
-void Track3D::get_bounds(const Angle &angle, Vector &minp, Vector &maxp) const
-{
- type.get_bounds(angle-track.get_rotation(), minp, maxp);
-
- Vector pos = rotated_vector(track.get_position(), -angle);
- minp += pos;
- maxp += pos;
-
- float slope = track.get_slope();
- if(slope>0)
- maxp.z += slope;
- else
- minp.z += slope;
-}
-
Vector Track3D::get_node() const
{
- Vector minp;
- Vector maxp;
- type.get_bounds(Angle::zero(), minp, maxp);
+ Geometry::BoundingBox<float, 3> bbox = track.get_type().get_shape()->get_axis_aligned_bounding_box();
+ const Vector &minp = bbox.get_minimum_point();
+ const Vector &maxp = bbox.get_maximum_point();
return track.get_position()+rotated_vector((minp+maxp)/2.0f, track.get_rotation())+Vector(0, 0, 0.02);
}
Layout3D &get_layout() const { return layout; }
Track &get_track() const { return track; }
const TrackType3D &get_type() const { return type; }
- void get_bounds(const Angle &, Vector &, Vector &) const;
Path3D &get_path() { return *path; }
virtual Vector get_node() const;
using namespace std;
using namespace Msp;
-namespace {
-
-bool compare_z(const R2C2::Vector &p1, const R2C2::Vector &p2)
-{
- return p1.z<p2.z;
-}
-
-template<typename Iter>
-Iter graham_scan(Iter begin, Iter end)
-{
- // http://en.wikipedia.org/wiki/Graham_scan
-
- // Find point with lowest X coordinate
- R2C2::Vector lowest = *begin;
- for(Iter i=begin; i!=end; ++i)
- if(i->x<lowest.x || (i->x==lowest.x && i->y>lowest.y))
- lowest = *i;
-
- // Compute tangents and sort points
- for(Iter i=begin; i!=end; ++i)
- i->z = (i->x==lowest.x ? 1e5/(i->y-lowest.y-1) : (i->y-lowest.y)/(i->x-lowest.x));
- sort(begin, end, compare_z);
-
- for(Iter k=begin, i=k++, j=k++;; )
- {
- // Compute winding by cross product
- float turn = cross(*j-*i, *k-*j).z;
-
- if(turn<1e-5)
- {
- // Right turn - throw the middle point away
- // Special case for collinear vertices in the beginning
- if(i==begin)
- j = k++;
- else
- j = i--;
- }
- else
- {
- // Left turn - store the middle point and advance
- if(++i!=j)
- *i = *j;
- j = k++;
- }
-
- // Cycle back to beginning and terminate after checking the last point
- if(k==end)
- k = begin;
- else if(j==begin)
- return ++i;
- }
-}
-
-}
-
namespace R2C2 {
TrackType3D::TrackType3D(Catalogue3D &cat3d, const TrackType &tt):
string obj_name = tt.get_object();
if(!obj_name.empty())
- {
object = &catalogue.get<GL::Object>(obj_name);
- const GL::Mesh *m = object->get_mesh();
- const GL::VertexArray &vertices = m->get_vertices();
- int vertex_offs = vertices.get_format().offset(GL::VERTEX2);
- if(vertex_offs>=0)
- {
- for(unsigned i=0; i<vertices.size(); ++i)
- {
- const float *v = vertices[i]+vertex_offs;
- border.push_back(Vector(v[0], v[1], 0));
- }
- }
- }
else
{
mesh = new GL::Mesh((GL::NORMAL3, GL::TEXCOORD2, GL::VERTEX3));
}
path_meshes.push_back(m);
}
-
- min_z = max_z = border.front().z;
- for(vector<Vector>::iterator i=border.begin(); i!=border.end(); ++i)
- {
- min_z = min(min_z, i->z);
- max_z = max(max_z, i->z);
- }
- border.erase(graham_scan(border.begin(), border.end()), border.end());
}
TrackType3D::~TrackType3D()
delete *i;
}
-void TrackType3D::get_bounds(const Angle &angle, Vector &minp, Vector &maxp) const
-{
- Transform trans = Transform::rotation(-angle, Vector(0, 0, 1));
-
- minp = maxp = Vector();
- minp.z = min_z;
- maxp.z = max_z;
-
- for(vector<Vector>::const_iterator i=border.begin(); i!=border.end(); ++i)
- {
- Vector v = trans.transform(*i);
-
- minp.x = min(minp.x, v.x);
- minp.y = min(minp.y, v.y);
- maxp.x = max(maxp.x, v.x);
- maxp.y = max(maxp.y, v.y);
- }
-}
-
const GL::Mesh &TrackType3D::get_path_mesh(unsigned p) const
{
if(p>=path_meshes.size())
bld.normal(n.x, n.y, n.z);
bld.vertex(p.x, p.y, p.z);
-
- border.push_back(p);
}
}
Msp::GL::Mesh *mesh;
Msp::GL::Object *object;
std::vector<Msp::GL::Mesh *> path_meshes;
- std::vector<Vector> border;
- float min_z;
- float max_z;
public:
TrackType3D(Catalogue3D &, const TrackType &);
~TrackType3D();
- void get_bounds(const Angle &, Vector &, Vector &) const;
const Msp::GL::Object &get_object() const { return *object; }
const Msp::GL::Mesh &get_path_mesh(unsigned) const;
void View3D::view_all(bool tight)
{
- Vector minp;
- Vector maxp;
+ const set<Track *> &tracks = layout.get_layout().get_tracks();
+ Geometry::BoundingBox<float, 3> bbox;
+ for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ bbox = bbox|(*i)->get_bounding_box();
- layout.get_bounds(minp, maxp);
+ const Vector &minp = bbox.get_minimum_point();
+ const Vector &maxp = bbox.get_maximum_point();
float t = tan(camera.get_field_of_view()/2)*2;
float size = max((maxp.y-minp.y+0.1), (maxp.x-minp.x+0.1)/camera.get_aspect());
cur_route(0),
cur_zone(0),
mode(SELECT),
+ sel_wrap(selection),
manipulator(*this, mouse, selection),
measure(*this)
{
statusbar->add(*lbl_status);
lbl_status->set_geometry(GLtk::Geometry(20, 2, 300, 16));
- track_wrap = new TrackWrap(*layout_3d, selection);
- pipeline->add_renderable_for_pass(*track_wrap, "unlit");
+ pipeline->add_renderable_for_pass(sel_wrap, "unlit");
overlay = new Overlay3D(ui_res.get_default_font());
pipeline->add_renderable_for_pass(*overlay, "overlay");
pipeline->add_renderable_for_pass(measure, "unlit");
for(vector<Toolbar *>::iterator i=toolbars.begin(); i!=toolbars.end(); ++i)
delete *i;
delete camera_ctl;
- delete track_wrap;
delete overlay;
delete main_view;
delete base_object;
const GL::Vector3 &cpos = view.get_camera().get_position();
GL::Vector4 cray = view.get_camera().unproject(GL::Vector4(pointer.x, pointer.y, 0, 0));
- return view.get_layout().get_layout().pick_object(Vector(cpos.x, cpos.y, cpos.z), Vector(cray.x, cray.y, cray.z));
+ return view.get_layout().get_layout().pick_object(Ray(cpos, Vector(cray)));
}
void Designer::update_track_icon(Track3D &track)
#include "manipulator.h"
#include "measure.h"
#include "selection.h"
-#include "trackwrap.h"
+#include "selectionwrap.h"
class Toolbar;
Mode mode;
Selection selection;
+ SelectionWrap sel_wrap;
Manipulator manipulator;
Measure measure;
CameraController *camera_ctl;
- TrackWrap *track_wrap;
Msp::Time::TimeStamp last_tick;
void Manipulator::update_objects()
{
- Vector minp, maxp;
+ Geometry::BoundingBox<float, 3> bbox;
for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
- {
- // XXX Use generic bounding box when it is implemented
- if(Track *track = dynamic_cast<Track *>(i->object))
- {
- unsigned n_endpoints = track->get_type().get_endpoints().size();
- for(unsigned j=0; j<n_endpoints; ++j)
- {
- Vector p = track->get_snap_node(j).position;
- if(i==objects.begin() && j==0)
- minp = maxp = p;
- else
- {
- minp.x = min(minp.x, p.x);
- maxp.x = max(maxp.x, p.x);
- minp.y = min(minp.y, p.y);
- maxp.y = max(maxp.y, p.y);
- minp.z = min(minp.z, p.z);
- }
- }
- }
- else
- {
- const Vector &p = i->object->get_position();
- minp.x = min(minp.x, p.x);
- maxp.x = max(maxp.x, p.x);
- minp.y = min(minp.y, p.y);
- maxp.y = max(maxp.y, p.y);
- minp.z = min(minp.z, p.z);
- }
- }
+ bbox = bbox|i->object->get_bounding_box();
+
+ const Vector &minp = bbox.get_minimum_point();
+ const Vector &maxp = bbox.get_maximum_point();
center = (minp+maxp)/2.0f;
center.z = minp.z;
--- /dev/null
+#include <msp/gl/matrix.h>
+#include <msp/gl/meshbuilder.h>
+#include <msp/gl/renderer.h>
+#include "selection.h"
+#include "selectionwrap.h"
+
+using namespace std;
+using namespace Msp;
+using namespace R2C2;
+
+SelectionWrap::SelectionWrap(Selection &s):
+ selection(s)
+{
+ selection.signal_changed.connect(sigc::mem_fun(this, &SelectionWrap::selection_changed));
+}
+
+SelectionWrap::~SelectionWrap()
+{
+ for(map<const ObjectType *, GL::Mesh *>::iterator i=meshes.begin(); i!=meshes.end(); ++i)
+ delete i->second;
+}
+
+void SelectionWrap::render(GL::Renderer &renderer, const GL::Tag &) const
+{
+ for(list<Wrap>::const_iterator i=wraps.begin(); i!=wraps.end(); ++i)
+ {
+ GL::MatrixStack::Push push(renderer.matrix_stack());
+ const Vector &pos = i->object->get_position();
+ renderer.matrix_stack() *= GL::Matrix::translation(pos);
+ renderer.matrix_stack() *= GL::Matrix::rotation(i->object->get_rotation(), 0, 0, 1);
+ i->mesh->draw(renderer);
+ }
+}
+
+void SelectionWrap::selection_changed()
+{
+ wraps.clear();
+ const set<Object *> &objects = selection.get_objects();
+ for(set<Object *>::iterator i=objects.begin(); i!=objects.end(); ++i)
+ {
+ Wrap wrap;
+ wrap.object = *i;
+ wrap.mesh = &get_mesh((*i)->get_type());
+ wraps.push_back(wrap);
+ }
+}
+
+GL::Mesh &SelectionWrap::get_mesh(const ObjectType &type)
+{
+ map<const ObjectType *, GL::Mesh *>::iterator j = meshes.find(&type);
+ if(j!=meshes.end())
+ return *j->second;
+
+ GL::Mesh *mesh = new GL::Mesh((GL::COLOR4_UBYTE, GL::VERTEX2));
+ GL::MeshBuilder bld(*mesh);
+ bld.color(0.0f, 1.0f, 0.0f, 1.0f);
+
+ const Shape *shape = type.get_shape();
+ if(!shape)
+ throw logic_error("No shape");
+
+ Geometry::BoundingBox<float, 3> bbox = shape->get_axis_aligned_bounding_box();
+ const Vector &min_pt = bbox.get_minimum_point();
+ const Vector &max_pt = bbox.get_maximum_point();
+
+ bld.begin(GL::LINE_LOOP);
+ bld.vertex(min_pt.x-0.005, min_pt.y-0.005);
+ bld.vertex(max_pt.x+0.005, min_pt.y-0.005);
+ bld.vertex(max_pt.x+0.005, max_pt.y+0.005);
+ bld.vertex(min_pt.x-0.005, max_pt.y+0.005);
+ bld.end();
+
+ meshes[&type] = mesh;
+
+ return *mesh;
+}
--- /dev/null
+#ifndef SELECTIONWRAP_H_
+#define SELECTIONWRAP_H_
+
+#include <msp/gl/mesh.h>
+#include <msp/gl/renderable.h>
+#include "libr2c2/object.h"
+#include "3d/layout.h"
+
+class Designer;
+class Selection;
+
+class SelectionWrap: public Msp::GL::Renderable
+{
+private:
+ struct Wrap
+ {
+ R2C2::Object *object;
+ Msp::GL::Mesh *mesh;
+ };
+
+ Selection &selection;
+ std::map<const R2C2::ObjectType *, Msp::GL::Mesh *> meshes;
+ std::list<Wrap> wraps;
+
+public:
+ SelectionWrap(Selection &);
+ ~SelectionWrap();
+
+ virtual void render(Msp::GL::Renderer &, const Msp::GL::Tag &) const;
+
+private:
+ void selection_changed();
+ Msp::GL::Mesh &get_mesh(const R2C2::ObjectType &);
+};
+
+#endif
+++ /dev/null
-#include <msp/gl/matrix.h>
-#include <msp/gl/meshbuilder.h>
-#include <msp/gl/renderer.h>
-#include "3d/tracktype.h"
-#include "selection.h"
-#include "trackwrap.h"
-
-using namespace std;
-using namespace Msp;
-using namespace R2C2;
-
-TrackWrap::TrackWrap(Layout3D &l, Selection &s):
- layout(l),
- selection(s)
-{
- selection.signal_changed.connect(sigc::mem_fun(this, &TrackWrap::selection_changed));
-}
-
-TrackWrap::~TrackWrap()
-{
- for(map<const TrackType *, GL::Mesh *>::iterator i=meshes.begin(); i!=meshes.end(); ++i)
- delete i->second;
-}
-
-void TrackWrap::render(GL::Renderer &renderer, const GL::Tag &) const
-{
- for(list<Wrap>::const_iterator i=wraps.begin(); i!=wraps.end(); ++i)
- {
- GL::MatrixStack::Push push(renderer.matrix_stack());
- const Vector &pos = i->track->get_position();
- renderer.matrix_stack() *= GL::Matrix::translation(pos);
- renderer.matrix_stack() *= GL::Matrix::rotation(i->track->get_rotation(), 0, 0, 1);
- i->mesh->draw(renderer);
- }
-}
-
-void TrackWrap::selection_changed()
-{
- wraps.clear();
- const set<Track *> &tracks = selection.get_objects<Track>();
- for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
- {
- Wrap wrap;
- wrap.track = *i;
- wrap.mesh = &get_mesh((*i)->get_type());
- wraps.push_back(wrap);
- }
-}
-
-GL::Mesh &TrackWrap::get_mesh(const TrackType &type)
-{
- map<const TrackType *, GL::Mesh *>::iterator j = meshes.find(&type);
- if(j!=meshes.end())
- return *j->second;
-
- const TrackType3D &type3d = layout.get_catalogue().get_track(type);
-
- float min_area = -1;
- Angle angle;
- Vector center;
- float width = 0;
- float height = 0;
- for(Angle a; a<Angle::half_turn(); a+=Angle::from_radians(0.01))
- {
- Vector minp, maxp;
- type3d.get_bounds(a, minp, maxp);
- float area = (maxp.x-minp.x)*(maxp.y-minp.y);
- if(area<min_area || min_area<0)
- {
- center = rotated_vector((minp+maxp)/2.0f, a);
- center.z = minp.z;
- angle = a;
- width = maxp.x-minp.x+0.01;
- height = maxp.y-minp.y+0.01;
-
- min_area = area;
- }
- }
-
- GL::Mesh *mesh = new GL::Mesh((GL::COLOR4_UBYTE, GL::VERTEX2));
- GL::MeshBuilder bld(*mesh);
- bld.color(0.0f, 1.0f, 0.0f, 1.0f);
-
- float c = cos(angle);
- float s = sin(angle);
-
- bld.begin(GL::LINE_LOOP);
- bld.vertex(center.x-c*width/2+s*height/2, center.y-s*width/2-c*height/2);
- bld.vertex(center.x+c*width/2+s*height/2, center.y+s*width/2-c*height/2);
- bld.vertex(center.x+c*width/2-s*height/2, center.y+s*width/2+c*height/2);
- bld.vertex(center.x-c*width/2-s*height/2, center.y-s*width/2+c*height/2);
- bld.end();
-
- meshes[&type] = mesh;
-
- return *mesh;
-}
+++ /dev/null
-#ifndef TRACKWRAP_H_
-#define TRACKWRAP_H_
-
-#include <msp/gl/mesh.h>
-#include <msp/gl/renderable.h>
-#include "libr2c2/track.h"
-#include "3d/layout.h"
-
-class Designer;
-class Selection;
-
-class TrackWrap: public Msp::GL::Renderable
-{
-private:
- struct Wrap
- {
- R2C2::Track *track;
- Msp::GL::Mesh *mesh;
- };
-
- R2C2::Layout3D &layout;
- Selection &selection;
- std::map<const R2C2::TrackType *, Msp::GL::Mesh *> meshes;
- std::list<Wrap> wraps;
-
-public:
- TrackWrap(R2C2::Layout3D &, Selection &);
- ~TrackWrap();
-
- virtual void render(Msp::GL::Renderer &, const Msp::GL::Tag &) const;
-
-private:
- void selection_changed();
- Msp::GL::Mesh &get_mesh(const R2C2::TrackType &);
-};
-
-#endif
GL::Vector3 up;
for(Angle angle; angle<Angle::half_turn(); angle+=Angle::from_radians(0.01))
{
- float min_x = 0;
- float max_x = 0;
- float min_y = 0;
- float max_y = 0;
+ Transform trans = Transform::rotation(-angle, Vector(0, 0, 1));
+ BoundingBox bbox;
for(Layout3D::TrackMap::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
- {
- Vector minp, maxp;
- i->second->get_bounds(angle, minp, maxp);
- min_x = min(min_x, minp.x);
- max_x = max(max_x, maxp.x);
- min_y = min(min_y, minp.y);
- max_y = max(max_y, maxp.y);
- }
+ bbox = bbox|trans.transform(i->second->get_track().get_bounding_box());
+
+ const Vector &minp = bbox.get_minimum_point();
+ const Vector &maxp = bbox.get_maximum_point();
- float width = max_x-min_x;
- float height = max_y-min_y;
+ float width = maxp.x-minp.x;
+ float height = maxp.y-minp.y;
float aspect = width/height;
float score = min(aspect/view_aspect, view_aspect/aspect);
float size = max(width/view_aspect, height);
float c = cos(angle);
float s = sin(angle);
- float x = (min_x+max_x)/2-size*105/window.get_height();
- float y = (min_y+max_y)/2;
+ float x = (minp.x+maxp.x)/2-size*105/window.get_height();
+ float y = (minp.y+maxp.y)/2;
float z = max(size*1.05/view_height, 0.15);
pos = GL::Vector3(c*x-s*y, s*x+c*y, z);
const GL::Vector3 &start = camera.get_position();
GL::Vector4 ray = camera.unproject(GL::Vector4(p.x, p.y, 0, 0));
- return layout.pick_track(Vector(start.x, start.y, start.z), Vector(ray.x, ray.y, ray.z));
+ return layout.pick_track(Ray(start, Vector(ray)));
}
void Engineer::process_new_train(Train &train)
#include <vector>
#include <msp/geometry/affinetransformation.h>
#include <msp/geometry/angle.h>
+#include <msp/geometry/ray.h>
+#include <msp/geometry/shape.h>
#include <msp/linal/vector.h>
namespace R2C2 {
inline Vector vector_at_angle(const Angle &a)
{ return rotated_vector(Vector(1, 0, 0), a); }
+typedef Msp::Geometry::Shape<float, 3> Shape;
+typedef Msp::Geometry::Ray<float, 3> Ray;
+typedef Msp::Geometry::BoundingBox<float, 3> BoundingBox;
+
struct TrackPoint
{
Vector pos;
}
}
-Track *Layout::pick_track(const Vector &start, const Vector &ray)
+Track *Layout::pick_track(const Ray &ray)
{
- return pick(tracks, start, ray);
+ return pick(tracks, ray);
}
void Layout::remove_track(Track &t)
signal_signal_added.emit(s);
}
-Signal *Layout::pick_signal(const Vector &start, const Vector &ray)
+Signal *Layout::pick_signal(const Ray &ray)
{
- return pick(signals, start, ray);
+ return pick(signals, ray);
}
void Layout::remove_signal(Signal &s)
signal_signal_removed.emit(s);
}
-Object *Layout::pick_object(const Vector &start, const Vector &ray)
+Object *Layout::pick_object(const Ray &ray)
{
- if(Object *obj = pick_track(start, ray))
+ if(Object *obj = pick_track(ray))
return obj;
- else if((obj = pick_signal(start, ray)))
+ else if((obj = pick_signal(ray)))
return obj;
return 0;
}
template<typename T>
-T *Layout::pick(const set<T *> &objs, const Vector &start, const Vector &ray)
+T *Layout::pick(const set<T *> &objs, const Ray &ray)
{
for(typename set<T *>::const_iterator i=objs.begin(); i!=objs.end(); ++i)
- if((*i)->collide_ray(start, ray))
+ if((*i)->collide_ray(ray))
return *i;
return 0;
void add_track(Track &);
const std::set<Track *> &get_tracks() const { return tracks; }
- Track *pick_track(const Vector &, const Vector &);
+ Track *pick_track(const Ray &);
void remove_track(Track &);
unsigned allocate_turnout_id();
void add_signal(Signal &);
const std::set<Signal *> &get_signals() const { return signals; }
- Signal *pick_signal(const Vector &, const Vector &);
+ Signal *pick_signal(const Ray &);
void remove_signal(Signal &);
- Object *pick_object(const Vector &, const Vector &);
+ Object *pick_object(const Ray &);
private:
template<typename T>
- static T *pick(const std::set<T *> &, const Vector &, const Vector &);
+ static T *pick(const std::set<T *> &, const Ray &);
public:
void add_block(Block &);
break_link(i);
}
+bool Object::collide_ray(const Ray &ray) const
+{
+ const Shape *shape = get_type().get_shape();
+ if(!shape)
+ return false;
+
+ Transform reverse_trans = Transform::rotation(rotation, Vector(0, 0, -1))*
+ Transform::translation(-position);
+ return shape->check_intersection(reverse_trans.transform(ray));
+}
+
+BoundingBox Object::get_bounding_box() const
+{
+ const Shape *shape = get_type().get_shape();
+ if(!shape)
+ return BoundingBox();
+
+ Transform trans = Transform::translation(position)*
+ Transform::rotation(rotation, Vector(0, 0, 1));
+ return trans.transform(shape->get_axis_aligned_bounding_box());
+}
+
} // namespace R2C2
virtual bool break_link(unsigned) { return false; }
virtual void break_links();
- virtual bool collide_ray(const Vector &, const Vector &) const = 0;
+ virtual bool collide_ray(const Ray &) const;
+ virtual BoundingBox get_bounding_box() const;
};
} // namespace R2C2
namespace R2C2 {
+ObjectType::ObjectType(const ArticleNumber &an):
+ art_nr(an),
+ shape(0)
+{ }
+
+ObjectType::~ObjectType()
+{
+ delete shape;
+}
+
+
ObjectType::Loader::Loader(ObjectType &ot):
DataFile::ObjectLoader<ObjectType>(ot)
{
#include <string>
#include <msp/datafile/objectloader.h>
#include "articlenumber.h"
+#include "geometry.h"
namespace R2C2 {
ArticleNumber art_nr;
std::string name;
std::string description;
+ Shape *shape;
- ObjectType(const ArticleNumber &a): art_nr(a) { }
+ ObjectType(const ArticleNumber &);
public:
- virtual ~ObjectType() { }
+ virtual ~ObjectType();
const ArticleNumber &get_article_number() const { return art_nr; }
const std::string &get_name() const { return name; }
const std::string &get_description() const { return description; }
+ const Shape *get_shape() const { return shape; }
};
} // namespace R2C2
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
- Vector center = position+rotated_vector(Vector(0, -0.035, 0), rotation);
- Vector span = center-start;
- float x = (span.x*ray.x+span.y*ray.y)/(ray.x*ray.x+ray.y*ray.y);
- Vector nearest = start+ray*x-center;
- if(nearest.z<0 || nearest.z>0.12)
- return false;
- nearest.z = 0;
- return dot(nearest, nearest)<0.0001;
-}
-
void Signal::tick(const Time::TimeDelta &)
{
if(check_allocated_blocks)
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 &);
private:
+#include <msp/geometry/circle.h>
+#include <msp/geometry/extrudedshape.h>
+#include <msp/geometry/transformedshape.h>
#include "signaltype.h"
using namespace std;
SignalType::SignalType(const ArticleNumber &an):
ObjectType(an)
{
+ shape = new Geometry::TransformedShape<float, 3>(
+ Geometry::ExtrudedShape<float, 3>(Geometry::Circle<float>(0.01), 0.12),
+ Transform::translation(Vector(0, -0.035, 0.06)));
}
return true;
}
-bool Track::collide_ray(const Vector &start, const Vector &ray) const
-{
- Transform trans = Transform::rotation(-rotation, Vector(0, 0, 1));
- Vector local_start = trans.transform(start-position);
- Vector local_ray = trans.transform(ray);
-
- float width = layout.get_catalogue().get_ballast_profile().get_width();
-
- return type.collide_ray(local_start, local_ray, width);
-}
-
void Track::save(list<DataFile::Statement> &st) const
{
st.push_back((DataFile::Statement("position"), position.x, position.y, position.z));
using Object::break_link;
virtual bool break_link(unsigned);
- virtual bool collide_ray(const Vector &, const Vector &) const;
-
void save(std::list<Msp::DataFile::Statement> &) const;
private:
void turnout_event(unsigned, unsigned);
#include <cmath>
+#include <msp/geometry/box.h>
+#include <msp/geometry/circle.h>
+#include <msp/geometry/extrudedshape.h>
+#include <msp/geometry/halfspace.h>
+#include <msp/geometry/intersection.h>
+#include <msp/geometry/negation.h>
+#include <msp/geometry/transformedshape.h>
#include "trackpart.h"
using namespace std;
length(0),
radius(0),
path(0),
- dead_end(false)
+ dead_end(false),
+ shape(0)
{
links[0] = 0;
links[1] = 0;
}
+TrackPart::~TrackPart()
+{
+ delete shape;
+}
+
float TrackPart::get_length() const
{
if(radius)
return links[i];
}
-bool TrackPart::collide_ray(const Vector &start, const Vector &ray, float width) const
+void TrackPart::create_shape()
{
- Transform trans = Transform::rotation(dir, Vector(0, 0, -1));
- Vector local_start = trans.transform(start);
- Vector local_ray = trans.transform_linear(ray);
-
- float d = -local_start.z/local_ray.z;
- if(d<0)
- return false;
-
- Vector base = local_start+d*local_ray;
-
+ Transform trans = Transform::translation(pos)*
+ Transform::rotation(dir, LinAl::Vector<float, 3>(0, 0, 1));
if(radius)
{
- base.y -= radius;
- if(radius<0)
- base.y = -base.y;
- float r = base.norm()-abs(radius);
- float a = atan2(base.x, -base.y);
- return (a>=0 && a<=length && r>=-width/2 && r<=width/2);
+ Geometry::ExtrudedShape<float, 3> ring(
+ Geometry::Intersection<float, 2>(
+ Geometry::Circle<float>(abs(radius)+0.02),
+ Geometry::Negation<float, 2>(Geometry::Circle<float>(abs(radius)-0.02))),
+ 0.01);
+ Geometry::HalfSpace<float, 3> start_wall(Vector(-1, 0, 0));
+ Geometry::HalfSpace<float, 3> end_wall(rotated_vector(Vector(1, 0, 0),
+ Angle::from_radians(length*(radius<0 ? -1 : 1))));
+ /* This box only exists so the bounding boxes of curves make sense. It
+ can be removed when libmspmath produces better bounding boxes. */
+ float w = sin(length)*(abs(radius)+0.02);
+ float h = (1-cos(length))*abs(radius-0.02)+0.04;
+ Geometry::TransformedShape<float, 3> bounds(
+ Geometry::Box<float>(w, h, 0.01),
+ Transform::translation(Vector(w/2, (h/2-0.02)*(radius<0 ? -1 : 1)-radius, 0.005)));
+ vector<const Shape *> shapes;
+ shapes.push_back(&ring);
+ shapes.push_back(&start_wall);
+ shapes.push_back(&end_wall);
+ shapes.push_back(&bounds);
+ shape = new Geometry::TransformedShape<float, 3>(
+ Geometry::Intersection<float, 3>::from_iterator_range(shapes.begin(), shapes.end()),
+ trans*Transform::translation(Vector(0, radius, 0.005)));
}
else
- return (base.x>=0 && base.x<=length && base.y>=-width/2 && base.y<=width/2);
+ {
+ // TODO Get the track profile dimensions from somewhere
+ shape = new Geometry::TransformedShape<float, 3>(
+ Geometry::Box<float>(length, 0.04, 0.01),
+ trans*Transform::translation(Vector(length/2, 0, 0.005)));
+ }
}
unsigned path;
bool dead_end;
TrackPart *links[2];
+ Shape *shape;
public:
TrackPart();
+ ~TrackPart();
float get_length() const;
bool is_curved() const { return radius; }
bool is_dead_end() const { return dead_end; }
void check_link(TrackPart &);
TrackPart *get_link(unsigned) const;
- bool collide_ray(const Vector &, const Vector &, float) const;
+ void create_shape();
+ const Shape &get_shape() const { return *shape; }
};
} // namespace R2C2
#include <cmath>
+#include <msp/geometry/union.h>
#include "tracktype.h"
using namespace std;
return result;
}
-bool TrackType::collide_ray(const Vector &start, const Vector &dir, float width) const
-{
- for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
- if(i->collide_ray(start, dir, width))
- return true;
-
- return false;
-}
-
void TrackType::collect_endpoints()
{
endpoints.clear();
void TrackType::Loader::finish()
{
obj.collect_endpoints();
+ vector<const Shape *> shapes;
+ for(vector<TrackPart>::iterator i=obj.parts.begin(); i!=obj.parts.end(); ++i)
+ {
+ i->create_shape();
+ shapes.push_back(&i->get_shape());
+ }
+ obj.shape = Geometry::Union<float, 3>::from_iterator_range(shapes.begin(), shapes.end()).clone();
}
void TrackType::Loader::part()
const Endpoint &get_endpoint(unsigned) const;
TrackPoint get_point(unsigned, unsigned, float) const;
TrackPoint get_nearest_point(const Vector &) const;
- bool collide_ray(const Vector &, const Vector &, float) const;
private:
void collect_endpoints();