-/* $Id$
-
-This file is part of R²C²
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
#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;
namespace R2C2 {
TrackPart::TrackPart():
- dir(0),
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)
{
TrackPoint result;
+ Transform dir_trans = Transform::rotation(dir, Vector(0, 0, 1));
if(radius)
{
- float a = d/radius;
- float c = cos(a);
- float s = sin(a);
- float rx = radius*sin(dir);
- float ry = -radius*cos(dir);
- result.pos = Point(pos.x+c*rx-s*ry-rx, pos.y+c*ry+s*rx-ry);
+ Angle a = Angle::from_radians(d/radius);
+ Vector r = dir_trans.transform(Vector(0, -radius, 0));
+ result.pos = pos-r+Transform::rotation(a, Vector(0, 0, 1)).transform(r);
result.dir = dir+a;
}
else
{
- result.pos = Point(pos.x+cos(dir)*d, pos.y+sin(dir)*d);
+ result.pos = pos+dir_trans.transform(Vector(d, 0, 0));
result.dir = dir;
}
return result;
}
+TrackPoint TrackPart::get_nearest_point(const Vector &p) const
+{
+ TrackPoint tp;
+ Transform dir_trans = Transform::rotation(dir, Vector(0, 0, 1));
+ if(radius)
+ {
+ Vector r = dir_trans.transform(Vector(0, -radius, 0));
+ Vector v = p-pos+r;
+ Angle a = Geometry::atan2(v.y, v.x)+Angle::quarter_turn()-dir;
+ if(radius<0)
+ a = Angle::half_turn()-a;
+ a.wrap_with_base(Angle::from_radians(length/2)-Angle::half_turn());
+ a = min(max(a, Angle::zero()), Angle::from_radians(length));
+ if(radius<0)
+ a = -a;
+ tp.pos = pos-r+Transform::rotation(a, Vector(0, 0, 1)).transform(r);
+ tp.dir = dir+a;
+ }
+ else
+ {
+ Vector v = p-pos;
+ Vector dir_vec = dir_trans.transform(Vector(1, 0, 0));
+ float d = min(max(dot(dir_vec, v), 0.0f), length);
+ tp.pos = pos+dir_vec*d;
+ tp.dir = dir;
+ }
+ return tp;
+}
+
void TrackPart::check_link(TrackPart &other)
{
unsigned n_eps = (dead_end ? 1 : 2);
{
TrackPoint p2 = other.get_point(j ? other.get_length() : 0);
- float dx = p2.pos.x-p1.pos.x;
- float dy = p2.pos.y-p1.pos.y;
+ Vector span = p2.pos-p1.pos;
- float da = p2.dir-p1.dir+M_PI*((i+j+1)%2);
- while(da>M_PI)
- da -= M_PI*2;
- while(da<-M_PI)
- da += M_PI*2;
+ Angle da = wrap_balanced(p2.dir-p1.dir+Angle::half_turn()*float((i+j+1)%2));
- if(dx*dx+dy*dy<1e-6 && da>=-0.01 && da<=0.01)
+ if(dot(span, span)<1e-6 && abs(da).radians()<=0.01)
{
links[i] = &other;
other.links[j] = this;
TrackPart *TrackPart::get_link(unsigned i) const
{
if(i>=2)
- throw InvalidParameterValue("Index out of range");
+ throw out_of_range("TrackPart::get_link");
return links[i];
}
+void TrackPart::create_shape()
+{
+ Transform trans = Transform::translation(pos)*
+ Transform::rotation(dir, LinAl::Vector<float, 3>(0, 0, 1));
+ if(radius)
+ {
+ 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
+ {
+ // 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)));
+ }
+}
+
TrackPart::Loader::Loader(TrackPart &p):
- Msp::DataFile::BasicLoader<TrackPart>(p)
+ Msp::DataFile::ObjectLoader<TrackPart>(p)
{
add("start", &Loader::start);
add("length", &TrackPart::length);
else
obj.length /= 1000;
- obj.pos.x /= 1000;
- obj.pos.y /= 1000;
- obj.dir *= M_PI/180;
+ obj.pos /= 1000;
}
void TrackPart::Loader::start(float x, float y, float d)
{
- obj.pos = Point(x, y);
- obj.dir = d;
+ obj.pos = Vector(x, y, 0);
+ obj.dir = Angle::from_degrees(d);
}
} // namespace R2C2