diff --git a/base/base_tests/math_test.cpp b/base/base_tests/math_test.cpp index 6a25fbf8c1..448fdeca58 100644 --- a/base/base_tests/math_test.cpp +++ b/base/base_tests/math_test.cpp @@ -123,4 +123,15 @@ UNIT_TEST(TEST_FLOAT_DOUBLE_EQUAL_macros) TEST_NOT_ALMOST_EQUAL(dx, 2.0, ()); } +UNIT_TEST(IsIntersect_Intervals) +{ + TEST(my::IsIntersect(0, 100, 100, 200), ()); + TEST(!my::IsIntersect(0, 100, 150, 200), ()); + TEST(my::IsIntersect(0, 100, 50, 150), ()); + TEST(my::IsIntersect(0, 100, 50, 80), ()); + TEST(my::IsIntersect(0, 100, -50, 50), ()); + TEST(my::IsIntersect(0, 100, -50, 0), ()); + TEST(!my::IsIntersect(0, 100, -50, -20), ()); +} + #include "../../base/stop_mem_debug.hpp" diff --git a/base/math.hpp b/base/math.hpp index 2a620cda0e..0fd40a2c25 100644 --- a/base/math.hpp +++ b/base/math.hpp @@ -101,6 +101,12 @@ inline size_t SizeAligned(size_t size, size_t align) return size + (static_cast(-static_cast(size)) & (align - 1)); } +template +bool IsIntersect(T const & x0, T const & x1, T const & x2, T const & x3) +{ + return !((x1 < x2) || (x3 < x0)); +} + // Computes x^n. template inline T PowUint(T x, uint64_t n) { diff --git a/geometry/aa_rect2d.hpp b/geometry/aa_rect2d.hpp new file mode 100644 index 0000000000..42b9210ea5 --- /dev/null +++ b/geometry/aa_rect2d.hpp @@ -0,0 +1,131 @@ +#pragma once + +#include "point2d.hpp" +#include "rect2d.hpp" +#include + +namespace m2 +{ + /// axis aligned rect + template + class AARect + { + private: + + Point m_i; + Point m_j; + Point m_zero; + Rect m_rect; + + Point const Convert(Point const & p, + Point const & fromI, + Point const & fromJ, + Point const & toI, + Point const & toJ) + { + Point i(1, 0); + Point j(0, 1); + + 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; + } + + Point const CoordConvertTo(Point const & p) + { + Point i(1, 0); + Point j(0, 1); + + Point res; + + res.x = p.x * DotProduct(i, m_i) + p.y * DotProduct(j, m_i); + res.y = p.x * DotProduct(i, m_j) + p.y * DotProduct(j, m_j); + + return res; + } + + Point const CoordConvertFrom(Point const & p) + { + Point res; + + Point i(1, 0); + Point j(0, 1); + + res.x = p.x * DotProduct(m_i, i) + p.y * DotProduct(m_j, i); + res.y = p.x * DotProduct(m_i, j) + p.y * DotProduct(m_j, j); + + return res; + } + + public: + + AARect(Point const & zero, T const & angle, Rect const & r) + : m_i(cos(angle), sin(angle)), m_j(-sin(angle), cos(angle)), + m_zero(CoordConvertTo(zero)), + m_rect(r) + { + } + bool IsPointInside(Point const & pt); + + bool IsIntersect(AARect const & r); + + Point const ConvertTo(Point const & p) + { + m2::PointD i(1, 0); + m2::PointD j(0, 1); + return Convert(p - Convert(m_zero, m_i, m_j, i, j), i, j, m_i, m_j); + } + + void ConvertTo(Point * pts, size_t count) + { + for (size_t i = 0; i < count; ++i) + pts[i] = ConvertTo(pts[i]); + } + + Point const ConvertFrom(Point const & p) + { + m2::PointD i(1, 0); + m2::PointD j(0, 1); + return Convert(p + m_zero, m_i, m_j, i, j); + } + + void ConvertFrom(Point * pts, size_t count) + { + for (size_t i = 0; i < count; ++i) + pts[i] = ConvertFrom(pts[i]); + } + + Rect const GetLocalRect() + { + return m_rect; + } + + Rect const GetGlobalRect() + { + Point pts[4]; + GetGlobalPoints(pts); + + Rect res(pts[0].x, pts[0].y, pts[0].x, pts[0].y); + + res.Add(pts[1]); + res.Add(pts[2]); + res.Add(pts[3]); + + return res; + } + + void GetGlobalPoints(Point * pts) + { + 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())); + } + }; + + typedef AARect AARectD; + typedef AARect AARectF; +} diff --git a/geometry/geometry.pro b/geometry/geometry.pro index 345ae13e50..2cfbb0f783 100644 --- a/geometry/geometry.pro +++ b/geometry/geometry.pro @@ -31,3 +31,4 @@ HEADERS += \ polygon.hpp \ region2d.hpp \ robust_orientation.hpp \ + aa_rect2d.hpp diff --git a/geometry/geometry_tests/aarect_test.cpp b/geometry/geometry_tests/aarect_test.cpp new file mode 100644 index 0000000000..4f35cc2943 --- /dev/null +++ b/geometry/geometry_tests/aarect_test.cpp @@ -0,0 +1,44 @@ +#include "../../base/SRC_FIRST.hpp" +#include "../../testing/testing.hpp" +#include "../aa_rect2d.hpp" + +UNIT_TEST(AARect_TestConvertTo) +{ + m2::AARectD r(m2::PointD(0, 0), math::pi / 4, m2::RectD(0, 0, 10, 10)); + + m2::PointD pt1(100, 0); + + TEST(r.ConvertTo(pt1).EqualDxDy(m2::PointD(100 / sqrt(2), -100 / sqrt(2)), 10e-5), ()); + TEST(r.ConvertTo(m2::PointD(100, 100)).EqualDxDy(m2::PointD(100 * sqrt(2), 0), 10e-5), ()); + + m2::AARectD r1(m2::PointD(100, 100), math::pi / 4, m2::RectD(0, 0, 10, 10)); + + m2::PointD pt(100, 100 + 50 * sqrt(2)); + + TEST(r1.ConvertTo(pt).EqualDxDy(m2::PointD(50, 50), 10e-5), ()); +} + +UNIT_TEST(AARect_TestConvertFrom) +{ + m2::AARectD r(m2::PointD(100, 100), math::pi / 6, m2::RectD(0, 0, 10, 10)); + + TEST(r.ConvertFrom(m2::PointD(50, 0)).EqualDxDy(m2::PointD(100 + 50 * sqrt(3) / 2 , 100 + 50 * 1 / 2.0), 10e-5), ()); + TEST(r.ConvertTo(m2::PointD(100 + 50 * sqrt(3) / 2, 100 + 50 * 1.0 / 2)).EqualDxDy(m2::PointD(50, 0), 10e-5), ()); +} + +UNIT_TEST(AARect_TestIntersection) +{ + m2::AARectD r0(m2::PointD(93.196, 104.21), 1.03, m2::RectD(2, 0, 4, 15)); + m2::AARectD r1(m2::PointD(99.713, 116.02), -1.03, m2::RectD(0, 0, 14, 14)); + + m2::PointD pts[4]; + r0.GetGlobalPoints(pts); + r1.ConvertTo(pts, 4); + m2::RectD r2(pts[0].x, pts[0].y, pts[0].x, pts[0].y); + r2.Add(pts[1]); + r2.Add(pts[2]); + r2.Add(pts[3]); + + TEST(r1.GetLocalRect().IsIntersect(r2) == false, ()); +} + diff --git a/geometry/geometry_tests/geometry_tests.pro b/geometry/geometry_tests/geometry_tests.pro index 51bc49822b..9fbbec2cc4 100644 --- a/geometry/geometry_tests/geometry_tests.pro +++ b/geometry/geometry_tests/geometry_tests.pro @@ -34,3 +34,4 @@ SOURCES += \ region_test.cpp \ rect_test.cpp \ robust_test.cpp \ + aarect_test.cpp