#pragma once #include "geometry/angles.hpp" #include "geometry/point2d.hpp" #include "geometry/rect2d.hpp" #include "geometry/rect_intersect.hpp" #include "base/math.hpp" #include #include #include namespace m2 { /// axis aligned rect template class AnyRect { public: using Corners = std::array, 4>; AnyRect() = default; /// 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::Zero(); 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 GlobalZero() const { return Convert(m_zero, i(), j(), Point(1, 0), Point(0, 1)); } Point i() const { return Point(m_angle.cos(), m_angle.sin()); } Point j() const { return Point(-m_angle.sin(), m_angle.cos()); } void SetAngle(ang::Angle const & a) { 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 GlobalCenter() const { return ConvertFrom(m_rect.Center()); } Point 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 { Corners arr1; GetGlobalPoints(arr1); std::sort(arr1.begin(), arr1.end()); Corners arr2; r.GetGlobalPoints(arr2); std::sort(arr2.begin(), arr2.end()); for (size_t i = 0; i < arr1.size(); ++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 { Corners pts; r.GetGlobalPoints(pts); ConvertTo(pts); 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 { if (r.GetLocalRect() == Rect()) return false; Corners pts; r.GetGlobalPoints(pts); ConvertTo(pts); { Rect r1; for (auto const & p : pts) r1.Add(p); 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 ConvertTo(Point const & p) const { Point i1(1, 0); Point j1(0, 1); return Convert(p - Convert(m_zero, i(), j(), i1, j1), i1, j1, i(), j()); } void ConvertTo(Corners & pts) const { for (auto & p : pts) p = ConvertTo(p); } /// Convert into global coordinates from the local coordinates of this AnyRect Point ConvertFrom(Point const & p) const { return Convert(p + m_zero, i(), j(), Point(1, 0), Point(0, 1)); } Rect const & GetLocalRect() const { return m_rect; } Rect GetGlobalRect() const { Corners pts; GetGlobalPoints(pts); Rect res; for (auto const & p : pts) res.Add(p); return res; } void GetGlobalPoints(Corners & pts) const { pts[0] = ConvertFrom(Point(m_rect.minX(), m_rect.minY())); pts[1] = ConvertFrom(Point(m_rect.minX(), m_rect.maxY())); pts[2] = ConvertFrom(Point(m_rect.maxX(), m_rect.maxY())); pts[3] = 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) { Corners pts; r.GetGlobalPoints(pts); ConvertTo(pts); for (auto const & p : pts) m_rect.Add(p); } 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 std::string DebugPrint(AnyRect const & r) { return "{ Zero = " + DebugPrint(r.m_zero) + ", Rect = " + DebugPrint(r.m_rect) + ", Ang = " + DebugPrint(r.m_angle) + " }"; } private: static Point 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; } ang::Angle m_angle; Point m_zero{}; Rect m_rect{}; }; using AnyRectD = AnyRect; using AnyRectF = AnyRect; template AnyRect Offset(AnyRect const & r, Point const & pt) { AnyRect res(r); res.Offset(pt); return res; } template AnyRect Inflate(AnyRect const & r, U const & dx, U const & dy) { AnyRect res = r; res.Inflate(dx, dy); return res; } template AnyRect Inflate(AnyRect const & r, Point const & pt) { return Inflate(r, pt.x, pt.y); } } // namespace m2