#pragma once #include "geometry/point2d.hpp" #include "base/assert.hpp" #include "base/internal/message.hpp" #include #include #include #include namespace m2 { namespace impl { template struct min_max_value; template struct min_max_value { T get_min() { return -get_max(); } T get_max() { return std::numeric_limits::max(); } }; template struct min_max_value { T get_min() { return std::numeric_limits::min(); } T get_max() { return std::numeric_limits::max(); } }; } // namespace impl template class Rect { public: using value_type = T; Rect() { MakeEmpty(); } Rect(T minX, T minY, T maxX, T maxY) : m_minX(minX), m_minY(minY), m_maxX(maxX), m_maxY(maxY) { ASSERT(minX <= maxX, ()); ASSERT(minY <= maxY, ()); } Rect(Point const & p1, Point const & p2) : m_minX(std::min(p1.x, p2.x)) , m_minY(std::min(p1.y, p2.y)) , m_maxX(std::max(p1.x, p2.x)) , m_maxY(std::max(p1.y, p2.y)) { } template explicit Rect(Rect const & src) : m_minX(src.minX()), m_minY(src.minY()), m_maxX(src.maxX()), m_maxY(src.maxY()) { } static Rect GetEmptyRect() { return Rect(); } static Rect GetInfiniteRect() { Rect r; r.MakeInfinite(); return r; } void MakeEmpty() { m_minX = m_minY = impl::min_max_value().get_max(); m_maxX = m_maxY = impl::min_max_value().get_min(); } void MakeInfinite() { m_minX = m_minY = impl::min_max_value().get_min(); m_maxX = m_maxY = impl::min_max_value().get_max(); } bool IsValid() const { return (m_minX <= m_maxX && m_minY <= m_maxY); } bool IsEmptyInterior() const { return m_minX >= m_maxX || m_minY >= m_maxY; } void Add(m2::Point const & p) { m_minX = std::min(p.x, m_minX); m_minY = std::min(p.y, m_minY); m_maxX = std::max(p.x, m_maxX); m_maxY = std::max(p.y, m_maxY); } void Add(m2::Rect const & r) { m_minX = std::min(r.m_minX, m_minX); m_minY = std::min(r.m_minY, m_minY); m_maxX = std::max(r.m_maxX, m_maxX); m_maxY = std::max(r.m_maxY, m_maxY); } void Offset(m2::Point const & p) { m_minX += p.x; m_minY += p.y; m_maxX += p.x; m_maxY += p.y; } void Offset(T const & dx, T const & dy) { m_minX += dx; m_minY += dy; m_maxX += dx; m_maxY += dy; } Point LeftTop() const { return Point(m_minX, m_maxY); } Point RightTop() const { return Point(m_maxX, m_maxY); } Point RightBottom() const { return Point(m_maxX, m_minY); } Point LeftBottom() const { return Point(m_minX, m_minY); } template void ForEachCorner(Fn && fn) const { fn(LeftTop()); fn(LeftBottom()); fn(RightBottom()); fn(RightTop()); } template void ForEachSide(Fn && fn) const { fn(LeftTop(), LeftBottom()); fn(LeftBottom(), RightBottom()); fn(RightBottom(), RightTop()); fn(RightTop(), LeftTop()); } bool IsIntersect(Rect const & r) const { return !((m_maxX < r.m_minX) || (m_minX > r.m_maxX) || (m_maxY < r.m_minY) || (m_minY > r.m_maxY)); } bool IsPointInside(Point const & pt) const { return !(m_minX > pt.x || pt.x > m_maxX || m_minY > pt.y || pt.y > m_maxY); } bool IsRectInside(Rect const & rect) const { return (IsPointInside(Point(rect.minX(), rect.minY())) && IsPointInside(Point(rect.maxX(), rect.maxY()))); } Point Center() const { return Point((m_minX + m_maxX) / 2.0, (m_minY + m_maxY) / 2.0); } T SizeX() const { return std::max(static_cast(0), m_maxX - m_minX); } T SizeY() const { return std::max(static_cast(0), m_maxY - m_minY); } T Area() const { return SizeX() * SizeY(); } void DivideByGreaterSize(Rect & r1, Rect & r2) const { if (SizeX() > SizeY()) { T const pivot = (m_minX + m_maxX) / 2; r1 = Rect(m_minX, m_minY, pivot, m_maxY); r2 = Rect(pivot, m_minY, m_maxX, m_maxY); } else { T const pivot = (m_minY + m_maxY) / 2; r1 = Rect(m_minX, m_minY, m_maxX, pivot); r2 = Rect(m_minX, pivot, m_maxX, m_maxY); } } void SetSizes(T dx, T dy) { ASSERT_GREATER(dx, 0, ()); ASSERT_GREATER(dy, 0, ()); dx /= 2; dy /= 2; Point const c = Center(); m_minX = c.x - dx; m_minY = c.y - dy; m_maxX = c.x + dx; m_maxY = c.y + dy; } void SetSizesToIncludePoint(Point const & pt) { Point const c = Center(); T const dx = base::Abs(pt.x - c.x); T const dy = base::Abs(pt.y - c.y); m_minX = c.x - dx; m_minY = c.y - dy; m_maxX = c.x + dx; m_maxY = c.y + dy; } void SetCenter(m2::Point const & p) { Offset(p - Center()); } T minX() const { return m_minX; } T minY() const { return m_minY; } T maxX() const { return m_maxX; } T maxY() const { return m_maxY; } void setMinX(T minX) { m_minX = minX; } void setMinY(T minY) { m_minY = minY; } void setMaxX(T maxX) { m_maxX = maxX; } void setMaxY(T maxY) { m_maxY = maxY; } void Scale(T scale) { scale *= 0.5; m2::Point const center = Center(); T const halfSizeX = SizeX() * scale; T const halfSizeY = SizeY() * scale; m_minX = center.x - halfSizeX; m_minY = center.y - halfSizeY; m_maxX = center.x + halfSizeX; m_maxY = center.y + halfSizeY; } void Inflate(T dx, T dy) { m_minX -= dx; m_maxX += dx; m_minY -= dy; m_maxY += dy; } bool Intersect(m2::Rect const & r) { T newMinX = std::max(m_minX, r.minX()); T newMaxX = std::min(m_maxX, r.maxX()); if (newMinX > newMaxX) return false; T newMinY = std::max(m_minY, r.minY()); T newMaxY = std::min(m_maxY, r.maxY()); if (newMinY > newMaxY) return false; m_minX = newMinX; m_minY = newMinY; m_maxX = newMaxX; m_maxY = newMaxY; return true; } bool operator==(m2::Rect const & r) const { return m_minX == r.m_minX && m_minY == r.m_minY && m_maxX == r.m_maxX && m_maxY == r.m_maxY; } bool operator!=(m2::Rect const & r) const { return !(*this == r); } private: enum { IsSigned = std::numeric_limits::is_signed }; template friend TArchive & operator<<(TArchive & ar, Rect const & rect); template friend TArchive & operator>>(TArchive & ar, Rect & rect); T m_minX, m_minY, m_maxX, m_maxY; }; using RectF = Rect; using RectD = Rect; using RectU = Rect; using RectU32 = Rect; using RectI = Rect; template bool IsEqual(Rect const & r1, Rect const & r2, double epsX, double epsY) { Rect r = r1; r.Inflate(epsX, epsY); if (!r.IsRectInside(r2)) return false; r = r2; r.Inflate(epsX, epsY); if (!r.IsRectInside(r1)) return false; return true; } template bool IsEqualSize(Rect const & r1, Rect const & r2, double epsX, double epsY) { return fabs(r1.SizeX() - r2.SizeX()) < epsX && fabs(r1.SizeY() - r2.SizeY()) < epsY; } template m2::Rect const Add(m2::Rect const & r, m2::Point const & p) { return m2::Rect(std::min(p.x, r.minX()), std::min(p.y, r.minY()), std::max(p.x, r.maxX()), std::max(p.y, r.maxY())); } template m2::Rect const Add(m2::Rect const & r1, m2::Rect const & r2) { return m2::Rect(std::min(r2.minX(), r1.minX()), std::min(r2.minY(), r1.minY()), std::max(r2.maxX(), r1.maxX()), std::max(r2.maxY(), r1.maxY())); } template m2::Rect const Offset(m2::Rect const & r1, m2::Point const & p) { return m2::Rect(r1.minX() + p.x, r1.minY() + p.y, r1.maxX() + p.x, r1.maxY() + p.y); } template m2::Rect const Inflate(m2::Rect const & r, T const & dx, T const & dy) { return m2::Rect(r.minX() - dx, r.minY() - dy, r.maxX() + dx, r.maxY() + dy); } template m2::Rect const Inflate(m2::Rect const & r, m2::Point const & p) { return Inflate(r, p.x, p.y); } template m2::Rect const Offset(m2::Rect const & r1, T const & dx, T const & dy) { return m2::Rect(r1.minX() + dx, r1.minY() + dy, r1.maxX() + dx, r1.maxY() + dy); } template bool HasIntersection(m2::Rect const & rect, TCollection const & geometry) { for (auto const & g : geometry) { if (rect.IsIntersect(g)) return true; } return false; }; template TArchive & operator>>(TArchive & ar, m2::Rect & rect) { ar >> rect.m_minX; ar >> rect.m_minY; ar >> rect.m_maxX; ar >> rect.m_maxY; return ar; } template TArchive & operator<<(TArchive & ar, m2::Rect const & rect) { ar << rect.m_minX; ar << rect.m_minY; ar << rect.m_maxX; ar << rect.m_maxY; return ar; } template std::string DebugPrint(m2::Rect const & r) { std::ostringstream out; out.precision(20); out << "m2::Rect(" << r.minX() << ", " << r.minY() << ", " << r.maxX() << ", " << r.maxY() << ")"; return out.str(); } } // namespace m2