From a5ad16d3a466097c33850c960bf79862adf860db Mon Sep 17 00:00:00 2001 From: louis-langholtz Date: Mon, 27 Nov 2017 09:40:48 -0700 Subject: [PATCH 1/4] Replaces duplicated code in favor of more generic code using templates. Gets more specific about what's available functionality from templates. --- PlayRho/Collision/Simplex.hpp | 2 +- PlayRho/Common/Fixed.hpp | 25 ++++++ PlayRho/Common/Math.hpp | 122 ++------------------------- UnitTests/CollideShapes.cpp | 30 +++---- UnitTests/ContactSolver.cpp | 8 +- UnitTests/Epsilon.cpp | 4 +- UnitTests/MassData.cpp | 4 +- UnitTests/PositionSolverManifold.cpp | 28 +++--- UnitTests/Simplex.cpp | 4 +- UnitTests/TimeOfImpact.cpp | 4 +- UnitTests/World.cpp | 44 +++++----- 11 files changed, 101 insertions(+), 174 deletions(-) diff --git a/PlayRho/Collision/Simplex.hpp b/PlayRho/Collision/Simplex.hpp index ee2569adaf..785f209c9b 100644 --- a/PlayRho/Collision/Simplex.hpp +++ b/PlayRho/Collision/Simplex.hpp @@ -293,7 +293,7 @@ namespace playrho { #ifndef NDEBUG const auto sum = std::accumulate(std::begin(normalizedWeights), std::end(normalizedWeights), Real(0)); - assert(AlmostEqual(1, sum)); + assert(AlmostEqual(Real{1}, sum)); #endif } diff --git a/PlayRho/Common/Fixed.hpp b/PlayRho/Common/Fixed.hpp index ab5cd5907d..66b853807b 100644 --- a/PlayRho/Common/Fixed.hpp +++ b/PlayRho/Common/Fixed.hpp @@ -737,6 +737,31 @@ namespace playrho && (value < Fixed::GetInfinity()); } + /// @brief Computes the rounded value of the given value. + template + inline Fixed RoundOff(Fixed value, std::uint32_t precision = 100000) + { + const auto factor = Fixed(precision); + return Round(value * factor) / factor; + } + + /// @brief Gets whether a given value is almost zero. + /// @details An almost zero value is "subnormal". Dividing by these values can lead to + /// odd results like a divide by zero trap occurring. + /// @return true if the given value is almost zero, false otherwise. + template + constexpr inline bool AlmostZero(Fixed value) + { + return value == 0; + } + + /// @brief Determines whether the given two values are "almost equal". + template + constexpr inline bool AlmostEqual(Fixed x, Fixed y, int ulp = 2) + { + return Abs(x - y) <= Fixed{0, static_cast(ulp)}; + } + /// @brief Output stream operator. template inline ::std::ostream& operator<<(::std::ostream& os, const Fixed& value) diff --git a/PlayRho/Common/Math.hpp b/PlayRho/Common/Math.hpp index e2c1e2be02..0d2563aa46 100644 --- a/PlayRho/Common/Math.hpp +++ b/PlayRho/Common/Math.hpp @@ -166,7 +166,7 @@ inline typename std::enable_if::value, bool>::type IsFinit /// @brief Rounds the given value. template -inline T Round(T arg) +inline typename std::enable_if::value, T>::type Round(T arg) { return std::round(arg); } @@ -236,15 +236,6 @@ inline auto Atan2(T y, T x) return Angle{static_cast(std::atan2(StripUnit(y), StripUnit(x))) * Radian}; } -/// @brief Computes the arc-tangent of the given y and x values. -/// @return Normalized angle - an angle between -Pi and Pi inclusively. -/// @sa http://en.cppreference.com/w/cpp/numeric/math/atan2 -template<> -inline auto Atan2(double y, double x) -{ - return Angle{static_cast(std::atan2(y, x)) * Radian}; -} - /// @brief Computes the average of the given values. template inline auto Average(Span span) @@ -264,54 +255,15 @@ inline auto Average(Span span) /// @brief Computes the rounded value of the given value. template -inline T RoundOff(T value, unsigned precision = 100000); - -/// @brief Computes the rounded value of the given value. -template <> -inline float RoundOff(float value, std::uint32_t precision) -{ - const auto factor = float(static_cast(precision)); - return std::round(value * factor) / factor; -} - -/// @brief Computes the rounded value of the given value. -template <> -inline double RoundOff(double value, std::uint32_t precision) +inline typename std::enable_if::value, T>::type +RoundOff(T value, unsigned precision = 100000) { - const auto factor = double(static_cast(precision)); + const auto factor = static_cast(precision); return std::round(value * factor) / factor; } /// @brief Computes the rounded value of the given value. -template <> -inline long double RoundOff(long double value, std::uint32_t precision) -{ - using ldouble = long double; - const auto factor = ldouble(static_cast(precision)); - return std::round(value * factor) / factor; -} - -/// @brief Computes the rounded value of the given value. -template <> -inline Fixed32 RoundOff(Fixed32 value, std::uint32_t precision) -{ - const auto factor = Fixed32(precision); - return Round(value * factor) / factor; -} - -#ifndef _WIN32 -/// @brief Computes the rounded value of the given value. -template <> -inline Fixed64 RoundOff(Fixed64 value, std::uint32_t precision) -{ - const auto factor = Fixed64(precision); - return Round(value * factor) / factor; -} -#endif - -/// @brief Computes the rounded value of the given value. -template <> -inline Vec2 RoundOff(Vec2 value, std::uint32_t precision) +inline Vec2 RoundOff(Vec2 value, std::uint32_t precision = 100000) { return Vec2{RoundOff(value[0], precision), RoundOff(value[1], precision)}; } @@ -333,52 +285,10 @@ AlmostZero(T value) return Abs(value) < std::numeric_limits::min(); } -/// @brief Gets whether a given value is almost zero. -/// @details An almost zero value is "subnormal". Dividing by these values can lead to -/// odd results like a divide by zero trap occurring. -/// @return true if the given value is almost zero, false otherwise. -constexpr inline bool AlmostZero(Fixed32 value) -{ - return value == 0; -} - -#ifndef _WIN32 -/// @brief Gets whether a given value is almost zero. -/// @details An almost zero value is "subnormal". Dividing by these values can lead to -/// odd results like a divide by zero trap occurring. -/// @return true if the given value is almost zero, false otherwise. -constexpr inline bool AlmostZero(Fixed64 value) -{ - return value == 0; -} -#endif - -/// @brief Determines whether the given two values are "almost equal". -constexpr inline bool AlmostEqual(float x, float y, int ulp = 2) -{ - // From http://en.cppreference.com/w/cpp/types/numeric_limits/epsilon : - // "the machine epsilon has to be scaled to the magnitude of the values used - // and multiplied by the desired precision in ULPs (units in the last place) - // unless the result is subnormal". - // Where "subnormal" means almost zero. - // - return (Abs(x - y) < (std::numeric_limits::epsilon() * Abs(x + y) * ulp)) || AlmostZero(x - y); -} - /// @brief Determines whether the given two values are "almost equal". -constexpr inline bool AlmostEqual(double x, double y, int ulp = 2) -{ - // From http://en.cppreference.com/w/cpp/types/numeric_limits/epsilon : - // "the machine epsilon has to be scaled to the magnitude of the values used - // and multiplied by the desired precision in ULPs (units in the last place) - // unless the result is subnormal". - // Where "subnormal" means almost zero. - // - return (Abs(x - y) < (std::numeric_limits::epsilon() * Abs(x + y) * ulp)) || AlmostZero(x - y); -} - -/// @brief Determines whether the given two values are "almost equal". -constexpr inline bool AlmostEqual(long double x, long double y, int ulp = 2) +template +constexpr inline typename std::enable_if::value, bool>::type +AlmostEqual(T x, T y, int ulp = 2) { // From http://en.cppreference.com/w/cpp/types/numeric_limits/epsilon : // "the machine epsilon has to be scaled to the magnitude of the values used @@ -386,23 +296,9 @@ constexpr inline bool AlmostEqual(long double x, long double y, int ulp = 2) // unless the result is subnormal". // Where "subnormal" means almost zero. // - return (Abs(x - y) < (std::numeric_limits::epsilon() * Abs(x + y) * ulp)) || AlmostZero(x - y); + return (Abs(x - y) < (std::numeric_limits::epsilon() * Abs(x + y) * ulp)) || AlmostZero(x - y); } -/// @brief Determines whether the given two values are "almost equal". -constexpr inline bool AlmostEqual(Fixed32 x, Fixed32 y, int ulp = 2) -{ - return Abs(x - y) <= Fixed32{0, static_cast(ulp)}; -} - -#ifndef _WIN32 -/// @brief Determines whether the given two values are "almost equal". -constexpr inline bool AlmostEqual(Fixed64 x, Fixed64 y, int ulp = 2) -{ - return Abs(x - y) <= Fixed64{0, static_cast(ulp)}; -} -#endif - /// @brief Modulo operation using std::fmod. /// @note Modulo via std::fmod appears slower than via std::trunc. /// @sa ModuloViaTrunc diff --git a/UnitTests/CollideShapes.cpp b/UnitTests/CollideShapes.cpp index c8c23782b0..609fd43003 100644 --- a/UnitTests/CollideShapes.cpp +++ b/UnitTests/CollideShapes.cpp @@ -847,20 +847,20 @@ TEST(CollideShapes, HorizontalOverlappingRects2) EXPECT_EQ(manifold.GetPointCount(), Manifold::size_type(2)); ASSERT_GT(manifold.GetPointCount(), Manifold::size_type(0)); - EXPECT_TRUE(AlmostEqual(GetX(manifold.GetPoint(0).localPoint) / Meter, Real(-2.0))); // left - EXPECT_TRUE(AlmostEqual(GetY(manifold.GetPoint(0).localPoint) / Meter, Real(-1.5))); // top - EXPECT_TRUE(AlmostEqual(manifold.GetPoint(0).normalImpulse / (1_Ns), Real(0))); - EXPECT_TRUE(AlmostEqual(manifold.GetPoint(0).tangentImpulse / (1_Ns), Real(0))); + EXPECT_TRUE(AlmostEqual(Real{GetX(manifold.GetPoint(0).localPoint) / Meter}, Real(-2.0))); // left + EXPECT_TRUE(AlmostEqual(Real{GetY(manifold.GetPoint(0).localPoint) / Meter}, Real(-1.5))); // top + EXPECT_TRUE(AlmostEqual(Real{manifold.GetPoint(0).normalImpulse / 1_Ns}, Real(0))); + EXPECT_TRUE(AlmostEqual(Real{manifold.GetPoint(0).tangentImpulse / 1_Ns}, Real(0))); EXPECT_EQ(manifold.GetPoint(0).contactFeature.typeA, ContactFeature::e_vertex); EXPECT_EQ(manifold.GetPoint(0).contactFeature.indexA, 0); EXPECT_EQ(manifold.GetPoint(0).contactFeature.typeB, ContactFeature::e_face); EXPECT_EQ(manifold.GetPoint(0).contactFeature.indexB, 2); ASSERT_GT(manifold.GetPointCount(), Manifold::size_type(1)); - EXPECT_TRUE(AlmostEqual(GetX(manifold.GetPoint(1).localPoint) / Meter, Real(-2.0))); // left - EXPECT_TRUE(AlmostEqual(GetY(manifold.GetPoint(1).localPoint) / Meter, Real(+1.5))); // bottom - EXPECT_TRUE(AlmostEqual(manifold.GetPoint(1).normalImpulse / (1_Ns), Real(0))); - EXPECT_TRUE(AlmostEqual(manifold.GetPoint(1).tangentImpulse / (1_Ns), Real(0))); + EXPECT_TRUE(AlmostEqual(Real{GetX(manifold.GetPoint(1).localPoint) / Meter}, Real(-2.0))); // left + EXPECT_TRUE(AlmostEqual(Real{GetY(manifold.GetPoint(1).localPoint) / Meter}, Real(+1.5))); // bottom + EXPECT_TRUE(AlmostEqual(Real{manifold.GetPoint(1).normalImpulse / 1_Ns}, Real(0))); + EXPECT_TRUE(AlmostEqual(Real{manifold.GetPoint(1).tangentImpulse / 1_Ns}, Real(0))); EXPECT_EQ(manifold.GetPoint(1).contactFeature.typeA, ContactFeature::e_vertex); EXPECT_EQ(manifold.GetPoint(1).contactFeature.indexA, 1); EXPECT_EQ(manifold.GetPoint(1).contactFeature.typeB, ContactFeature::e_face); @@ -875,12 +875,12 @@ TEST(CollideShapes, HorizontalOverlappingRects2) EXPECT_TRUE(AlmostEqual(world_manifold.GetNormal().GetY(), Real(0))); ASSERT_GT(world_manifold.GetPointCount(), Manifold::size_type(0)); - EXPECT_TRUE(AlmostEqual(GetX(world_manifold.GetPoint(0)) / Meter, Real(+0.5))); - EXPECT_TRUE(AlmostEqual(GetY(world_manifold.GetPoint(0)) / Meter, Real(-1.5))); + EXPECT_TRUE(AlmostEqual(Real{GetX(world_manifold.GetPoint(0)) / Meter}, Real(+0.5))); + EXPECT_TRUE(AlmostEqual(Real{GetY(world_manifold.GetPoint(0)) / Meter}, Real(-1.5))); ASSERT_GT(world_manifold.GetPointCount(), Manifold::size_type(1)); - EXPECT_TRUE(AlmostEqual(GetX(world_manifold.GetPoint(1)) / Meter, Real(+0.5))); - EXPECT_TRUE(AlmostEqual(GetY(world_manifold.GetPoint(1)) / Meter, Real(+1.5))); + EXPECT_TRUE(AlmostEqual(Real{GetX(world_manifold.GetPoint(1)) / Meter}, Real(+0.5))); + EXPECT_TRUE(AlmostEqual(Real{GetY(world_manifold.GetPoint(1)) / Meter}, Real(+1.5))); } TEST(CollideShapes, EdgeBelowPolygon) @@ -1398,14 +1398,14 @@ TEST(CollideShapes, EdgePolygonFaceB2) EXPECT_EQ(manifold.GetType(), Manifold::e_faceB); EXPECT_NEAR(double(StripUnit(GetX(manifold.GetLocalPoint()))), 0.0, 0.0001); - EXPECT_TRUE(AlmostEqual(GetY(manifold.GetLocalPoint()) / Meter, Real{0.5f})); + EXPECT_TRUE(AlmostEqual(Real{GetY(manifold.GetLocalPoint()) / Meter}, Real{0.5f})); EXPECT_TRUE(AlmostEqual(GetX(GetVec2(manifold.GetLocalNormal())), Real{0.0f})); EXPECT_TRUE(AlmostEqual(GetY(GetVec2(manifold.GetLocalNormal())), Real{1.0f})); EXPECT_EQ(manifold.GetPointCount(), Manifold::size_type(1)); ASSERT_GT(manifold.GetPointCount(), Manifold::size_type(0)); EXPECT_EQ(manifold.GetContactFeature(0), GetVertexFaceContactFeature(1, 1)); - EXPECT_TRUE(AlmostEqual(GetX(manifold.GetOpposingPoint(0)) / Meter, Real{-6.0f})); - EXPECT_TRUE(AlmostEqual(GetY(manifold.GetOpposingPoint(0)) / Meter, Real{0.0f})); + EXPECT_TRUE(AlmostEqual(Real{GetX(manifold.GetOpposingPoint(0)) / Meter}, Real{-6.0f})); + EXPECT_TRUE(AlmostEqual(Real{GetY(manifold.GetOpposingPoint(0)) / Meter}, Real{0.0f})); } #if 0 diff --git a/UnitTests/ContactSolver.cpp b/UnitTests/ContactSolver.cpp index 97945fa9f2..1736227ebb 100644 --- a/UnitTests/ContactSolver.cpp +++ b/UnitTests/ContactSolver.cpp @@ -201,7 +201,7 @@ TEST(ContactSolver, SolvePosConstraintsForHorOverlappingMovesHorOnly1) const auto conf = ConstraintSolverConf{}.UseResolutionRate(Baumgarte).UseMaxLinearCorrection(maxLinearCorrection); const auto solution = GaussSeidel::SolvePositionConstraint(pc, true, true, conf); - EXPECT_TRUE(AlmostEqual(solution.min_separation / Meter, Real(-2))); // -2.002398 + EXPECT_TRUE(AlmostEqual(Real{solution.min_separation / Meter}, Real(-2))); // -2.002398 // object a just moves left EXPECT_LT(GetX(solution.pos_a.linear), GetX(old_pA.linear)); @@ -255,7 +255,7 @@ TEST(ContactSolver, SolvePosConstraintsForHorOverlappingMovesHorOnly2) const auto conf = ConstraintSolverConf{}.UseResolutionRate(Baumgarte).UseMaxLinearCorrection(maxLinearCorrection); const auto solution = GaussSeidel::SolvePositionConstraint(pc, true, true, conf); - EXPECT_TRUE(AlmostEqual(solution.min_separation / Meter, Real(-2))); // -2.002398 + EXPECT_TRUE(AlmostEqual(Real{solution.min_separation / Meter}, Real(-2))); // -2.002398 // square A just moves right EXPECT_GT(GetX(solution.pos_a.linear), GetX(old_pA.linear)); @@ -309,7 +309,7 @@ TEST(ContactSolver, SolvePosConstraintsForVerOverlappingMovesVerOnly1) const auto conf = ConstraintSolverConf{}.UseResolutionRate(Baumgarte).UseMaxLinearCorrection(maxLinearCorrection); const auto solution = GaussSeidel::SolvePositionConstraint(pc, true, true, conf); - EXPECT_TRUE(AlmostEqual(solution.min_separation / Meter, Real(-2))); // -2.002398 + EXPECT_TRUE(AlmostEqual(Real{solution.min_separation / Meter}, Real(-2))); // -2.002398 // object a just moves down only EXPECT_EQ(GetX(solution.pos_a.linear), GetX(old_pA.linear)); @@ -376,7 +376,7 @@ TEST(ContactSolver, SolvePosConstraintsForVerOverlappingMovesVerOnly2) const auto conf = ConstraintSolverConf{}.UseResolutionRate(Baumgarte).UseMaxLinearCorrection(maxLinearCorrection); const auto solution = GaussSeidel::SolvePositionConstraint(pc, true, true, conf); - EXPECT_TRUE(AlmostEqual(solution.min_separation / Meter, Real(-2))); // -2.002398 + EXPECT_TRUE(AlmostEqual(Real{solution.min_separation / Meter}, Real(-2))); // -2.002398 // square A just moves up only EXPECT_EQ(GetX(solution.pos_a.linear), GetX(old_pA.linear)); diff --git a/UnitTests/Epsilon.cpp b/UnitTests/Epsilon.cpp index d59a85e824..2a91e44cb8 100644 --- a/UnitTests/Epsilon.cpp +++ b/UnitTests/Epsilon.cpp @@ -76,13 +76,13 @@ TEST(Epsilon, AlmostEqual) EXPECT_LT(a, std::numeric_limits::min()); EXPECT_LT(a, std::numeric_limits::epsilon()); EXPECT_TRUE(AlmostZero(a)); - EXPECT_TRUE(AlmostEqual(std::numeric_limits::min() * std::numeric_limits::epsilon() * 2, 0)); + EXPECT_TRUE(AlmostEqual(std::numeric_limits::min() * std::numeric_limits::epsilon() * 2, 0.0f)); EXPECT_TRUE(AlmostZero(std::numeric_limits::min() * std::numeric_limits::epsilon() * 2)); EXPECT_FALSE(AlmostZero(std::numeric_limits::min())); EXPECT_FALSE(AlmostEqual(std::numeric_limits::min() * 2, std::numeric_limits::min())); EXPECT_FALSE(AlmostEqual(std::numeric_limits::min(), 0.0f)); - EXPECT_FALSE(AlmostEqual(std::numeric_limits::min() * float(1.001), 0)); + EXPECT_FALSE(AlmostEqual(std::numeric_limits::min() * float(1.001), 0.0f)); EXPECT_TRUE(AlmostEqual(std::numeric_limits::min() * 0.5f, std::numeric_limits::min())); EXPECT_TRUE(AlmostEqual(std::numeric_limits::min() * 0.5f, 0.0f)); EXPECT_TRUE(AlmostZero(std::numeric_limits::min() * 0.5f)); diff --git a/UnitTests/MassData.cpp b/UnitTests/MassData.cpp index e3e2ec4d15..f37a3658d5 100644 --- a/UnitTests/MassData.cpp +++ b/UnitTests/MassData.cpp @@ -240,8 +240,8 @@ TEST(MassData, GetForZeroVertexRadiusRectangle) EXPECT_NEAR(double(StripUnit(mass_data.I)), 90.666664 * double(StripUnit(density)), 0.008); - EXPECT_TRUE(AlmostEqual(GetX(mass_data.center) / Meter, GetX(shape.GetCentroid()) / Meter)); - EXPECT_TRUE(AlmostEqual(GetY(mass_data.center) / Meter, GetY(shape.GetCentroid()) / Meter)); + EXPECT_TRUE(AlmostEqual(Real{GetX(mass_data.center) / Meter}, Real{GetX(shape.GetCentroid()) / Meter})); + EXPECT_TRUE(AlmostEqual(Real{GetY(mass_data.center) / Meter}, Real{GetY(shape.GetCentroid()) / Meter})); // Area moment of inertia (I) for a rectangle is Ix + Iy = (b * h^3) / 12 + (b^3 * h) / 12.... const auto i = 8.0 * 2.0 * 2.0 * 2.0 / 12.0 + 8.0 * 8.0 * 8.0 * 2.0 / 12.0; diff --git a/UnitTests/PositionSolverManifold.cpp b/UnitTests/PositionSolverManifold.cpp index 4ad77af795..d9c212e1f3 100644 --- a/UnitTests/PositionSolverManifold.cpp +++ b/UnitTests/PositionSolverManifold.cpp @@ -91,16 +91,16 @@ TEST(PositionSolverManifold, GetPSM) const auto total_radius = GetVertexRadius(shape0) + GetVertexRadius(shape1); ASSERT_GT(manifold.GetPointCount(), Manifold::size_type(0)); - EXPECT_TRUE(AlmostEqual(GetX(manifold.GetPoint(0).localPoint) / Meter, Real(-2.0))); // left - EXPECT_TRUE(AlmostEqual(GetY(manifold.GetPoint(0).localPoint) / Meter, Real(-1.5))); // top + EXPECT_TRUE(AlmostEqual(Real{GetX(manifold.GetPoint(0).localPoint) / Meter}, Real(-2.0))); // left + EXPECT_TRUE(AlmostEqual(Real{GetY(manifold.GetPoint(0).localPoint) / Meter}, Real(-1.5))); // top ASSERT_EQ(manifold.GetPoint(0).contactFeature.typeA, ContactFeature::e_vertex); ASSERT_EQ(manifold.GetPoint(0).contactFeature.indexA, 0); ASSERT_EQ(manifold.GetPoint(0).contactFeature.typeB, ContactFeature::e_face); ASSERT_EQ(manifold.GetPoint(0).contactFeature.indexB, 2); ASSERT_GT(manifold.GetPointCount(), Manifold::size_type(1)); - EXPECT_TRUE(AlmostEqual(GetX(manifold.GetPoint(1).localPoint) / Meter, Real(-2.0))); // left - EXPECT_TRUE(AlmostEqual(GetY(manifold.GetPoint(1).localPoint) / Meter, Real(+1.5))); // bottom + EXPECT_TRUE(AlmostEqual(Real{GetX(manifold.GetPoint(1).localPoint) / Meter}, Real(-2.0))); // left + EXPECT_TRUE(AlmostEqual(Real{GetY(manifold.GetPoint(1).localPoint) / Meter}, Real(+1.5))); // bottom ASSERT_EQ(manifold.GetPoint(1).contactFeature.typeA, ContactFeature::e_vertex); ASSERT_EQ(manifold.GetPoint(1).contactFeature.indexA, 1); ASSERT_EQ(manifold.GetPoint(1).contactFeature.typeB, ContactFeature::e_face); @@ -117,14 +117,14 @@ TEST(PositionSolverManifold, GetPSM) EXPECT_TRUE(AlmostEqual(world_manifold.GetNormal().GetY(), Real(0))); ASSERT_GT(world_manifold.GetPointCount(), Manifold::size_type(0)); - EXPECT_TRUE(AlmostEqual(GetX(world_manifold.GetPoint(0)) / Meter, Real(+0.5))); - EXPECT_TRUE(AlmostEqual(GetY(world_manifold.GetPoint(0)) / Meter, Real(-1.5))); - EXPECT_TRUE(AlmostEqual(world_manifold.GetSeparation(0) / Meter, Real(-1) - total_radius / Meter)); + EXPECT_TRUE(AlmostEqual(Real{GetX(world_manifold.GetPoint(0)) / Meter}, Real(+0.5))); + EXPECT_TRUE(AlmostEqual(Real{GetY(world_manifold.GetPoint(0)) / Meter}, Real(-1.5))); + EXPECT_TRUE(AlmostEqual(Real{world_manifold.GetSeparation(0) / Meter}, Real(-1) - total_radius / Meter)); ASSERT_GT(world_manifold.GetPointCount(), Manifold::size_type(1)); - EXPECT_TRUE(AlmostEqual(GetX(world_manifold.GetPoint(1)) / Meter, Real(+0.5))); - EXPECT_TRUE(AlmostEqual(GetY(world_manifold.GetPoint(1)) / Meter, Real(+1.5))); - EXPECT_TRUE(AlmostEqual(world_manifold.GetSeparation(1) / Meter, Real(-1) - total_radius / Meter)); + EXPECT_TRUE(AlmostEqual(Real{GetX(world_manifold.GetPoint(1)) / Meter}, Real(+0.5))); + EXPECT_TRUE(AlmostEqual(Real{GetY(world_manifold.GetPoint(1)) / Meter}, Real(+1.5))); + EXPECT_TRUE(AlmostEqual(Real{world_manifold.GetSeparation(1) / Meter}, Real(-1) - total_radius / Meter)); } { @@ -132,15 +132,15 @@ TEST(PositionSolverManifold, GetPSM) EXPECT_NEAR(static_cast(psm0.m_normal.GetX()), 1.0, 0.00001); EXPECT_NEAR(static_cast(psm0.m_normal.GetY()), 0.0, 0.00001); EXPECT_NEAR(static_cast(Real{psm0.m_separation/Meter}), -1.0, 0.00001); - EXPECT_TRUE(AlmostEqual(GetX(psm0.m_point) / Meter, Real(0))); - EXPECT_TRUE(AlmostEqual(GetY(psm0.m_point) / Meter, Real(-1.5))); + EXPECT_TRUE(AlmostEqual(Real{GetX(psm0.m_point) / Meter}, Real(0))); + EXPECT_TRUE(AlmostEqual(Real{GetY(psm0.m_point) / Meter}, Real(-1.5))); } { const auto psm1 = GetPSM(manifold, 1, xfm0, xfm1); EXPECT_NEAR(static_cast(psm1.m_normal.GetX()), 1.0, 0.0001); EXPECT_NEAR(static_cast(psm1.m_normal.GetY()), 0.0, 0.0001); EXPECT_NEAR(static_cast(Real{psm1.m_separation/Meter}), -1.0, 0.00001); - EXPECT_TRUE(AlmostEqual(GetX(psm1.m_point) / Meter, Real(0))); - EXPECT_TRUE(AlmostEqual(GetY(psm1.m_point) / Meter, Real(+1.5))); + EXPECT_TRUE(AlmostEqual(Real{GetX(psm1.m_point) / Meter}, Real(0))); + EXPECT_TRUE(AlmostEqual(Real{GetY(psm1.m_point) / Meter}, Real(+1.5))); } } diff --git a/UnitTests/Simplex.cpp b/UnitTests/Simplex.cpp index b8e9cb93b8..6b31cfbdb6 100644 --- a/UnitTests/Simplex.cpp +++ b/UnitTests/Simplex.cpp @@ -344,8 +344,8 @@ TEST(Simplex, Get2_rot45_half) const auto sv1 = SimplexEdge{va1, ia1, vb1, ib1}; const auto w1 = vb0 - va0; // Vec2{901, 6} - Vec2{-4, 33} = Vec2{905, -27} - EXPECT_TRUE(AlmostEqual(GetX(w1) / Meter, Real(905))); - EXPECT_TRUE(AlmostEqual(GetY(w1) / Meter, Real(-27))); + EXPECT_TRUE(AlmostEqual(Real{GetX(w1) / Meter}, Real(905))); + EXPECT_TRUE(AlmostEqual(Real{GetY(w1) / Meter}, Real(-27))); const auto w2 = vb1 - va1; // Vec2{316.4303, 320.67291} - Vec2{-13.081475, 10.253049} = Vec2{329.51178, 310.41986} EXPECT_NEAR(double(Real{GetX(w2) / Meter}), 329.51178, 0.001); EXPECT_NEAR(double(Real{GetY(w2) / Meter}), 310.41986, 0.001); diff --git a/UnitTests/TimeOfImpact.cpp b/UnitTests/TimeOfImpact.cpp index af3e683ed9..6444b4a341 100644 --- a/UnitTests/TimeOfImpact.cpp +++ b/UnitTests/TimeOfImpact.cpp @@ -236,13 +236,13 @@ TEST(TimeOfImpact, CollideCirclesHorizontally) if (std::is_same::value) { EXPECT_EQ(output.state, TOIOutput::e_nextAfter); - EXPECT_TRUE(AlmostEqual(output.time, approx_time_of_collision)); + EXPECT_TRUE(AlmostEqual(output.time, Real{approx_time_of_collision})); EXPECT_EQ(output.stats.toi_iters, 1); } else { EXPECT_EQ(output.state, TOIOutput::e_touching); - EXPECT_TRUE(AlmostEqual(output.time, approx_time_of_collision)); + EXPECT_TRUE(AlmostEqual(output.time, Real{approx_time_of_collision})); EXPECT_EQ(output.stats.toi_iters, 2); } } diff --git a/UnitTests/World.cpp b/UnitTests/World.cpp index 180e535cf6..e722fd6f3b 100644 --- a/UnitTests/World.cpp +++ b/UnitTests/World.cpp @@ -882,7 +882,7 @@ TEST(World, StepZeroTimeDoesNothing) pos = body->GetLocation(); EXPECT_EQ(GetX(GetLinearVelocity(*body)), 0_mps); - EXPECT_TRUE(AlmostEqual(Real{GetY(GetLinearVelocity(*body)) / 1_mps}, GetY(vel) / 1_mps)); + EXPECT_TRUE(AlmostEqual(Real{GetY(GetLinearVelocity(*body)) / 1_mps}, Real{GetY(vel) / 1_mps})); vel = GetLinearVelocity(*body); } } @@ -989,7 +989,8 @@ TEST(World, BodyAccelPerSpecWithNoVelOrPosIterations) EXPECT_EQ(GetX(GetLinearVelocity(*body)), 0_mps); EXPECT_LT(GetY(GetLinearVelocity(*body)), GetY(vel)); - EXPECT_TRUE(AlmostEqual(GetY(GetLinearVelocity(*body)) / 1_mps, (GetY(vel) + GetY(gravity) * time_inc) / 1_mps)); + EXPECT_TRUE(AlmostEqual(Real{GetY(GetLinearVelocity(*body)) / 1_mps}, + Real{(GetY(vel) + GetY(gravity) * time_inc) / 1_mps})); vel = GetLinearVelocity(*body); } } @@ -1038,7 +1039,8 @@ TEST(World, BodyAccelRevPerSpecWithNegativeTimeAndNoVelOrPosIterations) EXPECT_EQ(GetX(GetLinearVelocity(*body)), 0_mps); EXPECT_GT(GetY(GetLinearVelocity(*body)), GetY(vel)); - EXPECT_TRUE(AlmostEqual(GetY(GetLinearVelocity(*body)) / 1_mps, (GetY(vel) + GetY(gravity) * time_inc) / 1_mps)); + EXPECT_TRUE(AlmostEqual(Real{GetY(GetLinearVelocity(*body)) / 1_mps}, + Real{(GetY(vel) + GetY(gravity) * time_inc) / 1_mps})); vel = GetLinearVelocity(*body); } } @@ -1176,9 +1178,11 @@ TEST(World, NoCorrectionsWithNoVelOrPosIterations) world.Step(conf); ++steps; - EXPECT_TRUE(AlmostEqual(GetX(body_a->GetLocation()) / Meter, (GetX(pos_a) + x * time_inc * 1_mps) / Meter)); + EXPECT_TRUE(AlmostEqual(Real{GetX(body_a->GetLocation()) / Meter}, + Real{(GetX(pos_a) + x * time_inc * 1_mps) / Meter})); EXPECT_EQ(GetY(body_a->GetLocation()), 0_m); - EXPECT_TRUE(AlmostEqual(GetX(body_b->GetLocation()) / Meter, (GetX(pos_b) - x * time_inc * 1_mps) / Meter)); + EXPECT_TRUE(AlmostEqual(Real{GetX(body_b->GetLocation()) / Meter}, + Real{(GetX(pos_b) - x * time_inc * 1_mps) / Meter})); EXPECT_EQ(GetY(body_b->GetLocation()), 0_m); EXPECT_EQ(GetX(GetLinearVelocity(*body_a)), +x * 1_mps); @@ -1592,7 +1596,7 @@ TEST(World, PartiallyOverlappedSameCirclesSeparate) const auto new_pos_diff = body2->GetLocation() - body1->GetLocation(); const auto new_distance = GetMagnitude(new_pos_diff); - if (AlmostEqual(new_distance / Meter, full_separation / Meter) || new_distance > full_separation) + if (AlmostEqual(Real{new_distance / Meter}, Real{full_separation / Meter}) || new_distance > full_separation) { break; } @@ -1746,7 +1750,7 @@ TEST(World, PartiallyOverlappedSquaresSeparateProperly) auto distance = GetMagnitude(position_diff); auto angle = GetAngle(position_diff); - EXPECT_TRUE(AlmostEqual(angle / 1_rad, Real{0})); + EXPECT_TRUE(AlmostEqual(Real{angle / 1_rad}, Real{0})); auto lastpos1 = body1->GetLocation(); auto lastpos2 = body2->GetLocation(); @@ -1795,15 +1799,15 @@ TEST(World, PartiallyOverlappedSquaresSeparateProperly) EXPECT_EQ(GetX(v2.linear), 0_mps); EXPECT_EQ(GetY(v2.linear), 0_mps); - EXPECT_TRUE(AlmostEqual(body1->GetAngle() / 1_rad, last_angle_1 / 1_rad)); - EXPECT_TRUE(AlmostEqual(body2->GetAngle() / 1_rad, last_angle_2 / 1_rad)); + EXPECT_TRUE(AlmostEqual(Real{body1->GetAngle() / 1_rad}, Real{last_angle_1 / 1_rad})); + EXPECT_TRUE(AlmostEqual(Real{body2->GetAngle() / 1_rad}, Real{last_angle_2 / 1_rad})); last_angle_1 = body1->GetAngle(); last_angle_2 = body2->GetAngle(); const auto new_pos_diff = body1->GetLocation() - body2->GetLocation(); const auto new_distance = GetMagnitude(new_pos_diff); - if (AlmostEqual(new_distance / Meter, full_separation / Meter) || new_distance > full_separation) + if (AlmostEqual(Real{new_distance / Meter}, Real{full_separation / Meter}) || new_distance > full_separation) { break; } @@ -1829,11 +1833,11 @@ TEST(World, PartiallyOverlappedSquaresSeparateProperly) // Body 1 moves right only. EXPECT_GT(GetX(body1->GetLocation()), GetX(lastpos1)); - EXPECT_TRUE(AlmostEqual(GetY(body1->GetLocation()) / Meter, GetY(lastpos1) / Meter)); + EXPECT_TRUE(AlmostEqual(Real{GetY(body1->GetLocation()) / Meter}, Real{GetY(lastpos1) / Meter})); // Body 2 moves left only. EXPECT_LT(GetX(body2->GetLocation()), GetX(lastpos2)); - EXPECT_TRUE(AlmostEqual(GetY(body2->GetLocation()) / Meter, GetY(lastpos2) / Meter)); + EXPECT_TRUE(AlmostEqual(Real{GetY(body2->GetLocation()) / Meter}, Real{GetY(lastpos2) / Meter})); lastpos1 = body1->GetLocation(); lastpos2 = body2->GetLocation(); @@ -1845,7 +1849,7 @@ TEST(World, PartiallyOverlappedSquaresSeparateProperly) distance = new_distance; const auto new_angle = GetAngle(new_pos_diff); - EXPECT_TRUE(AlmostEqual(angle / 1_rad, new_angle / 1_rad)); + EXPECT_TRUE(AlmostEqual(Real{angle / 1_rad}, Real{new_angle / 1_rad})); angle = new_angle; } @@ -1953,7 +1957,8 @@ TEST(World, CollidingDynamicBodies) EXPECT_GT(GetX(body_b->GetLocation()) / Meter, Real(+1) - tolerance); // and their deltas from -1 and +1 should be about equal. - EXPECT_TRUE(AlmostEqual((GetX(body_a->GetLocation()) + 1_m) / Meter, (1_m - GetX(body_b->GetLocation())) / Meter)); + EXPECT_TRUE(AlmostEqual(Real{(GetX(body_a->GetLocation()) + 1_m) / Meter}, + Real{(1_m - GetX(body_b->GetLocation())) / Meter})); EXPECT_GE(GetX(listener.body_a[0]), -1_m); EXPECT_LE(GetX(listener.body_b[0]), +1_m); @@ -1976,7 +1981,8 @@ TEST(World, CollidingDynamicBodies) EXPECT_GT(GetX(body_b->GetLocation()), +1_m); // and their deltas from -1 and +1 should be about equal. - EXPECT_TRUE(AlmostEqual((GetX(body_a->GetLocation()) + 1_m) / Meter, (1_m - GetX(body_b->GetLocation())) / Meter)); + EXPECT_TRUE(AlmostEqual(Real{(GetX(body_a->GetLocation()) + 1_m) / Meter}, + Real{(1_m - GetX(body_b->GetLocation())) / Meter})); EXPECT_LT(GetX(listener.body_a[1]), -1_m); EXPECT_GT(GetX(listener.body_b[1]), +1_m); @@ -2482,8 +2488,8 @@ TEST(World, SpeedingBulletBallWontTunnel) } else { - EXPECT_TRUE(AlmostEqual(GetX(ball_body->GetVelocity().linear) / 1_mps, - static_cast(increments) * GetX(velocity) / 1_mps)); + EXPECT_TRUE(AlmostEqual(Real{GetX(ball_body->GetVelocity().linear) / 1_mps}, + Real{static_cast(increments) * GetX(velocity) / 1_mps})); } } @@ -2524,8 +2530,8 @@ TEST(World, SpeedingBulletBallWontTunnel) } else { - EXPECT_TRUE(AlmostEqual(GetX(ball_body->GetVelocity().linear) / 1_mps, - -static_cast(increments) * GetX(velocity) / 1_mps)); + EXPECT_TRUE(AlmostEqual(Real{GetX(ball_body->GetVelocity().linear) / 1_mps}, + Real{-static_cast(increments) * GetX(velocity) / 1_mps})); } } From eecb948741a947ecd621ffc72ccf25a33d647e7c Mon Sep 17 00:00:00 2001 From: louis-langholtz Date: Mon, 27 Nov 2017 13:15:00 -0700 Subject: [PATCH 2/4] Adds to the unit test coverage for UnitVec2 and TOI related code. --- UnitTests/TimeOfImpact.cpp | 75 ++++++++++++++++++++++++++++++++++++++ UnitTests/UnitVec2.cpp | 16 ++++++++ 2 files changed, 91 insertions(+) diff --git a/UnitTests/TimeOfImpact.cpp b/UnitTests/TimeOfImpact.cpp index 6444b4a341..504c2dbab9 100644 --- a/UnitTests/TimeOfImpact.cpp +++ b/UnitTests/TimeOfImpact.cpp @@ -141,6 +141,7 @@ TEST(TOIOutput, GetName) { std::set names; EXPECT_TRUE(names.insert(GetName(TOIOutput::e_unknown)).second); + ASSERT_FALSE(names.insert(GetName(TOIOutput::e_unknown)).second); EXPECT_TRUE(names.insert(GetName(TOIOutput::e_overlapped)).second); EXPECT_TRUE(names.insert(GetName(TOIOutput::e_touching)).second); EXPECT_TRUE(names.insert(GetName(TOIOutput::e_separated)).second); @@ -149,6 +150,10 @@ TEST(TOIOutput, GetName) EXPECT_TRUE(names.insert(GetName(TOIOutput::e_maxToiIters)).second); EXPECT_TRUE(names.insert(GetName(TOIOutput::e_belowMinTarget)).second); EXPECT_TRUE(names.insert(GetName(TOIOutput::e_maxDistIters)).second); + EXPECT_TRUE(names.insert(GetName(TOIOutput::e_targetDepthExceedsTotalRadius)).second); + EXPECT_TRUE(names.insert(GetName(TOIOutput::e_minTargetSquaredOverflow)).second); + EXPECT_TRUE(names.insert(GetName(TOIOutput::e_maxTargetSquaredOverflow)).second); + EXPECT_TRUE(names.insert(GetName(TOIOutput::e_notFinite)).second); } TEST(TimeOfImpact, Overlapped) @@ -884,6 +889,76 @@ TEST(TimeOfImpact, NextAfter) } } +TEST(TimeOfImpact, TargetDepthExceedsTotalRadius) +{ + const auto radius = 1_m; + const auto pos = Length2{}; + const auto proxyA = DistanceProxy{radius, 1, &pos, nullptr}; + const auto proxyB = DistanceProxy{radius, 1, &pos, nullptr}; + + const auto conf = ToiConf{} + .UseTargetDepth(4_m) + .UseMaxRootIters(0) + .UseMaxToiIters(0) + .UseMaxDistIters(0) + .UseTolerance(0) + ; + + const auto sweepA = Sweep{ + Position{Length2{-200_m, 0_m}, 0_deg}, + Position{Length2{+100_m, 0_m}, 0_deg} + }; + const auto sweepB = Sweep{ + Position{Length2{+200_m, 0_m}, 0_deg}, + Position{Length2{-10_m, 0_m}, 0_deg} + }; + const auto output = GetToiViaSat(proxyA, sweepA, proxyB, sweepB, conf); + + EXPECT_EQ(output.state, TOIOutput::e_targetDepthExceedsTotalRadius); + EXPECT_NEAR(static_cast(output.time), 0.0, 0.0001); + EXPECT_EQ(output.stats.max_dist_iters, 0); + EXPECT_EQ(output.stats.max_root_iters, 0); + EXPECT_EQ(output.stats.toi_iters, 0); + EXPECT_EQ(output.stats.sum_dist_iters, 0); + EXPECT_EQ(output.stats.sum_finder_iters, 0); + EXPECT_EQ(output.stats.sum_root_iters, 0); +} + +TEST(TimeOfImpact, MinTargetSquaredOverflow) +{ + const auto radius = std::numeric_limits::max() / 4; + const auto pos = Length2{}; + const auto proxyA = DistanceProxy{radius, 1, &pos, nullptr}; + const auto proxyB = DistanceProxy{radius, 1, &pos, nullptr}; + + const auto conf = ToiConf{} + .UseTargetDepth(0_m) + .UseMaxRootIters(0) + .UseMaxToiIters(0) + .UseMaxDistIters(0) + .UseTolerance(0_m) + ; + + const auto sweepA = Sweep{ + Position{Length2{-200_m, 0_m}, 0_deg}, + Position{Length2{+100_m, 0_m}, 0_deg} + }; + const auto sweepB = Sweep{ + Position{Length2{+200_m, 0_m}, 0_deg}, + Position{Length2{-10_m, 0_m}, 0_deg} + }; + const auto output = GetToiViaSat(proxyA, sweepA, proxyB, sweepB, conf); + + EXPECT_EQ(output.state, TOIOutput::e_minTargetSquaredOverflow); + EXPECT_NEAR(static_cast(output.time), 0.0, 0.0001); + EXPECT_EQ(output.stats.max_dist_iters, 0); + EXPECT_EQ(output.stats.max_root_iters, 0); + EXPECT_EQ(output.stats.toi_iters, 0); + EXPECT_EQ(output.stats.sum_dist_iters, 0); + EXPECT_EQ(output.stats.sum_finder_iters, 0); + EXPECT_EQ(output.stats.sum_root_iters, 0); +} + TEST(TimeOfImpact, MaxTargetSquaredOverflow) { const auto radius = 1_m; diff --git a/UnitTests/UnitVec2.cpp b/UnitTests/UnitVec2.cpp index 14208bc113..f091071de6 100644 --- a/UnitTests/UnitVec2.cpp +++ b/UnitTests/UnitVec2.cpp @@ -150,6 +150,22 @@ TEST(UnitVec2, GetForInvalid) } } +TEST(UnitVec2, Get) +{ + EXPECT_EQ(UnitVec2::Get(Real(+1), Real(0)).first, UnitVec2::GetRight()); + EXPECT_EQ(UnitVec2::Get(Real(-1), Real(0)).first, UnitVec2::GetLeft()); + EXPECT_EQ(UnitVec2::Get(Real(0), Real(+1)).first, UnitVec2::GetTop()); + EXPECT_EQ(UnitVec2::Get(Real(0), Real(-1)).first, UnitVec2::GetBottom()); + EXPECT_EQ(UnitVec2::Get(+std::numeric_limits::max(), Real(0)).first, UnitVec2::GetRight()); + EXPECT_EQ(UnitVec2::Get(-std::numeric_limits::max(), Real(0)).first, UnitVec2::GetLeft()); + EXPECT_EQ(UnitVec2::Get(Real(0), +std::numeric_limits::max()).first, UnitVec2::GetTop()); + EXPECT_EQ(UnitVec2::Get(Real(0), -std::numeric_limits::max()).first, UnitVec2::GetBottom()); + EXPECT_EQ(UnitVec2::Get(+std::numeric_limits::min(), Real(0)).first, UnitVec2::GetRight()); + EXPECT_EQ(UnitVec2::Get(-std::numeric_limits::min(), Real(0)).first, UnitVec2::GetLeft()); + EXPECT_EQ(UnitVec2::Get(Real(0), +std::numeric_limits::min()).first, UnitVec2::GetTop()); + EXPECT_EQ(UnitVec2::Get(Real(0), -std::numeric_limits::min()).first, UnitVec2::GetBottom()); +} + TEST(UnitVec2, Absolute) { EXPECT_EQ(UnitVec2::GetZero().Absolute(), UnitVec2::GetZero()); From 5d8b043e67e16756786576c721f27455a627fd94 Mon Sep 17 00:00:00 2001 From: louis-langholtz Date: Mon, 27 Nov 2017 13:15:37 -0700 Subject: [PATCH 3/4] Fixes bug in corner case detection code. --- PlayRho/Collision/TimeOfImpact.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlayRho/Collision/TimeOfImpact.cpp b/PlayRho/Collision/TimeOfImpact.cpp index cd4e070927..8b01dde72f 100644 --- a/PlayRho/Collision/TimeOfImpact.cpp +++ b/PlayRho/Collision/TimeOfImpact.cpp @@ -75,7 +75,7 @@ TOIOutput GetToiViaSat(const DistanceProxy& proxyA, const Sweep& sweepA, //assert(minTarget > 0_m && !AlmostZero(minTarget / Meter)); const auto minTargetSquared = Square(minTarget); - if (!IsFinite(minTargetSquared) && IsFinite(minTargetSquared)) + if (!IsFinite(minTargetSquared) && IsFinite(minTarget)) { return TOIOutput{0, stats, TOIOutput::e_minTargetSquaredOverflow}; } From 46df3fd198c3612c8485f0d923222a92b2427fb0 Mon Sep 17 00:00:00 2001 From: louis-langholtz Date: Mon, 27 Nov 2017 13:18:28 -0700 Subject: [PATCH 4/4] Changes related to handling larger scales and better understanding of numerical limitations. --- PlayRho/Common/Fixed.hpp | 21 +++++++-------------- PlayRho/Common/Math.hpp | 3 ++- PlayRho/Dynamics/StepConf.hpp | 6 ++---- PlayRho/Dynamics/World.cpp | 3 +++ Testbed/Tests/SolarSystem.hpp | 10 ++++++++-- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/PlayRho/Common/Fixed.hpp b/PlayRho/Common/Fixed.hpp index 66b853807b..0c6190beba 100644 --- a/PlayRho/Common/Fixed.hpp +++ b/PlayRho/Common/Fixed.hpp @@ -761,6 +761,13 @@ namespace playrho { return Abs(x - y) <= Fixed{0, static_cast(ulp)}; } + + /// @brief Gets an invalid value. + template + constexpr Fixed GetInvalid() noexcept + { + return Fixed::GetNaN(); + } /// @brief Output stream operator. template @@ -853,13 +860,6 @@ namespace playrho const auto result = lhs.Compare(rhs); return result == Fixed32::CmpResult::GreaterThan; } - - /// @brief Gets an invalid value. - template <> - constexpr Fixed32 GetInvalid() noexcept - { - return Fixed32::GetNaN(); - } /// @brief Gets the specialized name for the Fixed32 type. /// @details Provides an interface to a specialized function for getting C-style @@ -951,13 +951,6 @@ namespace playrho template<> struct Wider { using type = Fixed64; ///< Wider type. }; - - /// @brief Gets an invalid value. - template <> - constexpr Fixed64 GetInvalid() noexcept - { - return Fixed64::GetNaN(); - } /// @brief Gets the specialized name for the Fixed64 type. /// @details Provides an interface to a specialized function for getting C-style diff --git a/PlayRho/Common/Math.hpp b/PlayRho/Common/Math.hpp index 0d2563aa46..ec46f31bf5 100644 --- a/PlayRho/Common/Math.hpp +++ b/PlayRho/Common/Math.hpp @@ -303,7 +303,8 @@ AlmostEqual(T x, T y, int ulp = 2) /// @note Modulo via std::fmod appears slower than via std::trunc. /// @sa ModuloViaTrunc template -inline auto ModuloViaFmod(T dividend, T divisor) noexcept +inline typename std::enable_if::value, T>::type +ModuloViaFmod(T dividend, T divisor) noexcept { // Note: modulo via std::fmod appears slower than via std::trunc. return static_cast(std::fmod(dividend, divisor)); diff --git a/PlayRho/Dynamics/StepConf.hpp b/PlayRho/Dynamics/StepConf.hpp index a843fb796f..b34fbb60b9 100644 --- a/PlayRho/Dynamics/StepConf.hpp +++ b/PlayRho/Dynamics/StepConf.hpp @@ -152,11 +152,9 @@ class StepConf /// @brief Target depth. /// @details Target depth of overlap for calculating the TOI for CCD elligible bodies. - /// @note Must be greater than 0. - /// @note Must not be subnormal. - /// @note Must be less than twice the world's minimum vertex radius. + /// @note Recommend value that's less than twice the world's minimum vertex radius. /// @note Used in the TOI phase of step processing. - Positive targetDepth = DefaultLinearSlop * Real{3}; + Length targetDepth = DefaultLinearSlop * Real{3}; /// @brief Tolerance. /// @details The acceptable plus or minus tolerance from the target depth for TOI calculations. diff --git a/PlayRho/Dynamics/World.cpp b/PlayRho/Dynamics/World.cpp index a298e332ce..0967e58719 100644 --- a/PlayRho/Dynamics/World.cpp +++ b/PlayRho/Dynamics/World.cpp @@ -1387,6 +1387,9 @@ World::UpdateContactsData World::UpdateContactTOIs(const StepConf& conf) // Compute the TOI for this contact (one or both bodies are active and impenetrable). // Computes the time of impact in interval [0, 1] const auto output = CalcToi(c, toiConf); + assert((output.state == TOIOutput::State::e_touching) + || (output.state == TOIOutput::State::e_separated) + || (output.state == TOIOutput::State::e_overlapped)); // Use Min function to handle floating point imprecision which possibly otherwise // could provide a TOI that's greater than 1. diff --git a/Testbed/Tests/SolarSystem.hpp b/Testbed/Tests/SolarSystem.hpp index fb391d422a..6449b785b7 100644 --- a/Testbed/Tests/SolarSystem.hpp +++ b/Testbed/Tests/SolarSystem.hpp @@ -83,6 +83,12 @@ class SolarSystem: public Test os << " km (" << minName << "), to "; os << static_cast(Real{maxRadius / 1_km}); os << " km (" << maxName << ")."; + if (std::is_same::value) + { + os << "\n\n"; + os << "Note: recompile with playrho::Real set to use double (or bigger)"; + os << " for collisions to work better at these scales."; + } auto conf = Test::Conf{}; conf.description = os.str(); @@ -93,7 +99,7 @@ class SolarSystem: public Test conf.neededSettings |= (1u << NeedDrawLabelsField); conf.neededSettings |= (1u << NeedMaxTranslation); conf.neededSettings |= (1u << NeedDeltaTime); - conf.settings.linearSlop = 1000.0f; // minRadius / 1000_m; + conf.settings.linearSlop = 200 * 1000.0f; // 200_km; conf.settings.cameraZoom = 2.2e11f; conf.settings.drawLabels = true; conf.settings.maxTranslation = std::numeric_limits::infinity(); @@ -106,7 +112,7 @@ class SolarSystem: public Test SolarSystem(): Test(GetTestConf()) { m_world.SetGravity(LinearAcceleration2{}); - const auto DynamicBD = BodyDef{}.UseType(BodyType::Dynamic); + const auto DynamicBD = BodyDef{}.UseType(BodyType::Dynamic).UseBullet(true); for (auto& sso: SolarSystemBodies) { const auto p = sso.orbitalPeriod;