Browse Source

2D/3D alternatives of collision detection shapes.

vectorfields
Vladimír Vondruš 14 years ago
parent
commit
77c0ab9cd4
  1. 42
      doc/collision-detection.dox
  2. 5
      src/Physics/AbstractShape.cpp
  3. 93
      src/Physics/AbstractShape.h
  4. 7
      src/Physics/AxisAlignedBox.cpp
  5. 49
      src/Physics/AxisAlignedBox.h
  6. 5
      src/Physics/Box.cpp
  7. 44
      src/Physics/Box.h
  8. 27
      src/Physics/Capsule.cpp
  9. 74
      src/Physics/Capsule.h
  10. 10
      src/Physics/Line.cpp
  11. 72
      src/Physics/Line.h
  12. 20
      src/Physics/LineSegment.h
  13. 10
      src/Physics/Plane.cpp
  14. 24
      src/Physics/Plane.h
  15. 7
      src/Physics/Point.cpp
  16. 41
      src/Physics/Point.h
  17. 35
      src/Physics/ShapeGroup.cpp
  18. 154
      src/Physics/ShapeGroup.h
  19. 51
      src/Physics/Sphere.cpp
  20. 63
      src/Physics/Sphere.h
  21. 2
      src/Physics/Test/AxisAlignedBoxTest.cpp
  22. 2
      src/Physics/Test/BoxTest.cpp
  23. 18
      src/Physics/Test/CapsuleTest.cpp
  24. 2
      src/Physics/Test/LineTest.cpp
  25. 12
      src/Physics/Test/PlaneTest.cpp
  26. 2
      src/Physics/Test/PointTest.cpp
  27. 12
      src/Physics/Test/ShapeGroupTest.cpp
  28. 26
      src/Physics/Test/SphereTest.cpp

42
doc/collision-detection.dox

@ -13,12 +13,12 @@ together using various operations.
@subsection collision-detection-shapes1D One-dimensional shapes
- Physics::Point - @copybrief Physics::Point
- Physics::Line - @copybrief Physics::Line
- Physics::LineSegment - @copybrief Physics::LineSegment
- @ref Physics::Point "Physics::Point*D" - @copybrief Physics::Point
- @ref Physics::Line "Physics::Line*D" - @copybrief Physics::Line
- @ref Physics::LineSegment "Physics::LineSegment*D" - @copybrief Physics::LineSegment
One-dimensional shapes don't provide collision detection with each other
because of numerical instability.
Because of numerical instability it's not possible to detect collisions of
line and point. Collision of two lines can be detected only in 2D.
@subsection collision-detection-shapes2D Two-dimensional shapes
@ -26,10 +26,10 @@ because of numerical instability.
@subsection collision-detection-shapes3D Three-dimensional shapes
- Physics::Sphere - @copybrief Physics::Sphere
- Physics::Capsule - @copybrief Physics::Capsule
- Physics::AxisAlignedBox - @copybrief Physics::AxisAlignedBox
- Physics::Box - @copybrief Physics::Box
- @ref Physics::Sphere "Physics::Sphere*D" - @copybrief Physics::Sphere
- @ref Physics::Capsule "Physics::Capsule*D" - @copybrief Physics::Capsule
- @ref Physics::AxisAlignedBox "Physics::AxisAlignedBox*D" - @copybrief Physics::AxisAlignedBox
- @ref Physics::Box "Physics::Box*D" - @copybrief Physics::Box
The easiest (and most efficient) shape combination for detecting collisions
is point and sphere, followed by two spheres. Computing collision of two boxes
@ -42,10 +42,10 @@ operations: AND, OR and NOT. These operations are mapped to operator&&(),
operator||() and operator!(), so for example creating negation of logical OR
of line segment and point is simple as this:
@code
Physics::LineSegment segment;
Physics::Point point;
Physics::LineSegment3D segment;
Physics::Point3D point;
Physics::ShapeGroup group = !(segment || point);
Physics::ShapeGroup3D group = !(segment || point);
@endcode
@note Logical operations are not the same as set operations -- intersection of
@ -66,10 +66,10 @@ was not possible to change their properties such as sphere radius without
recreating the group again. You can, however, explicitly pass a reference to
original object, so you can change it later:
@code
Physics::LineSegment segment;
Physics::Point point;
Physics::LineSegment3D segment;
Physics::Point3D point;
Physics::ShapeGroup group = !(segment || std::ref(point));
Physics::ShapeGroup3D group = !(segment || std::ref(point));
point.setPosition({1.0f, -6.0f, 0.5f});
@endcode
@ -88,11 +88,11 @@ was detected with the simplified shape. It is in fact logical AND using
operator&&() - the collision is initially detected on first (simplified) shape
and then on the other:
@code
Physics::Sphere sphere;
Physics::Box box;
Physics::AxisAlignedBox simplified;
Physics::Sphere3D sphere;
Physics::Box3D box;
Physics::AxisAlignedBox3D simplified;
Physics::ShapeGroup object = simplified && (sphere || box);
Physics::ShapeGroup3D object = simplified && (sphere || box);
@endcode
@section collision-detection-shape-collisions Detecting shape collisions
@ -100,8 +100,8 @@ Physics::ShapeGroup object = simplified && (sphere || box);
Shape pairs which have collision detection implemented can be tested for
collision using operator%(), for example:
@code
Physics::Point point;
Physics::Sphere sphere;
Physics::Point3D point;
Physics::Sphere3D sphere;
bool collide = point % sphere;
@endcode

5
src/Physics/AbstractShape.cpp

