#pragma once #include "geometry/parametrized_segment.hpp" #include "geometry/point2d.hpp" #include "geometry/point_with_altitude.hpp" #include "geometry/rect2d.hpp" #include "base/internal/message.hpp" #include <algorithm> #include <cstddef> #include <initializer_list> #include <iterator> #include <limits> #include <string> #include <utility> #include <vector> namespace m2 { /// \returns a pair of minimum squared distance from |point| to the closest segment and /// a zero-based closest segment index in points in range [|beginIt|, |endIt|). template <typename It, typename T> std::pair<double, uint32_t> CalcMinSquaredDistance(It beginIt, It endIt, m2::Point<T> const & point) { CHECK_GREATER(std::distance(beginIt, endIt), 1, ()); auto squaredClosestSegDist = std::numeric_limits<double>::max(); auto i = beginIt; auto closestSeg = beginIt; for (auto j = i + 1; j != endIt; ++i, ++j) { m2::ParametrizedSegment<m2::Point<T>> seg(geometry::GetPoint(*i), geometry::GetPoint(*j)); auto const squaredSegDist = seg.SquaredDistanceToPoint(point); if (squaredSegDist < squaredClosestSegDist) { closestSeg = i; squaredClosestSegDist = squaredSegDist; } } return std::make_pair(squaredClosestSegDist, static_cast<uint32_t>(std::distance(beginIt, closestSeg))); } template <typename T> class Polyline { std::vector<Point<T>> m_points; public: using Container = std::vector<Point<T>>; using Iter = typename Container::const_iterator; Polyline() {} Polyline(std::initializer_list<Point<T>> const & points) : m_points(points) { ASSERT_GREATER(m_points.size(), 1, ()); } explicit Polyline(std::vector<Point<T>> const & points) : m_points(points) { ASSERT_GREATER(m_points.size(), 1, ()); } explicit Polyline(std::vector<Point<T>> && points) : m_points(std::move(points)) { ASSERT_GREATER(m_points.size(), 1, ()); } template <class Iter> Polyline(Iter beg, Iter end) : m_points(beg, end) { ASSERT_GREATER(m_points.size(), 1, ()); } double GetLength() const { // @todo add cached value and lazy init double dist = 0; for (size_t i = 1; i < m_points.size(); ++i) dist += m_points[i - 1].Length(m_points[i]); return dist; } double GetLength(size_t pointIndex) const { double dist = 0; for (size_t i = 0; i < std::min(pointIndex, m_points.size() - 1); ++i) dist += m_points[i].Length(m_points[i + 1]); return dist; } std::pair<double, uint32_t> CalcMinSquaredDistance(m2::Point<T> const & point) const { return m2::CalcMinSquaredDistance(m_points.begin(), m_points.end(), point); } Rect<T> GetLimitRect() const { // @todo add cached value and lazy init m2::Rect<T> rect; for (size_t i = 0; i < m_points.size(); ++i) rect.Add(m_points[i]); return rect; } void Clear() { m_points.clear(); } void Add(Point<T> const & pt) { m_points.push_back(pt); } void Append(Polyline const & poly) { m_points.insert(m_points.end(), poly.m_points.cbegin(), poly.m_points.cend()); } template <class Iter> void Append(Iter beg, Iter end) { m_points.insert(m_points.end(), beg, end); } void PopBack() { ASSERT(!m_points.empty(), ()); m_points.pop_back(); } void Swap(Polyline & rhs) { m_points.swap(rhs.m_points); } size_t GetSize() const { return m_points.size(); } bool operator==(Polyline const & rhs) const { return m_points == rhs.m_points; } Iter Begin() const { return m_points.begin(); } Iter End() const { return m_points.end(); } Point<T> const & Front() const { return m_points.front(); } Point<T> const & Back() const { return m_points.back(); } Point<T> const & GetPoint(size_t idx) const { ASSERT_LESS(idx, m_points.size(), ()); return m_points[idx]; } Point<T> GetPointByDistance(T distance) const { if (distance < 0) return m_points.front(); T dist = 0; for (size_t i = 1; i < m_points.size(); ++i) { T const oldDist = dist; dist += m_points[i - 1].Length(m_points[i]); if (distance <= dist) { T const t = (distance - oldDist) / (dist - oldDist); return m_points[i - 1] * (1 - t) + m_points[i] * t; } } return m_points.back(); } std::vector<Point<T>> ExtractSegment(size_t segmentIndex, bool reversed) const { if (segmentIndex + 1 >= m_points.size()) return std::vector<Point<T>>(); return reversed ? std::vector<Point<T>>{m_points[segmentIndex + 1], m_points[segmentIndex]} : std::vector<Point<T>>{m_points[segmentIndex], m_points[segmentIndex + 1]}; } std::vector<Point<T>> ExtractSegment(size_t startPointIndex, size_t endPointIndex) const { if (startPointIndex > endPointIndex || startPointIndex >= m_points.size() || endPointIndex >= m_points.size()) { return std::vector<Point<T>>(); } std::vector<Point<T>> result(endPointIndex - startPointIndex + 1); for (size_t i = startPointIndex, j = 0; i <= endPointIndex; ++i, ++j) result[j] = m_points[i]; return result; } std::vector<Point<T>> const & GetPoints() const { return m_points; } friend std::string DebugPrint(Polyline const & p) { return ::DebugPrint(p.m_points); } }; using PolylineD = Polyline<double>; } // namespace m2