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 {
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 {
return Capsule<dimensions>(matrix.transformPoint(_a), matrix.transformPoint(_b),
(matrix.rotationScaling()*unitVector<dimensions>()).length()*_radius);
return Capsule<dimensions>(matrix.transformPoint(_a), matrix.transformPoint(_b), matrix.uniformScaling()*_radius);
}
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
Unlike other elements the capsule doesn't support asymmetric scaling. When
applying transformation, the scale factor is averaged from all axes. See
@ref shapes for brief introduction.
Unlike other elements the capsule expects uniform scaling. See @ref shapes for
brief introduction.
@see Capsule2D, Capsule3D
@todo Assert for asymmetric scaling to avoid costly sqrt?
*/
template<UnsignedInt dimensions> class MAGNUM_SHAPES_EXPORT Capsule {
public:

5
src/Shapes/Plane.cpp

@ -35,8 +35,11 @@ using namespace Magnum::Math::Geometry;
namespace Magnum { namespace Shapes {
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),
matrix.rotation()*_normal);
matrix.rotationScaling()*_normal/matrix.uniformScaling());
}
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)
See @ref shapes for brief introduction.
@todo Assert for uniform scaling to avoid costly normalization?
Unlike other elements the plane expects uniform scaling. See @ref shapes for
brief introduction.
*/
class MAGNUM_SHAPES_EXPORT Plane {
public:

15
src/Shapes/Sphere.cpp

@ -36,21 +36,8 @@ using namespace Magnum::Math::Geometry;
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 {
return Sphere<dimensions>(matrix.transformPoint(_position),
(matrix.rotationScaling()*unitVector<dimensions>()).length()*_radius);
return Sphere<dimensions>(matrix.transformPoint(_position), matrix.uniformScaling()*_radius);
}
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
Unlike other elements the sphere doesn't support asymmetric scaling. When
applying transformation, the scale factor is averaged from all axes. See
@ref shapes for brief introduction.
Unlike other elements the sphere expects uniform scaling. See @ref shapes for
brief introduction.
@see Sphere2D, Sphere3D
@todo Assert for asymmetric scaling to avoid costly sqrt?
*/
template<UnsignedInt dimensions> class MAGNUM_SHAPES_EXPORT Sphere {
public:

33
src/Shapes/Test/CapsuleTest.cpp

@ -37,44 +37,25 @@ class CapsuleTest: public TestSuite::Tester {
public:
CapsuleTest();
void transformed2D();
void transformed3D();
void transformed();
void transformedAverageScaling();
void collisionPoint();
void collisionSphere();
};
CapsuleTest::CapsuleTest() {
addTests({&CapsuleTest::transformed2D,
&CapsuleTest::transformed3D,
addTests({&CapsuleTest::transformed,
&CapsuleTest::collisionPoint,
&CapsuleTest::collisionSphere});
}
void CapsuleTest::transformed2D() {
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() {
void CapsuleTest::transformed() {
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()));
CORRADE_COMPARE(transformed.a(), Vector3(-2.0f, 1.0f, 3.0f));
CORRADE_COMPARE(transformed.b(), Vector3(2.0f, -1.0f, -3.0f));
CORRADE_COMPARE(transformed.radius(), 7.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);
const auto transformed = capsule.transformed(Matrix4::scaling(Vector3(2.0f))*Matrix4::rotation(Deg(90.0f), Vector3::zAxis()));
CORRADE_COMPARE(transformed.a(), Vector3(-4.0f, 2.0f, 6.0f));
CORRADE_COMPARE(transformed.b(), Vector3(4.0f, -2.0f, -6.0f));
CORRADE_COMPARE(transformed.radius(), 14.0f);
}
void CapsuleTest::collisionPoint() {

10
src/Shapes/Test/PlaneTest.cpp

@ -49,14 +49,10 @@ PlaneTest::PlaneTest() {
void PlaneTest::transformed() {
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()));
CORRADE_COMPARE(transformed.position(), Vector3(1.0f, -3.0f, 2.0f));
/* The normal should stay normalized after scaling */
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()));
/* 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() {

40
src/Shapes/Test/SphereTest.cpp

@ -37,8 +37,7 @@ class SphereTest: public TestSuite::Tester {
public:
SphereTest();
void transformed2D();
void transformed3D();
void transformed();
void collisionPoint();
void collisionLine();
void collisionLineSegment();
@ -46,46 +45,19 @@ class SphereTest: public TestSuite::Tester {
};
SphereTest::SphereTest() {
addTests({&SphereTest::transformed2D,
&SphereTest::transformed3D,
addTests({&SphereTest::transformed,
&SphereTest::collisionPoint,
&SphereTest::collisionLine,
&SphereTest::collisionLineSegment,
&SphereTest::collisionSphere});
}
void SphereTest::transformed2D() {
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() {
void SphereTest::transformed() {
const Shapes::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 7.0f);
const auto transformed = sphere.transformed(Matrix4::rotation(Deg(90.0f), Vector3::yAxis()));
CORRADE_COMPARE(transformed.position(), Vector3(3.0f, 2.0f, -1.0f));
CORRADE_COMPARE(transformed.radius(), 7.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);
const auto transformed = sphere.transformed(Matrix4::scaling(Vector3(2.0f))*Matrix4::rotation(Deg(90.0f), Vector3::yAxis()));
CORRADE_COMPARE(transformed.position(), Vector3(6.0f, 4.0f, -2.0f));
CORRADE_COMPARE(transformed.radius(), 14.0f);
}
void SphereTest::collisionPoint() {

Loading…
Cancel
Save