Skip to content

Commit

Permalink
Merge pull request #224 from louis-langholtz/for-real-concept-2017112701
Browse files Browse the repository at this point in the history
For real concept 2017112701
  • Loading branch information
louis-langholtz authored Nov 27, 2017
2 parents 6ddc006 + 46df3fd commit c5c6d81
Show file tree
Hide file tree
Showing 16 changed files with 215 additions and 196 deletions.
2 changes: 1 addition & 1 deletion PlayRho/Collision/Simplex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion PlayRho/Collision/TimeOfImpact.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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};
}
Expand Down
46 changes: 32 additions & 14 deletions PlayRho/Common/Fixed.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,38 @@ namespace playrho
&& (value < Fixed<BT, FB>::GetInfinity());
}

/// @brief Computes the rounded value of the given value.
template <typename BT, unsigned int FB>
inline Fixed<BT, FB> RoundOff(Fixed<BT, FB> value, std::uint32_t precision = 100000)
{
const auto factor = Fixed<BT, FB>(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 <code>true</code> if the given value is almost zero, <code>false</code> otherwise.
template <typename BT, unsigned int FB>
constexpr inline bool AlmostZero(Fixed<BT, FB> value)
{
return value == 0;
}

/// @brief Determines whether the given two values are "almost equal".
template <typename BT, unsigned int FB>
constexpr inline bool AlmostEqual(Fixed<BT, FB> x, Fixed<BT, FB> y, int ulp = 2)
{
return Abs(x - y) <= Fixed<BT, FB>{0, static_cast<std::uint32_t>(ulp)};
}

/// @brief Gets an invalid value.
template <typename BT, unsigned int FB>
constexpr Fixed<BT, FB> GetInvalid() noexcept
{
return Fixed<BT, FB>::GetNaN();
}

/// @brief Output stream operator.
template <typename BT, unsigned int FB>
inline ::std::ostream& operator<<(::std::ostream& os, const Fixed<BT, FB>& value)
Expand Down Expand Up @@ -828,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
Expand Down Expand Up @@ -926,13 +951,6 @@ namespace playrho
template<> struct Wider<Fixed32> {
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
Expand Down
125 changes: 11 additions & 114 deletions PlayRho/Common/Math.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ inline typename std::enable_if<std::is_arithmetic<T>::value, bool>::type IsFinit

/// @brief Rounds the given value.
template <typename T>
inline T Round(T arg)
inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type Round(T arg)
{
return std::round(arg);
}
Expand Down Expand Up @@ -236,15 +236,6 @@ inline auto Atan2(T y, T x)
return Angle{static_cast<Real>(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<Real>(std::atan2(y, x)) * Radian};
}

/// @brief Computes the average of the given values.
template <typename T>
inline auto Average(Span<const T> span)
Expand All @@ -264,54 +255,15 @@ inline auto Average(Span<const T> span)

/// @brief Computes the rounded value of the given value.
template <typename T>
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<std::int64_t>(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<std::is_arithmetic<T>::value, T>::type
RoundOff(T value, unsigned precision = 100000)
{
const auto factor = double(static_cast<std::int64_t>(precision));
const auto factor = static_cast<T>(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<std::int64_t>(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)};
}
Expand All @@ -333,81 +285,26 @@ AlmostZero(T value)
return Abs(value) < std::numeric_limits<T>::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 <code>true</code> if the given value is almost zero, <code>false</code> 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 <code>true</code> if the given value is almost zero, <code>false</code> 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<float>::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<double>::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 <typename T>
constexpr inline typename std::enable_if<std::is_floating_point<T>::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
// 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<long double>::epsilon() * Abs(x + y) * ulp)) || AlmostZero(x - y);
return (Abs(x - y) < (std::numeric_limits<T>::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<std::uint32_t>(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<std::uint32_t>(ulp)};
}
#endif

/// @brief Modulo operation using std::fmod.
/// @note Modulo via std::fmod appears slower than via std::trunc.
/// @sa ModuloViaTrunc
template <typename T>
inline auto ModuloViaFmod(T dividend, T divisor) noexcept
inline typename std::enable_if<std::is_floating_point<T>::value, T>::type
ModuloViaFmod(T dividend, T divisor) noexcept
{
// Note: modulo via std::fmod appears slower than via std::trunc.
return static_cast<T>(std::fmod(dividend, divisor));
Expand Down
6 changes: 2 additions & 4 deletions PlayRho/Dynamics/StepConf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Length> 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.
Expand Down
3 changes: 3 additions & 0 deletions PlayRho/Dynamics/World.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 8 additions & 2 deletions Testbed/Tests/SolarSystem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ class SolarSystem: public Test
os << " km (" << minName << "), to ";
os << static_cast<float>(Real{maxRadius / 1_km});
os << " km (" << maxName << ").";
if (std::is_same<Real, float>::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();
Expand All @@ -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<float>::infinity();
Expand All @@ -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;
Expand Down
30 changes: 15 additions & 15 deletions UnitTests/CollideShapes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit c5c6d81

Please sign in to comment.