From 4b7187dd367da567a74eac1108686e85631e2b56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 4 Sep 2013 12:45:22 +0200 Subject: [PATCH] Shapes: Collision object, providing more detailed collision information. --- Doxyfile | 1 + doc/coding-style.dox | 8 +- src/Shapes/CMakeLists.txt | 1 + src/Shapes/Collision.h | 131 ++++++++++++++++++++++++++++++ src/Shapes/Shapes.h | 4 + src/Shapes/Test/CMakeLists.txt | 1 + src/Shapes/Test/CollisionTest.cpp | 61 ++++++++++++++ 7 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 src/Shapes/Collision.h create mode 100644 src/Shapes/Test/CollisionTest.cpp diff --git a/Doxyfile b/Doxyfile index 18a8b85e8..90af7401d 100644 --- a/Doxyfile +++ b/Doxyfile @@ -204,6 +204,7 @@ ALIASES = \ "configurationvalueref{1}=@see @ref configurationvalues \"Corrade::Utility::ConfigurationValue<\1>\"" \ "configurationvalue{1}=@brief %Configuration value parser and writer @xrefitem configurationvalues \"Configuration value parser and writer\" \"Configuration value parsers and writers for custom types\" Allows parsing and writing \1 from and to Corrade::Utility::Configuration." \ "collisionoccurenceoperator{2}=@relates \1\n@brief %Collision occurence of %\1 and %\2\n@see \2::operator%(const \1&) const" \ + "collisionoperator{2}=@relates \1\n@brief %Collision of %\1 and %\2\n@see \2::operator/(const \1&) const" \ "todoc=@xrefitem todoc \"Documentation todo\" \"Documentation-related todo list\"" \ "fn_gl{1}=gl\1()" \ "fn_gl_extension{3}=gl\1\2()" \ diff --git a/doc/coding-style.dox b/doc/coding-style.dox index d520b14cf..b20bab75d 100644 --- a/doc/coding-style.dox +++ b/doc/coding-style.dox @@ -102,11 +102,15 @@ Additionally to @c \@todoc, @c \@debugoperator @c \@configurationvalue and @subsubsection documentation-commands-collisionoperator Shape collision operators -Out-of-class operators for collision occurence in Shapes namespace should be -marked with @c \@collisionoccurenceoperator, e.g.: +Out-of-class operators for collision and collision occurence in Shapes +namespace should be marked with @c \@collisionoperator and @c \@collisionoccurenceoperator, +e.g.: @code // @collisionoccurenceoperator{Point,Sphere} inline bool operator%(const Point& a, const Sphere& b) { return b % a; } + +// @collisionoperator{Point,Sphere} +inline Collision operator/(const Point& a, const Sphere& b) { return (b/a).reverted(); } @endcode They will appear as related functions within documentation of class for which the operator is implemented (not of class in which the operator is diff --git a/src/Shapes/CMakeLists.txt b/src/Shapes/CMakeLists.txt index de98a9474..72f85a6ea 100644 --- a/src/Shapes/CMakeLists.txt +++ b/src/Shapes/CMakeLists.txt @@ -46,6 +46,7 @@ set(MagnumShapes_HEADERS Box.h Capsule.h Cylinder.h + Collision.h Composition.h Line.h LineSegment.h diff --git a/src/Shapes/Collision.h b/src/Shapes/Collision.h new file mode 100644 index 000000000..c1f3731ce --- /dev/null +++ b/src/Shapes/Collision.h @@ -0,0 +1,131 @@ +#ifndef Magnum_Shapes_Collision_h +#define Magnum_Shapes_Collision_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::Shapes::Collision + */ + +#include "Math/Vector2.h" +#include "Math/Vector3.h" +#include "DimensionTraits.h" + +namespace Magnum { namespace Shapes { + +/** +@brief %Collision data + +Contains information about collision between objects A and B, described by +contact position, separation normal and separation distance. + +If the collision occured, contact position is on object B surface, separation +normal is *normalized* vector in which direction should object A be moved to +separate the bodies, separation distance is positive and describes minimal +movement of object A in direction of separation normal after which the contact +position will no longer be colliding with object A. + +If the collision not occured, contact position and separation normal is +undefined (i.e., *not* normalized) and separation distance is negative or zero. +@see @ref Collision2D, @ref Collision3D +*/ +template class Collision { + public: + /** + * @brief Default constructor + * + * Sets position, normal and separation distance to zero, as if no + * collision happened. + */ + /*implicit*/ Collision(): _separationDistance(0.0f) {} + + /** + * @brief Constructor + * + * If separation distance is positive, the separation normal is + * expected to be normalized. + */ + explicit Collision(typename DimensionTraits::VectorType position, typename DimensionTraits::VectorType separationNormal, Float separationDistance) noexcept: _position(position), _separationNormal(separationNormal), _separationDistance(separationDistance) { + CORRADE_ASSERT(_separationDistance < Math::TypeTraits::epsilon() || separationNormal.isNormalized(), "Shapes::Collision::Collision: separation normal is not normalized", ); + } + + /** + * @brief Whether the collision happened + * + * Negative or zero separation distance means that no collision + * happened. + * @see @ref separationDistance() + */ + operator bool() const { return _separationDistance > 0.0f; } + + /** @brief %Collision position */ + typename DimensionTraits::VectorType position() const { + return _position; + } + + /** + * @brief Separation normal + * + * @see @ref separationDistance(), @ref flipped() + */ + typename DimensionTraits::VectorType separationNormal() const { + return _separationNormal; + } + + /** + * @brief Separation distance + * + * @see @ref separationNormal(), operator bool() + */ + Float separationDistance() const { + return _separationDistance; + } + + /** + * @brief Flipped collision + * + * Returns new collision object as if the collision occured between + * flipped pair of objects, i.e. with flipped separation normal and + * contact position on surface of object A. + * @see @ref position(), @ref separationNormal() + */ + Collision flipped() const { + return Collision(_position - _separationDistance*_separationNormal, -_separationNormal, _separationDistance); + } + + private: + typename DimensionTraits::VectorType _position; + typename DimensionTraits::VectorType _separationNormal; + Float _separationDistance; +}; + +/** @brief Two-dimensional collision data */ +typedef Collision<2> Collision2D; + +/** @brief Three-dimensional collision data */ +typedef Collision<3> Collision3D; + +}} + +#endif diff --git a/src/Shapes/Shapes.h b/src/Shapes/Shapes.h index dc292db2e..2bda9882c 100644 --- a/src/Shapes/Shapes.h +++ b/src/Shapes/Shapes.h @@ -48,6 +48,10 @@ template class Capsule; typedef Capsule<2> Capsule2D; typedef Capsule<3> Capsule3D; +template class Collision; +typedef Collision<2> Collision2D; +typedef Collision<3> Collision3D; + template class Composition; typedef Composition<2> Composition2D; typedef Composition<3> Composition3D; diff --git a/src/Shapes/Test/CMakeLists.txt b/src/Shapes/Test/CMakeLists.txt index b08d2ec0a..83cf11eaa 100644 --- a/src/Shapes/Test/CMakeLists.txt +++ b/src/Shapes/Test/CMakeLists.txt @@ -26,6 +26,7 @@ corrade_add_test(ShapesShapeImplementationTest ShapeImplementationTest.cpp LIBRA corrade_add_test(ShapesAxisAlignedBoxTest AxisAlignedBoxTest.cpp LIBRARIES MagnumShapes) corrade_add_test(ShapesBoxTest BoxTest.cpp LIBRARIES MagnumShapes) corrade_add_test(ShapesCapsuleTest CapsuleTest.cpp LIBRARIES MagnumShapes) +corrade_add_test(ShapesCollisionTest CollisionTest.cpp LIBRARIES MagnumShapes) corrade_add_test(ShapesCylinderTest CylinderTest.cpp LIBRARIES MagnumShapes) corrade_add_test(ShapesLineTest LineTest.cpp LIBRARIES MagnumShapes) corrade_add_test(ShapesPlaneTest PlaneTest.cpp LIBRARIES MagnumShapes) diff --git a/src/Shapes/Test/CollisionTest.cpp b/src/Shapes/Test/CollisionTest.cpp new file mode 100644 index 000000000..545d13f35 --- /dev/null +++ b/src/Shapes/Test/CollisionTest.cpp @@ -0,0 +1,61 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include "Shapes/Collision.h" +#include "Magnum.h" + +namespace Magnum { namespace Shapes { namespace Test { + +class CollisionTest: public TestSuite::Tester { + public: + explicit CollisionTest(); + + void boolConversion(); + void flipped(); +}; + +CollisionTest::CollisionTest() { + addTests({&CollisionTest::boolConversion, + &CollisionTest::flipped}); +} + +void CollisionTest::boolConversion() { + CORRADE_VERIFY(!Collision3D()); + CORRADE_VERIFY(!Collision3D({}, {2.0f, 0.0f, 0.0f}, 0.0f)); + CORRADE_VERIFY(!Collision3D({}, {0.0f, 0.0f, 2.0f}, -0.1f)); + CORRADE_VERIFY(Collision3D({}, {0.0f, 1.0f, 0.0f}, 0.1f)); +} + +void CollisionTest::flipped() { + const auto flipped = Collision3D({-1.0f, 0.5f, 3.0f}, {1.0f, 0.0f, 0.0f}, 0.5f).flipped(); + CORRADE_COMPARE(flipped.position(), Vector3(-1.5f, 0.5f, 3.0f)); + CORRADE_COMPARE(flipped.separationNormal(), Vector3(-1.0f, 0.0f, 0.0f)); + CORRADE_COMPARE(flipped.separationDistance(), 0.5f); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Shapes::Test::CollisionTest)