diff options
author | Maxim Pimenov <m@maps.me> | 2018-07-27 15:09:39 +0300 |
---|---|---|
committer | Vladimir Byko-Ianko <bykoianko@gmail.com> | 2018-08-01 13:45:25 +0300 |
commit | 2bc35257235bd3e4a6c6ba94f44d3eea8ad6a8fd (patch) | |
tree | 69e9ba2d4d8730cf56a1aebb0e6721faaee0bb05 /geometry | |
parent | 159e70c4f76754f884b97f11d869ee0bd4bd338b (diff) |
[geometry] Functors instead of factories in simplification.hpp.
Diffstat (limited to 'geometry')
-rw-r--r-- | geometry/geometry_tests/simplification_test.cpp | 45 | ||||
-rw-r--r-- | geometry/parametrized_segment.hpp | 21 | ||||
-rw-r--r-- | geometry/simplification.hpp | 41 |
3 files changed, 55 insertions, 52 deletions
diff --git a/geometry/geometry_tests/simplification_test.cpp b/geometry/geometry_tests/simplification_test.cpp index 81748256aa..b9e26aea69 100644 --- a/geometry/geometry_tests/simplification_test.cpp +++ b/geometry/geometry_tests/simplification_test.cpp @@ -19,18 +19,10 @@ using namespace std; namespace { -struct DistanceFact -{ - m2::ParametrizedSegment<m2::PointD> operator()(m2::PointD const & p0, m2::PointD const & p1) const - { - return m2::ParametrizedSegment<m2::PointD>(p0, p1); - } -}; - using P = m2::PointD; +using DistanceFn = m2::SquaredDistanceFromSegmentToPoint<P>; using PointOutput = BackInsertFunctor<vector<m2::PointD>>; -typedef void (*SimplifyFn)(m2::PointD const *, m2::PointD const *, double, DistanceFact, - PointOutput); +typedef void (*SimplifyFn)(m2::PointD const *, m2::PointD const *, double, DistanceFn, PointOutput); struct LargePolylineTestData { @@ -46,7 +38,7 @@ void TestSimplificationSmoke(SimplifyFn simplifyFn) m2::PointD const points[] = {P(0.0, 1.0), P(2.2, 3.6), P(3.2, 3.6)}; double const epsilon = 0.1; vector<m2::PointD> result, expectedResult(points, points + 3); - simplifyFn(points, points + 3, epsilon, DistanceFact(), MakeBackInsertFunctor(result)); + simplifyFn(points, points + 3, epsilon, DistanceFn(), MakeBackInsertFunctor(result)); TEST_EQUAL(result, expectedResult, (epsilon)); } @@ -56,7 +48,7 @@ void TestSimplificationOfLine(SimplifyFn simplifyFn) for (double epsilon = numeric_limits<double>::denorm_min(); epsilon < 1000; epsilon *= 2) { vector<m2::PointD> result, expectedResult(points, points + 2); - simplifyFn(points, points + 2, epsilon, DistanceFact(), MakeBackInsertFunctor(result)); + simplifyFn(points, points + 2, epsilon, DistanceFn(), MakeBackInsertFunctor(result)); TEST_EQUAL(result, expectedResult, (epsilon)); } } @@ -66,7 +58,7 @@ void TestSimplificationOfPoly(m2::PointD const * points, size_t count, SimplifyF for (double epsilon = 0.00001; epsilon < 0.11; epsilon *= 10) { vector<m2::PointD> result; - simplifyFn(points, points + count, epsilon, DistanceFact(), MakeBackInsertFunctor(result)); + simplifyFn(points, points + count, epsilon, DistanceFn(), MakeBackInsertFunctor(result)); // LOG(LINFO, ("eps:", epsilon, "size:", result.size())); TEST_GREATER(result.size(), 1, ()); @@ -76,24 +68,23 @@ void TestSimplificationOfPoly(m2::PointD const * points, size_t count, SimplifyF } } -void SimplifyNearOptimal10(m2::PointD const * f, m2::PointD const * l, double e, - DistanceFact distFact, PointOutput out) +void SimplifyNearOptimal10(m2::PointD const * f, m2::PointD const * l, double e, DistanceFn distFn, + PointOutput out) { - SimplifyNearOptimal(10, f, l, e, distFact, out); + SimplifyNearOptimal(10, f, l, e, distFn, out); } -void SimplifyNearOptimal20(m2::PointD const * f, m2::PointD const * l, double e, - DistanceFact distFact, PointOutput out) +void SimplifyNearOptimal20(m2::PointD const * f, m2::PointD const * l, double e, DistanceFn distFn, + PointOutput out) { - SimplifyNearOptimal(20, f, l, e, distFact, out); + SimplifyNearOptimal(20, f, l, e, distFn, out); } void CheckDPStrict(P const * arr, size_t n, double eps, size_t expectedCount) { vector<P> vec; - DistanceFact distFact; - SimplifyDP(arr, arr + n, eps, distFact, - AccumulateSkipSmallTrg<DistanceFact, P>(distFact, vec, eps)); + DistanceFn distFn; + SimplifyDP(arr, arr + n, eps, distFn, AccumulateSkipSmallTrg<DistanceFn, P>(distFn, vec, eps)); TEST_GREATER(vec.size(), 1, ()); TEST_EQUAL(arr[0], vec.front(), ()); @@ -106,8 +97,8 @@ void CheckDPStrict(P const * arr, size_t n, double eps, size_t expectedCount) for (size_t i = 2; i < vec.size(); ++i) { - auto const d = DistanceFact()(vec[i - 2], vec[i]); - TEST_GREATER_OR_EQUAL(d.SquaredDistanceToPoint(vec[i - 1]), eps, ()); + auto const d = DistanceFn(); + TEST_GREATER_OR_EQUAL(d(vec[i - 2], vec[i], vec[i - 1]), eps, ()); } } } // namespace @@ -118,14 +109,14 @@ UNIT_TEST(Simplification_TestDataIsCorrect) // LOG(LINFO, ("Polyline test size:", LargePolylineTestData::m_Size)); } -UNIT_TEST(Simplification_DP_Smoke) { TestSimplificationSmoke(&SimplifyDP<DistanceFact>); } +UNIT_TEST(Simplification_DP_Smoke) { TestSimplificationSmoke(&SimplifyDP<DistanceFn>); } -UNIT_TEST(Simplification_DP_Line) { TestSimplificationOfLine(&SimplifyDP<DistanceFact>); } +UNIT_TEST(Simplification_DP_Line) { TestSimplificationOfLine(&SimplifyDP<DistanceFn>); } UNIT_TEST(Simplification_DP_Polyline) { TestSimplificationOfPoly(LargePolylineTestData::m_Data, LargePolylineTestData::m_Size, - &SimplifyDP<DistanceFact>); + &SimplifyDP<DistanceFn>); } UNIT_TEST(Simplification_Opt_Smoke) { TestSimplificationSmoke(&SimplifyNearOptimal10); } diff --git a/geometry/parametrized_segment.hpp b/geometry/parametrized_segment.hpp index b684659171..b79e9cc913 100644 --- a/geometry/parametrized_segment.hpp +++ b/geometry/parametrized_segment.hpp @@ -1,5 +1,3 @@ -// todo(@m) Do we need helpers like ClosestPointTo(p0, p1)? - #pragma once #include "geometry/point2d.hpp" @@ -16,12 +14,12 @@ namespace m2 // The parametrization is of the form // p(t) = p0 + t * dir. // Other conditions: -// dir is normalized (p1 - p0) +// dir is the normalized (p1 - p0) vector. // length(dir) = 1. // p(0) = p0. // p(T) = p1 with T = length(p1 - p0). // -// The points with t in [0, T] correspond to the points of the segment. +// The points with t in [0, T] are the points of the segment. template <typename Point> class ParametrizedSegment { @@ -78,4 +76,19 @@ public: m2::PointD m_d; double m_length; }; + +// This functor is here only for backward compatibility. It is not obvious +// when looking at a call site whether x should be the first or the last parameter to the fuction. +// For readability, consider creating a parametrized segment and using its methods instead +// of using this functor. +template <typename Point> +struct SquaredDistanceFromSegmentToPoint +{ + // Returns squared distance from the segment [a, b] to the point x. + double operator()(Point const & a, Point const & b, Point const & x) const + { + ParametrizedSegment<Point> segment(a, b); + return segment.SquaredDistanceToPoint(x); + } +}; } // namespace m2 diff --git a/geometry/simplification.hpp b/geometry/simplification.hpp index f04ad3ac54..95e63b68ae 100644 --- a/geometry/simplification.hpp +++ b/geometry/simplification.hpp @@ -20,17 +20,16 @@ namespace impl { ///@name This functions take input range NOT like STL does: [first, last]. //@{ -template <typename SegmentFact, typename Iter> -std::pair<double, Iter> MaxDistance(Iter first, Iter last, SegmentFact & segFact) +template <typename DistanceFn, typename Iter> +std::pair<double, Iter> MaxDistance(Iter first, Iter last, DistanceFn & distFn) { std::pair<double, Iter> res(0.0, last); if (std::distance(first, last) <= 1) return res; - auto const segment = segFact(m2::PointD(*first), m2::PointD(*last)); for (Iter i = first + 1; i != last; ++i) { - double const d = segment.SquaredDistanceToPoint(m2::PointD(*i)); + double const d = distFn(m2::PointD(*first), m2::PointD(*last), m2::PointD(*i)); if (res.first < d) { res.first = d; @@ -42,18 +41,18 @@ std::pair<double, Iter> MaxDistance(Iter first, Iter last, SegmentFact & segFact } // Actual SimplifyDP implementation. -template <typename SegmentFact, typename Iter, typename Out> -void SimplifyDP(Iter first, Iter last, double epsilon, SegmentFact & segFact, Out & out) +template <typename DistanceFn, typename Iter, typename Out> +void SimplifyDP(Iter first, Iter last, double epsilon, DistanceFn & distFn, Out & out) { - std::pair<double, Iter> maxDist = impl::MaxDistance(first, last, segFact); + std::pair<double, Iter> maxDist = impl::MaxDistance(first, last, distFn); if (maxDist.second == last || maxDist.first < epsilon) { out(*last); } else { - impl::SimplifyDP(first, maxDist.second, epsilon, segFact, out); - impl::SimplifyDP(maxDist.second, last, epsilon, segFact, out); + impl::SimplifyDP(first, maxDist.second, epsilon, distFn, out); + impl::SimplifyDP(maxDist.second, last, epsilon, distFn, out); } } //@} @@ -72,13 +71,13 @@ struct SimplifyOptimalRes // Douglas-Peucker algorithm for STL-like range [beg, end). // Iteratively includes the point with max distance from the current simplification. // Average O(n log n), worst case O(n^2). -template <typename SegmentFact, typename Iter, typename Out> -void SimplifyDP(Iter beg, Iter end, double epsilon, SegmentFact segFact, Out out) +template <typename DistanceFn, typename Iter, typename Out> +void SimplifyDP(Iter beg, Iter end, double epsilon, DistanceFn distFn, Out out) { if (beg != end) { out(*beg); - impl::SimplifyDP(beg, end - 1, epsilon, segFact, out); + impl::SimplifyDP(beg, end - 1, epsilon, distFn, out); } } @@ -88,9 +87,9 @@ void SimplifyDP(Iter beg, Iter end, double epsilon, SegmentFact segFact, Out out // which limits the number of points to try, that produce error > epsilon. // Essentially, it's a trade-off between optimality and performance. // Values around 20 - 200 are reasonable. -template <typename SegmentFact, typename Iter, typename Out> +template <typename DistanceFn, typename Iter, typename Out> void SimplifyNearOptimal(int kMaxFalseLookAhead, Iter beg, Iter end, double epsilon, - SegmentFact segFact, Out out) + DistanceFn distFn, Out out) { int32_t const n = static_cast<int32_t>(end - beg); if (n <= 2) @@ -109,7 +108,7 @@ void SimplifyNearOptimal(int kMaxFalseLookAhead, Iter beg, Iter end, double epsi uint32_t const newPointCount = F[j].m_PointCount + 1; if (newPointCount < F[i].m_PointCount) { - if (impl::MaxDistance(beg + i, beg + j, segFact).first < epsilon) + if (impl::MaxDistance(beg + i, beg + j, distFn).first < epsilon) { F[i].m_NextPoint = j; F[i].m_PointCount = newPointCount; @@ -128,12 +127,12 @@ void SimplifyNearOptimal(int kMaxFalseLookAhead, Iter beg, Iter end, double epsi // Additional points filter to use in simplification. // SimplifyDP can produce points that define degenerate triangle. -template <class SegmentFact, class Point> +template <class DistanceFn, class Point> class AccumulateSkipSmallTrg { public: - AccumulateSkipSmallTrg(SegmentFact & segFact, std::vector<Point> & vec, double eps) - : m_segFact(segFact), m_vec(vec), m_eps(eps) + AccumulateSkipSmallTrg(DistanceFn & distFn, std::vector<Point> & vec, double eps) + : m_distFn(distFn), m_vec(vec), m_eps(eps) { } @@ -143,8 +142,8 @@ public: size_t count; while ((count = m_vec.size()) >= 2) { - auto const segment = m_segFact(m2::PointD(m_vec[count - 2]), m2::PointD(p)); - if (segment.SquaredDistanceToPoint(m2::PointD(m_vec[count - 1])) < m_eps) + if (m_distFn(m2::PointD(m_vec[count - 2]), m2::PointD(p), m2::PointD(m_vec[count - 1])) < + m_eps) m_vec.pop_back(); else break; @@ -154,7 +153,7 @@ public: } private: - SegmentFact & m_segFact; + DistanceFn & m_distFn; std::vector<Point> & m_vec; double m_eps; }; |