2 #include <msp/geometry/box.h>
3 #include <msp/geometry/circle.h>
4 #include <msp/geometry/extrudedshape.h>
5 #include <msp/geometry/halfspace.h>
6 #include <msp/geometry/intersection.h>
7 #include <msp/geometry/negation.h>
8 #include <msp/geometry/transformedshape.h>
16 TrackPart::TrackPart():
27 TrackPart::~TrackPart()
32 float TrackPart::get_length() const
35 return abs(radius)*length;
40 OrientedPoint TrackPart::get_point(float d) const
46 Angle a = Angle::from_radians(d/radius);
47 result.position = transform.transform(Vector(sin(a)*abs(radius), radius-cos(a)*radius, 0));
48 result.rotation = dir+a;
52 result.position = transform.transform(Vector(d, 0, 0));
53 result.rotation = dir;
59 OrientedPoint TrackPart::get_nearest_point(const Vector &p) const
64 Vector v = p-transform.transform(Vector(0, radius, 0));
65 Angle a = Geometry::atan2(v.y, v.x)+Angle::quarter_turn()-dir;
67 a = Angle::half_turn()-a;
68 a.wrap_with_base(Angle::from_radians(length/2)-Angle::half_turn());
69 a = min(max(a, Angle::zero()), Angle::from_radians(length));
72 result.position = transform.transform(Vector(sin(a)*abs(radius), radius-cos(a)*radius, 0));
73 result.rotation = dir+a;
78 Vector dir_vec = transform.transform_linear(Vector(1, 0, 0));
79 float d = min(max(dot(dir_vec, v), 0.0f), length);
80 result.position = pos+dir_vec*d;
81 result.rotation = dir;
86 void TrackPart::check_link(TrackPart &other)
88 unsigned n_eps = (dead_end ? 1 : 2);
89 unsigned n_other_eps = (other.is_dead_end() ? 1 : 2);
90 for(unsigned i=0; i<n_eps; ++i)
92 OrientedPoint p1 = get_point(i ? get_length() : 0);
93 for(unsigned j=0; j<n_other_eps; ++j)
95 OrientedPoint p2 = other.get_point(j ? other.get_length() : 0);
97 Vector span = p2.position-p1.position;
99 Angle da = wrap_balanced(p2.rotation-p1.rotation+Angle::half_turn()*float((i+j+1)%2));
101 if(dot(span, span)<1e-6 && abs(da).radians()<=0.01)
104 other.links[j] = this;
111 TrackPart *TrackPart::get_link(unsigned i) const
114 throw out_of_range("TrackPart::get_link");
118 void TrackPart::create_shape()
122 Geometry::ExtrudedShape<float, 3> ring(
123 Geometry::Intersection<float, 2>(
124 Geometry::Circle<float>(abs(radius)+0.02),
125 Geometry::Negation<float, 2>(Geometry::Circle<float>(abs(radius)-0.02))),
127 Geometry::HalfSpace<float, 3> start_wall(Vector(-1, 0, 0));
128 Geometry::HalfSpace<float, 3> end_wall(rotated_vector(Vector(1, 0, 0),
129 Angle::from_radians(length*(radius<0 ? -1 : 1))));
130 /* This box only exists so the bounding boxes of curves make sense. It
131 can be removed when libmspmath produces better bounding boxes. */
132 float w = sin(length)*(abs(radius)+0.02);
133 float h = (1-cos(length))*abs(radius-0.02)+0.04;
134 Geometry::TransformedShape<float, 3> bounds(
135 Geometry::Box<float>(w, h, 0.01),
136 Transform::translation(Vector(w/2, (h/2-0.02)*(radius<0 ? -1 : 1)-radius, 0.005)));
137 vector<const Shape *> shapes;
138 shapes.push_back(&ring);
139 shapes.push_back(&start_wall);
140 shapes.push_back(&end_wall);
141 shapes.push_back(&bounds);
142 shape = new Geometry::TransformedShape<float, 3>(
143 Geometry::Intersection<float, 3>::from_iterator_range(shapes.begin(), shapes.end()),
144 transform*Transform::translation(Vector(0, radius, 0.005)));
148 // TODO Get the track profile dimensions from somewhere
149 shape = new Geometry::TransformedShape<float, 3>(
150 Geometry::Box<float>(length, 0.04, 0.01),
151 transform*Transform::translation(Vector(length/2, 0, 0.005)));
156 TrackPart::Loader::Loader(TrackPart &p):
157 Msp::DataFile::ObjectLoader<TrackPart>(p)
159 add("start", &Loader::start);
160 add("length", &TrackPart::length);
161 add("radius", &TrackPart::radius);
162 add("path", &TrackPart::path);
163 add("dead_end", &TrackPart::dead_end);
166 void TrackPart::Loader::finish()
170 obj.length *= M_PI/180;
178 obj.transform = Transform::translation(obj.pos)*
179 Transform::rotation(obj.dir, Vector(0, 0, 1));
182 void TrackPart::Loader::start(float x, float y, float d)
184 obj.pos = Vector(x, y, 0);
185 obj.dir = Angle::from_degrees(d);