Browse Source

Shapes: expect only uniform scaling for some primitives.

Capsule, Sphere and Plane would behave weirdly if non-uniform scale
would be applied to them. This restriction allows to use more performant
code (i.e. less normalization and matrix multiplication). Updated the
tests to verify that uniform scaling is handled well, for Capsule and
Sphere there is now also no need to test both 2D and 3D transformation.
pull/23/head
Vladimír Vondruš 13 years ago
parent
commit
55afd472fe
  1. 15
      src/Shapes/Capsule.cpp
  2. 6
      src/Shapes/Capsule.h
  3. 5
      src/Shapes/Plane.cpp
  4. 4
      src/Shapes/Plane.h
  5. 15
      src/Shapes/Sphere.cpp
  6. 6
      src/Shapes/Sphere.h
  7. 33
      src/Shapes/Test/CapsuleTest.cpp
  8. 10
      src/Shapes/Test/PlaneTest.cpp
  9. 40
      src/Shapes/Test/SphereTest.cpp

15
src/Shapes/Capsule.cpp

@ -36,21 +36,8 @@ using namespace Magnum::Math::Geometry;
namespace Magnum { namespace Shapes { namespace Magnum { namespace Shapes {
namespace {
template<UnsignedInt dimensions> static typename DimensionTraits<dimensions, Float>::VectorType unitVector();
template<> inline Vector2 unitVector<2>() {
return Vector2(1/Constants::sqrt2());
}
template<> inline Vector3 unitVector<3>() {
return Vector3(1/Constants::sqrt3());
}
}
template<UnsignedInt dimensions> Capsule<dimensions> Capsule<dimensions>::transformed(const typename DimensionTraits<dimensions, Float>::MatrixType& matrix) const { template<UnsignedInt dimensions> Capsule<dimensions> Capsule<dimensions>::transformed(const typename DimensionTraits<dimensions, Float>::MatrixType& matrix) const {
return Capsule<dimensions>(matrix.transformPoint(_a), matrix.transformPoint(_b), return Capsule<dimensions>(matrix.transformPoint(_a), matrix.transformPoint(_b), matrix.uniformScaling()*_radius);
(matrix.rotationScaling()*unitVector<dimensions>()).length()*_radius);
} }
template<UnsignedInt dimensions> bool Capsule<dimensions>::operator%(const Point<dimensions>& other) const { template<UnsignedInt dimensions> bool Capsule<dimensions>::operator%(const Point<dimensions>& other) const {

6
src/Shapes/Capsule.h

@ -38,11 +38,9 @@ namespace Magnum { namespace Shapes {
/** /**
@brief %Capsule defined by cylinder start and end point and radius @brief %Capsule defined by cylinder start and end point and radius
Unlike other elements the capsule doesn't support asymmetric scaling. When Unlike other elements the capsule expects uniform scaling. See @ref shapes for
applying transformation, the scale factor is averaged from all axes. See brief introduction.
@ref shapes for brief introduction.
@see Capsule2D, Capsule3D @see Capsule2D, Capsule3D
@todo Assert for asymmetric scaling to avoid costly sqrt?
*/ */
template<UnsignedInt dimensions> class MAGNUM_SHAPES_EXPORT Capsule { template<UnsignedInt dimensions> class MAGNUM_SHAPES_EXPORT Capsule {
public: public:

5
src/Shapes/Plane.cpp

@ -35,8 +35,11 @@ using namespace Magnum::Math::Geometry;
namespace Magnum { namespace Shapes { namespace Magnum { namespace Shapes {
Plane Plane::transformed(const Matrix4& matrix) const { Plane Plane::transformed(const Matrix4& matrix) const {
/* Using matrix.rotation() would result in two more normalizations (slow),
using .normalized() instead of matrix.uniformScaling() would not check
uniform scaling */
return Plane(matrix.transformPoint(_position), return Plane(matrix.transformPoint(_position),
matrix.rotation()*_normal); matrix.rotationScaling()*_normal/matrix.uniformScaling());
} }
bool Plane::operator%(const Line3D& other) const { bool Plane::operator%(const Line3D& other) const {

4
src/Shapes/Plane.h

@ -38,8 +38,8 @@ namespace Magnum { namespace Shapes {
/** /**
@brief Infinite plane, defined by position and normal (3D only) @brief Infinite plane, defined by position and normal (3D only)
See @ref shapes for brief introduction. Unlike other elements the plane expects uniform scaling. See @ref shapes for
@todo Assert for uniform scaling to avoid costly normalization? brief introduction.
*/ */
class MAGNUM_SHAPES_EXPORT Plane { class MAGNUM_SHAPES_EXPORT Plane {
public: public:

15
src/Shapes/Sphere.cpp

@ -36,21 +36,8 @@ using namespace Magnum::Math::Geometry;
namespace Magnum { namespace Shapes { namespace Magnum { namespace Shapes {
namespace {
template<UnsignedInt dimensions> static typename DimensionTraits<dimensions, Float>::VectorType unitVector();
template<> inline Vector2 unitVector<2>() {
return Vector2(1/Constants::sqrt2());
}
template<> inline Vector3 unitVector<3>() {
return Vector3(1/Constants::sqrt3());
}
}
template<UnsignedInt dimensions> Sphere<dimensions> Sphere<dimensions>::transformed(const typename DimensionTraits<dimensions, Float>::MatrixType& matrix) const { template<UnsignedInt dimensions> Sphere<dimensions> Sphere<dimensions>::transformed(const typename DimensionTraits<dimensions, Float>::MatrixType& matrix) const {
return Sphere<dimensions>(matrix.transformPoint(_position), return Sphere<dimensions>(matrix.transformPoint(_position), matrix.uniformScaling()*_radius);
(matrix.rotationScaling()*unitVector<dimensions>()).length()*_radius);
} }
template<UnsignedInt dimensions> bool Sphere<dimensions>::operator%(const Point<dimensions>& other) const { template<UnsignedInt dimensions> bool Sphere<dimensions>::operator%(const Point<dimensions>& other) const {

6
src/Shapes/Sphere.h

@ -38,11 +38,9 @@ namespace Magnum { namespace Shapes {
/** /**
@brief %Sphere defined by position and radius @brief %Sphere defined by position and radius
Unlike other elements the sphere doesn't support asymmetric scaling. When Unlike other elements the sphere expects uniform scaling. See @ref shapes for
applying transformation, the scale factor is averaged from all axes. See brief introduction.
@ref shapes for brief introduction.
@see Sphere2D, Sphere3D @see Sphere2D, Sphere3D
@todo Assert for asymmetric scaling to avoid costly sqrt?
*/ */
template<UnsignedInt dimensions> class MAGNUM_SHAPES_EXPORT Sphere { template<UnsignedInt dimensions> class MAGNUM_SHAPES_EXPORT Sphere {
public: public:

33
src/Shapes/Test/CapsuleTest.cpp

@ -37,44 +37,25 @@ class CapsuleTest: public TestSuite::Tester {
public: public:
CapsuleTest(); CapsuleTest();
void transformed2D(); void transformed();
void transformed3D();
void transformedAverageScaling(); void transformedAverageScaling();
void collisionPoint(); void collisionPoint();
void collisionSphere(); void collisionSphere();
}; };
CapsuleTest::CapsuleTest() { CapsuleTest::CapsuleTest() {
addTests({&CapsuleTest::transformed2D, addTests({&CapsuleTest::transformed,
&CapsuleTest::transformed3D,
&CapsuleTest::collisionPoint, &CapsuleTest::collisionPoint,
&CapsuleTest::collisionSphere}); &CapsuleTest::collisionSphere});
} }
void CapsuleTest::transformed2D() { void CapsuleTest::transformed() {
const Shapes::Capsule2D capsule({1.0f, 2.0f}, {-1.0f, -2.0f}, 7.0f);
const auto transformed = capsule.transformed(Matrix3::rotation(Deg(90.0f)));
CORRADE_COMPARE(transformed.a(), Vector2(-2.0f, 1.0f));
CORRADE_COMPARE(transformed.b(), Vector2(2.0f, -1.0f));
CORRADE_COMPARE(transformed.radius(), 7.0f);
/* Apply average scaling to radius */
const auto scaled = capsule.transformed(Matrix3::scaling({-Constants::sqrt2(), 2.0f}));
CORRADE_COMPARE(scaled.radius(), Constants::sqrt3()*7.0f);
}
void CapsuleTest::transformed3D() {
const Shapes::Capsule3D capsule({1.0f, 2.0f, 3.0f}, {-1.0f, -2.0f, -3.0f}, 7.0f); const Shapes::Capsule3D capsule({1.0f, 2.0f, 3.0f}, {-1.0f, -2.0f, -3.0f}, 7.0f);
const auto transformed = capsule.transformed(Matrix4::rotation(Deg(90.0f), Vector3::zAxis())); const auto transformed = capsule.transformed(Matrix4::scaling(Vector3(2.0f))*Matrix4::rotation(Deg(90.0f), Vector3::zAxis()));
CORRADE_COMPARE(transformed.a(), Vector3(-2.0f, 1.0f, 3.0f)); CORRADE_COMPARE(transformed.a(), Vector3(-4.0f, 2.0f, 6.0f));
CORRADE_COMPARE(transformed.b(), Vector3(2.0f, -1.0f, -3.0f)); CORRADE_COMPARE(transformed.b(), Vector3(4.0f, -2.0f, -6.0f));
CORRADE_COMPARE(transformed.radius(), 7.0f); CORRADE_COMPARE(transformed.radius(), 14.0f);
/* Apply average scaling to radius */
const auto scaled = capsule.transformed(Matrix4::scaling({Constants::sqrt3(), -Constants::sqrt2(), 2.0f}));
CORRADE_COMPARE(scaled.radius(), Constants::sqrt3()*7.0f);
} }
void CapsuleTest::collisionPoint() { void CapsuleTest::collisionPoint() {

10
src/Shapes/Test/PlaneTest.cpp

@ -49,14 +49,10 @@ PlaneTest::PlaneTest() {
void PlaneTest::transformed() { void PlaneTest::transformed() {
const Shapes::Plane plane({1.0f, 2.0f, 3.0f}, {Constants::sqrt2(), -Constants::sqrt2(), 0}); const Shapes::Plane plane({1.0f, 2.0f, 3.0f}, {Constants::sqrt2(), -Constants::sqrt2(), 0});
const auto transformed = plane.transformed(Matrix4::rotation(Deg(90.0f), Vector3::xAxis())); /* The normal should stay normalized after scaling */
CORRADE_COMPARE(transformed.position(), Vector3(1.0f, -3.0f, 2.0f)); const auto transformed = plane.transformed(Matrix4::scaling(Vector3(2.0f))*Matrix4::rotation(Deg(90.0f), Vector3::xAxis()));
CORRADE_COMPARE(transformed.position(), Vector3(2.0f, -6.0f, 4.0f));
CORRADE_COMPARE(transformed.normal(), Vector3(Constants::sqrt2(), 0, -Constants::sqrt2())); CORRADE_COMPARE(transformed.normal(), Vector3(Constants::sqrt2(), 0, -Constants::sqrt2()));
/* The normal should stay normalized */
const auto scaled = plane.transformed(Matrix4::scaling({1.5f, 2.0f, 3.0f}));
CORRADE_COMPARE(scaled.position(), Vector3(1.5f, 4.0f, 9.0f));
CORRADE_COMPARE(scaled.normal(), Vector3(Constants::sqrt2(), -Constants::sqrt2(), 0));
} }
void PlaneTest::collisionLine() { void PlaneTest::collisionLine() {

40
src/Shapes/Test/SphereTest.cpp

@ -37,8 +37,7 @@ class SphereTest: public TestSuite::Tester {
public: public:
SphereTest(); SphereTest();
void transformed2D(); void transformed();
void transformed3D();
void collisionPoint(); void collisionPoint();
void collisionLine(); void collisionLine();
void collisionLineSegment(); void collisionLineSegment();
@ -46,46 +45,19 @@ class SphereTest: public TestSuite::Tester {
}; };
SphereTest::SphereTest() { SphereTest::SphereTest() {
addTests({&SphereTest::transformed2D, addTests({&SphereTest::transformed,
&SphereTest::transformed3D,
&SphereTest::collisionPoint, &SphereTest::collisionPoint,
&SphereTest::collisionLine, &SphereTest::collisionLine,
&SphereTest::collisionLineSegment, &SphereTest::collisionLineSegment,
&SphereTest::collisionSphere}); &SphereTest::collisionSphere});
} }
void SphereTest::transformed2D() { void SphereTest::transformed() {
const Shapes::Sphere2D sphere({1.0f, 2.0f}, 7.0f);
const auto transformed = sphere.transformed(Matrix3::rotation(Deg(90.0f)));
CORRADE_COMPARE(transformed.position(), Vector2(-2.0f, 1.0f));
CORRADE_COMPARE(transformed.radius(), 7.0f);
/* Symmetric scaling */
const auto scaled = sphere.transformed(Matrix3::scaling(Vector2(2.0f)));
CORRADE_COMPARE(scaled.position(), Vector2(2.0f, 4.0f));
CORRADE_COMPARE(scaled.radius(), 14.0f);
/* Apply average scaling to radius */
const auto nonEven = sphere.transformed(Matrix3::scaling({-Constants::sqrt2(), 2.0f}));
CORRADE_COMPARE(nonEven.radius(), Constants::sqrt3()*7.0f);
}
void SphereTest::transformed3D() {
const Shapes::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 7.0f); const Shapes::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 7.0f);
const auto transformed = sphere.transformed(Matrix4::rotation(Deg(90.0f), Vector3::yAxis())); const auto transformed = sphere.transformed(Matrix4::scaling(Vector3(2.0f))*Matrix4::rotation(Deg(90.0f), Vector3::yAxis()));
CORRADE_COMPARE(transformed.position(), Vector3(3.0f, 2.0f, -1.0f)); CORRADE_COMPARE(transformed.position(), Vector3(6.0f, 4.0f, -2.0f));
CORRADE_COMPARE(transformed.radius(), 7.0f); CORRADE_COMPARE(transformed.radius(), 14.0f);
/* Symmetric scaling */
const auto scaled = sphere.transformed(Matrix4::scaling(Vector3(2.0f)));
CORRADE_COMPARE(scaled.position(), Vector3(2.0f, 4.0f, 6.0f));
CORRADE_COMPARE(scaled.radius(), 14.0f);
/* Apply average scaling to radius */
const auto nonEven = sphere.transformed(Matrix4::scaling({Constants::sqrt3(), -Constants::sqrt2(), 2.0f}));
CORRADE_COMPARE(nonEven.radius(), Constants::sqrt3()*7.0f);
} }
void SphereTest::collisionPoint() { void SphereTest::collisionPoint() {

Loading…
Cancel
Save