#pragma once #include "base/assert.hpp" #include "base/base.hpp" #include "base/math.hpp" #include "base/matrix.hpp" #include #include #include #include #include #include namespace m2 { template class Point { public: using value_type = T; T x, y; Point() : x(T()), y(T()) {} Point(T x_, T y_) : x(x_), y(y_) {} template explicit Point(Point const & u) : x(u.x), y(u.y) { } static Point Zero() { return Point(0, 0); } static Point Max() { return Point(std::numeric_limits::max(), std::numeric_limits::max());} bool EqualDxDy(Point const & p, T eps) const { return ((fabs(x - p.x) < eps) && (fabs(y - p.y) < eps)); } T SquaredLength(Point const & p) const { return pow(x - p.x, 2.0) + pow(y - p.y, 2.0); } T SquaredLength() const { return x * x + y * y; } double Length(Point const & p) const { return std::sqrt(SquaredLength(p)); } bool IsAlmostZero() const { return AlmostEqualULPs(*this, Point(0, 0)); } Point Move(T len, T ang) const { return Point(x + len * cos(ang), y + len * sin(ang)); } Point Move(T len, T angSin, T angCos) const { return m2::Point(x + len * angCos, y + len * angSin); } Point const & operator-=(Point const & a) { x -= a.x; y -= a.y; return *this; } Point const & operator+=(Point const & a) { x += a.x; y += a.y; return *this; } template Point const & operator*=(U const & k) { x = static_cast(x * k); y = static_cast(y * k); return *this; } template Point const & operator=(Point const & a) { x = static_cast(a.x); y = static_cast(a.y); return *this; } bool operator==(m2::Point const & p) const { return x == p.x && y == p.y; } bool operator!=(m2::Point const & p) const { return !(*this == p); } m2::Point operator+(m2::Point const & pt) const { return m2::Point(x + pt.x, y + pt.y); } m2::Point operator-(m2::Point const & pt) const { return m2::Point(x - pt.x, y - pt.y); } m2::Point operator-() const { return m2::Point(-x, -y); } m2::Point operator*(T scale) const { return m2::Point(x * scale, y * scale); } m2::Point const operator*(math::Matrix const & m) const { m2::Point res; res.x = x * m(0, 0) + y * m(1, 0) + m(2, 0); res.y = x * m(0, 1) + y * m(1, 1) + m(2, 1); return res; } m2::Point operator/(T scale) const { return m2::Point(x / scale, y / scale); } m2::Point mid(m2::Point const & p) const { return m2::Point((x + p.x) * 0.5, (y + p.y) * 0.5); } /// @name VectorOperationsOnPoint double Length() const { return std::sqrt(SquaredLength()); } Point Normalize() const { if (IsAlmostZero()) return Zero(); double const length = this->Length(); return Point(x / length, y / length); } std::pair, Point> Normals(T prolongationFactor = 1) const { T const prolongatedX = prolongationFactor * x; T const prolongatedY = prolongationFactor * y; return std::pair, Point>( Point(static_cast(-prolongatedY), static_cast(prolongatedX)), Point(static_cast(prolongatedY), static_cast(-prolongatedX))); } m2::Point const & operator*=(math::Matrix const & m) { T tempX = x; x = tempX * m(0, 0) + y * m(1, 0) + m(2, 0); y = tempX * m(0, 1) + y * m(1, 1) + m(2, 1); return *this; } void Rotate(double angle) { T cosAngle = cos(angle); T sinAngle = sin(angle); T oldX = x; x = cosAngle * oldX - sinAngle * y; y = sinAngle * oldX + cosAngle * y; } // Returns vector rotated 90 degrees counterclockwise. Point Ort() const { return Point(-y, x); } void Transform(m2::Point const & org, m2::Point const & dx, m2::Point const & dy) { T oldX = x; x = org.x + oldX * dx.x + y * dy.x; y = org.y + oldX * dx.y + y * dy.y; } struct Hash { size_t operator()(m2::Point const & p) const { return base::Hash(p.x, p.y); } }; }; using PointF = Point; using PointD = Point; using PointU = Point; using PointU64 = Point; using PointI = Point; using PointI64 = Point; template Point const operator-(Point const & a, Point const & b) { return Point(a.x - b.x, a.y - b.y); } template Point const operator+(Point const & a, Point const & b) { return Point(a.x + b.x, a.y + b.y); } template T const DotProduct(Point const & a, Point const & b) { return a.x * b.x + a.y * b.y; } template T const CrossProduct(Point const & a, Point const & b) { return a.x * b.y - a.y * b.x; } template Point const Rotate(Point const & pt, T a) { Point res(pt); res.Rotate(a); return res; } template Point const Shift(Point const & pt, U const & dx, U const & dy) { return Point(pt.x + dx, pt.y + dy); } template Point const Shift(Point const & pt, Point const & offset) { return Shift(pt, offset.x, offset.y); } template Point const Floor(Point const & pt) { Point res; res.x = floor(pt.x); res.y = floor(pt.y); return res; } template std::string DebugPrint(m2::Point const & p) { std::ostringstream out; out.precision(20); out << "m2::Point<" << typeid(T).name() << ">(" << p.x << ", " << p.y << ")"; return out.str(); } template bool AlmostEqualAbs(m2::Point const & a, m2::Point const & b, double const eps) { return base::AlmostEqualAbs(a.x, b.x, eps) && base::AlmostEqualAbs(a.y, b.y, eps); } template bool AlmostEqualULPs(m2::Point const & a, m2::Point const & b, unsigned int maxULPs = 256) { return base::AlmostEqualULPs(a.x, b.x, maxULPs) && base::AlmostEqualULPs(a.y, b.y, maxULPs); } /// Calculate three points of a triangle (p1, p2 and p3) which give an arrow that /// presents an equilateral triangle with the median /// starting at point b and having direction b,e. /// The height of the equilateral triangle is l and the base of the triangle is 2 * w template > void GetArrowPoints(PointT const & b, PointT const & e, T w, T l, std::array, 3> & arr) { ASSERT(!m2::AlmostEqualULPs(b, e), ()); PointT const beVec = e - b; PointT beNormalizedVec = beVec.Normalize(); std::pair beNormVecs = beNormalizedVec.Normals(w); arr[0] = e + beNormVecs.first; arr[1] = e + beNormalizedVec * l; arr[2] = e + beNormVecs.second; } /// Returns a point which is belonged to the segment p1, p2 with respet the indent shiftFromP1 from /// p1. If shiftFromP1 is more the distance between (p1, p2) it returns p2. If shiftFromP1 is less /// or equal zero it returns p1. template Point PointAtSegment(Point const & p1, Point const & p2, T shiftFromP1) { Point p12 = p2 - p1; shiftFromP1 = base::clamp(shiftFromP1, static_cast(0.0), static_cast(p12.Length())); return p1 + p12.Normalize() * shiftFromP1; } template TArchive & operator>>(TArchive & ar, m2::Point & pt) { ar >> pt.x; ar >> pt.y; return ar; } template TArchive & operator<<(TArchive & ar, m2::Point const & pt) { ar << pt.x; ar << pt.y; return ar; } template bool operator<(Point const & l, Point const & r) { if (l.x != r.x) return l.x < r.x; return l.y < r.y; } } // namespace m2 namespace base { template bool AlmostEqualULPs(m2::Point const & p1, m2::Point const & p2, unsigned int maxULPs = 256) { return m2::AlmostEqualULPs(p1, p2, maxULPs); } template bool AlmostEqualAbs(m2::Point const & p1, m2::Point const & p2, double const & eps) { return m2::AlmostEqualAbs(p1, p2, eps); } } // namespace base