#pragma once #include "geometry/point2d.hpp" #include "geometry/rect2d.hpp" #include "geometry/rect_intersect.hpp" #include "geometry/angles.hpp" #include "base/math.hpp" namespace m2 { /// axis aligned rect template class AnyRect { ang::Angle m_angle; Point m_zero; Rect m_rect; static Point const Convert(Point const & p, Point const & fromI, Point const & fromJ, Point const & toI, Point const & toJ) { Point res; res.x = p.x * DotProduct(fromI, toI) + p.y * DotProduct(fromJ, toI); res.y = p.x * DotProduct(fromI, toJ) + p.y * DotProduct(fromJ, toJ); return res; } public: AnyRect() : m_zero(0, 0), m_rect() {} /// creating from regular rect explicit AnyRect(Rect const & r) { if (r.IsValid()) { m_zero = Point(r.minX(), r.minY()); m_rect = Rect(0, 0, r.SizeX(), r.SizeY()); } else { m_zero = Point(0, 0); m_rect = r; } } AnyRect(Point const & zero, ang::Angle const & angle, Rect const & r) : m_angle(angle), m_rect(r) { m_zero = Convert(zero, Point(1, 0), Point(0, 1), i(), j()); } Point const & LocalZero() const { return m_zero; } Point const GlobalZero() const { return Convert(m_zero, i(), j(), m2::Point(1, 0), m2::Point(0, 1)); } Point const i() const { return Point(m_angle.cos(), m_angle.sin()); } Point const j() const { return Point(-m_angle.sin(), m_angle.cos()); } void SetAngle(ang::Angle const & a) { m2::Point glbZero = GlobalZero(); m_angle = a; m_zero = Convert(glbZero, Point(1, 0), Point(0, 1), i(), j()); } ang::Angle const & Angle() const { return m_angle; } Point const GlobalCenter() const { return ConvertFrom(m_rect.Center()); } Point const LocalCenter() const { return m_rect.Center(); } T GetMaxSize() const { return max(m_rect.SizeX(), m_rect.SizeY()); } bool EqualDxDy(AnyRect const & r, T eps) const { m2::Point arr1[4]; GetGlobalPoints(arr1); sort(arr1, arr1 + 4); m2::Point arr2[4]; r.GetGlobalPoints(arr2); sort(arr2, arr2 + 4); for (size_t i = 0; i < 4; ++i) if (!arr1[i].EqualDxDy(arr2[i], eps)) return false; return true; } bool IsPointInside(Point const & pt) const { return m_rect.IsPointInside(ConvertTo(pt)); } bool IsRectInside(AnyRect const & r) const { m2::Point pts[4]; r.GetGlobalPoints(pts); ConvertTo(pts, 4); return m_rect.IsPointInside(pts[0]) && m_rect.IsPointInside(pts[1]) && m_rect.IsPointInside(pts[2]) && m_rect.IsPointInside(pts[3]); } bool IsIntersect(AnyRect const & r) const { m2::Point pts[4]; if (r.GetLocalRect() == Rect()) return false; r.GetGlobalPoints(pts); ConvertTo(pts, 4); m2::Rect r1(pts[0], pts[0]); r1.Add(pts[1]); r1.Add(pts[2]); r1.Add(pts[3]); if (!GetLocalRect().IsIntersect(r1)) return false; if (r.IsRectInside(*this)) return true; if (IsRectInside(r)) return true; return Intersect(GetLocalRect(), pts[0], pts[1]) || Intersect(GetLocalRect(), pts[1], pts[2]) || Intersect(GetLocalRect(), pts[2], pts[3]) || Intersect(GetLocalRect(), pts[3], pts[0]); } /// Convert into coordinate system of this AnyRect Point const ConvertTo(Point const & p) const { m2::Point i1(1, 0); m2::Point j1(0, 1); return Convert(p - Convert(m_zero, i(), j(), i1, j1), i1, j1, i(), j()); } void ConvertTo(Point * pts, size_t count) const { for (size_t i = 0; i < count; ++i) pts[i] = ConvertTo(pts[i]); } /// Convert into global coordinates from the local coordinates of this AnyRect Point const ConvertFrom(Point const & p) const { return Convert(p + m_zero, i(), j(), m2::Point(1, 0), m2::Point(0, 1)); } void ConvertFrom(Point * pts, size_t count) const { for (size_t i = 0; i < count; ++i) pts[i] = ConvertFrom(pts[i]); } Rect const & GetLocalRect() const { return m_rect; } Rect const GetGlobalRect() const { Point pts[4]; GetGlobalPoints(pts); Rect res(pts[0], pts[1]); res.Add(pts[2]); res.Add(pts[3]); return res; } void GetGlobalPoints(Point * pts) const { pts[0] = Point(ConvertFrom(Point(m_rect.minX(), m_rect.minY()))); pts[1] = Point(ConvertFrom(Point(m_rect.minX(), m_rect.maxY()))); pts[2] = Point(ConvertFrom(Point(m_rect.maxX(), m_rect.maxY()))); pts[3] = Point(ConvertFrom(Point(m_rect.maxX(), m_rect.minY()))); } template void Inflate(U const & dx, U const & dy) { m_rect.Inflate(dx, dy); } void Add(AnyRect const & r) { Point pts[4]; r.GetGlobalPoints(pts); ConvertTo(pts, 4); m_rect.Add(pts[0]); m_rect.Add(pts[1]); m_rect.Add(pts[2]); m_rect.Add(pts[3]); } void Offset(Point const & p) { m_zero = ConvertTo(ConvertFrom(m_zero) + p); } Point const Center() const { return ConvertFrom(m_rect.Center()); } void SetSizesToIncludePoint(Point const & p) { m_rect.SetSizesToIncludePoint(ConvertTo(p)); } friend string DebugPrint(m2::AnyRect const & r) { return "{ Zero = " + DebugPrint(r.m_zero) + ", Rect = " + DebugPrint(r.m_rect) + ", Ang = " + DebugPrint(r.m_angle) + " }"; } }; template AnyRect const Offset(AnyRect const & r, Point const & pt) { AnyRect res(r); res.Offset(pt); return res; } template AnyRect const Inflate(AnyRect const & r, U const & dx, U const & dy) { AnyRect res = r; res.Inflate(dx, dy); return res; } template AnyRect const Inflate(AnyRect const & r, Point const & pt) { return Inflate(r, pt.x, pt.y); } typedef AnyRect AnyRectD; typedef AnyRect AnyRectF; }