// -*- C++ -*- #ifndef MBOSTOCK_SHAPE_H #define MBOSTOCK_SHAPE_H #include "vector.h" namespace mbostock { class Plane; class Sphere; /** * Represents the projection of a point onto the closest corresponding point * on the surface of a shape. A projection is used when the bounding sphere of * a particle interpenetrates a shape and, as this constitutes a collision, * the sphere center is projected onto the surface of the shape to determine * how to move the particle out in response to the collision. * * When a point is projected onto a surface, the projection includes the * surface normal. However, if the point is projected onto a line, the * corresponding normal is undefined. * * Projections define a natural order based on the length of the projection, * so that it is convenient to find the shortest projection given a number of * candidate projections. */ class Projection { public: Projection(); Projection(const Vector& x, float length); Projection(const Vector& x, float length, const Vector& normal); /** The location of the projected point on the shape's surface. */ Vector x; /** The distance from the original point to the projected point. */ float length; /** The surface normal at the projected point. */ Vector normal; bool operator<(const Projection& p) const; bool operator<=(const Projection& p) const; bool operator>(const Projection& p) const; bool operator>=(const Projection& p) const; }; /** * Represents a shape in three dimensions. Since particles are represented * solely as spheres, shapes need only define intersection tests with spheres, * and how to project points onto the closest point on the surface of the * shape. */ class Shape { public: virtual ~Shape() {} /** Returns true if the specified sphere intersects with this shape. */ virtual bool intersects(const Sphere& s) const = 0; /** * Projects the specified point onto the surface of the shape. The * projection specifies the closest point on the surface of the shape to the * specified point, the pre-computed length of the projection (the distance * between two points), and the surface normal at the projected point, if * defined. */ virtual Projection project(const Vector& x) const = 0; }; /** Represents a sphere using a center point and a radius. */ class Sphere : public Shape { public: Sphere(); Sphere(const Vector& x, float radius); /** The center point of the sphere. */ inline Vector& x() { return x_; } inline const Vector& x() const { return x_; } /** The radius of the sphere. */ inline float radius() const { return r_; } /** Returns true if the sphere is entirely above the specified plane. */ bool above(const Plane& p) const; /** Returns true if the sphere is entirely below the specified plane. */ bool below(const Plane& p) const; virtual bool intersects(const Sphere& s) const; virtual Projection project(const Vector& p) const; private: Vector x_; float r_; float r2_; friend class AxisAlignedBox; friend class Cylinder; friend class LineSegment; friend class Plane; friend class Triangle; }; /** Represents a line segment using a start point and an end point. */ class LineSegment : public Shape { public: LineSegment(); LineSegment(const Vector& x0, const Vector& x1); /** The start point of the line segment. */ inline const Vector& x0() const { return x0_; } /** The end point of the line segment. */ inline const Vector& x1() const { return x1_; } virtual bool intersects(const Sphere& s) const; virtual Projection project(const Vector& p) const; private: Vector x0_; Vector x1_; Vector x01_; float l2_; friend class Cylinder; }; /** Represents a plane using a point on the plane and a normal. */ class Plane : public Shape { public: Plane(); Plane(const Vector& x, const Vector& normal); /** A point on the plane. */ inline const Vector& x() const { return x_; } /** The normal. */ inline const Vector& normal() const { return n_; } virtual bool intersects(const Sphere& s) const; virtual Projection project(const Vector& p) const; private: Vector x_; Vector n_; friend class Sphere; }; /** Represents a triangle using three coplanar points. */ class Triangle : public Shape { public: Triangle(); Triangle(const Vector& x0, const Vector& x1, const Vector& x2); /** The first point of the triangle. */ inline const Vector& x0() const { return x01_.x0(); } /** The second point of the triangle. */ inline const Vector& x1() const { return x12_.x0(); } /** The third point of the triangle. */ inline const Vector& x2() const { return x20_.x0(); } /** The normal of the triangle, using counterclockwise orientation. */ inline const Vector& normal() const { return p_.normal(); } virtual bool intersects(const Sphere& s) const; virtual Projection project(const Vector& p) const; private: bool contains(const Vector& p) const; LineSegment x01_; LineSegment x12_; LineSegment x20_; Plane p_; friend class Wedge; }; /** Represents a quad using four coplanar points. */ class Quad : public Shape { public: Quad(); Quad(const Vector& x0, const Vector& x1, const Vector& x2, const Vector& x3); /** The first point of the quad. */ inline const Vector& x0() const { return x01_.x0(); } /** The second point of the quad. */ inline const Vector& x1() const { return x12_.x0(); } /** The third point of the quad. */ inline const Vector& x2() const { return x23_.x0(); } /** The fourth point of the quad. */ inline const Vector& x3() const { return x30_.x0(); } /** The normal of the quad, using counterclockwise orientation. */ inline const Vector& normal() const { return p_.normal(); } /** The plane of the quad. */ inline const Plane& plane() const { return p_; } virtual bool intersects(const Sphere& s) const; virtual Projection project(const Vector& p) const; private: bool contains(const Vector& p) const; LineSegment x01_; LineSegment x12_; LineSegment x23_; LineSegment x30_; Plane p_; friend class Wedge; }; /** * Represents a wedge using the four coplanar points of the top surface. * Points x1 and x2 define the upper edge of the wedge; points x4 and x5 lie * directly beneath them to define the fifth side. */ class Wedge : public Shape { public: Wedge(); Wedge(const Vector& x0, const Vector& x1, const Vector& x2, const Vector& x3); /** Returns the first (lower) point of the wedge. */ inline const Vector& x0() const { return top_.x0(); } /** Returns the second (upper) point of the wedge. */ inline const Vector& x1() const { return top_.x1(); } /** Returns the third (upper) point of the wedge. */ inline const Vector& x2() const { return top_.x2(); } /** Returns the forth (upper) point of the wedge. */ inline const Vector& x3() const { return top_.x3(); } /** Returns the fifth point of the wedge, below x1. */ inline const Vector& x4() const { return right_.x2(); } /** Returns the six point of the wedge, below x2. */ inline const Vector& x5() const { return right_.x3(); } /** Returns the top quad. */ inline const Quad& top() const { return top_; } /** Returns the right quad. */ inline const Quad& right() const { return right_; } /** Returns the bottom quad. */ inline const Quad& bottom() const { return bottom_; } /** Returns the front triangle. */ inline const Triangle& front() const { return front_; } /** Returns the back triangle. */ inline const Triangle& back() const { return back_; } virtual bool intersects(const Sphere& s) const; virtual Projection project(const Vector& p) const; private: Quad top_; Quad right_; Quad bottom_; Triangle front_; Triangle back_; }; /** * Represents an axis-aligned bounding box (AABB) using two points. The min * point is the bottom back left point, and the max point is the top front * right point. */ class AxisAlignedBox : public Shape { public: AxisAlignedBox(); AxisAlignedBox(const Vector& min, const Vector& max); /** Returns the min point, x0. */ inline const Vector& min() const { return min_; } /** Returns the max point, x7. */ inline const Vector& max() const { return max_; } /** Returns the left bottom back point. */ inline const Vector& x0() const { return min_; } /** Returns the right bottom back point. */ inline Vector x1() const { return Vector(max_.x, min_.y, min_.z); } /** Returns the right bottom front point. */ inline Vector x2() const { return Vector(max_.x, min_.y, max_.z); } /** Returns the left bottom front point. */ inline Vector x3() const { return Vector(min_.x, min_.y, max_.z); } /** Returns the right top back point. */ inline Vector x4() const { return Vector(max_.x, max_.y, min_.z); } /** Returns the left top back point. */ inline Vector x5() const { return Vector(min_.x, max_.y, min_.z); } /** Returns the left top front point. */ inline Vector x6() const { return Vector(min_.x, max_.y, max_.z); } /** Returns the right top front point. */ inline const Vector& x7() const { return max_; } /** Returns true if this box contains the specified point. */ bool contains(const Vector& p) const; virtual bool intersects(const Sphere& s) const; virtual Projection project(const Vector& p) const; private: Projection projectOut(const Vector& p) const; Projection projectIn(const Vector& p) const; Vector min_; Vector max_; }; /** * Represents an oriented bounding box (OBB) using eight points. */ class Box : public Shape { public: Box(); Box(const AxisAlignedBox& aab); Box(const Vector& c, const Vector& x, const Vector& y, const Vector& z); Box(const Vector& x0, const Vector& x1, const Vector& x2, const Vector& x3, const Vector& x4, const Vector& x5, const Vector& x6, const Vector& x7); /** Returns the left bottom back point. */ inline const Vector& x0() const { return bottom_.x0(); } /** Returns the right bottom back point. */ inline const Vector& x1() const { return bottom_.x1(); } /** Returns the right bottom front point. */ inline const Vector& x2() const { return bottom_.x2(); } /** Returns the left bottom front point. */ inline const Vector& x3() const { return bottom_.x3(); } /** Returns the right top back point. */ inline const Vector& x4() const { return top_.x0(); } /** Returns the left top back point. */ inline const Vector& x5() const { return top_.x1(); } /** Returns the left top front point. */ inline const Vector& x6() const { return top_.x2(); } /** Returns the right top front point. */ inline const Vector& x7() const { return top_.x3(); } /** Returns the left quad. */ inline const Quad& left() const { return back_; } /** Returns the right quad. */ inline const Quad& right() const { return right_; } /** Returns the bottom quad. */ inline const Quad& bottom() const { return bottom_; } /** Returns the top quad. */ inline const Quad& top() const { return top_; } /** Returns the back quad. */ inline const Quad& back() const { return back_; } /** Returns the front quad. */ inline const Quad& front() const { return front_; } virtual bool intersects(const Sphere& s) const; virtual Projection project(const Vector& p) const; private: Quad top_; Quad bottom_; Quad left_; Quad right_; Quad front_; Quad back_; }; /** Represents a cylinder as two points and a radius. */ class Cylinder : public Shape { public: Cylinder(); Cylinder(const Vector& x0, const Vector& x1, float radius); /** Returns the start point of the cylinder's main axis. */ inline const Vector& x0() const { return axis_.x0(); } /** Returns the end point of the cylinder's main axis. */ inline const Vector& x1() const { return axis_.x1(); } /** Returns the radius. */ inline float radius() const { return r_; } /** Returns the length of the cylinder. */ inline float length() const { return l_; } /** Returns the unit direction vector of the cylinder. */ inline const Vector& z() const { return v_; } virtual bool intersects(const Sphere& s) const; virtual Projection project(const Vector& p) const; private: LineSegment axis_; float l_; float r_; Vector v_; }; }; #endif