#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2015 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // If the library is compiled as a DLL, this ensures symbols are exported #define NUCLEX_GEOMETRY_SOURCE 1 #include "Nuclex/Geometry/Lines/Interpolators/CubicInterpolator.h" #include "Nuclex/Geometry/Point2.h" #include namespace { // ------------------------------------------------------------------------------------------- // /// Vertex traits for Point2 types when used in interpolators struct Point2VertexTraits { /// Type that is used to measure distance between vertices public: typedef float TDistance; /// Type that is used to specify the interpolation interval public: typedef float TScalar; /// A vertex with a value of zero public: static const Nuclex::Geometry::Point2 ZeroVertex; /// Measures the distance between two vertices /// First vertex for the distance calculation /// Second vertex for the distance calculation /// The distance between the two vertices public: static TDistance DistanceBetween( const Nuclex::Geometry::Point2 &first, const Nuclex::Geometry::Point2 &second ) { return Nuclex::Geometry::Point2::DistanceBetween(first, second); } /// Sums a point with another point /// Point to which another point wlll be added /// Other point that will be added to the point public: static void Add( Nuclex::Geometry::Point2 &base, Nuclex::Geometry::Point2 amount ) { base.X += amount.X; base.Y += amount.Y; } /// Subtracts another point from a point /// Point from which another point wlll be subtracted /// Other point that will be substracted from the point public: static void Subtract( Nuclex::Geometry::Point2 &base, Nuclex::Geometry::Point2 amount ) { base.X -= amount.X; base.Y -= amount.Y; } /// Scales a point by a factor /// Point that will be scaled by a factor /// Factor the point will be scaled by public: static void Scale(Nuclex::Geometry::Point2 &base, float factor) { base.X *= factor; base.Y *= factor; } /// Rounds a scalar / interval value to the closest integer /// Scalar / interval value that will be rounded /// The closest integer to the specified scalar / interval value public: static std::size_t RoundScalarToInteger(float t) { return static_cast(t + 0.5f); } /// Truncates a scalar / interval value to the next lower integer /// Scalar / interval value that will be truncated /// The next lower integer to the specified scalar / interval value public: static std::size_t TruncateScalarToInteger(float t) { return static_cast(t); } }; // ------------------------------------------------------------------------------------------- // const Nuclex::Geometry::Point2 Point2VertexTraits::ZeroVertex(0.0f, 0.0f); // ------------------------------------------------------------------------------------------- // /// Verifies that a point lies near to another point /// First point that will be compared /// Second point that will be compared /// True if the first point is near the void ExpectIsNear( const Nuclex::Geometry::Point2 &first, const Nuclex::Geometry::Point2 &second ) { const float Tolerance = 1.e-005f; EXPECT_NEAR(first.X, second.X, Tolerance); EXPECT_NEAR(first.Y, second.Y, Tolerance); } // ------------------------------------------------------------------------------------------- // /// Interpolator we use for testing typedef Nuclex::Geometry::Lines::Interpolators::CubicInterpolator< Nuclex::Geometry::Point2, Point2VertexTraits > InterpolatorType; // ------------------------------------------------------------------------------------------- // } // anonymous namespace namespace Nuclex { namespace Geometry { namespace Lines { namespace Interpolators { // ------------------------------------------------------------------------------------------- // TEST(CubicInterpolatorTest, InterpolateByTimeHandlesEmptyPath) { std::vector> vertices; EXPECT_EQ( Point2::Zero, InterpolatorType::InterpolateByTime(vertices.begin(), vertices.end(), 0.5f) ); } // ------------------------------------------------------------------------------------------- // TEST(CubicInterpolatorTest, InterpolateByTimeHandlesSingleVertex) { std::vector> vertices; vertices.push_back(Point2(5.0f, 5.0f)); EXPECT_EQ( vertices[0], InterpolatorType::InterpolateByTime(vertices.begin(), vertices.end(), -1.1f) ); EXPECT_EQ( vertices[0], InterpolatorType::InterpolateByTime(vertices.begin(), vertices.end(), 0.0f) ); EXPECT_EQ( vertices[0], InterpolatorType::InterpolateByTime(vertices.begin(), vertices.end(), 1.1f) ); } // ------------------------------------------------------------------------------------------- // TEST(CubicInterpolatorTest, VertexCanBeInterpolatedByTime) { std::vector> vertices; vertices.push_back(Point2(1.0f, 5.0f)); vertices.push_back(Point2(10.0f, 6.0f)); vertices.push_back(Point2(9.0f, 10.0f)); vertices.push_back(Point2(5.0f, 1.0f)); EXPECT_EQ( vertices[0], InterpolatorType::InterpolateByTime(vertices.begin(), vertices.end(), 0.0f) ); ExpectIsNear( Point2(3.296875f, 5.0625f), InterpolatorType::InterpolateByTime(vertices.begin(), vertices.end(), 0.25f) ); ExpectIsNear( Point2(7.890625f, 5.1875f), InterpolatorType::InterpolateByTime(vertices.begin(), vertices.end(), 0.75f) ); ExpectIsNear( Point2(8.072f, 8.712f), InterpolatorType::InterpolateByTime(vertices.begin(), vertices.end(), 2.2f) ); ExpectIsNear( Point2(5.768f, 2.928f), InterpolatorType::InterpolateByTime(vertices.begin(), vertices.end(), 2.8f) ); } // ------------------------------------------------------------------------------------------- // TEST(CubicInterpolatorTest, InterpolateByTimeClamps) { std::vector> vertices; vertices.push_back(Point2(1.0f, 5.0f)); vertices.push_back(Point2(10.0f, 1.0f)); vertices.push_back(Point2(5.0f, 10.0f)); EXPECT_EQ( vertices[0], InterpolatorType::InterpolateByTime(vertices.begin(), vertices.end(), -0.1f) ); EXPECT_EQ( vertices[0], InterpolatorType::InterpolateByTime(vertices.begin(), vertices.end(), -2.5f) ); EXPECT_EQ( vertices[2], InterpolatorType::InterpolateByTime(vertices.begin(), vertices.end(), 2.1f) ); EXPECT_EQ( vertices[2], InterpolatorType::InterpolateByTime(vertices.begin(), vertices.end(), 4.5f) ); } // ------------------------------------------------------------------------------------------- // TEST(CubicInterpolatorTest, InterpolateByDistanceHandlesEmptyPath) { std::vector> vertices; EXPECT_EQ( Point2::Zero, InterpolatorType::InterpolateByDistance(vertices.begin(), vertices.end(), 0.5f) ); } // ------------------------------------------------------------------------------------------- // TEST(CubicInterpolatorTest, InterpolateByDistanceHandlesSingleVertex) { std::vector> vertices; vertices.push_back(Point2(5.0f, 5.0f)); EXPECT_EQ( vertices[0], InterpolatorType::InterpolateByDistance(vertices.begin(), vertices.end(), -1.1f) ); EXPECT_EQ( vertices[0], InterpolatorType::InterpolateByDistance(vertices.begin(), vertices.end(), 0.0f) ); EXPECT_EQ( vertices[0], InterpolatorType::InterpolateByDistance(vertices.begin(), vertices.end(), 1.1f) ); } // ------------------------------------------------------------------------------------------- // TEST(CubicInterpolatorTest, VertexCanBeInterpolatedByDistance) { std::vector> vertices; vertices.push_back(Point2(1.0f, 5.0f)); vertices.push_back(Point2(10.0f, 6.0f)); vertices.push_back(Point2(9.0f, 10.0f)); vertices.push_back(Point2(5.0f, 1.0f)); EXPECT_EQ( vertices[0], InterpolatorType::InterpolateByDistance(vertices.begin(), vertices.end(), 0.0f) ); ExpectIsNear( Point2(5.5348591023442699f, 5.0001860366579622f), InterpolatorType::InterpolateByDistance(vertices.begin(), vertices.end(), 4.5f) ); ExpectIsNear( Point2(5.6356805274240491f, 5.0000026729331379f), InterpolatorType::InterpolateByDistance(vertices.begin(), vertices.end(), 4.6f) ); ExpectIsNear( Point2(7.9330876753704489f, 8.4554048020316213f), InterpolatorType::InterpolateByDistance(vertices.begin(), vertices.end(), 18.0f) ); ExpectIsNear( Point2(7.8481170698040952f, 8.2902662879818756f), InterpolatorType::InterpolateByDistance(vertices.begin(), vertices.end(), 18.2f) ); } // ------------------------------------------------------------------------------------------- // TEST(CubicInterpolatorTest, InterpolateByDistanceClamps) { std::vector> vertices; vertices.push_back(Point2(1.0f, 5.0f)); vertices.push_back(Point2(10.0f, 1.0f)); vertices.push_back(Point2(5.0f, 10.0f)); EXPECT_EQ( vertices[0], InterpolatorType::InterpolateByDistance(vertices.begin(), vertices.end(), -0.1f) ); EXPECT_EQ( vertices[0], InterpolatorType::InterpolateByDistance(vertices.begin(), vertices.end(), -15.0f) ); EXPECT_EQ( vertices[2], InterpolatorType::InterpolateByDistance(vertices.begin(), vertices.end(), 23.1f) ); EXPECT_EQ( vertices[2], InterpolatorType::InterpolateByDistance(vertices.begin(), vertices.end(), 35.0f) ); } // ------------------------------------------------------------------------------------------- // TEST(CubicInterpolatorTest, LengthHandlesEmptyPath) { std::vector> vertices; EXPECT_EQ( 0.0f, InterpolatorType::EstimateLength(vertices.begin(), vertices.end()) ); } // ------------------------------------------------------------------------------------------- // TEST(CubicInterpolatorTest, LengthCanBeEstimated) { std::vector> vertices; vertices.push_back(Point2(1.0f, 5.0f)); vertices.push_back(Point2(10.0f, 6.0f)); vertices.push_back(Point2(9.0f, 10.0f)); vertices.push_back(Point2(5.0f, 1.0f)); EXPECT_NEAR( 25.599182241049128f, InterpolatorType::EstimateLength(vertices.begin(), vertices.end()), 0.001f ); } // ------------------------------------------------------------------------------------------- // }}}} // namespace Nuclex::Geometry::Lines::Interpolators