@ -17,7 +17,7 @@
namespace Magnum { namespace Physics {
bool AbstractShape::collides(const AbstractShape* other) const {
template<size_t dimensions> bool AbstractShape<dimensions>::collides(const AbstractShape* other) const {
/* Operate only with simpler types than this */
if(static_cast<int>(other->type()) > static_cast<int>(type()))
return other->collides(this);
@ -25,4 +25,7 @@ bool AbstractShape::collides(const AbstractShape* other) const {
return false;
}
template class AbstractShape<2>;
template class AbstractShape<3>;
}}

93
src/Physics/AbstractShape.h

@ -25,13 +25,67 @@
namespace Magnum { namespace Physics {
#ifndef DOXYGEN_GENERATING_OUTPUT
namespace Implementation {
template<size_t dimensions> struct ShapeDimensionTraits {};
template<> struct ShapeDimensionTraits<2> {
typedef Vector2 VectorType;
typedef Point2D PointType;
typedef Matrix3 MatrixType;
enum class Type {
Point,
Line,
LineSegment,
Sphere,
Capsule,
AxisAlignedBox,
Box,
ShapeGroup
};
};
template<> struct ShapeDimensionTraits<3> {
typedef Vector3 VectorType;
typedef Point3D PointType;
typedef Matrix4 MatrixType;
enum class Type {
Point,
Line,
LineSegment,
Sphere,
Capsule,
AxisAlignedBox,
Box,
ShapeGroup,
Plane
};
};
}
#endif
/**
@brief Base class for shapes
See @ref collision-detection for brief introduction.
@see AbstractShape2D, AbstractShape3D
*/
class PHYSICS_EXPORT AbstractShape {
template<size_t dimensions> class PHYSICS_EXPORT AbstractShape {
public:
/** @brief %Vector type for given dimension count */
typedef typename Implementation::ShapeDimensionTraits<dimensions>::VectorType VectorType;
/** @brief %Point type for given dimension count */
typedef typename Implementation::ShapeDimensionTraits<dimensions>::PointType PointType;
/** @brief %Matrix type for given dimension count */
typedef typename Implementation::ShapeDimensionTraits<dimensions>::MatrixType MatrixType;
/** @brief Dimension count */
static const size_t Dimensions = dimensions;
/**
* @brief Shape type
*
@ -39,17 +93,21 @@ class PHYSICS_EXPORT AbstractShape {
* the list provides collision detection for previous shapes, not
* the other way around.
*/
#ifdef DOXYGEN_GENERATING_OUTPUT
enum class Type {
Point,
Line,
LineSegment,
Plane,
Sphere,
Capsule,
AxisAlignedBox,
Box,
ShapeGroup
Point, /**< Point */
Line, /**< Line */
LineSegment, /**< @ref LineSegment "Line segment" */
Sphere, /**< Sphere */
Capsule, /**< Capsule */
AxisAlignedBox, /**< @ref AxisAlignedBox "Axis aligned box" */
Box, /**< Box */
ShapeGroup, /**< @ref ShapeGroup "Shape group" */
Plane /**< Plane (3D only) */
};
#else
typedef typename Implementation::ShapeDimensionTraits<dimensions>::Type Type;
#endif
/** @brief Destructor */
virtual inline ~AbstractShape() {}
@ -63,7 +121,7 @@ class PHYSICS_EXPORT AbstractShape {
* Applies transformation to user-defined shape properties and caches
* them for later usage in collision detection.
*/
virtual void applyTransformation(const Matrix4& transformation) = 0;
virtual void applyTransformation(const MatrixType& transformation) = 0;
/**
* @brief Detect collision with other shape
@ -73,9 +131,20 @@ class PHYSICS_EXPORT AbstractShape {
* @internal If other shape is more complex than this, returns
* `other->collides(this)`.
*/
virtual bool collides(const AbstractShape* other) const;
virtual bool collides(const AbstractShape<dimensions>* other) const;
};
#ifndef DOXYGEN_GENERATING_OUTPUT
extern template class PHYSICS_EXPORT AbstractShape<2>;
extern template class PHYSICS_EXPORT AbstractShape<3>;
#endif
/** @brief Abstract two-dimensional shape */
typedef AbstractShape<2> AbstractShape2D;
/** @brief Abstract three-dimensional shape */
typedef AbstractShape<3> AbstractShape3D;
}}
#endif

7
src/Physics/AxisAlignedBox.cpp

@ -20,9 +20,12 @@
namespace Magnum { namespace Physics {
void AxisAlignedBox::applyTransformation(const Matrix4& transformation) {
_transformedPosition = (transformation*Point3D(_position)).xyz();
template<size_t dimensions> void AxisAlignedBox<dimensions>::applyTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation) {
_transformedPosition = (transformation*typename AxisAlignedBox<dimensions>::PointType(_position)).vector();
_transformedSize = transformation.rotationScaling()*_size;
}
template class AxisAlignedBox<2>;
template class AxisAlignedBox<3>;
}}

49
src/Physics/AxisAlignedBox.h

@ -16,7 +16,7 @@
*/
/** @file
* @brief Class Magnum::Physics::AxisAlignedBox
* @brief Class Magnum::Physics::AxisAlignedBox, typedef Magnum::Physics::AxisAlignedBox2D, Magnum::Physics.:AxisAlignedBox3D
*/
#include "Math/Vector3.h"
@ -24,48 +24,71 @@
namespace Magnum { namespace Physics {
/** @brief Axis aligned box */
class PHYSICS_EXPORT AxisAlignedBox: public AbstractShape {
/**
@brief Axis-aligned box
@see AxisAlignedBox2D, AxisAlignedBox3D
*/
template<size_t dimensions> class PHYSICS_EXPORT AxisAlignedBox: public AbstractShape<dimensions> {
public:
/** @brief Constructor */
inline AxisAlignedBox(const Vector3& position, const Vector3& size): _position(position), _transformedPosition(position), _size(size), _transformedSize(size) {}
inline AxisAlignedBox(const typename AbstractShape<dimensions>::VectorType& position, const typename AbstractShape<dimensions>::VectorType& size): _position(position), _transformedPosition(position), _size(size), _transformedSize(size) {}
void applyTransformation(const Matrix4& transformation);
#ifndef DOXYGEN_GENERATING_OUTPUT
void applyTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation);
#else
void applyTransformation(const MatrixType& transformation);
#endif
/** @brief Position */
inline Vector3 position() const { return _position; }
inline typename AbstractShape<dimensions>::VectorType position() const {
return _position;
}
/** @brief Set position */
inline void setPosition(const Vector3& position) {
inline void setPosition(const typename AbstractShape<dimensions>::VectorType& position) {
_position = position;
}
/** @brief Size */
inline Vector3 size() const { return _size; }
inline typename AbstractShape<dimensions>::VectorType size() const { return _size; }
/** @brief Set size */
inline void setSize(const Vector3& size) {
inline void setSize(const typename AbstractShape<dimensions>::VectorType& size) {
_size = size;
}
/** @brief Transformed position */
inline Vector3 transformedPosition() const {
inline typename AbstractShape<dimensions>::VectorType transformedPosition() const {
return _transformedPosition;
}
/** @brief Transformed size */
inline Vector3 transformedSize() const {
inline typename AbstractShape<dimensions>::VectorType transformedSize() const {
return _transformedSize;
}
protected:
inline Type type() const { return Type::AxisAlignedBox; }
inline typename AbstractShape<dimensions>::Type type() const {
return AbstractShape<dimensions>::Type::AxisAlignedBox;
}
private:
Vector3 _position, _transformedPosition,
typename AbstractShape<dimensions>::VectorType _position, _transformedPosition,
_size, _transformedSize;
};
#ifndef DOXYGEN_GENERATING_OUTPUT
extern template class PHYSICS_EXPORT AxisAlignedBox<2>;
extern template class PHYSICS_EXPORT AxisAlignedBox<3>;
#endif
/** @brief Two-dimensional axis-aligned box */
typedef AxisAlignedBox<2> AxisAlignedBox2D;
/** @brief Three-dimensional axis-aligned box */
typedef AxisAlignedBox<3> AxisAlignedBox3D;
}}
#endif

5
src/Physics/Box.cpp

@ -20,8 +20,11 @@
namespace Magnum { namespace Physics {
void Box::applyTransformation(const Matrix4& transformation) {
template<size_t dimensions> void Box<dimensions>::applyTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation) {
_transformedTransformation = (transformation*_transformation);
}
template class Box<2>;
template class Box<3>;
}}

44
src/Physics/Box.h

@ -16,7 +16,7 @@
*/
/** @file
* @brief Class Magnum::Physics::Box
* @brief Class Magnum::Physics::Box, typedef Magnum::Physics::Box2D, Magnum::Physics::Box3D
*/
#include "Math/Matrix4.h"
@ -24,34 +24,58 @@
namespace Magnum { namespace Physics {
/** @brief Unit size box with assigned transformation matrix */
class PHYSICS_EXPORT Box: public AbstractShape {
/**
@brief Unit-size box with assigned transformation matrix
@see Box2D, Box3D
*/
template<size_t dimensions> class PHYSICS_EXPORT Box: public AbstractShape<dimensions> {
public:
/** @brief Constructor */
inline Box(const Matrix4& transformation): _transformation(transformation), _transformedTransformation(transformation) {}
inline Box(const typename AbstractShape<dimensions>::MatrixType& transformation): _transformation(transformation), _transformedTransformation(transformation) {}
void applyTransformation(const Matrix4& transformation);
#ifndef DOXYGEN_GENERATING_OUTPUT
void applyTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation);
#else
void applyTransformation(const MatrixType& transformation);
#endif
/** @brief Transformation */
inline Matrix4 transformation() const { return _transformation; }
inline typename AbstractShape<dimensions>::MatrixType transformation() const {
return _transformation;
}
/** @brief Set transformation */
inline void setTransformation(const Matrix4& transformation) {
inline void setTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation) {
_transformation = transformation;
}
/** @brief Transformed transformation */
inline Matrix4 transformedTransformation() const {
inline typename AbstractShape<dimensions>::MatrixType transformedTransformation() const {
return _transformedTransformation;
}
protected:
inline Type type() const { return Type::Box; }
inline typename AbstractShape<dimensions>::Type type() const {
return AbstractShape<dimensions>::Type::Box;
}
private:
Matrix4 _transformation, _transformedTransformation;
typename AbstractShape<dimensions>::MatrixType _transformation,
_transformedTransformation;
};
#ifndef DOXYGEN_GENERATING_OUTPUT
extern template class PHYSICS_EXPORT Box<2>;
extern template class PHYSICS_EXPORT Box<3>;
#endif
/** @brief Two-dimensional box */
typedef Box<2> Box2D;
/** @brief Three-dimensional box */
typedef Box<3> Box3D;
}}
#endif

27
src/Physics/Capsule.cpp

@ -27,30 +27,33 @@ using namespace Magnum::Math::Geometry;
namespace Magnum { namespace Physics {
void Capsule::applyTransformation(const Matrix4& transformation) {
_transformedA = (transformation*Point3D(_a)).xyz();
_transformedB = (transformation*Point3D(_b)).xyz();
float scaling = (transformation.rotationScaling()*Vector3(1/Math::Constants<float>::sqrt3())).length();
template<size_t dimensions> void Capsule<dimensions>::applyTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation) {
_transformedA = (transformation*typename AbstractShape<dimensions>::PointType(_a)).vector();
_transformedB = (transformation*typename AbstractShape<dimensions>::PointType(_b)).vector();
float scaling = (transformation.rotationScaling()*typename AbstractShape<dimensions>::VectorType(1/Math::Constants<float>::sqrt3())).length();
_transformedRadius = scaling*_radius;
}
bool Capsule::collides(const AbstractShape* other) const {
if(other->type() == Type::Point)
return *this % *static_cast<const Point*>(other);
if(other->type() == Type::Sphere)
return *this % *static_cast<const Sphere*>(other);
template<size_t dimensions> bool Capsule<dimensions>::collides(const AbstractShape<dimensions>* other) const {
if(other->type() == AbstractShape<dimensions>::Type::Point)
return *this % *static_cast<const Point<dimensions>*>(other);
if(other->type() == AbstractShape<dimensions>::Type::Sphere)
return *this % *static_cast<const Sphere<dimensions>*>(other);
return AbstractShape::collides(other);
return AbstractShape<dimensions>::collides(other);
}
bool Capsule::operator%(const Point& other) const {
template<size_t dimensions> bool Capsule<dimensions>::operator%(const Point<dimensions>& other) const {
return Distance::lineSegmentPointSquared(transformedA(), transformedB(), other.transformedPosition()) <
Math::pow<2>(transformedRadius());
}
bool Capsule::operator%(const Sphere& other) const {
template<size_t dimensions> bool Capsule<dimensions>::operator%(const Sphere<dimensions>& other) const {
return Distance::lineSegmentPointSquared(transformedA(), transformedB(), other.transformedPosition()) <
Math::pow<2>(transformedRadius()+other.transformedRadius());
}
template class Capsule<2>;
template class Capsule<3>;
}}

74
src/Physics/Capsule.h

@ -16,7 +16,7 @@
*/
/** @file
* @brief Class Magnum::Physics::Capsule
* @brief Class Magnum::Physics::Capsule, typedef Magnum::Physics::Capsule2D, Magnum::Physics::Capsule3D
*/
#include "Math/Vector3.h"
@ -24,29 +24,48 @@
namespace Magnum { namespace Physics {
class Point;
class Sphere;
template<size_t> class Point;
template<size_t> class Sphere;
/**
@brief %Capsule defined by cylinder start and end point and radius
Unlike other elements the capsule doesn't support asymmetric scaling. When
applying transformation, the scale factor is averaged from all axes.
@see Capsule2D, Capsule3D
*/
class PHYSICS_EXPORT Capsule: public AbstractShape {
template<size_t dimensions> class PHYSICS_EXPORT Capsule: public AbstractShape<dimensions> {
public:
/** @brief Constructor */
inline Capsule(const Vector3& a, const Vector3& b, float radius): _a(a), _transformedA(a), _b(b), _transformedB(b), _radius(radius), _transformedRadius(radius) {}
inline Capsule(const typename AbstractShape<dimensions>::VectorType& a, const typename AbstractShape<dimensions>::VectorType& b, float radius): _a(a), _transformedA(a), _b(b), _transformedB(b), _radius(radius), _transformedRadius(radius) {}
void applyTransformation(const Matrix4& transformation);
#ifndef DOXYGEN_GENERATING_OUTPUT
void applyTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation);
#else
void applyTransformation(const MatrixType& transformation);
#endif
bool collides(const AbstractShape* other) const;
bool collides(const AbstractShape<dimensions>* other) const;
inline Vector3 a() const { return _a; } /**< @brief Start point */
inline Vector3 b() const { return _a; } /**< @brief End point */
/** @brief Start point */
inline typename AbstractShape<dimensions>::VectorType a() const {
return _a;
}
/** @brief End point */
inline typename AbstractShape<dimensions>::VectorType b() const {
return _a;
}
inline void setA(const Vector3& a) { _a = a; } /**< @brief Set start point */
inline void setB(const Vector3& b) { _b = b; } /**< @brief Set end point */
/** @brief Set start point */
inline void setA(const typename AbstractShape<dimensions>::VectorType& a) {
_a = a;
}
/** @brief Set end point */
inline void setB(const typename AbstractShape<dimensions>::VectorType& b) {
_b = b;
}
/** @brief Radius */
inline float radius() const { return _radius; }
@ -55,10 +74,14 @@ class PHYSICS_EXPORT Capsule: public AbstractShape {
inline void setRadius(float radius) { _radius = radius; }
/** @brief Transformed first point */
inline Vector3 transformedA() const { return _transformedA; }
inline typename AbstractShape<dimensions>::VectorType transformedA() const {
return _transformedA;
}
/** @brief Transformed second point */
inline Vector3 transformedB() const { return _transformedB; }
inline typename AbstractShape<dimensions>::VectorType transformedB() const {
return _transformedB;
}
/** @brief Transformed radius */
inline float transformedRadius() const {
@ -66,25 +89,38 @@ class PHYSICS_EXPORT Capsule: public AbstractShape {
}
/** @brief Collision with point */
bool operator%(const Point& other) const;
bool operator%(const Point<dimensions>& other) const;
/** @brief Collision with sphere */
bool operator%(const Sphere& other) const;
bool operator%(const Sphere<dimensions>& other) const;
protected:
inline Type type() const { return Type::Capsule; }
inline typename AbstractShape<dimensions>::Type type() const {
return AbstractShape<dimensions>::Type::Capsule;
}
private:
Vector3 _a, _transformedA,
typename AbstractShape<dimensions>::VectorType _a, _transformedA,
_b, _transformedB;
float _radius, _transformedRadius;
};
#ifndef DOXYGEN_GENERATING_OUTPUT
extern template class PHYSICS_EXPORT Capsule<2>;
extern template class PHYSICS_EXPORT Capsule<3>;
#endif
/** @brief Two-dimensional capsule */
typedef Capsule<2> Capsule2D;
/** @brief Three-dimensional capsule */
typedef Capsule<3> Capsule3D;
/** @collisionoperator{Point,Capsule} */
inline bool operator%(const Point& a, const Capsule& b) { return b % a; }
template<size_t dimensions> inline bool operator%(const Point<dimensions>& a, const Capsule<dimensions>& b) { return b % a; }
/** @collisionoperator{Sphere,Capsule} */
inline bool operator%(const Sphere& a, const Capsule& b) { return b % a; }
template<size_t dimensions> inline bool operator%(const Sphere<dimensions>& a, const Capsule<dimensions>& b) { return b % a; }
}}

10
src/Physics/Line.cpp

@ -20,9 +20,13 @@
namespace Magnum { namespace Physics {
void Line::applyTransformation(const Matrix4& transformation) {
_transformedA = (transformation*Point3D(_a)).xyz();
_transformedB = (transformation*Point3D(_b)).xyz();
template<size_t dimensions> void Line<dimensions>::applyTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation) {
_transformedA = (transformation*typename AbstractShape<dimensions>::PointType(_a)).vector();
_transformedB = (transformation*typename AbstractShape<dimensions>::PointType(_b)).vector();
}
/* Explicitly instantiate the templates */
template class Line<2>;
template class Line<3>;
}}

72
src/Physics/Line.h

@ -16,7 +16,7 @@
*/
/** @file
* @brief Class Magnum::Physics::Line
* @brief Class Magnum::Physics::Line, typedef Magnum::Physics::Line2D, Magnum::Physics::Line3D
*/
#include "Math/Vector3.h"
@ -24,34 +24,74 @@
namespace Magnum { namespace Physics {
/** @brief Infinite line, defined by two points */
class PHYSICS_EXPORT Line: public AbstractShape {
/**
@brief Infinite line, defined by two points
@see Line2D, Line3D
@todo collision detection of two Line2D
*/
template<size_t dimensions> class PHYSICS_EXPORT Line: public AbstractShape<dimensions> {
public:
/** @brief Constructor */
inline Line(const Vector3& a, const Vector3& b): _a(a), _transformedA(a), _b(b), _transformedB(b) {}
void applyTransformation(const Matrix4& transformation);
inline Vector3 a() const { return _a; } /**< @brief First point */
inline Vector3 b() const { return _a; } /**< @brief Second point */
inline void setA(const Vector3& a) { _a = a; } /**< @brief Set first point */
inline void setB(const Vector3& b) { _b = b; } /**< @brief Set second point */
inline Line(const typename AbstractShape<dimensions>::VectorType& a, const typename AbstractShape<dimensions>::VectorType& b): _a(a), _transformedA(a), _b(b), _transformedB(b) {}
#ifndef DOXYGEN_GENERATING_OUTPUT
void applyTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation);
#else
void applyTransformation(const MatrixType& transformation);
#endif
/** @brief First point */
inline typename AbstractShape<dimensions>::VectorType a() const {
return _a;
}
/** @brief Second point */
inline typename AbstractShape<dimensions>::VectorType b() const {
return _a;
}
/** @brief Set first point */
inline void setA(const typename AbstractShape<dimensions>::VectorType& a) {
_a = a;
}
/** @brief Set second point */
inline void setB(const typename AbstractShape<dimensions>::VectorType& b) {
_b = b;
}
/** @brief Transformed first point */
inline Vector3 transformedA() const { return _transformedA; }
inline typename AbstractShape<dimensions>::VectorType transformedA() const {
return _transformedA;
}
/** @brief Transformed second point */
inline Vector3 transformedB() const { return _transformedB; }
inline typename AbstractShape<dimensions>::VectorType transformedB() const {
return _transformedB;
}
protected:
inline Type type() const { return Type::Line; }
inline typename AbstractShape<dimensions>::Type type() const {
return AbstractShape<dimensions>::Type::Line;
}
private:
Vector3 _a, _transformedA,
typename AbstractShape<dimensions>::VectorType _a, _transformedA,
_b, _transformedB;
};
#ifndef DOXYGEN_GENERATING_OUTPUT
extern template class PHYSICS_EXPORT Line<2>;
extern template class PHYSICS_EXPORT Line<3>;
#endif
/** @brief Infinite two-dimensional line */
typedef Line<2> Line2D;
/** @brief Infinite three-dimensional line */
typedef Line<3> Line3D;
}}
#endif

20
src/Physics/LineSegment.h

@ -16,23 +16,33 @@
*/
/** @file
* @brief Class Magnum::Physics::LineSegment
* @brief Class Magnum::Physics::LineSegment, typedef Magnum::Physics::LineSegment2D, Magnum::Physics::LineSegment3D
*/
#include "Line.h"
namespace Magnum { namespace Physics {
/** @brief %Line segment, defined by starting and ending point */
class LineSegment: public Line {
/**
@brief %Line segment, defined by starting and ending point
@see LineSegment2D, LineSegment3D
*/
template<size_t dimensions> class LineSegment: public Line<dimensions> {
public:
/** @brief Constructor */
inline LineSegment(const Vector3& a, const Vector3& b): Line(a, b) {}
inline LineSegment(const typename AbstractShape<dimensions>::VectorType& a, const typename AbstractShape<dimensions>::VectorType& b): Line<dimensions>(a, b) {}
protected:
inline Type type() const { return Type::LineSegment; }
inline typename AbstractShape<dimensions>::Type type() const { return AbstractShape<dimensions>::Type::LineSegment; }
};
/** @brief Two-dimensional line segment */
typedef LineSegment<2> LineSegment2D;
/** @brief Three-dimensional line segment */
typedef LineSegment<3> LineSegment3D;
}}
#endif

10
src/Physics/Plane.cpp

@ -32,21 +32,21 @@ void Plane::applyTransformation(const Matrix4& transformation) {
_transformedNormal = transformation.rotation()*_normal;
}
bool Plane::collides(const AbstractShape* other) const {
bool Plane::collides(const AbstractShape<3>* other) const {
if(other->type() == Type::Line)
return *this % *static_cast<const Line*>(other);
return *this % *static_cast<const Line3D*>(other);
if(other->type() == Type::LineSegment)
return *this % *static_cast<const LineSegment*>(other);
return *this % *static_cast<const LineSegment3D*>(other);
return AbstractShape::collides(other);
}
bool Plane::operator%(const Line& other) const {
bool Plane::operator%(const Line3D& other) const {
float t = Intersection::planeLine(transformedPosition(), transformedNormal(), other.transformedA(), other.transformedB());
return t != t || (t != numeric_limits<float>::infinity() && t != -numeric_limits<float>::infinity());
}
bool Plane::operator%(const LineSegment& other) const {
bool Plane::operator%(const LineSegment3D& other) const {
float t = Intersection::planeLine(transformedPosition(), transformedNormal(), other.transformedA(), other.transformedB());
return t > 0.0f && t < 1.0f;
}

24
src/Physics/Plane.h

@ -24,18 +24,24 @@
namespace Magnum { namespace Physics {
class Line;
class LineSegment;
template<size_t> class Line;
typedef Line<3> Line3D;
template<size_t> class LineSegment;
typedef LineSegment<3> LineSegment3D;
/** @brief Infinite plane, defined by position and normal */
class PHYSICS_EXPORT Plane: public AbstractShape {
/** @brief Infinite plane, defined by position and normal (3D only) */
class PHYSICS_EXPORT Plane: public AbstractShape<3> {
public:
/** @brief Constructor */
inline Plane(const Vector3& position, const Vector3& normal): _position(position), _transformedPosition(position), _normal(normal), _transformedNormal(normal) {}
#ifndef DOXYGEN_GENERATING_OUTPUT
void applyTransformation(const Matrix4& transformation);
bool collides(const AbstractShape<3>* other) const;
#else
void applyTransformation(const MatrixType& transformation);
bool collides(const AbstractShape* other) const;
#endif
/** @brief Position */
inline Vector3 position() const { return _position; }
@ -64,10 +70,10 @@ class PHYSICS_EXPORT Plane: public AbstractShape {
}
/** @brief Collision with line */
bool operator%(const Line& other) const;
bool operator%(const Line3D& other) const;
/** @brief Collision with line segment */
bool operator%(const LineSegment& other) const;
bool operator%(const LineSegment3D& other) const;
protected:
inline Type type() const { return Type::Plane; }
@ -78,10 +84,10 @@ class PHYSICS_EXPORT Plane: public AbstractShape {
};
/** @collisionoperator{Line,Plane} */
inline bool operator%(const Line& a, const Plane& b) { return b % a; }
inline bool operator%(const Line3D& a, const Plane& b) { return b % a; }
/** @collisionoperator{LineSegment,Plane} */
inline bool operator%(const LineSegment& a, const Plane& b) { return b % a; }
inline bool operator%(const LineSegment3D& a, const Plane& b) { return b % a; }
}}

7
src/Physics/Point.cpp

@ -20,8 +20,11 @@
namespace Magnum { namespace Physics {
void Point::applyTransformation(const Matrix4& transformation) {
_transformedPosition = (transformation*Point3D(_position)).xyz();
template<size_t dimensions> void Point<dimensions>::applyTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation) {
_transformedPosition = (transformation*typename AbstractShape<dimensions>::PointType(_position)).vector();
}
template class Point<2>;
template class Point<3>;
}}

41
src/Physics/Point.h

@ -16,7 +16,7 @@
*/
/** @file
* @brief Class Magnum::Physics::Point
* @brief Class Magnum::Physics::Point, typedef Magnum::Physics::Point2D, Magnum::Physics::Point3D
*/
#include "Math/Vector3.h"
@ -24,34 +24,55 @@
namespace Magnum { namespace Physics {
/** @brief %Point */
class PHYSICS_EXPORT Point: public AbstractShape {
/**
@brief %Point
@see Point2D, Point3D
*/
template<size_t dimensions> class PHYSICS_EXPORT Point: public AbstractShape<dimensions> {
public:
/** @brief Constructor */
inline Point(const Vector3& position): _position(position), _transformedPosition(position) {}
inline Point(const typename AbstractShape<dimensions>::VectorType& position): _position(position), _transformedPosition(position) {}
void applyTransformation(const Matrix4& transformation);
#ifndef DOXYGEN_GENERATING_OUTPUT
void applyTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation);
#else
void applyTransformation(const MatrixType& transformation);
#endif
/** @brief Position */
inline Vector3 position() const { return _position; }
inline typename AbstractShape<dimensions>::VectorType position() const {
return _position;
}
/** @brief Set position */
inline void setPosition(const Vector3& position) {
inline void setPosition(const typename AbstractShape<dimensions>::VectorType& position) {
_position = position;
}
/** @brief Transformed position */
inline Vector3 transformedPosition() const {
inline typename AbstractShape<dimensions>::VectorType transformedPosition() const {
return _transformedPosition;
}
protected:
inline Type type() const { return Type::Point; }
inline typename AbstractShape<dimensions>::Type type() const { return AbstractShape<dimensions>::Type::Point; }
private:
Vector3 _position, _transformedPosition;
typename AbstractShape<dimensions>::VectorType _position, _transformedPosition;
};
#ifndef DOXYGEN_GENERATING_OUTPUT
extern template class PHYSICS_EXPORT Point<2>;
extern template class PHYSICS_EXPORT Point<3>;
#endif
/** @brief Two-dimensional point */
typedef Point<2> Point2D;
/** @brief Three-dimensional point */
typedef Point<3> Point3D;
}}
#endif

35
src/Physics/ShapeGroup.cpp

@ -17,47 +17,50 @@
namespace Magnum { namespace Physics {
ShapeGroup::ShapeGroup(ShapeGroup&& other): operation(other.operation), a(other.a), b(other.b) {
other.operation = AlwaysFalse;
template<size_t dimensions> ShapeGroup<dimensions>::ShapeGroup(ShapeGroup<dimensions>&& other): operation(other.operation), a(other.a), b(other.b) {
other.operation = Implementation::GroupOperation::AlwaysFalse;
other.a = nullptr;
other.b = nullptr;
}
ShapeGroup::~ShapeGroup() {
if(!(operation & RefA)) delete a;
if(!(operation & RefB)) delete b;
template<size_t dimensions> ShapeGroup<dimensions>::~ShapeGroup() {
if(!(operation & Implementation::GroupOperation::RefA)) delete a;
if(!(operation & Implementation::GroupOperation::RefB)) delete b;
}
ShapeGroup& ShapeGroup::operator=(ShapeGroup&& other) {
if(!(operation & RefA)) delete a;
if(!(operation & RefB)) delete b;
template<size_t dimensions> ShapeGroup<dimensions>& ShapeGroup<dimensions>::operator=(ShapeGroup<dimensions>&& other) {
if(!(operation & Implementation::GroupOperation::RefA)) delete a;
if(!(operation & Implementation::GroupOperation::RefB)) delete b;
operation = other.operation;
a = other.a;
b = other.b;
other.operation = AlwaysFalse;
other.operation = Implementation::GroupOperation::AlwaysFalse;
other.a = nullptr;
other.b = nullptr;
return *this;
}
void ShapeGroup::applyTransformation(const Matrix4& transformation) {
template<size_t dimensions> void ShapeGroup<dimensions>::applyTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation) {
if(a) a->applyTransformation(transformation);
if(b) b->applyTransformation(transformation);
}
bool ShapeGroup::collides(const AbstractShape* other) const {
switch(operation & ~RefAB) {
case And: return a->collides(other) && b->collides(other);
case Or: return a->collides(other) || b->collides(other);
case Not: return !a->collides(other);
case FirstObjectOnly: return a->collides(other);
template<size_t dimensions> bool ShapeGroup<dimensions>::collides(const AbstractShape<dimensions>* other) const {
switch(operation & ~Implementation::GroupOperation::RefAB) {
case Implementation::GroupOperation::And: return a->collides(other) && b->collides(other);
case Implementation::GroupOperation::Or: return a->collides(other) || b->collides(other);
case Implementation::GroupOperation::Not: return !a->collides(other);
case Implementation::GroupOperation::FirstObjectOnly: return a->collides(other);
default:
return false;
}
}
template class ShapeGroup<2>;
template class ShapeGroup<3>;
}}

154
src/Physics/ShapeGroup.h

@ -16,7 +16,7 @@
*/
/** @file
* @brief Class Magnum::Physics::ShapeGroup
* @brief Class Magnum::Physics::ShapeGroup, typedef Magnum::Physics::ShapeGroup2D, Magnum::Physics::ShapeGroup3D
*/
#include "AbstractShape.h"
@ -27,8 +27,25 @@
namespace Magnum { namespace Physics {
#ifndef DOXYGEN_GENERATING_OUTPUT
#define enableIfIsBaseType typename std::enable_if<std::is_base_of<AbstractShape, T>::value, ShapeGroup>::type
#define enableIfAreBaseType typename std::enable_if<std::is_base_of<AbstractShape, T>::value && std::is_base_of<AbstractShape, U>::value, ShapeGroup>::type
namespace Implementation {
enum GroupOperation {
RefA = 0x01,
RefB = 0x02,
RefAB = 0x03,
// Complement = 1 << 2,
// Union = 2 << 2,
// Intersection = 3 << 2,
// Difference = 4 << 2,
// Xor = 5 << 2,
And = 6 << 2,
Or = 7 << 2,
Not = 8 << 2,
FirstObjectOnly = 9 << 2,
AlwaysFalse = 10 << 2
};
}
#define enableIfIsBaseType typename std::enable_if<std::is_base_of<AbstractShape<T::Dimensions>, T>::value, ShapeGroup<T::Dimensions>>::type
#define enableIfAreBaseType typename std::enable_if<T::Dimensions == U::Dimensions && std::is_base_of<AbstractShape<T::Dimensions>, T>::value && std::is_base_of<AbstractShape<T::Dimensions>, U>::value, ShapeGroup<T::Dimensions>>::type
#endif
/**
@ -36,26 +53,27 @@ namespace Magnum { namespace Physics {
Result of logical operations on shapes.
See @ref collision-detection for brief introduction.
@see ShapeGroup2D, ShapeGroup3D
*/
class PHYSICS_EXPORT ShapeGroup: public AbstractShape {
template<size_t dimensions> class PHYSICS_EXPORT ShapeGroup: public AbstractShape<dimensions> {
#ifndef DOXYGEN_GENERATING_OUTPUT
// template<class T> friend constexpr enableIfIsBaseType operator~(const T& a);
// template<class T> friend constexpr enableIfIsBaseType operator~(T&& a);
// template<class T> friend constexpr enableIfIsBaseType operator~(T& a);
template<class T> friend constexpr enableIfIsBaseType operator!(const T& a);
template<class T> friend constexpr enableIfIsBaseType operator!(T&& a);
template<class T> friend constexpr enableIfIsBaseType operator!(T& a);
// template<class T> friend constexpr operator~(const T& a) -> enableIfIsBaseType;
// template<class T> friend constexpr operator~(T&& a) -> enableIfIsBaseType;
// template<class T> friend constexpr operator~(T& a) -> enableIfIsBaseType;
template<class T> friend constexpr auto operator!(const T& a) -> enableIfIsBaseType;
template<class T> friend constexpr auto operator!(T&& a) -> enableIfIsBaseType;
template<class T> friend constexpr auto operator!(T& a) -> enableIfIsBaseType;
#define friendOp(char) \
template<class T, class U> friend constexpr enableIfAreBaseType operator char(const T& a, const U& b); \
template<class T, class U> friend constexpr enableIfAreBaseType operator char(const T& a, U&& b); \
template<class T, class U> friend constexpr enableIfAreBaseType operator char(T&& a, const U& b); \
template<class T, class U> friend constexpr enableIfAreBaseType operator char(T&& a, U&& b); \
template<class T, class U> friend constexpr enableIfAreBaseType operator char(const T& a, std::reference_wrapper<U> b); \
template<class T, class U> friend constexpr enableIfAreBaseType operator char(T&& a, std::reference_wrapper<U> b); \
template<class T, class U> friend constexpr enableIfAreBaseType operator char(std::reference_wrapper<T> a, const U& b); \
template<class T, class U> friend constexpr enableIfAreBaseType operator char(std::reference_wrapper<T> a, U&& b); \
template<class T, class U> friend constexpr enableIfAreBaseType operator char(std::reference_wrapper<T> a, std::reference_wrapper<U> b);
template<class T, class U> friend constexpr auto operator char(const T& a, const U& b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(const T& a, U&& b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(T&& a, const U& b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(T&& a, U&& b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(const T& a, std::reference_wrapper<U> b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(T&& a, std::reference_wrapper<U> b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(std::reference_wrapper<T> a, const U& b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(std::reference_wrapper<T> a, U&& b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(std::reference_wrapper<T> a, std::reference_wrapper<U> b) -> enableIfAreBaseType;
// friendOp(|)
// friendOp(&)
// friendOp(-)
@ -68,26 +86,9 @@ class PHYSICS_EXPORT ShapeGroup: public AbstractShape {
ShapeGroup(const ShapeGroup& other) = delete;
ShapeGroup& operator=(const ShapeGroup& other) = delete;
private:
enum Operation {
RefA = 0x01,
RefB = 0x02,
RefAB = 0x03,
// Complement = 1 << 2,
// Union = 2 << 2,
// Intersection = 3 << 2,
// Difference = 4 << 2,
// Xor = 5 << 2,
And = 6 << 2,
Or = 7 << 2,
Not = 8 << 2,
FirstObjectOnly = 9 << 2,
AlwaysFalse = 10 << 2
};
public:
/** @brief Default constructor */
inline ShapeGroup(): operation(AlwaysFalse), a(nullptr), b(nullptr) {}
inline ShapeGroup(): operation(Implementation::GroupOperation::AlwaysFalse), a(nullptr), b(nullptr) {}
/** @brief Move constructor */
ShapeGroup(ShapeGroup&& other);
@ -98,21 +99,36 @@ class PHYSICS_EXPORT ShapeGroup: public AbstractShape {
/** @brief Move assignment */
ShapeGroup& operator=(ShapeGroup&& other);
void applyTransformation(const Matrix4& transformation);
#ifndef DOXYGEN_GENERATING_OUTPUT
void applyTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation);
#else
void applyTransformation(const MatrixType& transformation);
#endif
bool collides(const AbstractShape* other) const;
bool collides(const AbstractShape<dimensions>* other) const;
protected:
virtual Type type() const { return Type::ShapeGroup; }
virtual typename AbstractShape<dimensions>::Type type() const { return AbstractShape<dimensions>::Type::ShapeGroup; }
private:
inline ShapeGroup(int operation, AbstractShape* a, AbstractShape* b): operation(operation), a(a), b(b) {}
inline ShapeGroup(int operation, AbstractShape<dimensions>* a, AbstractShape<dimensions>* b): operation(operation), a(a), b(b) {}
int operation;
AbstractShape* a;
AbstractShape* b;
AbstractShape<dimensions>* a;
AbstractShape<dimensions>* b;
};
#ifndef DOXYGEN_GENERATING_OUTPUT
extern template class PHYSICS_EXPORT ShapeGroup<2>;
extern template class PHYSICS_EXPORT ShapeGroup<3>;
#endif
/** @brief Two-dimensional shape group */
typedef ShapeGroup<2> ShapeGroup2D;
/** @brief Three-dimensional shape group */
typedef ShapeGroup<3> ShapeGroup3D;
// /* @brief Complement of shape */
// template<class T> inline constexpr enableIfIsBaseType operator~(const T& a) {
// return ShapeGroup(ShapeGroup::Complement, new T(a), nullptr);
@ -129,15 +145,15 @@ class PHYSICS_EXPORT ShapeGroup: public AbstractShape {
/** @relates ShapeGroup
@brief Logical NOT of shape
*/
template<class T> inline constexpr enableIfIsBaseType operator!(const T& a) {
return ShapeGroup(ShapeGroup::Not, new T(a), nullptr);
template<class T> inline constexpr auto operator!(const T& a) -> enableIfIsBaseType {
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::Not, new T(a), nullptr);
}
#ifndef DOXYGEN_GENERATING_OUTPUT
template<class T> inline constexpr enableIfIsBaseType operator!(T&& a) {
return ShapeGroup(ShapeGroup::Not, new T(std::forward<T>(a)), nullptr);
template<class T> inline constexpr auto operator!(T&& a) -> enableIfIsBaseType {
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::Not, new T(std::forward<T>(a)), nullptr);
}
template<class T> inline constexpr enableIfIsBaseType operator!(T& a) {
return ShapeGroup(ShapeGroup::Not|ShapeGroup::RefA, &a.get(), nullptr);
template<class T> inline constexpr auto operator!(T& a) -> enableIfIsBaseType {
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::Not|Implementation::GroupOperation::RefA, &a.get(), nullptr);
}
#endif
@ -161,7 +177,7 @@ is used here, so this operation can be used for providing simplified shape
version, because collision with @p b is computed only if @p a collides.
See @ref collision-detection-shape-simplification for an example.
*/
template<class T, class U> inline constexpr ShapeGroup operator&&(T a, U b);
template<size_t dimensions, class T, class U> inline constexpr ShapeGroup<dimensions> operator&&(T a, U b);
/** @relates ShapeGroup
@brief Logical OR of two shapes
@ -170,35 +186,35 @@ template<class T, class U> inline constexpr ShapeGroup operator&&(T a, U b);
is used, so if collision with @p a is detected, collision with @p b is not
computed.
*/
template<class T, class U> inline constexpr ShapeGroup operator||(T a, U b);
template<size_t dimensions, class T, class U> inline constexpr ShapeGroup<dimensions> operator||(T a, U b);
#else
#define op(type, char) \
template<class T, class U> inline constexpr enableIfAreBaseType operator char(const T& a, const U& b) { \
return ShapeGroup(ShapeGroup::type, new T(a), new U(b)); \
template<class T, class U> inline constexpr auto operator char(const T& a, const U& b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type, new T(a), new U(b)); \
} \
template<class T, class U> inline constexpr enableIfAreBaseType operator char(const T& a, U&& b) { \
return ShapeGroup(ShapeGroup::type, new T(a), new U(std::forward<U>(b))); \
template<class T, class U> inline constexpr auto operator char(const T& a, U&& b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type, new T(a), new U(std::forward<U>(b))); \
} \
template<class T, class U> inline constexpr enableIfAreBaseType operator char(T&& a, const U& b) { \
return ShapeGroup(ShapeGroup::type, new T(std::forward<T>(a)), new U(b)); \
template<class T, class U> inline constexpr auto operator char(T&& a, const U& b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type, new T(std::forward<T>(a)), new U(b)); \
} \
template<class T, class U> inline constexpr enableIfAreBaseType operator char(T&& a, U&& b) { \
return ShapeGroup(ShapeGroup::type, new T(std::forward<T>(a)), new U(std::forward<U>(b))); \
template<class T, class U> inline constexpr auto operator char(T&& a, U&& b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type, new T(std::forward<T>(a)), new U(std::forward<U>(b))); \
} \
template<class T, class U> inline constexpr enableIfAreBaseType operator char(const T& a, std::reference_wrapper<U> b) { \
return ShapeGroup(ShapeGroup::type|ShapeGroup::RefB, new T(a), &b.get()); \
template<class T, class U> inline constexpr auto operator char(const T& a, std::reference_wrapper<U> b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type|Implementation::GroupOperation::RefB, new T(a), &b.get()); \
} \
template<class T, class U> inline constexpr enableIfAreBaseType operator char(T&& a, std::reference_wrapper<U> b) { \
return ShapeGroup(ShapeGroup::type|ShapeGroup::RefB, new T(std::forward<T>(a)), &b.get()); \
template<class T, class U> inline constexpr auto operator char(T&& a, std::reference_wrapper<U> b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type|Implementation::GroupOperation::RefB, new T(std::forward<T>(a)), &b.get()); \
} \
template<class T, class U> inline constexpr enableIfAreBaseType operator char(std::reference_wrapper<T> a, const U& b) { \
return ShapeGroup(ShapeGroup::type|ShapeGroup::RefA, &a.get(), new U(b)); \
template<class T, class U> inline constexpr auto operator char(std::reference_wrapper<T> a, const U& b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type|Implementation::GroupOperation::RefA, &a.get(), new U(b)); \
} \
template<class T, class U> inline constexpr enableIfAreBaseType operator char(std::reference_wrapper<T> a, U&& b) { \
return ShapeGroup(ShapeGroup::type|ShapeGroup::RefA, &a.get(), new U(std::forward<U>(b))); \
template<class T, class U> inline constexpr auto operator char(std::reference_wrapper<T> a, U&& b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type|Implementation::GroupOperation::RefA, &a.get(), new U(std::forward<U>(b))); \
} \
template<class T, class U> inline constexpr enableIfAreBaseType operator char(std::reference_wrapper<T> a, std::reference_wrapper<U> b) { \
return ShapeGroup(ShapeGroup::type|ShapeGroup::RefAB, &a.get(), &b.get()); \
template<class T, class U> inline constexpr auto operator char(std::reference_wrapper<T> a, std::reference_wrapper<U> b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type|Implementation::GroupOperation::RefAB, &a.get(), &b.get()); \
}
// op(Union, |)
// op(Intersection, &)

51
src/Physics/Sphere.cpp

@ -27,43 +27,58 @@ using namespace Magnum::Math::Geometry;
namespace Magnum { namespace Physics {
void Sphere::applyTransformation(const Matrix4& transformation) {
_transformedPosition = (transformation*Point3D(_position)).xyz();
float scaling = (transformation.rotationScaling()*Vector3(1/Math::Constants<float>::sqrt3())).length();
namespace {
template<size_t dimensions> static typename AbstractShape<dimensions>::VectorType unitVector();
template<> inline Vector2 unitVector<2>() {
return Vector2(1/Math::Constants<float>::sqrt2());
}
template<> inline Vector3 unitVector<3>() {
return Vector3(1/Math::Constants<float>::sqrt3());
}
}
template<size_t dimensions> void Sphere<dimensions>::applyTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation) {
_transformedPosition = (transformation*typename AbstractShape<dimensions>::PointType(_position)).vector();
float scaling = (transformation.rotationScaling()*unitVector<dimensions>()).length();
_transformedRadius = scaling*_radius;
}
bool Sphere::collides(const AbstractShape* other) const {
if(other->type() == Type::Point)
return *this % *static_cast<const Point*>(other);
if(other->type() == Type::Line)
return *this % *static_cast<const Line*>(other);
if(other->type() == Type::LineSegment)
return *this % *static_cast<const LineSegment*>(other);
if(other->type() == Type::Sphere)
return *this % *static_cast<const Sphere*>(other);
return AbstractShape::collides(other);
template<size_t dimensions> bool Sphere<dimensions>::collides(const AbstractShape<dimensions>* other) const {
if(other->type() == AbstractShape<dimensions>::Type::Point)
return *this % *static_cast<const Point<dimensions>*>(other);
if(other->type() == AbstractShape<dimensions>::Type::Line)
return *this % *static_cast<const Line<dimensions>*>(other);
if(other->type() == AbstractShape<dimensions>::Type::LineSegment)
return *this % *static_cast<const LineSegment<dimensions>*>(other);
if(other->type() == AbstractShape<dimensions>::Type::Sphere)
return *this % *static_cast<const Sphere<dimensions>*>(other);
return AbstractShape<dimensions>::collides(other);
}
bool Sphere::operator%(const Point& other) const {
template<size_t dimensions> bool Sphere<dimensions>::operator%(const Point<dimensions>& other) const {
return (other.transformedPosition()-transformedPosition()).dot() <
Math::pow<2>(transformedRadius());
}
bool Sphere::operator%(const Line& other) const {
template<size_t dimensions> bool Sphere<dimensions>::operator%(const Line<dimensions>& other) const {
return Distance::linePointSquared(other.transformedA(), other.transformedB(), transformedPosition()) <
Math::pow<2>(transformedRadius());
}
bool Sphere::operator%(const LineSegment& other) const {
template<size_t dimensions> bool Sphere<dimensions>::operator%(const LineSegment<dimensions>& other) const {
return Distance::lineSegmentPointSquared(other.transformedA(), other.transformedB(), transformedPosition()) <
Math::pow<2>(transformedRadius());
}
bool Sphere::operator%(const Sphere& other) const {
template<size_t dimensions> bool Sphere<dimensions>::operator%(const Sphere<dimensions>& other) const {
return (other.transformedPosition()-transformedPosition()).dot() <
Math::pow<2>(transformedRadius()+other.transformedRadius());
}
template class Sphere<2>;
template class Sphere<3>;
}}

63
src/Physics/Sphere.h

@ -16,7 +16,7 @@
*/
/** @file
* @brief Class Magnum::Physics::Sphere
* @brief Class Magnum::Physics::Sphere, typedef Magnum::Physics::Sphere2D, Magnum::Physics::Sphere3D
*/
#include "Math/Vector3.h"
@ -24,30 +24,39 @@
namespace Magnum { namespace Physics {
class Line;
class LineSegment;
class Point;
template<size_t> class Line;
template<size_t> class LineSegment;
template<size_t> class Point;
/**
@brief %Sphere defined by position and radius
Unlike other elements the sphere doesn't support asymmetric scaling. When
applying transformation, the scale factor is averaged from all axes.
@see Sphere2D, Sphere3D
*/
class PHYSICS_EXPORT Sphere: public AbstractShape {
template<size_t dimensions> class PHYSICS_EXPORT Sphere: public AbstractShape<dimensions> {
public:
/** @brief Constructor */
inline Sphere(const Vector3& position, float radius): _position(position), _transformedPosition(position), _radius(radius), _transformedRadius(radius) {}
inline Sphere(const typename AbstractShape<dimensions>::VectorType& position, float radius): _position(position), _transformedPosition(position), _radius(radius), _transformedRadius(radius) {}
void applyTransformation(const Matrix4& transformation);
#ifndef DOXYGEN_GENERATING_OUTPUT
void applyTransformation(const typename AbstractShape<dimensions>::MatrixType& transformation);
#else
void applyTransformation(const MatrixType& transformation);
#endif
bool collides(const AbstractShape* other) const;
bool collides(const AbstractShape<dimensions>* other) const;
/** @brief Position */
inline Vector3 position() const { return _position; }
inline typename AbstractShape<dimensions>::VectorType position() const {
return _position;
}
/** @brief Set position */
inline void setPosition(const Vector3& position) { _position = position; }
inline void setPosition(const typename AbstractShape<dimensions>::VectorType& position) {
_position = position;
}
/** @brief Radius */
inline float radius() const { return _radius; }
@ -56,7 +65,7 @@ class PHYSICS_EXPORT Sphere: public AbstractShape {
inline void setRadius(float radius) { _radius = radius; }
/** @brief Transformed position */
inline Vector3 transformedPosition() const {
inline typename AbstractShape<dimensions>::VectorType transformedPosition() const {
return _transformedPosition;
}
@ -66,33 +75,47 @@ class PHYSICS_EXPORT Sphere: public AbstractShape {
}
/** @brief Collision with point */
bool operator%(const Point& other) const;
bool operator%(const Point<dimensions>& other) const;
/** @brief Collision with line */
bool operator%(const Line& other) const;
bool operator%(const Line<dimensions>& other) const;
/** @brief Collision with line segment */
bool operator%(const LineSegment& other) const;
bool operator%(const LineSegment<dimensions>& other) const;
/** @brief Collision with sphere */
bool operator%(const Sphere& other) const;
bool operator%(const Sphere<dimensions>& other) const;
protected:
inline Type type() const { return Type::Sphere; }
inline typename AbstractShape<dimensions>::Type type() const {
return AbstractShape<dimensions>::Type::Sphere;
}
private:
Vector3 _position, _transformedPosition;
typename AbstractShape<dimensions>::VectorType _position,
_transformedPosition;
float _radius, _transformedRadius;
};
#ifndef DOXYGEN_GENERATING_OUTPUT
extern template class PHYSICS_EXPORT Sphere<2>;
extern template class PHYSICS_EXPORT Sphere<3>;
#endif
/** @brief Two-dimensional sphere */
typedef Sphere<2> Sphere2D;
/** @brief Three-dimensional sphere */
typedef Sphere<3> Sphere3D;
/** @collisionoperator{Point,Sphere} */
inline bool operator%(const Point& a, const Sphere& b) { return b % a; }
template<size_t dimensions> inline bool operator%(const Point<dimensions>& a, const Sphere<dimensions>& b) { return b % a; }
/** @collisionoperator{Line,Sphere} */
inline bool operator%(const Line& a, const Sphere& b) { return b % a; }
template<size_t dimensions> inline bool operator%(const Line<dimensions>& a, const Sphere<dimensions>& b) { return b % a; }
/** @collisionoperator{LineSegment,Sphere} */
inline bool operator%(const LineSegment& a, const Sphere& b) { return b % a; }
template<size_t dimensions> inline bool operator%(const LineSegment<dimensions>& a, const Sphere<dimensions>& b) { return b % a; }
}}

2
src/Physics/Test/AxisAlignedBoxTest.cpp

@ -28,7 +28,7 @@ AxisAlignedBoxTest::AxisAlignedBoxTest() {
}
void AxisAlignedBoxTest::applyTransformation() {
Physics::AxisAlignedBox box({-1.0f, -2.0f, -3.0f}, {1.0f, 2.0f, 3.0f});
Physics::AxisAlignedBox3D box({-1.0f, -2.0f, -3.0f}, {1.0f, 2.0f, 3.0f});
box.applyTransformation(Matrix4::scaling({2.0f, -1.0f, 1.5f}));
CORRADE_COMPARE(box.transformedPosition(), Vector3(-2.0f, 2.0f, -4.5f));

2
src/Physics/Test/BoxTest.cpp

@ -27,7 +27,7 @@ BoxTest::BoxTest() {
}
void BoxTest::applyTransformation() {
Physics::Box box(Matrix4::translation({1.0f, 2.0f, -3.0f}));
Physics::Box3D box(Matrix4::translation({1.0f, 2.0f, -3.0f}));
box.applyTransformation(Matrix4::scaling({2.0f, -1.0f, 1.5f}));
CORRADE_COMPARE(box.transformedTransformation(), Matrix4::scaling({2.0f, -1.0f, 1.5f})*Matrix4::translation({1.0f, 2.0f, -3.0f}));

18
src/Physics/Test/CapsuleTest.cpp

@ -30,7 +30,7 @@ CapsuleTest::CapsuleTest() {
}
void CapsuleTest::applyTransformation() {
Physics::Capsule capsule({1.0f, 2.0f, 3.0f}, {-1.0f, -2.0f, -3.0f}, 7.0f);
Physics::Capsule3D capsule({1.0f, 2.0f, 3.0f}, {-1.0f, -2.0f, -3.0f}, 7.0f);
capsule.applyTransformation(Matrix4::rotation(deg(90.0f), Vector3::zAxis()));
CORRADE_COMPARE(capsule.transformedA(), Vector3(-2.0f, 1.0f, 3.0f));
@ -43,10 +43,10 @@ void CapsuleTest::applyTransformation() {
}
void CapsuleTest::collisionPoint() {
Physics::Capsule capsule({-1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, 2.0f);
Physics::Point point({2.0f, 0.0f, 0.0f});
Physics::Point point1({2.9f, 1.0f, 0.0f});
Physics::Point point2({1.0f, 3.1f, 0.0f});
Physics::Capsule3D capsule({-1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, 2.0f);
Physics::Point3D point({2.0f, 0.0f, 0.0f});
Physics::Point3D point1({2.9f, 1.0f, 0.0f});
Physics::Point3D point2({1.0f, 3.1f, 0.0f});
randomTransformation(capsule);
randomTransformation(point);
@ -59,10 +59,10 @@ void CapsuleTest::collisionPoint() {
}
void CapsuleTest::collisionSphere() {
Physics::Capsule capsule({-1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, 2.0f);
Physics::Sphere sphere({3.0f, 0.0f, 0.0f}, 0.9f);
Physics::Sphere sphere1({3.5f, 1.0f, 0.0f}, 0.6f);
Physics::Sphere sphere2({1.0f, 4.1f, 0.0f}, 1.0f);
Physics::Capsule3D capsule({-1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, 2.0f);
Physics::Sphere3D sphere({3.0f, 0.0f, 0.0f}, 0.9f);
Physics::Sphere3D sphere1({3.5f, 1.0f, 0.0f}, 0.6f);
Physics::Sphere3D sphere2({1.0f, 4.1f, 0.0f}, 1.0f);
randomTransformation(capsule);
randomTransformation(sphere);

2
src/Physics/Test/LineTest.cpp

@ -28,7 +28,7 @@ LineTest::LineTest() {
}
void LineTest::applyTransformation() {
Physics::Line line({1.0f, 2.0f, 3.0f}, {-1.0f, -2.0f, -3.0f});
Physics::Line3D line({1.0f, 2.0f, 3.0f}, {-1.0f, -2.0f, -3.0f});
line.applyTransformation(Matrix4::rotation(deg(90.0f), Vector3::zAxis()));
CORRADE_COMPARE(line.transformedA(), Vector3(-2.0f, 1.0f, 3.0f));
CORRADE_COMPARE(line.transformedB(), Vector3(2.0f, -1.0f, -3.0f));

12
src/Physics/Test/PlaneTest.cpp

@ -45,9 +45,9 @@ void PlaneTest::applyTransformation() {
void PlaneTest::collisionLine() {
Physics::Plane plane(Vector3(), Vector3::yAxis());
Physics::Line line({0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f});
Physics::Line line2({0.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f});
Physics::Line line3({0.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f});
Physics::Line3D line({0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f});
Physics::Line3D line2({0.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f});
Physics::Line3D line3({0.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f});
randomTransformation(plane);
randomTransformation(line);
@ -61,9 +61,9 @@ void PlaneTest::collisionLine() {
void PlaneTest::collisionLineSegment() {
Physics::Plane plane(Vector3(), Vector3::yAxis());
Physics::LineSegment line({0.0f, -0.1f, 0.0f}, {0.0f, 7.0f, 0.0f});
Physics::LineSegment line2({0.0f, 0.1f, 0.0f}, {0.0f, 7.0f, 0.0f});
Physics::LineSegment line3({0.0f, -7.0f, 0.0f}, {0.0f, -0.1f, 0.0f});
Physics::LineSegment3D line({0.0f, -0.1f, 0.0f}, {0.0f, 7.0f, 0.0f});
Physics::LineSegment3D line2({0.0f, 0.1f, 0.0f}, {0.0f, 7.0f, 0.0f});
Physics::LineSegment3D line3({0.0f, -7.0f, 0.0f}, {0.0f, -0.1f, 0.0f});
randomTransformation(plane);
randomTransformation(line);

2
src/Physics/Test/PointTest.cpp

@ -27,7 +27,7 @@ PointTest::PointTest() {
}
void PointTest::applyTransformation() {
Physics::Point point({1.0f, 2.0f, 3.0f});
Physics::Point3D point({1.0f, 2.0f, 3.0f});
point.applyTransformation(Matrix4::translation({5.0f, 6.0f, 7.0f}));
CORRADE_COMPARE(point.transformedPosition(), Vector3(6.0f, 8.0f, 10.0f));
}

12
src/Physics/Test/ShapeGroupTest.cpp

@ -32,10 +32,10 @@ ShapeGroupTest::ShapeGroupTest() {
}
void ShapeGroupTest::copy() {
ShapeGroup group;
ShapeGroup3D group;
{
Physics::Point point({1.0f, 2.0f, 3.0f});
Physics::LineSegment segment({2.0f, 1.0f, 30.0f}, {1.0f, -20.0f, 3.0f});
Physics::Point3D point({1.0f, 2.0f, 3.0f});
Physics::LineSegment3D segment({2.0f, 1.0f, 30.0f}, {1.0f, -20.0f, 3.0f});
group = !(point || segment);
}
@ -47,10 +47,10 @@ void ShapeGroupTest::copy() {
}
void ShapeGroupTest::reference() {
Physics::Point point({1.0f, 2.0f, 3.0f});
Physics::LineSegment segment({2.0f, 1.0f, 30.0f}, {1.0f, -20.0f, 3.0f});
Physics::Point3D point({1.0f, 2.0f, 3.0f});
Physics::LineSegment3D segment({2.0f, 1.0f, 30.0f}, {1.0f, -20.0f, 3.0f});
ShapeGroup group = !(ref(point) || ref(segment));
ShapeGroup3D group = !(ref(point) || ref(segment));
group.applyTransformation(Matrix4::translation(Vector3(1.0f)));

26
src/Physics/Test/SphereTest.cpp

@ -33,7 +33,7 @@ SphereTest::SphereTest() {
}
void SphereTest::applyTransformation() {
Physics::Sphere sphere({1.0f, 2.0f, 3.0f}, 7.0f);
Physics::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 7.0f);
sphere.applyTransformation(Matrix4::rotation(deg(90.0f), Vector3::yAxis()));
CORRADE_COMPARE(sphere.transformedPosition(), Vector3(3.0f, 2.0f, -1.0f));
@ -50,9 +50,9 @@ void SphereTest::applyTransformation() {
}
void SphereTest::collisionPoint() {
Physics::Sphere sphere({1.0f, 2.0f, 3.0f}, 2.0f);
Physics::Point point({1.0f, 3.0f, 3.0f});
Physics::Point point2({1.0f, 3.0f, 1.0f});
Physics::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 2.0f);
Physics::Point3D point({1.0f, 3.0f, 3.0f});
Physics::Point3D point2({1.0f, 3.0f, 1.0f});
randomTransformation(sphere);
randomTransformation(point);
@ -63,9 +63,9 @@ void SphereTest::collisionPoint() {
}
void SphereTest::collisionLine() {
Physics::Sphere sphere({1.0f, 2.0f, 3.0f}, 2.0f);
Physics::Line line({1.0f, 1.5f, 3.5f}, {1.0f, 2.5f, 2.5f});
Physics::Line line2({1.0f, 2.0f, 5.1f}, {1.0f, 3.0f, 5.1f});
Physics::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 2.0f);
Physics::Line3D line({1.0f, 1.5f, 3.5f}, {1.0f, 2.5f, 2.5f});
Physics::Line3D line2({1.0f, 2.0f, 5.1f}, {1.0f, 3.0f, 5.1f});
randomTransformation(sphere);
randomTransformation(line);
@ -76,9 +76,9 @@ void SphereTest::collisionLine() {
}
void SphereTest::collisionLineSegment() {
Physics::Sphere sphere({1.0f, 2.0f, 3.0f}, 2.0f);
Physics::LineSegment line({1.0f, 2.0f, 4.9f}, {1.0f, 2.0f, 7.0f});
Physics::LineSegment line2({1.0f, 2.0f, 5.1f}, {1.0f, 2.0f, 7.0f});
Physics::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 2.0f);
Physics::LineSegment3D line({1.0f, 2.0f, 4.9f}, {1.0f, 2.0f, 7.0f});
Physics::LineSegment3D line2({1.0f, 2.0f, 5.1f}, {1.0f, 2.0f, 7.0f});
randomTransformation(sphere);
randomTransformation(line);
@ -89,9 +89,9 @@ void SphereTest::collisionLineSegment() {
}
void SphereTest::collisionSphere() {
Physics::Sphere sphere({1.0f, 2.0f, 3.0f}, 2.0f);
Physics::Sphere sphere1({1.0f, 3.0f, 5.0f}, 1.0f);
Physics::Sphere sphere2({1.0f, 3.0f, 0.0f}, 1.0f);
Physics::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 2.0f);
Physics::Sphere3D sphere1({1.0f, 3.0f, 5.0f}, 1.0f);
Physics::Sphere3D sphere2({1.0f, 3.0f, 0.0f}, 1.0f);
randomTransformation(sphere);
randomTransformation(sphere1);

Loading…
Cancel
Save