Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,49 @@
project(geometry_tests)
set(SRC
algorithm_test.cpp
angle_test.cpp
anyrect_test.cpp
area_on_earth_tests.cpp
bounding_box_tests.cpp
calipers_box_tests.cpp
cellid_test.cpp
circle_on_earth_tests.cpp
clipping_test.cpp
common_test.cpp
convex_hull_tests.cpp
covering_test.cpp
diamond_box_tests.cpp
distance_on_sphere_test.cpp
equality.hpp
intersect_test.cpp
intersection_score_tests.cpp
large_polygon.hpp
latlon_test.cpp
line2d_tests.cpp
mercator_test.cpp
nearby_points_sweeper_test.cpp
oblate_spheroid_tests.cpp
packer_test.cpp
parametrized_segment_tests.cpp
point3d_tests.cpp
point_test.cpp
polygon_test.cpp
polyline_tests.cpp
rect_test.cpp
region2d_binary_op_test.cpp
region_tests.cpp
robust_test.cpp
screen_test.cpp
segment2d_tests.cpp
simplification_test.cpp
spline_test.cpp
test_regions.hpp
transformations_test.cpp
tree_test.cpp
vector_test.cpp
)
omim_add_test(${PROJECT_NAME} ${SRC} NO_PLATFORM_INIT)
target_link_libraries(${PROJECT_NAME} geometry)

View file

@ -0,0 +1,118 @@
#include "testing/testing.hpp"
#include "geometry/algorithm.hpp"
#include "geometry/mercator.hpp"
#include "base/assert.hpp"
#include <vector>
namespace algorithm_test
{
using namespace std;
using m2::CalculateBoundingBox;
using m2::CalculatePointOnSurface;
using m2::CalculatePolyLineCenter;
using m2::PointD;
using m2::RectD;
PointD GetPolyLineCenter(vector<PointD> const & points)
{
return m2::ApplyCalculator(points, m2::CalculatePolyLineCenter());
}
RectD GetBoundingBox(vector<PointD> const & points)
{
return m2::ApplyCalculator(points, m2::CalculateBoundingBox());
}
PointD GetPointOnSurface(vector<PointD> const & points)
{
ASSERT(!points.empty() && points.size() % 3 == 0, ("points.size() =", points.size()));
auto const boundingBox = GetBoundingBox(points);
return m2::ApplyCalculator(points, m2::CalculatePointOnSurface(boundingBox));
}
bool PointsAlmostEqual(PointD const & p1, PointD const & p2)
{
return p1.EqualDxDy(p2, mercator::kPointEqualityEps);
}
UNIT_TEST(CalculatePolyLineCenter)
{
{
vector<PointD> const points{{0, 0}, {1, 1}, {2, 2}};
TEST_EQUAL(GetPolyLineCenter(points), PointD(1, 1), ());
}
{
vector<PointD> const points{{0, 2}, {1, 1}, {2, 2}};
TEST_EQUAL(GetPolyLineCenter(points), PointD(1, 1), ());
}
{
vector<PointD> const points{
{1, 1},
{2, 2},
{4, 4},
};
TEST_EQUAL(GetPolyLineCenter(points), PointD(2.5, 2.5), ());
}
{
vector<PointD> const points{{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}};
// Also logs a warning message.
TEST_EQUAL(GetPolyLineCenter(points), PointD(0, 0), ());
}
{
vector<PointD> const points{{0, 0}, {0, 0}, {0, 0}, {0, 0.000001}, {0, 0.000001}, {0, 0.000001}, {0, 0.000001}};
// Also logs a warning message.
TEST(GetPolyLineCenter(points).EqualDxDy(PointD(0, .0000005), 1e-7), ());
}
}
UNIT_TEST(CalculateCenter)
{
{
vector<PointD> const points{{2, 2}};
TEST_EQUAL(GetBoundingBox(points), RectD({2, 2}, {2, 2}), ());
}
{
vector<PointD> const points{{0, 0}, {1, 1}, {2, 2}};
TEST_EQUAL(GetBoundingBox(points), RectD({0, 0}, {2, 2}), ());
}
{
vector<PointD> const points{{0, 2}, {1, 1}, {2, 2}, {1, 2}, {2, 2}, {2, 0}};
TEST_EQUAL(GetBoundingBox(points), RectD({0, 0}, {2, 2}), ());
}
}
UNIT_TEST(CalculatePointOnSurface)
{
{
vector<PointD> const points{{0, 0}, {1, 1}, {2, 2}};
TEST_EQUAL(GetPointOnSurface(points), PointD(1, 1), ());
}
{
vector<PointD> const points{{0, 2}, {1, 1}, {2, 2}, {1, 2}, {2, 2}, {2, 0}};
TEST_EQUAL(GetPointOnSurface(points), PointD(1, 1), ());
}
{
vector<PointD> const points{
{0, 0}, {1, 1}, {2, 0}, {1, 1}, {2, 0}, {3, 1}, // Center of this triangle is used as a result.
{2, 0}, {3, 1}, {4, 0},
{4, 0}, {3, 1}, {4, 2}, {3, 1}, {4, 2}, {3, 3}, // Or this.
{4, 2}, {3, 3}, {4, 4},
{3, 3}, {4, 4}, {2, 4}, {3, 3}, {2, 4}, {1, 3}, // Or this.
{1, 3}, {2, 4}, {0, 4},
{0, 4}, {1, 3}, {0, 2}, {1, 3}, {0, 2}, {1, 1}, // Or this
{0, 2}, {1, 1}, {0, 0},
};
auto const result = GetPointOnSurface(points);
TEST(PointsAlmostEqual(result, {10.0 / 3.0, 2}) || PointsAlmostEqual(result, {2, 2.0 / 3.0}) ||
PointsAlmostEqual(result, {2, 10.0 / 3.0}) || PointsAlmostEqual(result, {2.0 / 3.0, 2}),
("result = ", result));
}
}
} // namespace algorithm_test

View file

@ -0,0 +1,109 @@
#include "geometry/geometry_tests/equality.hpp"
#include "base/macros.hpp"
#include "testing/testing.hpp"
#include "geometry/angles.hpp"
using namespace test;
using math::pi;
UNIT_TEST(Atan)
{
double const h4 = math::pi / 4.0;
TEST(is_equal_atan(1, 1, h4), ());
TEST(is_equal_atan(-1, 1, math::pi - h4), ());
TEST(is_equal_atan(-1, -1, h4 - math::pi), ());
TEST(is_equal_atan(1, -1, -h4), ());
double const hh = atan(1.0 / 2.0);
TEST(is_equal_atan(2, 1, hh), ());
TEST(is_equal_atan(-2, 1, math::pi - hh), ());
TEST(is_equal_atan(-2, -1, hh - math::pi), ());
TEST(is_equal_atan(2, -1, -hh), ());
}
UNIT_TEST(Atan2)
{
TEST_ALMOST_EQUAL_ULPS(atan2(1, 0), pi / 2.0, ());
TEST_ALMOST_EQUAL_ULPS(atan2(-1, 0), -pi / 2.0, ());
TEST_ALMOST_EQUAL_ULPS(atan2(0, 1), 0.0, ());
TEST_ALMOST_EQUAL_ULPS(atan2(0, -1), pi, ());
TEST_ALMOST_EQUAL_ULPS(atan2(1, 1), pi / 4.0, ());
}
namespace
{
void check_avg(double arr[], size_t n, double v)
{
ang::AverageCalc calc;
for (size_t i = 0; i < n; ++i)
calc.Add(arr[i]);
double const avg = calc.GetAverage();
TEST(is_equal_angle(avg, v), (avg, v));
}
} // namespace
UNIT_TEST(Average)
{
double constexpr eps = 1.0E-3;
double arr1[] = {math::pi - eps, -math::pi + eps};
TEST(is_equal_angle(ang::GetMiddleAngle(arr1[0], arr1[1]), math::pi), ());
check_avg(arr1, ARRAY_SIZE(arr1), math::pi);
double arr2[] = {eps, -eps};
TEST(is_equal_angle(ang::GetMiddleAngle(arr2[0], arr2[1]), 0.0), ());
check_avg(arr2, ARRAY_SIZE(arr2), 0.0);
}
UNIT_TEST(ShortestDistance)
{
TEST_ALMOST_EQUAL_ULPS(ang::GetShortestDistance(0, math::pi), math::pi, ());
TEST_ALMOST_EQUAL_ULPS(ang::GetShortestDistance(0, math::pi + 1), -math::pi + 1, ());
TEST_ALMOST_EQUAL_ULPS(ang::GetShortestDistance(math::pi - 1, 0), -math::pi + 1, ());
TEST_ALMOST_EQUAL_ULPS(ang::GetShortestDistance(math::pi + 1, 0), math::pi - 1, ());
}
UNIT_TEST(TwoVectorsAngle)
{
double constexpr eps = 1e-10;
TEST(AlmostEqualAbs(ang::TwoVectorsAngle(m2::Point<double>(0, 0) /* p */, m2::Point<double>(0, 1) /* p1 */,
m2::Point<double>(1, 0)) /* p2 */,
3 * math::pi2, eps),
());
TEST(AlmostEqualAbs(ang::TwoVectorsAngle(m2::Point<double>(1, 1) /* p */, m2::Point<double>(2, 2) /* p1 */,
m2::Point<double>(1, 2)) /* p2 */,
math::pi4, eps),
());
TEST(AlmostEqualAbs(ang::TwoVectorsAngle(m2::Point<double>(0, 0) /* p */, m2::Point<double>(1, 0) /* p1 */,
m2::Point<double>(0, -1)) /* p2 */,
3 * math::pi2, eps),
());
TEST(AlmostEqualAbs(ang::TwoVectorsAngle(m2::Point<double>(0, 0) /* p */, m2::Point<double>(1, 0) /* p1 */,
m2::Point<double>(-1, 0)) /* p2 */,
math::pi, eps),
());
}
UNIT_TEST(Azimuth)
{
TEST(is_equal_angle(ang::Azimuth(m2::Point<double>(-1, 0), m2::Point<double>(0, 1), math::pi2), -math::pi4), ());
TEST(is_equal_angle(ang::Azimuth(m2::Point<double>(-1, 0), m2::Point<double>(0, 1), 0.0), math::pi4), ());
TEST(is_equal_angle(ang::Azimuth(m2::Point<double>(-1, 1), m2::Point<double>(1, -1), 0.0), 3 * math::pi4), ());
TEST(is_equal_angle(ang::Azimuth(m2::Point<double>(1, 1), m2::Point<double>(0, 1), -math::pi2), 0.0), ());
TEST(is_equal_angle(ang::Azimuth(m2::Point<double>(1, -1), m2::Point<double>(-1, -1), math::pi), math::pi2), ());
TEST(is_equal_angle(ang::Azimuth(m2::Point<double>(0, 0), m2::Point<double>(-1, -1), math::pi4), math::pi), ());
TEST(is_equal_angle(ang::Azimuth(m2::Point<double>(0.5, -0.5), m2::Point<double>(-0.5, 0.5), math::pi4), -math::pi2),
());
TEST(is_equal_angle(ang::Azimuth(m2::Point<double>(0.1, 0.1), m2::Point<double>(0.2, 0.2), math::pi4), 0.0), ());
TEST(is_equal_angle(ang::Azimuth(m2::Point<double>(0.7, 0.7), m2::Point<double>(-0.2, -0.2), math::pi2),
3 * math::pi4),
());
}

View file

@ -0,0 +1,93 @@
#include "testing/testing.hpp"
#include "geometry/any_rect2d.hpp"
#include <array>
#include <cmath>
namespace m2
{
UNIT_TEST(AnyRect_TestConvertTo)
{
AnyRectD const r(PointD(0, 0), ang::Angle<double>(math::pi / 4), RectD(0, 0, 10, 10));
PointD const pt1(100, 0);
double const sqrt2 = sqrt(2.0);
TEST(r.ConvertTo(pt1).EqualDxDy(PointD(100 / sqrt2, -100 / sqrt2), 10e-5), ());
TEST(r.ConvertTo(PointD(100, 100)).EqualDxDy(PointD(100 * sqrt2, 0), 10e-5), ());
AnyRectD r1(PointD(100, 100), ang::Angle<double>(math::pi / 4), RectD(0, 0, 10, 10));
PointD pt(100, 100 + 50 * sqrt2);
TEST(r1.ConvertTo(pt).EqualDxDy(PointD(50, 50), 10e-5), ());
}
UNIT_TEST(AnyRect_TestConvertFrom)
{
AnyRectD const r(PointD(100, 100), ang::Angle<double>(math::pi / 6), RectD(0, 0, 10, 10));
double const sqrt3 = sqrt(3.0);
TEST(r.ConvertFrom(PointD(50, 0)).EqualDxDy(PointD(100 + 50 * sqrt3 / 2, 100 + 50 * 1 / 2.0), 10e-5), ());
TEST(r.ConvertTo(PointD(100 + 50 * sqrt3 / 2, 100 + 50 * 1.0 / 2)).EqualDxDy(PointD(50, 0), 10e-5), ());
}
UNIT_TEST(AnyRect_ZeroRect)
{
AnyRectD const r0(RectD(0, 0, 0, 0));
PointD const centerPt(300.0, 300.0);
AnyRectD const r1(Offset(r0, centerPt));
TEST_EQUAL(r1.GlobalCenter(), centerPt, ());
AnyRectD const r2(Inflate(r0, 2.0, 2.0));
TEST_EQUAL(r2.GetLocalRect(), RectD(-2, -2, 2, 2), ());
}
UNIT_TEST(AnyRect_TestIntersection)
{
AnyRectD const r0(PointD(93.196, 104.21), ang::Angle<double>(+1.03), RectD(2, 0, 4, 15));
AnyRectD const r1(PointD(99.713, 116.02), ang::Angle<double>(-1.03), RectD(0, 0, 14, 14));
std::array<PointD, 4> pts;
r0.GetGlobalPoints(pts);
r1.ConvertTo(pts);
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, ());
}
UNIT_TEST(AnyRect_TestIsIntersect)
{
auto const pi6 = ang::Angle<double>(math::pi / 6);
auto const pi8 = ang::Angle<double>(math::pi / 8);
AnyRectD const r0(PointD(100, 100), pi6, RectD(0, 0, 50, 20));
AnyRectD const r1(PointD(100, 100), pi6, RectD(0, -10, 50, 10));
AnyRectD const r2(PointD(100, 100), pi6, RectD(0, -21, 50, -1));
TEST(r0.IsIntersect(r1), ());
TEST(r1.IsIntersect(r2), ());
TEST(!r0.IsIntersect(r2), ());
TEST(r1.IsIntersect(r2), ());
AnyRectD const r3(PointD(50, 50), pi8, RectD(0, 0, 80, 30));
TEST(r0.IsIntersect(r3), ());
}
UNIT_TEST(AnyRect_SetSizesToIncludePoint)
{
AnyRectD rect(PointD(100, 100), ang::Angle<double>(math::pi / 6), RectD(0, 0, 50, 50));
TEST(!rect.IsPointInside(PointD(0, 0)), ());
TEST(!rect.IsPointInside(PointD(200, 200)), ());
rect.SetSizesToIncludePoint(PointD(0, 0));
TEST(rect.IsPointInside(PointD(0, 0)), ());
rect.SetSizesToIncludePoint(PointD(200, 200));
TEST(rect.IsPointInside(PointD(200, 200)), ());
}
} // namespace m2

View file

@ -0,0 +1,28 @@
#include "testing/testing.hpp"
#include "geometry/area_on_earth.hpp"
#include "geometry/distance_on_sphere.hpp"
#include "geometry/mercator.hpp"
#include "base/math.hpp"
#include <iostream>
UNIT_TEST(AreaOnEarth_Circle)
{
double const kEarthSurfaceArea = 4.0 * math::pi * ms::kEarthRadiusMeters * ms::kEarthRadiusMeters;
TEST_ALMOST_EQUAL_ABS(ms::CircleAreaOnEarth(math::pi * ms::kEarthRadiusMeters), kEarthSurfaceArea, 1e-1, ());
TEST_ALMOST_EQUAL_ABS(ms::CircleAreaOnEarth(2.0 /* radiusMeters */), math::pi * 2.0 * 2.0, 1e-2, ());
TEST_ALMOST_EQUAL_ABS(ms::CircleAreaOnEarth(2000.0 /* radiusMeters */), math::pi * 2000.0 * 2000.0, 1.0, ());
}
UNIT_TEST(AreaOnEarth_ThreePoints)
{
double const kEarthSurfaceArea = 4.0 * math::pi * ms::kEarthRadiusMeters * ms::kEarthRadiusMeters;
TEST_ALMOST_EQUAL_ABS(ms::AreaOnEarth({90.0, 0.0}, {0.0, 0.0}, {0.0, 90.0}), kEarthSurfaceArea / 8.0, 1e-1, ());
TEST_ALMOST_EQUAL_ABS(ms::AreaOnEarth({90.0, 0.0}, {0.0, 90.0}, {0.0, -90.0}), kEarthSurfaceArea / 4.0, 1e-1, ());
}

View file

@ -0,0 +1,33 @@
#include "testing/testing.hpp"
#include "geometry/bounding_box.hpp"
namespace bounding_box_tests
{
UNIT_TEST(BoundingBox_Smoke)
{
{
m2::BoundingBox bbox;
TEST(!bbox.HasPoint(0, 0), ());
TEST(!bbox.HasPoint(-1, 1), ());
}
{
m2::BoundingBox bbox;
bbox.Add(0, 0);
TEST(bbox.HasPoint(0, 0), ());
TEST(!bbox.HasPoint(1, 0), ());
TEST(!bbox.HasPoint(0, 1), ());
TEST(!bbox.HasPoint(1, 1), ());
TEST(!bbox.HasPoint(0.5, 0.5), ());
bbox.Add(1, 1);
TEST(bbox.HasPoint(1, 0), ());
TEST(bbox.HasPoint(0, 1), ());
TEST(bbox.HasPoint(1, 1), ());
TEST(bbox.HasPoint(0.5, 0.5), ());
}
}
} // namespace bounding_box_tests

View file

@ -0,0 +1,73 @@
#include "testing/testing.hpp"
#include "geometry/calipers_box.hpp"
#include "geometry/point2d.hpp"
#include <vector>
namespace calipers_box_tests
{
using namespace m2;
using namespace std;
UNIT_TEST(CalipersBox_Smoke)
{
{
CalipersBox const cbox(vector<PointD>{});
TEST(cbox.Points().empty(), ());
TEST(!cbox.HasPoint(0, 0), ());
TEST(!cbox.HasPoint(0, 1), ());
TEST(!cbox.HasPoint(1, 0), ());
}
{
vector<PointD> const points = {{PointD(2, 3)}};
CalipersBox const cbox(points);
TEST_EQUAL(cbox.Points(), points, ());
TEST(cbox.HasPoint(2, 3), ());
TEST(!cbox.HasPoint(4, 6), ());
TEST(!cbox.HasPoint(0, 0), ());
}
{
vector<PointD> const points = {{PointD(2, 3), PointD(2, 3), PointD(2, 3)}};
CalipersBox const cbox(points);
TEST_EQUAL(cbox.Points(), vector<PointD>{{PointD(2, 3)}}, ());
}
{
vector<PointD> const points = {{PointD(1, 1), PointD(1, 2)}};
CalipersBox const cbox(points);
TEST_EQUAL(cbox.Points(), points, ());
TEST(cbox.HasPoint(1, 1.5), ());
TEST(!cbox.HasPoint(1, 3), ());
TEST(!cbox.HasPoint(0, 0), ());
}
{
vector<PointD> const points = {
{PointD(0, 0), PointD(-2, 3), PointD(1, 5), PointD(3, 2), PointD(1, 2), PointD(0, 3)}};
CalipersBox const cbox(points);
TEST_EQUAL(cbox.Points(), (vector<PointD>{{PointD(-2, 3), PointD(0, 0), PointD(3, 2), PointD(1, 5)}}), ());
for (auto const & p : points)
TEST(cbox.HasPoint(p), (p));
TEST(!cbox.HasPoint(1, 0), ());
}
{
vector<PointD> const points = {{PointD(0, 0), PointD(1, 0), PointD(0, 5), PointD(1, 5), PointD(-2, 2),
PointD(-2, 3), PointD(3, 2), PointD(3, 3)}};
vector<PointD> const expected = {{PointD(-2.5, 2.5), PointD(0.5, -0.5), PointD(3.5, 2.5), PointD(0.5, 5.5)}};
CalipersBox const cbox(points);
TEST_EQUAL(cbox.Points(), expected, ());
for (auto const & p : points)
TEST(cbox.HasPoint(p), (p));
TEST(cbox.HasPoint(0, 2), ());
TEST(!cbox.HasPoint(2, 0), ());
TEST(!cbox.HasPoint(4, 2), ());
}
}
} // namespace calipers_box_tests

View file

@ -0,0 +1,240 @@
#include "testing/testing.hpp"
#include "geometry/cellid.hpp"
#include "base/logging.hpp"
#include <algorithm>
#include <string>
#include <vector>
namespace cellid_test
{
using std::make_pair, std::string, std::vector;
UNIT_TEST(CellId_Parent)
{
TEST_EQUAL(m2::CellId<3>("1").Parent(), m2::CellId<3>(""), ());
TEST_EQUAL(m2::CellId<4>("032").Parent(), m2::CellId<4>("03"), ());
}
UNIT_TEST(CellId_AncestorAtLevel)
{
TEST_EQUAL(m2::CellId<3>("1").AncestorAtLevel(0), m2::CellId<3>(""), ());
TEST_EQUAL(m2::CellId<4>("032").AncestorAtLevel(2), m2::CellId<4>("03"), ());
TEST_EQUAL(m2::CellId<4>("032").AncestorAtLevel(1), m2::CellId<4>("0"), ());
TEST_EQUAL(m2::CellId<4>("032").AncestorAtLevel(0), m2::CellId<4>(""), ());
}
UNIT_TEST(CellId_FromString)
{
TEST_EQUAL(m2::CellId<3>(""), m2::CellId<3>::FromBitsAndLevel(0, 0), ());
TEST_EQUAL(m2::CellId<4>("032"), m2::CellId<4>::FromBitsAndLevel(14, 3), ());
TEST_EQUAL(m2::CellId<3>("03"), m2::CellId<3>::FromBitsAndLevel(3, 2), ());
}
UNIT_TEST(CellId_ToString)
{
TEST_EQUAL(m2::CellId<3>("").ToString(), "", ());
TEST_EQUAL(m2::CellId<4>("032").ToString(), "032", ());
TEST_EQUAL(m2::CellId<3>("03").ToString(), "03", ());
}
UNIT_TEST(CellId_ToInt64)
{
TEST_EQUAL(m2::CellId<3>("").ToInt64(3), 1, ());
TEST_EQUAL(m2::CellId<3>("0").ToInt64(3), 2, ());
TEST_EQUAL(m2::CellId<3>("1").ToInt64(3), 7, ());
TEST_EQUAL(m2::CellId<3>("2").ToInt64(3), 12, ());
TEST_EQUAL(m2::CellId<3>("3").ToInt64(3), 17, ());
TEST_EQUAL(m2::CellId<3>("00").ToInt64(3), 3, ());
TEST_EQUAL(m2::CellId<3>("01").ToInt64(3), 4, ());
TEST_EQUAL(m2::CellId<3>("03").ToInt64(3), 6, ());
TEST_EQUAL(m2::CellId<3>("10").ToInt64(3), 8, ());
TEST_EQUAL(m2::CellId<3>("20").ToInt64(3), 13, ());
TEST_EQUAL(m2::CellId<3>("23").ToInt64(3), 16, ());
TEST_EQUAL(m2::CellId<3>("30").ToInt64(3), 18, ());
TEST_EQUAL(m2::CellId<3>("31").ToInt64(3), 19, ());
TEST_EQUAL(m2::CellId<3>("33").ToInt64(3), 21, ());
}
UNIT_TEST(CellId_ToInt64_LevelLessThanDepth)
{
TEST_EQUAL(m2::CellId<3>("").ToInt64(2), 1, ());
TEST_EQUAL(m2::CellId<3>("0").ToInt64(2), 2, ());
TEST_EQUAL(m2::CellId<3>("1").ToInt64(2), 3, ());
TEST_EQUAL(m2::CellId<3>("2").ToInt64(2), 4, ());
TEST_EQUAL(m2::CellId<3>("3").ToInt64(2), 5, ());
TEST_EQUAL(m2::CellId<3>("00").ToInt64(2), 2, ());
TEST_EQUAL(m2::CellId<3>("01").ToInt64(2), 2, ());
TEST_EQUAL(m2::CellId<3>("03").ToInt64(2), 2, ());
TEST_EQUAL(m2::CellId<3>("10").ToInt64(2), 3, ());
TEST_EQUAL(m2::CellId<3>("20").ToInt64(2), 4, ());
TEST_EQUAL(m2::CellId<3>("23").ToInt64(2), 4, ());
TEST_EQUAL(m2::CellId<3>("30").ToInt64(2), 5, ());
TEST_EQUAL(m2::CellId<3>("31").ToInt64(2), 5, ());
TEST_EQUAL(m2::CellId<3>("33").ToInt64(2), 5, ());
}
UNIT_TEST(CellId_FromInt64)
{
TEST_EQUAL(m2::CellId<3>(""), m2::CellId<3>::FromInt64(1, 3), ());
TEST_EQUAL(m2::CellId<3>("0"), m2::CellId<3>::FromInt64(2, 3), ());
TEST_EQUAL(m2::CellId<3>("1"), m2::CellId<3>::FromInt64(7, 3), ());
TEST_EQUAL(m2::CellId<3>("2"), m2::CellId<3>::FromInt64(12, 3), ());
TEST_EQUAL(m2::CellId<3>("3"), m2::CellId<3>::FromInt64(17, 3), ());
TEST_EQUAL(m2::CellId<3>("00"), m2::CellId<3>::FromInt64(3, 3), ());
TEST_EQUAL(m2::CellId<3>("01"), m2::CellId<3>::FromInt64(4, 3), ());
TEST_EQUAL(m2::CellId<3>("03"), m2::CellId<3>::FromInt64(6, 3), ());
TEST_EQUAL(m2::CellId<3>("10"), m2::CellId<3>::FromInt64(8, 3), ());
TEST_EQUAL(m2::CellId<3>("20"), m2::CellId<3>::FromInt64(13, 3), ());
TEST_EQUAL(m2::CellId<3>("23"), m2::CellId<3>::FromInt64(16, 3), ());
TEST_EQUAL(m2::CellId<3>("30"), m2::CellId<3>::FromInt64(18, 3), ());
TEST_EQUAL(m2::CellId<3>("31"), m2::CellId<3>::FromInt64(19, 3), ());
TEST_EQUAL(m2::CellId<3>("33"), m2::CellId<3>::FromInt64(21, 3), ());
}
UNIT_TEST(CellId_XY)
{
TEST_EQUAL(m2::CellId<3>("").XY(), make_pair(4U, 4U), ());
TEST_EQUAL(m2::CellId<3>("0").XY(), make_pair(2U, 2U), ());
TEST_EQUAL(m2::CellId<3>("1").XY(), make_pair(6U, 2U), ());
TEST_EQUAL(m2::CellId<3>("2").XY(), make_pair(2U, 6U), ());
TEST_EQUAL(m2::CellId<3>("3").XY(), make_pair(6U, 6U), ());
TEST_EQUAL(m2::CellId<3>("00").XY(), make_pair(1U, 1U), ());
TEST_EQUAL(m2::CellId<3>("01").XY(), make_pair(3U, 1U), ());
TEST_EQUAL(m2::CellId<3>("03").XY(), make_pair(3U, 3U), ());
TEST_EQUAL(m2::CellId<3>("10").XY(), make_pair(5U, 1U), ());
TEST_EQUAL(m2::CellId<3>("20").XY(), make_pair(1U, 5U), ());
TEST_EQUAL(m2::CellId<3>("23").XY(), make_pair(3U, 7U), ());
TEST_EQUAL(m2::CellId<3>("30").XY(), make_pair(5U, 5U), ());
TEST_EQUAL(m2::CellId<3>("31").XY(), make_pair(7U, 5U), ());
TEST_EQUAL(m2::CellId<3>("33").XY(), make_pair(7U, 7U), ());
TEST_EQUAL(m2::CellId<3>("33").XY(), make_pair(7U, 7U), ());
}
UNIT_TEST(CellId_Radius)
{
TEST_EQUAL(m2::CellId<3>("").Radius(), 4, ());
TEST_EQUAL(m2::CellId<3>("1").Radius(), 2, ());
TEST_EQUAL(m2::CellId<3>("00").Radius(), 1, ());
}
UNIT_TEST(CellId_FromXY)
{
TEST_EQUAL((m2::CellId<3>::FromXY(0, 0, 2)), (m2::CellId<3>("00")), ());
TEST_EQUAL((m2::CellId<3>::FromXY(0, 0, 1)), (m2::CellId<3>("0")), ());
TEST_EQUAL((m2::CellId<3>::FromXY(0, 0, 0)), (m2::CellId<3>("")), ());
TEST_EQUAL((m2::CellId<3>::FromXY(5, 4, 0)), (m2::CellId<3>("")), ());
TEST_EQUAL((m2::CellId<3>::FromXY(5, 0, 2)), (m2::CellId<3>("10")), ());
TEST_EQUAL((m2::CellId<3>::FromXY(5, 0, 1)), (m2::CellId<3>("1")), ());
TEST_EQUAL((m2::CellId<3>::FromXY(5, 0, 1)), (m2::CellId<3>("1")), ());
TEST_EQUAL((m2::CellId<3>::FromXY(7, 7, 2)), (m2::CellId<3>("33")), ());
TEST_EQUAL((m2::CellId<3>::FromXY(7, 7, 1)), (m2::CellId<3>("3")), ());
TEST_EQUAL((m2::CellId<3>::FromXY(7, 7, 0)), (m2::CellId<3>("")), ());
TEST_EQUAL((m2::CellId<3>::FromXY(8, 8, 2)), (m2::CellId<3>("33")), ());
}
UNIT_TEST(CellId_FromXY_XY_Match)
{
TEST_EQUAL((m2::CellId<9>::FromXY(48, 80, 8).XY()), make_pair(49U, 81U), ());
TEST_EQUAL((m2::CellId<9>::FromXY(192, 320, 8).XY()), make_pair(193U, 321U), ());
TEST_EQUAL((m2::CellId<11>::FromXY(768, 1280, 10).XY()), make_pair(769U, 1281U), ());
TEST_EQUAL((m2::CellId<21>::FromXY(786432, 1310720, 20).XY()), make_pair(786433U, 1310721U), ());
}
UNIT_TEST(CellId_SubTreeSize)
{
TEST_EQUAL(m2::CellId<3>("00").SubTreeSize(3), 1, ());
TEST_EQUAL(m2::CellId<3>("22").SubTreeSize(3), 1, ());
TEST_EQUAL(m2::CellId<3>("33").SubTreeSize(3), 1, ());
TEST_EQUAL(m2::CellId<3>("0").SubTreeSize(3), 5, ());
TEST_EQUAL(m2::CellId<3>("1").SubTreeSize(3), 5, ());
TEST_EQUAL(m2::CellId<3>("3").SubTreeSize(3), 5, ());
TEST_EQUAL(m2::CellId<3>("").SubTreeSize(3), 21, ());
}
UNIT_TEST(CellId_LessQueueOrder)
{
vector<string> v;
v.emplace_back("0");
v.emplace_back("1");
v.emplace_back("00");
v.emplace_back("00");
v.emplace_back("02");
v.emplace_back("002");
v.emplace_back("101");
vector<string> e = v;
do
{
vector<m2::CellId<4>> tst, exp;
for (size_t i = 0; i < v.size(); ++i)
{
tst.emplace_back(e[i]);
exp.emplace_back(v[i]);
}
sort(tst.begin(), tst.end(), m2::CellId<4>::LessLevelOrder());
TEST_EQUAL(tst, exp, ());
}
while (next_permutation(e.begin(), e.end()));
}
UNIT_TEST(CellId_LessStackOrder)
{
vector<string> v;
v.emplace_back("0");
v.emplace_back("00");
v.emplace_back("00");
v.emplace_back("002");
v.emplace_back("02");
v.emplace_back("1");
v.emplace_back("101");
vector<string> e = v;
do
{
vector<m2::CellId<4>> tst, exp;
for (size_t i = 0; i < v.size(); ++i)
{
tst.emplace_back(e[i]);
exp.emplace_back(v[i]);
}
sort(tst.begin(), tst.end(), m2::CellId<4>::LessPreOrder());
TEST_EQUAL(tst, exp, ());
}
while (next_permutation(e.begin(), e.end()));
}
UNIT_TEST(CellId_IsStringValid)
{
using Id = m2::CellId<9>;
TEST(Id::IsCellId("0123132"), ());
TEST(Id::IsCellId(""), ());
TEST(!Id::IsCellId("-1332"), ());
TEST(!Id::IsCellId("023."), ());
TEST(!Id::IsCellId("121832"), ());
}
UNIT_TEST(CellId_ToAndFromInt64ZOrder)
{
int const kMaxDepth = 4;
using Id = m2::CellId<kMaxDepth>;
for (int depth = 1; depth <= kMaxDepth; ++depth)
{
int64_t const treeSize = ((int64_t{1} << (2 * depth)) - 1) / 3;
LOG(LINFO, ("Depth =", depth, " TreeSize =", treeSize));
for (int64_t id = 1; id <= treeSize; ++id)
{
auto const cell = Id::FromInt64ZOrder(id, depth);
TEST_EQUAL(id, cell.ToInt64ZOrder(depth), ());
}
}
vector<string> const atDepth3 = {
"", "0", "00", "01", "02", "03", "1", "10", "11", "12", "13",
"2", "20", "21", "22", "23", "3", "30", "31", "32", "33",
};
for (uint64_t id = 1; id <= atDepth3.size(); ++id)
TEST_EQUAL(Id::FromInt64(id, 3), Id(atDepth3[id - 1]), ());
}
} // namespace cellid_test

View file

@ -0,0 +1,73 @@
#include "testing/testing.hpp"
#include "geometry/circle_on_earth.hpp"
#include "geometry/distance_on_sphere.hpp"
#include "geometry/mercator.hpp"
#include "geometry/point2d.hpp"
#include "base/math.hpp"
#include <sstream>
#include <vector>
namespace
{
void TestGeometryAlmostEqualAbs(std::vector<m2::PointD> const & geometry, std::vector<m2::PointD> const & answer,
double eps)
{
TEST_EQUAL(geometry.size(), answer.size(), ());
for (size_t i = 0; i < geometry.size(); ++i)
TEST_ALMOST_EQUAL_ABS(geometry[i], answer[i], eps, ());
}
void TestGeometryAlmostEqualMeters(std::vector<m2::PointD> const & geometry, std::vector<m2::PointD> const & answer,
double epsMeters)
{
TEST_EQUAL(geometry.size(), answer.size(), ());
for (size_t i = 0; i < geometry.size(); ++i)
TEST_LESS(mercator::DistanceOnEarth(geometry[i], answer[i]), epsMeters, ());
}
} // namespace
UNIT_TEST(CircleOnEarth)
{
ms::LatLon const center(90.0 /* lat */, 0.0 /* lon */);
double const radiusMeters = 2.0 * math::pi * ms::kEarthRadiusMeters / 4.0;
auto const geometry = ms::CreateCircleGeometryOnEarth(center, radiusMeters, 90.0 /* angleStepDegree */);
std::vector<m2::PointD> const result = {mercator::FromLatLon(0.0, 0.0), mercator::FromLatLon(0.0, 90.0),
mercator::FromLatLon(0.0, 180.0), mercator::FromLatLon(0.0, -90.0)};
TestGeometryAlmostEqualAbs(geometry, result, 1e-9 /* eps */);
}
UNIT_TEST(CircleOnEarthEquator)
{
ms::LatLon const center(0.0 /* lat */, 0.0 /* lon */);
auto const point = mercator::FromLatLon(center);
auto constexpr kRadiusMeters = 10000.0;
double const kRadiusMercator = mercator::MetersToMercator(kRadiusMeters);
auto constexpr kAngleStepDegree = 30.0;
auto constexpr kN = static_cast<size_t>(360.0 / kAngleStepDegree);
std::vector<m2::PointD> result;
result.reserve(kN);
auto constexpr kStepRad = math::DegToRad(kAngleStepDegree);
double angleSumRad = 0.0;
double angleRad = -math::pi2;
while (angleSumRad < 2 * math::pi)
{
angleSumRad += kStepRad;
result.emplace_back(point.x + kRadiusMercator * cos(angleRad), point.y + kRadiusMercator * sin(angleRad));
angleRad += kStepRad;
if (angleRad > 2 * math::pi)
angleRad -= 2 * math::pi;
}
auto const geometry = ms::CreateCircleGeometryOnEarth(center, kRadiusMeters, kAngleStepDegree);
TestGeometryAlmostEqualMeters(geometry, result, 20.0 /* epsMeters */);
}

View file

@ -0,0 +1,278 @@
#include "testing/testing.hpp"
#include "geometry/clipping.hpp"
#include <cstddef>
#include <utility>
#include <vector>
namespace clipping_test
{
using namespace std;
bool CompareTriangleLists(vector<m2::PointD> const & list1, vector<m2::PointD> const & list2)
{
if (list1.size() != list2.size())
return false;
double const kEps = 1e-5;
for (size_t i = 0; i < list1.size(); i++)
if (!list1[i].EqualDxDy(list2[i], kEps))
return false;
return true;
}
bool CompareSplineLists(vector<m2::SharedSpline> const & list1, vector<m2::SharedSpline> const & list2)
{
if (list1.size() != list2.size())
return false;
double const kEps = 1e-5;
for (size_t i = 0; i < list1.size(); i++)
{
auto & path1 = list1[i]->GetPath();
auto & path2 = list2[i]->GetPath();
if (path1.size() != path2.size())
return false;
for (size_t j = 0; j < path1.size(); j++)
if (!path1[j].EqualDxDy(path2[j], kEps))
return false;
}
return true;
}
vector<m2::SharedSpline> ConstructSplineList(vector<vector<m2::PointD>> const & segments)
{
vector<m2::SharedSpline> result;
result.reserve(segments.size());
for (size_t i = 0; i < segments.size(); i++)
{
m2::SharedSpline s;
s.Reset(new m2::Spline(segments[i].size()));
for (size_t j = 0; j < segments[i].size(); j++)
s->AddPoint(segments[i][j]);
result.push_back(std::move(s));
}
return result;
}
UNIT_TEST(Clipping_ClipTriangleByRect)
{
m2::RectD r(-1.0, -1.0, 1.0, 1.0);
// Completely inside.
vector<m2::PointD> result1;
m2::ClipTriangleByRect(r, m2::PointD(0.5, 0.5), m2::PointD(0.5, -0.5), m2::PointD(0.0, 0.0),
[&result1](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3)
{
result1.push_back(p1);
result1.push_back(p2);
result1.push_back(p3);
});
vector<m2::PointD> expectedResult1 = {m2::PointD(0.5, 0.5), m2::PointD(0.5, -0.5), m2::PointD(0.0, 0.0)};
TEST(CompareTriangleLists(result1, expectedResult1), (result1, expectedResult1));
// 1 point inside.
vector<m2::PointD> result2;
m2::ClipTriangleByRect(r, m2::PointD(0.0, 0.0), m2::PointD(2.0, 2.0), m2::PointD(2.0, -2.0),
[&result2](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3)
{
result2.push_back(p1);
result2.push_back(p2);
result2.push_back(p3);
});
vector<m2::PointD> expectedResult2 = {m2::PointD(0.0, 0.0), m2::PointD(1.0, 1.0), m2::PointD(1.0, -1.0)};
TEST(CompareTriangleLists(result2, expectedResult2), (result2, expectedResult2));
// 2 points inside.
vector<m2::PointD> result3;
m2::ClipTriangleByRect(r, m2::PointD(0.0, 0.5), m2::PointD(2.0, 0.0), m2::PointD(0.0, -0.5),
[&result3](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3)
{
result3.push_back(p1);
result3.push_back(p2);
result3.push_back(p3);
});
vector<m2::PointD> expectedResult3 = {m2::PointD(0.0, 0.5), m2::PointD(1.0, 0.25), m2::PointD(1.0, -0.25),
m2::PointD(0.0, 0.5), m2::PointD(1.0, -0.25), m2::PointD(0.0, -0.5)};
TEST(CompareTriangleLists(result3, expectedResult3), (result3, expectedResult3));
// 2 edges clipping.
vector<m2::PointD> result4;
m2::ClipTriangleByRect(r, m2::PointD(0.0, 0.0), m2::PointD(0.0, 1.5), m2::PointD(1.5, 0.0),
[&result4](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3)
{
result4.push_back(p1);
result4.push_back(p2);
result4.push_back(p3);
});
vector<m2::PointD> expectedResult4 = {m2::PointD(0.0, 0.0), m2::PointD(0.0, 1.0), m2::PointD(0.5, 1.0),
m2::PointD(0.0, 0.0), m2::PointD(0.5, 1.0), m2::PointD(1.0, 0.5),
m2::PointD(0.0, 0.0), m2::PointD(1.0, 0.5), m2::PointD(1.0, 0.0)};
TEST(CompareTriangleLists(result4, expectedResult4), (result4, expectedResult4));
// 3 edges clipping.
vector<m2::PointD> result5;
m2::ClipTriangleByRect(r, m2::PointD(-1.5, 0.0), m2::PointD(0.0, 1.5), m2::PointD(1.5, 0.0),
[&result5](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3)
{
result5.push_back(p1);
result5.push_back(p2);
result5.push_back(p3);
});
vector<m2::PointD> expectedResult5 = {m2::PointD(-1.0, 0.5), m2::PointD(-0.5, 1.0), m2::PointD(0.5, 1.0),
m2::PointD(-1.0, 0.5), m2::PointD(0.5, 1.0), m2::PointD(1.0, 0.5),
m2::PointD(-1.0, 0.5), m2::PointD(1.0, 0.5), m2::PointD(1.0, 0.0),
m2::PointD(-1.0, 0.5), m2::PointD(1.0, 0.0), m2::PointD(-1.0, 0.0)};
TEST(CompareTriangleLists(result5, expectedResult5), (result5, expectedResult5));
// Completely outside.
vector<m2::PointD> result6;
m2::ClipTriangleByRect(r, m2::PointD(1.5, 1.5), m2::PointD(1.5, -1.5), m2::PointD(2.0, 0.0),
[&result6](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3)
{
result6.push_back(p1);
result6.push_back(p2);
result6.push_back(p3);
});
vector<m2::PointD> expectedResult6 = {};
TEST(CompareTriangleLists(result6, expectedResult6), (result6, expectedResult6));
// Clip with an angle of rect.
vector<m2::PointD> result7;
m2::ClipTriangleByRect(r, m2::PointD(0.5, 0.5), m2::PointD(0.5, 2.0), m2::PointD(2.0, 0.5),
[&result7](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3)
{
result7.push_back(p1);
result7.push_back(p2);
result7.push_back(p3);
});
vector<m2::PointD> expectedResult7 = {m2::PointD(0.5, 0.5), m2::PointD(0.5, 1.0), m2::PointD(1.0, 1.0),
m2::PointD(0.5, 0.5), m2::PointD(1.0, 1.0), m2::PointD(1.0, 0.5)};
TEST(CompareTriangleLists(result7, expectedResult7), (result7, expectedResult7));
// Triangle covers rect.
vector<m2::PointD> result8;
m2::ClipTriangleByRect(r, m2::PointD(0.0, 3.0), m2::PointD(5.0, -2.0), m2::PointD(-5.0, -2.0),
[&result8](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3)
{
result8.push_back(p1);
result8.push_back(p2);
result8.push_back(p3);
});
vector<m2::PointD> expectedResult8 = {m2::PointD(-1.0, 1.0), m2::PointD(1.0, 1.0), m2::PointD(1.0, -1.0),
m2::PointD(1.0, -1.0), m2::PointD(-1.0, -1.0), m2::PointD(-1.0, 1.0)};
TEST(CompareTriangleLists(result8, expectedResult8), (result8, expectedResult8));
// Clip with an angle of rect.
vector<m2::PointD> result9;
m2::ClipTriangleByRect(r, m2::PointD(1.5, 0.0), m2::PointD(1.5, -1.5), m2::PointD(0.0, -1.5),
[&result9](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3)
{
result9.push_back(p1);
result9.push_back(p2);
result9.push_back(p3);
});
vector<m2::PointD> expectedResult9 = {m2::PointD(0.5, -1.0), m2::PointD(1.0, -0.5), m2::PointD(1.0, -1.0)};
TEST(CompareTriangleLists(result9, expectedResult9), (result9, expectedResult9));
// Clip with an angle of rect.
vector<m2::PointD> result10;
m2::ClipTriangleByRect(r, m2::PointD(-2.0, -0.5), m2::PointD(-0.5, -0.5), m2::PointD(-0.5, -2.0),
[&result10](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3)
{
result10.push_back(p1);
result10.push_back(p2);
result10.push_back(p3);
});
vector<m2::PointD> expectedResult10 = {m2::PointD(-1.0, -0.5), m2::PointD(-0.5, -0.5), m2::PointD(-0.5, -1.0),
m2::PointD(-1.0, -0.5), m2::PointD(-0.5, -1.0), m2::PointD(-1.0, -1.0)};
TEST(CompareTriangleLists(result10, expectedResult10), (result10, expectedResult10));
// Clip with 3 angles of rect.
vector<m2::PointD> result11;
m2::ClipTriangleByRect(r, m2::PointD(2.0, -3.0), m2::PointD(-2.0, 1.0), m2::PointD(2.0, 2.0),
[&result11](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3)
{
result11.push_back(p1);
result11.push_back(p2);
result11.push_back(p3);
});
vector<m2::PointD> expectedResult11 = {m2::PointD(0.0, -1.0), m2::PointD(-1.0, 0.0), m2::PointD(-1.0, 1.0),
m2::PointD(0.0, -1.0), m2::PointD(-1.0, 1.0), m2::PointD(1.0, 1.0),
m2::PointD(0.0, -1.0), m2::PointD(1.0, 1.0), m2::PointD(1.0, -1.0)};
TEST(CompareTriangleLists(result11, expectedResult11), (result11, expectedResult11));
}
UNIT_TEST(Clipping_ClipSplineByRect)
{
m2::RectD r(-1.0, -1.0, 1.0, 1.0);
// Intersection.
m2::SharedSpline spline1;
spline1.Reset(new m2::Spline(2));
spline1->AddPoint(m2::PointD(-2.0, 0.0));
spline1->AddPoint(m2::PointD(2.0, 1.0));
vector<m2::SharedSpline> result1 = m2::ClipSplineByRect(r, spline1);
vector<m2::SharedSpline> expectedResult1 = ConstructSplineList({{m2::PointD(-1.0, 0.25), m2::PointD(1.0, 0.75)}});
TEST(CompareSplineLists(result1, expectedResult1), ());
// Intersection. Several segments.
m2::SharedSpline spline2;
spline2.Reset(new m2::Spline(4));
spline2->AddPoint(m2::PointD(-2.0, 0.0));
spline2->AddPoint(m2::PointD(2.0, 1.0));
spline2->AddPoint(m2::PointD(0.5, -2.0));
spline2->AddPoint(m2::PointD(-0.5, -0.5));
vector<m2::SharedSpline> result2 = m2::ClipSplineByRect(r, spline2);
vector<m2::SharedSpline> expectedResult2 = ConstructSplineList(
{{m2::PointD(-1.0, 0.25), m2::PointD(1.0, 0.75)}, {m2::PointD(-0.166666666, -1.0), m2::PointD(-0.5, -0.5)}});
TEST(CompareSplineLists(result2, expectedResult2), ());
// Completely outside.
m2::SharedSpline spline3;
spline3.Reset(new m2::Spline(2));
spline3->AddPoint(m2::PointD(-2.0, 2.0));
spline3->AddPoint(m2::PointD(2.0, 3.0));
vector<m2::SharedSpline> result3 = m2::ClipSplineByRect(r, spline3);
vector<m2::SharedSpline> expectedResult3 = {};
TEST(CompareSplineLists(result3, expectedResult3), ());
// Completely inside.
m2::SharedSpline spline4;
spline4.Reset(new m2::Spline(2));
spline4->AddPoint(m2::PointD(-0.5, 0.0));
spline4->AddPoint(m2::PointD(0.5, 0.5));
vector<m2::SharedSpline> result4 = m2::ClipSplineByRect(r, spline4);
vector<m2::SharedSpline> expectedResult4 = ConstructSplineList({{m2::PointD(-0.5, 0.0), m2::PointD(0.5, 0.5)}});
TEST(CompareSplineLists(result4, expectedResult4), ());
// Intersection. Long spline.
m2::SharedSpline spline5;
spline5.Reset(new m2::Spline(4));
spline5->AddPoint(m2::PointD(-2.0, 0.0));
spline5->AddPoint(m2::PointD(0.0, 0.0));
spline5->AddPoint(m2::PointD(0.5, 0.5));
spline5->AddPoint(m2::PointD(2.0, 1.0));
vector<m2::SharedSpline> result5 = m2::ClipSplineByRect(r, spline5);
vector<m2::SharedSpline> expectedResult5 = ConstructSplineList(
{{m2::PointD(-1.0, 0.0), m2::PointD(0.0, 0.0), m2::PointD(0.5, 0.5), m2::PointD(1.0, 0.66666666)}});
TEST(CompareSplineLists(result5, expectedResult5), ());
// Intersection. Several segments.
m2::SharedSpline spline6;
spline6.Reset(new m2::Spline(5));
spline6->AddPoint(m2::PointD(-1.0, 0.0));
spline6->AddPoint(m2::PointD(-0.5, 1.0));
spline6->AddPoint(m2::PointD(-0.5, 1.000000001));
spline6->AddPoint(m2::PointD(0.0, 1.5));
spline6->AddPoint(m2::PointD(0.0, 0.0));
vector<m2::SharedSpline> result6 = m2::ClipSplineByRect(r, spline6);
vector<m2::SharedSpline> expectedResult6 = ConstructSplineList(
{{m2::PointD(-1.0, 0.0), m2::PointD(-0.5, 1.0)}, {m2::PointD(0.0, 1.0), m2::PointD(0.0, 0.0)}});
TEST(CompareSplineLists(result6, expectedResult6), ());
}
} // namespace clipping_test

View file

@ -0,0 +1,39 @@
#include "base/macros.hpp"
#include "geometry/geometry_tests/equality.hpp"
#include "testing/testing.hpp"
#include "geometry/rect2d.hpp"
using namespace test;
UNIT_TEST(Rect)
{
m2::RectD rect(0, 0, 500, 300);
double factor[] = {0.2, 0.3, 0.5, 0.7, 1.0, 1.3, 1.5, 2.0};
for (size_t i = 0; i < ARRAY_SIZE(factor); ++i)
{
m2::RectD r(rect);
r.Scale(factor[i]);
TEST(is_equal_center(rect, r), ());
}
m2::RectD external(0, 0, 100, 100);
TEST(external.IsIntersect(m2::RectD(10, 10, 90, 90)), ());
TEST(external.IsPointInside(m2::PointD(5, 33)), ());
TEST(external.IsIntersect(m2::RectD(99, 99, 1000, 1000)), ());
}
UNIT_TEST(Point)
{
double const l = sqrt(2.0);
double const a = math::pi / 4.0;
m2::PointD const start(0.0, 0.0);
TEST(is_equal(start.Move(l, a), m2::PointD(1, 1)), ());
TEST(is_equal(start.Move(l, math::pi - a), m2::PointD(-1, 1)), ());
TEST(is_equal(start.Move(l, -math::pi + a), m2::PointD(-1, -1)), ());
TEST(is_equal(start.Move(l, -a), m2::PointD(1, -1)), ());
}

View file

@ -0,0 +1,46 @@
#include "testing/testing.hpp"
#include "geometry/convex_hull.hpp"
#include "geometry/point2d.hpp"
#include <vector>
namespace convex_hull_tests
{
using namespace m2;
using namespace std;
double constexpr kEps = 1e-12;
vector<PointD> BuildConvexHull(vector<PointD> const & points)
{
return ConvexHull(points, kEps).Points();
}
UNIT_TEST(ConvexHull_Smoke)
{
TEST_EQUAL(BuildConvexHull({}), vector<PointD>{}, ());
TEST_EQUAL(BuildConvexHull({PointD(0, 0)}), vector<PointD>{PointD(0, 0)}, ());
TEST_EQUAL(BuildConvexHull({PointD(0, 0), PointD(0, 0)}), vector<PointD>{PointD(0, 0)}, ());
TEST_EQUAL(BuildConvexHull({PointD(0, 0), PointD(1, 1), PointD(0, 0)}), vector<PointD>({PointD(0, 0), PointD(1, 1)}),
());
TEST_EQUAL(BuildConvexHull({PointD(0, 0), PointD(1, 1), PointD(2, 2)}), vector<PointD>({PointD(0, 0), PointD(2, 2)}),
());
{
int const kXMax = 100;
int const kYMax = 200;
vector<PointD> points;
for (int x = 0; x <= kXMax; ++x)
for (int y = 0; y <= kYMax; ++y)
points.emplace_back(x, y);
TEST_EQUAL(BuildConvexHull(points),
vector<PointD>({PointD(0, 0), PointD(kXMax, 0), PointD(kXMax, kYMax), PointD(0, kYMax)}), ());
}
TEST_EQUAL(BuildConvexHull({PointD(0, 0), PointD(0, 5), PointD(10, 5), PointD(3, 3), PointD(10, 0)}),
vector<PointD>({PointD(0, 0), PointD(10, 0), PointD(10, 5), PointD(0, 5)}), ());
}
} // namespace convex_hull_tests

View file

@ -0,0 +1,136 @@
#include "testing/testing.hpp"
#include "geometry/cellid.hpp"
#include "geometry/covering.hpp"
#include "geometry/covering_utils.hpp"
#include "geometry/point2d.hpp"
#include "base/stl_helpers.hpp"
#include <vector>
// TODO: Add covering unit tests here.
namespace covering_test
{
using namespace std;
using CellId = m2::CellId<5>;
UNIT_TEST(CoverTriangle_Simple)
{
vector<CellId> v;
covering::Covering<CellId> c(m2::PointD(3 * 2, 3 * 2), m2::PointD(4 * 2, 12 * 2), m2::PointD(14 * 2, 3 * 2));
c.OutputToVector(v);
vector<CellId> e;
e.push_back(CellId("03"));
e.push_back(CellId("003"));
e.push_back(CellId("012"));
e.push_back(CellId("013"));
e.push_back(CellId("102"));
e.push_back(CellId("103"));
e.push_back(CellId("112"));
e.push_back(CellId("120"));
e.push_back(CellId("121"));
e.push_back(CellId("122"));
e.push_back(CellId("210"));
e.push_back(CellId("211"));
e.push_back(CellId("212"));
e.push_back(CellId("0211"));
e.push_back(CellId("0213"));
e.push_back(CellId("0231"));
e.push_back(CellId("0233"));
e.push_back(CellId("1130"));
e.push_back(CellId("1132"));
e.push_back(CellId("1230"));
e.push_back(CellId("1300"));
e.push_back(CellId("2011"));
e.push_back(CellId("2013"));
e.push_back(CellId("2031"));
e.push_back(CellId("2033"));
e.push_back(CellId("2130"));
e.push_back(CellId("2211"));
e.push_back(CellId("2300"));
e.push_back(CellId("3000"));
TEST_EQUAL(v, e, ());
}
UNIT_TEST(CoverTriangle_TriangleInsideCell)
{
vector<CellId> v;
covering::Covering<CellId> c(m2::PointD(0.1, 0.1), m2::PointD(0.2, 0.2), m2::PointD(0.2, 0.4));
c.OutputToVector(v);
vector<CellId> e;
e.push_back(CellId("0000"));
TEST_EQUAL(v, e, ());
}
UNIT_TEST(Covering_Append_Simple)
{
vector<CellId> v1, v2, v3;
v1.push_back(CellId("012"));
v2.push_back(CellId("0123"));
v3.push_back(CellId("012"));
v1.push_back(CellId("023"));
v2.push_back(CellId("023"));
v3.push_back(CellId("023"));
v1.push_back(CellId("130"));
v1.push_back(CellId("131"));
v2.push_back(CellId("133"));
v1.push_back(CellId("1320"));
v1.push_back(CellId("1321"));
v2.push_back(CellId("1322"));
v2.push_back(CellId("1323"));
v3.push_back(CellId("13"));
covering::Covering<CellId> c1(v1);
c1.Append(covering::Covering<CellId>(v2));
vector<CellId> v4;
c1.OutputToVector(v4);
sort(v4.begin(), v4.end(), CellId::LessPreOrder());
TEST_EQUAL(v3, v4, ());
}
UNIT_TEST(IntersectCellWithTriangle_EmptyTriangle)
{
m2::PointD pt(27.0, 31.0);
TEST_EQUAL(covering::CELL_OBJECT_NO_INTERSECTION, covering::IntersectCellWithTriangle(CellId("0"), pt, pt, pt), ());
TEST_EQUAL(covering::CELL_OBJECT_NO_INTERSECTION, covering::IntersectCellWithTriangle(CellId("1"), pt, pt, pt), ());
TEST_EQUAL(covering::CELL_OBJECT_NO_INTERSECTION, covering::IntersectCellWithTriangle(CellId("2"), pt, pt, pt), ());
TEST_EQUAL(covering::OBJECT_INSIDE_CELL, covering::IntersectCellWithTriangle(CellId("3"), pt, pt, pt), ());
}
UNIT_TEST(Covering_EmptyTriangle)
{
m2::PointU pt(27, 31);
m2::PointD ptd(pt);
CellId const expectedCellId = CellId::FromXY(pt.x, pt.y, CellId::DEPTH_LEVELS - 1);
TEST_GREATER(expectedCellId.ToInt64(CellId::DEPTH_LEVELS), 5, ());
covering::Covering<CellId> covering(ptd, ptd, ptd);
vector<CellId> ids;
covering.OutputToVector(ids);
TEST_EQUAL(ids, vector<CellId>(1, expectedCellId), ());
}
UNIT_TEST(Covering_Simplify_Smoke)
{
vector<CellId> v;
v.push_back(CellId("03"));
v.push_back(CellId("020"));
v.push_back(CellId("021"));
v.push_back(CellId("022"));
v.push_back(CellId("0012"));
covering::Covering<CellId> covering(v);
v.clear();
covering.Simplify();
covering.OutputToVector(v);
vector<CellId> e;
e.push_back(CellId("02"));
e.push_back(CellId("03"));
e.push_back(CellId("0012"));
TEST_EQUAL(v, e, ());
}
} // namespace covering_test

View file

@ -0,0 +1,50 @@
#include "testing/testing.hpp"
#include "geometry/diamond_box.hpp"
namespace diamond_box_tests
{
UNIT_TEST(DiamondBox_Smoke)
{
{
m2::DiamondBox dbox;
TEST(!dbox.HasPoint(0, 0), ());
}
{
m2::DiamondBox dbox;
dbox.Add(0, 0);
TEST(dbox.HasPoint(0, 0), ());
TEST(!dbox.HasPoint(0, 1), ());
TEST(!dbox.HasPoint(1, 0), ());
TEST(!dbox.HasPoint(1, 1), ());
TEST(!dbox.HasPoint(0.5, 0.5), ());
dbox.Add(1, 1);
TEST(dbox.HasPoint(0, 0), ());
TEST(dbox.HasPoint(1, 1), ());
TEST(dbox.HasPoint(0.5, 0.5), ());
TEST(!dbox.HasPoint(1, 0), ());
TEST(!dbox.HasPoint(0, 1), ());
}
{
m2::DiamondBox dbox;
dbox.Add(0, 1);
dbox.Add(0, -1);
dbox.Add(-1, 0);
dbox.Add(1, 0);
TEST(dbox.HasPoint(0, 0), ());
TEST(dbox.HasPoint(0.5, 0.5), ());
TEST(dbox.HasPoint(0.5, -0.5), ());
TEST(dbox.HasPoint(-0.5, 0.5), ());
TEST(dbox.HasPoint(-0.5, -0.5), ());
TEST(!dbox.HasPoint(0.51, 0.51), ());
TEST(!dbox.HasPoint(0.51, -0.51), ());
TEST(!dbox.HasPoint(-0.51, 0.51), ());
TEST(!dbox.HasPoint(-0.51, -0.51), ());
}
}
} // namespace diamond_box_tests

View file

@ -0,0 +1,24 @@
#include "testing/testing.hpp"
#include "geometry/distance_on_sphere.hpp"
#include "base/math.hpp"
UNIT_TEST(DistanceOnSphere)
{
TEST_LESS(fabs(ms::DistanceOnSphere(0, -180, 0, 180)), 1.0e-6, ());
TEST_LESS(fabs(ms::DistanceOnSphere(30, 0, 30, 360)), 1.0e-6, ());
TEST_LESS(fabs(ms::DistanceOnSphere(-30, 23, -30, 23)), 1.0e-6, ());
TEST_LESS(fabs(ms::DistanceOnSphere(90, 0, 90, 120)), 1.0e-6, ());
TEST_LESS(fabs(ms::DistanceOnSphere(0, 0, 0, 180) - math::pi), 1.0e-6, ());
TEST_LESS(fabs(ms::DistanceOnSphere(90, 0, -90, 120) - math::pi), 1.0e-6, ());
}
UNIT_TEST(DistanceOnEarth)
{
TEST_LESS(fabs(ms::DistanceOnEarth(30, 0, 30, 180) * 0.001 - 13358), 1, ());
TEST_LESS(fabs(ms::DistanceOnEarth(30, 0, 30, 45) * 0.001 - 4309), 1, ());
TEST_LESS(fabs(ms::DistanceOnEarth(-30, 0, -30, 45) * 0.001 - 4309), 1, ());
TEST_LESS(fabs(ms::DistanceOnEarth(47.37, 8.56, 53.91, 27.56) * 0.001 - 1519), 1, ());
TEST_LESS(fabs(ms::DistanceOnEarth(43, 132, 38, -122.5) * 0.001 - 8302), 1, ());
}

View file

@ -0,0 +1,49 @@
#pragma once
#include "base/math.hpp"
#include "geometry/angles.hpp"
#include "geometry/rect2d.hpp"
namespace test
{
inline bool is_equal(double a1, double a2)
{
return (fabs(a1 - a2) < 1.0E-10);
}
inline bool is_equal_atan(double x, double y, double v)
{
return is_equal(ang::AngleTo(m2::PointD(0, 0), m2::PointD(x, y)), v);
}
inline bool is_equal_angle(double a1, double a2)
{
double const two_pi = 2.0 * math::pi;
if (a1 < 0.0)
a1 += two_pi;
if (a2 < 0.0)
a2 += two_pi;
return is_equal(a1, a2);
}
inline bool is_equal(m2::PointD const & p1, m2::PointD const & p2)
{
return p1.EqualDxDy(p2, 1.0E-8);
}
inline bool is_equal_center(m2::RectD const & r1, m2::RectD const & r2)
{
return is_equal(r1.Center(), r2.Center());
}
struct strict_equal
{
bool operator()(m2::PointD const & p1, m2::PointD const & p2) const { return p1 == p2; }
};
struct epsilon_equal
{
bool operator()(m2::PointD const & p1, m2::PointD const & p2) const { return is_equal(p1, p2); }
};
} // namespace test

View file

@ -0,0 +1,177 @@
#include "geometry/geometry_tests/equality.hpp"
#include "testing/testing.hpp"
#include "geometry/angles.hpp"
#include "geometry/rect_intersect.hpp"
using namespace test;
namespace
{
typedef m2::PointD P;
}
m2::PointD get_point(m2::RectD const & r, int ind)
{
switch (ind % 4)
{
case 0: return r.LeftBottom();
case 1: return r.LeftTop();
case 2: return r.RightTop();
case 3: return r.RightBottom();
default: ASSERT(false, ()); return m2::PointD();
}
}
void make_section_longer(m2::PointD & p1, m2::PointD & p2, double sm)
{
if (p1.x == p2.x)
{
if (p1.y > p2.y)
sm = -sm;
p1.y -= sm;
p2.y += sm;
}
else if (p1.y == p2.y)
{
if (p1.x > p2.x)
sm = -sm;
p1.x -= sm;
p2.x += sm;
}
else
{
double const az = ang::AngleTo(p1, p2);
p1.Move(-sm, az);
p2.Move(sm, az);
}
}
template <class TComp>
void check_full_equal(m2::RectD const & r, m2::PointD const & p1, m2::PointD const & p2, TComp comp)
{
m2::PointD pp1 = p1;
m2::PointD pp2 = p2;
make_section_longer(pp1, pp2, 1000.0);
TEST(m2::Intersect(r, pp1, pp2), ());
TEST(comp(pp1, p1) && comp(pp2, p2), ());
}
void check_inside(m2::RectD const & r, m2::PointD const & p1, m2::PointD const & p2)
{
m2::PointD pp1 = p1;
m2::PointD pp2 = p2;
TEST(m2::Intersect(r, pp1, pp2), ());
TEST((pp1 == p1) && (pp2 == p2), ());
}
void check_intersect_boundaries(m2::RectD const & r)
{
for (int i = 0; i < 4; ++i)
{
check_full_equal(r, get_point(r, i), get_point(r, i + 1), strict_equal());
check_inside(r, get_point(r, i), get_point(r, i + 1));
}
}
void check_intersect_diagonal(m2::RectD const & r)
{
for (int i = 0; i < 4; ++i)
{
check_full_equal(r, get_point(r, i), get_point(r, i + 2), epsilon_equal());
check_inside(r, get_point(r, i), get_point(r, i + 2));
}
}
void check_sides(m2::RectD const & r)
{
for (int i = 0; i < 4; ++i)
{
m2::PointD p1 = (get_point(r, i) + get_point(r, i + 1)) / 2.0;
m2::PointD p2 = (get_point(r, i + 2) + get_point(r, i + 3)) / 2.0;
check_full_equal(r, p1, p2, strict_equal());
check_inside(r, p1, p2);
}
}
void check_eps_boundaries(m2::RectD const & r, double eps = 1.0E-6)
{
m2::RectD rr = r;
rr.Inflate(eps, eps);
for (int i = 0; i < 4; ++i)
{
m2::PointD p1 = get_point(rr, i);
m2::PointD p2 = get_point(rr, i + 1);
TEST(!m2::Intersect(r, p1, p2), ());
}
rr = r;
rr.Inflate(-eps, -eps);
for (int i = 0; i < 4; ++i)
check_inside(r, get_point(rr, i), get_point(rr, i + 1));
}
UNIT_TEST(IntersectRect_Section)
{
m2::RectD r(-1, -1, 2, 2);
check_intersect_boundaries(r);
check_intersect_diagonal(r);
check_sides(r);
check_eps_boundaries(r);
}
namespace
{
void check_point_in_rect(m2::RectD const & r, m2::PointD const & p)
{
m2::PointD p1 = p;
m2::PointD p2 = p;
TEST(m2::Intersect(r, p1, p2), ());
TEST(p == p1 && p == p2, ());
}
} // namespace
UNIT_TEST(IntersectRect_Point)
{
{
m2::RectD r(-100, -100, 200, 200);
for (int i = 0; i < 4; ++i)
{
check_point_in_rect(r, get_point(r, i));
check_point_in_rect(r, (get_point(r, i) + get_point(r, i + 1)) / 2.0);
}
}
{
m2::RectD r(-1000, -1000, 1000, 1000);
double const eps = 1.0E-6;
P sm[] = {P(-eps, -eps), P(-eps, eps), P(eps, eps), P(eps, -eps)};
for (int i = 0; i < 4; ++i)
{
P p1 = get_point(r, i);
P p2 = p1 - sm[i];
check_inside(r, p1, p2);
p1 = p1 + sm[i];
p2 = p1 + sm[i];
TEST(!m2::Intersect(r, p1, p2), ());
}
}
}
UNIT_TEST(IntersectRect_NAN)
{
m2::RectD r(-47.622792787168442885, -8.5438097219402173721, 134.06976090684074165, 9.0000000000000337508);
m2::PointD p1(134.06976090684077008, 9.0000000000001847411);
m2::PointD p2(134.06976090684074165, -8.5438097219401640814);
m2::Intersect(r, p1, p2);
}

View file

@ -0,0 +1,93 @@
#include "testing/testing.hpp"
#include "geometry/any_rect2d.hpp"
#include "geometry/intersection_score.hpp"
#include "geometry/point2d.hpp"
#include "geometry/rect2d.hpp"
#include "base/math.hpp"
#include <vector>
UNIT_TEST(IntersectionScore_PointsToPolygon)
{
{
m2::RectD rectD = {0, 0, 10, 10};
m2::AnyRectD const anyRect1(rectD);
rectD = {0, 0, 10, 9};
m2::AnyRectD const anyRect2(rectD);
m2::AnyRectD::Corners corners1;
anyRect1.GetGlobalPoints(corners1);
m2::AnyRectD::Corners corners2;
anyRect2.GetGlobalPoints(corners2);
auto const score = geometry::GetIntersectionScoreForPoints(corners1, corners2);
TEST(AlmostEqualAbs(score, 0.9, 1e-10), ());
}
{
m2::RectD rectD = {0, 0, 10, 10};
m2::AnyRectD const anyRect1(rectD);
rectD = {10, 10, 20, 20};
m2::AnyRectD const anyRect2(rectD);
m2::AnyRectD::Corners corners1;
anyRect1.GetGlobalPoints(corners1);
m2::AnyRectD::Corners corners2;
anyRect2.GetGlobalPoints(corners2);
auto const score = geometry::GetIntersectionScoreForPoints(corners1, corners2);
TEST(AlmostEqualAbs(score, 0.0, 1e-10), ());
}
{
m2::RectD rectD = {0, 0, 10, 10};
m2::AnyRectD const anyRect1(rectD);
m2::AnyRectD::Corners corners1;
anyRect1.GetGlobalPoints(corners1);
// Backward
m2::AnyRectD::Corners corners2 = {m2::PointD{10.0, 10.0}, {10.0, 0.0}, {0.0, 0.0}, {0.0, 10.0}};
auto const score = geometry::GetIntersectionScoreForPoints(corners1, corners2);
TEST(AlmostEqualAbs(score, 1.0, 1e-10), ());
}
}
UNIT_TEST(IntersectionScore_TrianglesToPolygon)
{
{
std::vector<m2::PointD> triangiulated1 = {{0.0, 0.0}, {0.0, 10.0}, {10.0, 0.0},
{10.0, 0.0}, {0.0, 10.0}, {10.0, 10.0}};
std::vector<m2::PointD> triangiulated2 = {{0.0, 0.0}, {0.0, 9.0}, {10.0, 0.0},
{10.0, 0.0}, {0.0, 9.0}, {10.0, 9.0}};
auto const score = geometry::GetIntersectionScoreForTriangulated(triangiulated1, triangiulated2);
TEST(AlmostEqualAbs(score, 0.9, 1e-10), ());
}
{
m2::RectD rectD = {0, 0, 10, 10};
m2::AnyRectD const anyRect1(rectD);
rectD = {10, 10, 20, 20};
m2::AnyRectD const anyRect2(rectD);
m2::AnyRectD::Corners corners1;
anyRect1.GetGlobalPoints(corners1);
m2::AnyRectD::Corners corners2;
anyRect2.GetGlobalPoints(corners2);
std::vector<m2::PointD> triangiulated1 = {{0.0, 0.0}, {0.0, 10.0}, {10.0, 0.0},
{10.0, 0.0}, {0.0, 10.0}, {10.0, 10.0}};
std::vector<m2::PointD> triangiulated2 = {{10.0, 10.0}, {10.0, 20.0}, {20.0, 10.0},
{20.0, 10.0}, {10.0, 20.0}, {20.0, 20.0}};
auto const score = geometry::GetIntersectionScoreForTriangulated(triangiulated1, triangiulated2);
TEST(AlmostEqualAbs(score, 0.0, 1e-10), ());
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
#include "geometry/latlon.hpp"
#include "geometry/mercator.hpp"
#include "geometry/point2d.hpp"
#include "testing/testing.hpp"
UNIT_TEST(LatLonPointConstructorTest)
{
m2::PointD basePoint(39.123, 42.456);
ms::LatLon wgsPoint = mercator::ToLatLon(basePoint);
m2::PointD resultPoint = mercator::FromLatLon(wgsPoint);
TEST_ALMOST_EQUAL_ULPS(basePoint.x, resultPoint.x, ());
TEST_ALMOST_EQUAL_ULPS(basePoint.y, resultPoint.y, ());
}

View file

@ -0,0 +1,48 @@
#include "testing/testing.hpp"
#include "geometry/line2d.hpp"
#include "geometry/point2d.hpp"
#include "geometry/segment2d.hpp"
namespace line2d_tests
{
using namespace m2;
double const kEps = 1e-12;
using Result = IntersectionResult;
using Type = Result::Type;
Result Intersect(Line2D const & lhs, Line2D const & rhs)
{
return Intersect(lhs, rhs, kEps);
}
UNIT_TEST(LineIntersection_Smoke)
{
{
Line2D const line(Segment2D(PointD(0, 0), PointD(1, 0)));
TEST_EQUAL(Intersect(line, line).m_type, Type::Infinity, ());
}
{
Line2D const lhs(Segment2D(PointD(0, 0), PointD(1, 1)));
Line2D const rhs(Segment2D(PointD(-10, -10), PointD(-100, -100)));
TEST_EQUAL(Intersect(lhs, rhs).m_type, Type::Infinity, ());
}
{
Line2D const lhs(Segment2D(PointD(0, 0), PointD(10, 10)));
Line2D const rhs(Segment2D(PointD(10, 11), PointD(0, 1)));
TEST_EQUAL(Intersect(lhs, rhs).m_type, Type::Zero, ());
}
{
Line2D const lhs(Segment2D(PointD(10, 0), PointD(9, 10)));
Line2D const rhs(Segment2D(PointD(-10, 0), PointD(-9, 10)));
auto const result = Intersect(lhs, rhs);
TEST_EQUAL(result.m_type, Type::One, ());
TEST(AlmostEqualAbs(result.m_point, PointD(0, 100), kEps), (result.m_point));
}
}
} // namespace line2d_tests

View file

@ -0,0 +1,81 @@
#include "testing/testing.hpp"
#include "geometry/mercator.hpp"
#include "base/logging.hpp"
#include "base/macros.hpp"
#include "base/math.hpp"
UNIT_TEST(Mercator_Grid)
{
for (int lat = -85; lat <= 85; ++lat)
{
for (int lon = -180; lon <= 180; ++lon)
{
double const x = mercator::LonToX(lon);
double const y = mercator::LatToY(lat);
double const lat1 = mercator::YToLat(y);
double const lon1 = mercator::XToLon(x);
// Normal assumption for any projection.
TEST_ALMOST_EQUAL_ULPS(static_cast<double>(lat), lat1, ());
TEST_ALMOST_EQUAL_ULPS(static_cast<double>(lon), lon1, ());
// x is actually lon unmodified.
TEST_ALMOST_EQUAL_ULPS(x, static_cast<double>(lon), ());
}
}
}
UNIT_TEST(Mercator_DirectInferseF)
{
double const eps = 0.0000001;
double lon = 63.45421;
double x = mercator::LonToX(lon);
double lon1 = mercator::XToLon(x);
TEST_LESS(fabs(lon - lon1), eps, ("Too big round error"));
double lat = 34.28754;
double y = mercator::LatToY(lat);
double lat1 = mercator::YToLat(y);
TEST_LESS(fabs(lat - lat1), eps, ("Too big round error"));
TEST_LESS(fabs(mercator::Bounds::kMaxX - mercator::Bounds::kMaxY), eps, ("Non-square maxX and maxY"));
TEST_LESS(fabs(mercator::Bounds::kMinX - mercator::Bounds::kMinY), eps, ("Non-square minX and minY"));
}
UNIT_TEST(Mercator_ErrorToRadius)
{
double const points[] = {-85.0, -45.0, -10.0, -1.0, -0.003, 0.0, 0.003, 1.0, 10.0, 45.0, 85.0};
double const error1 = 1.0; // 1 metre
double const error10 = 10.0; // 10 metres
for (size_t i = 0; i < ARRAY_SIZE(points); ++i)
{
for (size_t j = 0; j < ARRAY_SIZE(points); ++j)
{
double const lon = points[i];
double const lat = points[j];
m2::PointD const mercPoint(mercator::LonToX(lon), mercator::LatToY(lat));
m2::RectD const radius1 = mercator::MetersToXY(lon, lat, error1);
TEST(radius1.IsPointInside(mercPoint), (lat, lon));
TEST(radius1.Center().EqualDxDy(mercPoint, 1.0E-8), ());
m2::RectD const radius10 = mercator::MetersToXY(lon, lat, error10);
TEST(radius10.IsPointInside(mercPoint), (lat, lon));
TEST(radius10.Center().EqualDxDy(mercPoint, 1.0E-8), ());
TEST_EQUAL(m2::Add(radius10, radius1), radius10, (lat, lon));
TEST(radius10.IsPointInside(radius1.LeftTop()), (lat, lon));
TEST(radius10.IsPointInside(radius1.LeftBottom()), (lat, lon));
TEST(radius10.IsPointInside(radius1.RightTop()), (lat, lon));
TEST(radius10.IsPointInside(radius1.RightBottom()), (lat, lon));
}
}
}
UNIT_TEST(Mercator_Sample1)
{
LOG(LINFO, (mercator::XToLon(27.531491200000001385), mercator::YToLat(64.392864299248202542)));
}

View file

@ -0,0 +1,146 @@
#include "testing/testing.hpp"
#include "geometry/nearby_points_sweeper.hpp"
#include "geometry/point2d.hpp"
#include "base/stl_helpers.hpp"
#include <set>
#include <utility>
#include <vector>
namespace nearby_points_sweeper_test
{
using namespace m2;
using namespace std;
// Multiset is used here to catch situations when some index is reported more than once.
using TIndexSet = multiset<size_t>;
UNIT_TEST(NearbyPointsSweeper_Smoke)
{
{
uint8_t const priority = 0;
NearbyPointsSweeper sweeper(0.0);
for (size_t i = 0; i < 10; ++i)
sweeper.Add(10.0, 10.0, i, priority);
TIndexSet expected = {0};
TIndexSet actual;
sweeper.Sweep(base::MakeInsertFunctor(actual));
TEST_EQUAL(expected, actual, ());
}
{
uint8_t const priority = 0;
vector<double> const coords = {0.0, 0.5, 1.0, 1.5, 1.4, 1.6};
{
NearbyPointsSweeper sweeper(0.5);
for (size_t i = 0; i < coords.size(); ++i)
sweeper.Add(coords[i], 0.0, i, priority);
TIndexSet expected = {0, 2, 5};
TIndexSet actual;
sweeper.Sweep(base::MakeInsertFunctor(actual));
TEST_EQUAL(expected, actual, ());
}
{
NearbyPointsSweeper sweeper(0.5);
for (size_t i = 0; i < coords.size(); ++i)
sweeper.Add(0.0, coords[i], i, priority);
TIndexSet expected = {0, 2, 5};
TIndexSet actual;
sweeper.Sweep(base::MakeInsertFunctor(actual));
TEST_EQUAL(expected, actual, ());
}
}
{
uint8_t const priority = 0;
vector<PointD> const points = {PointD(0.0, 0.0), PointD(1.0, 1.0), PointD(1.5, 0.0), PointD(1.5 + 1.01, 1.5 + 1.0)};
NearbyPointsSweeper sweeper(1.0);
for (size_t i = 0; i < points.size(); ++i)
sweeper.Add(points[i].x, points[i].y, i, priority);
TIndexSet expected = {0, 2, 3};
TIndexSet actual;
sweeper.Sweep(base::MakeInsertFunctor(actual));
TEST_EQUAL(expected, actual, ());
}
{
uint8_t const priority = 0;
vector<PointD> const points = {PointD(0, 0), PointD(0, 0), PointD(1, 0), PointD(0, 1),
PointD(1, 1), PointD(1, 0), PointD(0.5, 0.5), PointD(0, 1)};
NearbyPointsSweeper sweeper(10.0);
for (size_t i = 0; i < points.size(); ++i)
sweeper.Add(points[i].x, points[i].y, i, priority);
TIndexSet expected = {0};
TIndexSet actual;
sweeper.Sweep(base::MakeInsertFunctor(actual));
TEST_EQUAL(expected, actual, ());
}
}
UNIT_TEST(NearbyPointsSweeper_Priority)
{
{
NearbyPointsSweeper sweeper(0.0);
for (size_t i = 0; i < 10; ++i)
sweeper.Add(10.0, 10.0, i /* index */, i /* priority */);
TIndexSet expected = {9};
TIndexSet actual;
sweeper.Sweep(base::MakeInsertFunctor(actual));
TEST_EQUAL(expected, actual, ());
}
{
vector<pair<double, uint8_t>> const objects = {{0.0, 0}, {0.5, 1}, {1.0, 1}, {1.5, 1}, {1.4, 0}, {1.6, 0}};
NearbyPointsSweeper sweeper(0.5);
for (size_t i = 0; i < objects.size(); ++i)
sweeper.Add(objects[i].first, 0.0, i /* index */, objects[i].second);
TIndexSet expected = {1, 3};
TIndexSet actual;
sweeper.Sweep(base::MakeInsertFunctor(actual));
TEST_EQUAL(expected, actual, ());
}
{
vector<pair<PointD, uint8_t>> const objects = {
{PointD(0.0, 0.0), 0}, {PointD(1.0, 1.0), 1}, {PointD(1.5, 0.0), 0}, {PointD(1.5 + 1.01, 1.5 + 1.0), 0}};
NearbyPointsSweeper sweeper(1.0);
for (size_t i = 0; i < objects.size(); ++i)
sweeper.Add(objects[i].first.x, objects[i].first.y, i /* index */, objects[i].second);
TIndexSet expected = {1, 3};
TIndexSet actual;
sweeper.Sweep(base::MakeInsertFunctor(actual));
TEST_EQUAL(expected, actual, ());
}
}
} // namespace nearby_points_sweeper_test

View file

@ -0,0 +1,71 @@
#include "testing/testing.hpp"
#include "geometry/latlon.hpp"
#include "geometry/oblate_spheroid.hpp"
namespace
{
double constexpr kAccuracyEps = 1e-3;
void testDistance(ms::LatLon const & a, ms::LatLon const & b, double planDistance)
{
double const factDistance = oblate_spheroid::GetDistance(a, b);
TEST_ALMOST_EQUAL_ABS(factDistance, planDistance, kAccuracyEps, ());
}
} // namespace
UNIT_TEST(Distance_EdgeCaseEquatorialLine)
{
ms::LatLon const a(0.0, 0.0);
ms::LatLon const b(0.0, 45.0);
testDistance(a, b, 5009377.085);
}
UNIT_TEST(Distance_EdgeCaseSameLatitude)
{
ms::LatLon const a(30.0, 30.0);
ms::LatLon const b(30.0, 70.0);
testDistance(a, b, 3839145.440);
}
UNIT_TEST(Distance_EdgeCaseSameLongtitude)
{
ms::LatLon const a(10.0, 40.0);
ms::LatLon const b(21.0, 40.0);
testDistance(a, b, 1217222.035);
}
UNIT_TEST(Distance_Long)
{
ms::LatLon const a(-24.02861, 123.53353);
ms::LatLon const b(58.25020, -6.54459);
testDistance(a, b, 14556482.656);
}
UNIT_TEST(Distance_NearlyAntipodal)
{
ms::LatLon const a(52.02247, 168.18196);
ms::LatLon const b(31.22321, -171.07584);
testDistance(a, b, 2863337.631);
}
UNIT_TEST(Distance_Small)
{
ms::LatLon const a(54.15820, 36.95131);
ms::LatLon const b(54.15814, 36.95143);
testDistance(a, b, 10.29832);
}
UNIT_TEST(Distance_ReallyTiny)
{
ms::LatLon const a(-34.39292, -71.16413);
ms::LatLon const b(-34.39294, -71.16410);
testDistance(a, b, 3.54013);
}
UNIT_TEST(Distance_Fallback)
{
ms::LatLon const a(0.0, 0.0);
ms::LatLon const b(0.5, 179.5);
testDistance(a, b, 19958365.368);
}

View file

@ -0,0 +1,77 @@
#include "testing/testing.hpp"
#include "geometry/packer.hpp"
static int i = 1;
void rectOverflowFn1()
{
i = i + 5;
}
void rectOverflowFn2()
{
i *= 2;
}
UNIT_TEST(PackerTest_SimplePack)
{
m2::Packer p(20, 20);
m2::Packer::handle_t h0 = p.pack(10, 10);
p.addOverflowFn(rectOverflowFn2, 10);
p.addOverflowFn(rectOverflowFn1, 0);
TEST_EQUAL(p.isPacked(h0), true, ());
m2::RectU r0 = p.find(h0).second;
TEST_EQUAL(r0, m2::RectU(0, 0, 10, 10), ());
m2::Packer::handle_t h1 = p.pack(20, 10);
TEST_EQUAL(p.isPacked(h1), true, ());
m2::RectU r1 = p.find(h1).second;
TEST_EQUAL(r1, m2::RectU(0, 10, 20, 20), ());
m2::Packer::handle_t h2 = p.pack(5, 5);
// Possibly we should restore this checks
// TEST_EQUAL(p.isPacked(h0), false, ());
// TEST_EQUAL(p.isPacked(h1), false, ());
TEST_EQUAL(p.isPacked(h2), true, ());
TEST_EQUAL(i, 7, ("Handlers priorities doesn't work"));
TEST_NOT_EQUAL(i, 12, ("Handlers priorities doesn't work"));
m2::RectU r2 = p.find(h2).second;
TEST_EQUAL(r2, m2::RectU(0, 0, 5, 5), ());
}
UNIT_TEST(PackerTest_HasRoom_Sequence)
{
m2::Packer p(20, 20);
m2::PointU pts[] = {m2::PointU(10, 10), m2::PointU(11, 3), m2::PointU(5, 5), m2::PointU(5, 5)};
TEST(p.hasRoom(pts, sizeof(pts) / sizeof(m2::PointU)), ());
m2::PointU pts1[] = {m2::PointU(10, 10), m2::PointU(11, 3), m2::PointU(5, 5), m2::PointU(5, 5), m2::PointU(16, 5)};
TEST(!p.hasRoom(pts1, sizeof(pts1) / sizeof(m2::PointU)), ());
m2::PointU pts2[] = {m2::PointU(10, 10), m2::PointU(11, 3), m2::PointU(5, 5), m2::PointU(5, 5), m2::PointU(10, 6)};
TEST(!p.hasRoom(pts2, sizeof(pts2) / sizeof(m2::PointU)), ());
m2::PointU pts3[] = {m2::PointU(10, 10), m2::PointU(11, 3), m2::PointU(5, 5), m2::PointU(5, 5), m2::PointU(15, 5)};
TEST(p.hasRoom(pts3, sizeof(pts3) / sizeof(m2::PointU)), ());
m2::PointU pts4[] = {m2::PointU(10, 10), m2::PointU(11, 3), m2::PointU(5, 5), m2::PointU(5, 5), m2::PointU(16, 5)};
TEST(!p.hasRoom(pts4, sizeof(pts4) / sizeof(m2::PointU)), ());
}

View file

@ -0,0 +1,69 @@
#include "testing/testing.hpp"
#include "geometry/parametrized_segment.hpp"
#include "geometry/point2d.hpp"
#include "base/macros.hpp"
#include "base/math.hpp"
template <typename Point>
void FloatingPointsTest()
{
m2::ParametrizedSegment<Point> d(Point(-1, 3), Point(2, 1));
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(Point(-1, 3)), 0.0, ());
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(Point(2, 1)), 0.0, ());
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(Point(-0.5, 0.5)), 3.25, ());
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(Point(3.5, 0.0)), 3.25, ());
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(Point(4.0, 4.0)), 13.0, ());
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(Point(0.5, 2.0)), 0.0, ());
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(Point(0.0, 1.25)), 0.5 * 0.5 + 0.75 * 0.75, ());
}
UNIT_TEST(ParametrizedSegment2D_Floating)
{
FloatingPointsTest<m2::PointD>();
FloatingPointsTest<m2::PointF>();
}
UNIT_TEST(ParametrizedSegment2D_Integer)
{
m2::ParametrizedSegment<m2::PointI> d(m2::PointI(-1, 3), m2::PointI(2, 1));
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(m2::PointI(-1, 3)), 0.0, ());
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(m2::PointI(2, 1)), 0.0, ());
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(m2::PointI(4, 4)), 13.0, ());
double const sqSin = 4.0 / m2::PointI(-1, 3).SquaredLength(m2::PointI(2, 1));
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(m2::PointI(0, 1)), 4.0 * sqSin, ());
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(m2::PointI(-1, 1)), 9.0 * sqSin, ());
}
UNIT_TEST(ParametrizedSegment2D_DegenerateSection)
{
using P = m2::PointD;
m2::ParametrizedSegment<P> d(P(5, 5), P(5, 5));
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(P(5, 5)), 0.0, ());
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(P(6, 6)), 2.0, ());
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(P(0, 0)), 50.0, ());
TEST_ALMOST_EQUAL_ULPS(d.SquaredDistanceToPoint(P(-1, -2)), 36.0 + 49.0, ());
}
UNIT_TEST(ParametrizedSegment2D_ClosestPoint)
{
using P = m2::PointD;
P arr[][4] = {{P(3, 4), P(0, 0), P(10, 0), P(3, 0)}, {P(3, 4), P(0, 0), P(0, 10), P(0, 4)},
{P(3, 5), P(2, 2), P(5, 5), P(4, 4)}, {P(5, 3), P(2, 2), P(5, 5), P(4, 4)},
{P(2, 4), P(2, 2), P(5, 5), P(3, 3)}, {P(4, 2), P(2, 2), P(5, 5), P(3, 3)},
{P(5, 6), P(2, 2), P(5, 5), P(5, 5)}, {P(1, 0), P(2, 2), P(5, 5), P(2, 2)}};
for (size_t i = 0; i < ARRAY_SIZE(arr); ++i)
{
m2::ParametrizedSegment<P> segment(arr[i][1], arr[i][2]);
TEST(AlmostEqualULPs(segment.ClosestPointTo(arr[i][0]), arr[i][3]), (i));
}
}

View file

@ -0,0 +1,100 @@
#include "testing/testing.hpp"
#include "geometry/point3d.hpp"
namespace point3d_tests
{
UNIT_TEST(Point3d_DotProduct)
{
m3::Point<int> p1(1, 4, 3);
m3::Point<int> p2(1, 2, 3);
m3::Point<int> p3(1, 0, 3);
TEST_EQUAL(m3::DotProduct(p1, p2), 18, ());
TEST_EQUAL(m3::DotProduct(p2, p3), 10, ());
TEST_EQUAL(m3::DotProduct(p3, p2), 10, ());
TEST_EQUAL(m3::DotProduct(p1, p3), 10, ());
}
UNIT_TEST(Point3d_CrossProduct_1)
{
m3::Point<int> p1(1, 0, 0);
m3::Point<int> p2(0, 1, 0);
m3::Point<int> p3(0, 0, 1);
TEST_EQUAL(m3::CrossProduct(p1, p2), p3, ());
TEST_EQUAL(m3::CrossProduct(p2, p3), p1, ());
TEST_EQUAL(m3::CrossProduct(p3, p1), p2, ());
}
UNIT_TEST(Point3d_CrossProduct_2)
{
m3::Point<int> p1(1, 2, 3);
m3::Point<int> p2(4, 5, 6);
TEST_EQUAL(m3::CrossProduct(p1, p2), m3::Point<int>(-3, 6, -3), ());
}
UNIT_TEST(Point3d_CrossProduct_3)
{
m3::Point<int> p1(3, 7, 1);
m3::Point<int> p2(6, 2, 9);
TEST_EQUAL(m3::CrossProduct(p1, p2), m3::Point<int>(61, -21, -36), ());
}
UNIT_TEST(Point3d_RotateX_1)
{
m3::PointD p(0.0, 1.0, 0.0);
auto const rotated = p.RotateAroundX(90.0);
TEST_ALMOST_EQUAL_ABS(rotated, m3::PointD(0.0, 0.0, 1.0), 1e-10, ());
}
UNIT_TEST(Point3d_RotateX_2)
{
m3::PointD p(1.0, 2.0, 3.0);
auto const rotated = p.RotateAroundX(90.0);
TEST_ALMOST_EQUAL_ABS(rotated, m3::PointD(1.0, -3.0, 2.0), 1e-10, ());
}
UNIT_TEST(Point3d_RotateY_1)
{
m3::PointD p(1.0, 0.0, 0.0);
auto const rotated = p.RotateAroundY(90.0);
TEST_ALMOST_EQUAL_ABS(rotated, m3::PointD(0.0, 0.0, -1.0), 1e-10, ());
}
UNIT_TEST(Point3d_RotateY_2)
{
m3::PointD p(1.0, 2.0, 3.0);
auto const rotated = p.RotateAroundY(90.0);
TEST_ALMOST_EQUAL_ABS(rotated, m3::PointD(3.0, 2.0, -1.0), 1e-10, ());
}
UNIT_TEST(Point3d_RotateZ_1)
{
m3::PointD p(1.0, 0.0, 0.0);
auto const rotated = p.RotateAroundZ(90.0);
TEST_ALMOST_EQUAL_ABS(rotated, m3::PointD(0.0, 1.0, 0.0), 1e-10, ());
}
UNIT_TEST(Point3d_RotateZ_2)
{
m3::PointD p(1.0, 2.0, 3.0);
auto const rotated = p.RotateAroundZ(90.0);
TEST_ALMOST_EQUAL_ABS(rotated, m3::PointD(-2.0, 1.0, 3.0), 1e-10, ());
}
UNIT_TEST(Point3d_RotateXYZ)
{
m3::PointD p(1.0, 1.0, 1.0);
auto const rotatedFirst = p.RotateAroundZ(-45.0);
TEST_ALMOST_EQUAL_ABS(rotatedFirst, m3::PointD(std::sqrt(2.0), 0.0, 1.0), 1e-10, ());
double const angleDegree = math::RadToDeg(acos(rotatedFirst.z / rotatedFirst.Length()));
auto const north = rotatedFirst.RotateAroundY(-angleDegree);
TEST_ALMOST_EQUAL_ABS(north, m3::PointD(0.0, 0.0, std::sqrt(3.0)), 1e-10, ());
}
} // namespace point3d_tests

View file

@ -0,0 +1,88 @@
#include "testing/testing.hpp"
#include "geometry/geometry_tests/equality.hpp"
#include "geometry/point2d.hpp"
#include "geometry/triangle2d.hpp"
#include <array>
UNIT_TEST(Point_Rotate)
{
m2::PointD p(1.0, 0.0);
p.Rotate(math::pi / 6.0);
TEST(test::is_equal(p.x, sqrt(3.0) / 2.0), ());
TEST(test::is_equal(p.y, 1.0 / 2.0), ());
}
UNIT_TEST(PointInTriangle)
{
m2::PointD const a(1, 0);
m2::PointD const b(2, 0);
m2::PointD const c(-1, 3);
TEST(!m2::IsPointStrictlyInsideTriangle(a, a, a, a), ());
TEST(!m2::IsPointStrictlyInsideTriangle(a, a, a, b), ());
TEST(!m2::IsPointStrictlyInsideTriangle(b, a, a, b), ());
TEST(!m2::IsPointStrictlyInsideTriangle(a, a, b, a), ());
TEST(!m2::IsPointStrictlyInsideTriangle(b, a, b, a), ());
TEST(!m2::IsPointStrictlyInsideTriangle(a, a, b, c), ());
TEST(!m2::IsPointStrictlyInsideTriangle(a, a, c, b), ());
TEST(!m2::IsPointStrictlyInsideTriangle(b, a, b, c), ());
TEST(!m2::IsPointStrictlyInsideTriangle(b, a, c, b), ());
TEST(!m2::IsPointStrictlyInsideTriangle(c, a, b, c), ());
TEST(!m2::IsPointStrictlyInsideTriangle(c, a, c, b), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(0, 1), a, b, c), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(0, 1), a, c, b), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(0, 1.5), a, b, c), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(0, 1.5), a, c, b), ());
TEST(m2::IsPointStrictlyInsideTriangle(m2::PointD(0, 1.77), a, b, c), ());
TEST(m2::IsPointStrictlyInsideTriangle(m2::PointD(0, 1.77), a, c, b), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(0, 2), a, b, c), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(0, 2), a, c, b), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(1, 1), a, b, c), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(1, 1), a, c, b), ());
TEST(m2::IsPointStrictlyInsideTriangle(m2::PointD(1, 0.5), a, b, c), ());
TEST(m2::IsPointStrictlyInsideTriangle(m2::PointD(1, 0.5), a, c, b), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(100, 100), a, b, c), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(100, 100), a, c, b), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(5, 0), a, b, c), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(5, 0), a, c, b), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(-1, -1), a, c, b), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(0, 0.5), a, b, c), ());
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(0, 0.5), a, c, b), ());
}
UNIT_TEST(PointInTriangle_EmptyTriangle)
{
m2::PointD pt(27, 31);
TEST(!m2::IsPointStrictlyInsideTriangle(m2::PointD(0, 16), pt, pt, pt), ());
}
/// @todo add more tests
UNIT_TEST(GetArrowPoints)
{
std::array<m2::PointF, 3> arrPntsFlt;
m2::GetArrowPoints(m2::PointF(0, 0), m2::PointF(1, 0), 1.f, 1.f, arrPntsFlt);
TEST(AlmostEqualULPs(arrPntsFlt[0], m2::PointF(1.f, 1.f)), ());
TEST(AlmostEqualULPs(arrPntsFlt[1], m2::PointF(2.f, 0.f)), ());
TEST(AlmostEqualULPs(arrPntsFlt[2], m2::PointF(1.f, -1.f)), ());
std::array<m2::PointD, 3> arrPntsDbl;
m2::GetArrowPoints(m2::PointD(-1., 2.), m2::PointD(-1., 100.), 2., 5., arrPntsDbl);
TEST(AlmostEqualULPs(arrPntsDbl[0], m2::PointD(-3.f, 100.f)), ());
TEST(AlmostEqualULPs(arrPntsDbl[1], m2::PointD(-1.f, 105.f)), ());
TEST(AlmostEqualULPs(arrPntsDbl[2], m2::PointD(1.f, 100.f)), ());
}
UNIT_TEST(PointAtSegment)
{
TEST(AlmostEqualULPs(m2::PointAtSegment(m2::PointF(0, 0), m2::PointF(1, 0), 0.5f), m2::PointF(0.5f, 0.f)), ());
TEST(AlmostEqualULPs(m2::PointAtSegment(m2::PointF(0, 0), m2::PointF(0, 1), 0.3f), m2::PointF(0.f, 0.3f)), ());
TEST(AlmostEqualULPs(m2::PointAtSegment(m2::PointD(0., 0.), m2::PointD(30., 40.), 5.), m2::PointD(3., 4.)), ());
TEST(AlmostEqualULPs(m2::PointAtSegment(m2::PointF(-3, -4), m2::PointF(-30, -40), 5.f), m2::PointF(-6.f, -8.f)), ());
TEST(AlmostEqualULPs(m2::PointAtSegment(m2::PointD(14., -48.), m2::PointD(70., -240.), 25.), m2::PointD(21., -72.)),
());
}

View file

@ -0,0 +1,200 @@
#include "testing/testing.hpp"
#include "geometry/point2d.hpp"
#include "geometry/polygon.hpp"
#include "geometry/triangle2d.hpp"
#include "base/macros.hpp"
#include "base/stl_helpers.hpp"
#include <algorithm>
namespace polygon_test
{
using namespace std;
using namespace m2::robust;
using P = m2::PointD;
template <typename Iter>
void TestDiagonalVisible(Iter beg, Iter end, Iter i0, Iter i1, bool res)
{
TEST_EQUAL(IsDiagonalVisible(beg, end, i0, i1), res, ());
TEST_EQUAL(IsDiagonalVisible(beg, end, i1, i0), res, ());
}
void TestFindStrip(P const * beg, size_t n)
{
size_t const i = FindSingleStrip(n, IsDiagonalVisibleFunctor<P const *>(beg, beg + n));
TEST_LESS(i, n, ());
vector<size_t> test;
MakeSingleStripFromIndex(i, n, base::MakeBackInsertFunctor(test));
base::SortUnique(test);
TEST_EQUAL(test.size(), n, ());
}
void TestFindStripMulti(P const * beg, size_t n)
{
for (size_t i = 3; i <= n; ++i)
TestFindStrip(beg, i);
}
template <typename Iter>
void TestPolygonCCW(Iter beg, Iter end)
{
TEST_EQUAL(m2::robust::CheckPolygonSelfIntersections(beg, end), false, ());
TEST(IsPolygonCCW(beg, end), ());
using ReverseIter = reverse_iterator<Iter>;
TEST(!IsPolygonCCW(ReverseIter(end), ReverseIter(beg)), ());
}
template <typename Iter>
void TestPolygonOrReverseCCW(Iter beg, Iter end)
{
TEST_EQUAL(m2::robust::CheckPolygonSelfIntersections(beg, end), false, ());
bool const bForwardCCW = IsPolygonCCW(beg, end);
using ReverseIter = reverse_iterator<Iter>;
bool const bReverseCCW = IsPolygonCCW(ReverseIter(end), ReverseIter(beg));
TEST_NOT_EQUAL(bForwardCCW, bReverseCCW, ());
}
UNIT_TEST(IsSegmentInCone)
{
TEST(IsSegmentInCone(P(0, 0), P(0, 3), P(-1, -1), P(1, -1)), ());
TEST(IsSegmentInCone(P(0, 0), P(2, 3), P(-1, -1), P(1, -1)), ());
TEST(IsSegmentInCone(P(0, 0), P(-3, 3), P(-1, -1), P(1, -1)), ());
TEST(IsSegmentInCone(P(0, 0), P(-3, 0), P(-1, -1), P(1, -1)), ());
TEST(IsSegmentInCone(P(0, 0), P(3, 0), P(-1, -1), P(1, -1)), ());
TEST(!IsSegmentInCone(P(0, 0), P(0, -1), P(-1, -1), P(1, -1)), ());
TEST(!IsSegmentInCone(P(0, 0), P(1, -3), P(-1, -1), P(1, -1)), ());
TEST(!IsSegmentInCone(P(0, 0), P(-1, -3), P(-1, -1), P(1, -1)), ());
TEST(IsSegmentInCone(P(0, 0), P(0, 3), P(-1, 1), P(1, 1)), ());
TEST(IsSegmentInCone(P(0, 0), P(2, 3), P(-1, 1), P(1, 1)), ());
TEST(!IsSegmentInCone(P(0, 0), P(-3, 3), P(-1, 1), P(1, 1)), ());
TEST(!IsSegmentInCone(P(0, 0), P(-3, 0), P(-1, 1), P(1, 1)), ());
TEST(!IsSegmentInCone(P(0, 0), P(3, 0), P(-1, 1), P(1, 1)), ());
TEST(!IsSegmentInCone(P(0, 0), P(0, -1), P(-1, 1), P(1, 1)), ());
TEST(!IsSegmentInCone(P(0, 0), P(1, -3), P(-1, 1), P(1, 1)), ());
TEST(!IsSegmentInCone(P(0, 0), P(-1, -3), P(-1, 1), P(1, 1)), ());
}
UNIT_TEST(IsDiagonalVisible)
{
P const poly[] = {P(0, 0), P(3, 0), P(3, 2), P(2, 2), P(2, 1), P(0, 1)};
P const * b = poly;
P const * e = poly + ARRAY_SIZE(poly);
TestDiagonalVisible(b, e, b + 0, b + 1, true);
TestDiagonalVisible(b, e, b + 0, b + 2, false);
TestDiagonalVisible(b, e, b + 0, b + 3, false);
TestDiagonalVisible(b, e, b + 0, b + 4, true);
TestDiagonalVisible(b, e, b + 0, b + 5, true);
TestDiagonalVisible(b, e, b + 5, b + 4, true);
TestDiagonalVisible(b, e, b + 5, b + 3, false);
TestDiagonalVisible(b, e, b + 5, b + 2, false);
TestDiagonalVisible(b, e, b + 5, b + 1, true);
}
UNIT_TEST(FindSingleStrip)
{
{
P const poly[] = {P(0, 0), P(3, 0), P(3, 2), P(2, 2), P(2, 1), P(0, 1)};
TestFindStripMulti(poly, ARRAY_SIZE(poly));
}
{
P const poly[] = {P(0, 0), P(2, 0), P(2, -1), P(3, -1), P(3, 2), P(2, 2), P(2, 1), P(0, 1)};
size_t const n = ARRAY_SIZE(poly);
TEST_EQUAL(FindSingleStrip(n, IsDiagonalVisibleFunctor<P const *>(poly, poly + n)), n, ());
}
{
// Minsk, Bobryiskaya str., 7
P const poly[] = {P(53.8926922, 27.5460021), P(53.8926539, 27.5461821), P(53.8926164, 27.5461591),
P(53.8925455, 27.5464921), P(53.8925817, 27.5465143), P(53.8925441, 27.5466909),
P(53.8923762, 27.5465881), P(53.8925229, 27.5458984)};
TestFindStrip(poly, ARRAY_SIZE(poly));
}
}
UNIT_TEST(IsPolygonCCW_Smoke)
{
P arr1[] = {P(1, 1), P(2, 0), P(3, 2)};
TestPolygonCCW(arr1, arr1 + ARRAY_SIZE(arr1));
P arr2[] = {P(0, 0), P(1, 0), P(0, 1)};
TestPolygonCCW(arr2, arr2 + ARRAY_SIZE(arr2));
P arr3[] = {P(0, 1), P(1, 1), P(1, 0), P(2, 0), P(2, 1), P(1, 1), P(1, 2), P(0, 2)};
TestPolygonCCW(arr3, arr3 + ARRAY_SIZE(arr3));
}
UNIT_TEST(IsPolygonCCW_DataSet)
{
P arr[] = {P(27.3018836975098, 61.7740631103516), P(27.2981071472168, 61.7816162109375),
P(27.2962188720703, 61.7831611633301), P(27.293815612793, 61.7814445495605),
P(27.2926139831543, 61.783332824707), P(27.2919273376465, 61.787109375),
P(27.2948455810547, 61.7865943908691), P(27.2958755493164, 61.7883110046387),
P(27.3001670837402, 61.779899597168), P(27.3036003112793, 61.7771530151367),
P(27.3015403747559, 61.7747497558594)};
TestPolygonOrReverseCCW(arr, arr + ARRAY_SIZE(arr));
}
UNIT_TEST(PolygonArea_Smoke)
{
{
P arr[] = {P(-1, 0), P(0, 1), P(1, -1)};
TEST_ALMOST_EQUAL_ULPS(m2::GetTriangleArea(arr[0], arr[1], arr[2]), GetPolygonArea(arr, arr + ARRAY_SIZE(arr)), ());
}
{
P arr[] = {P(-5, -7), P(-3.5, 10), P(7.2, 5), P(14, -6.4)};
TEST_ALMOST_EQUAL_ULPS(m2::GetTriangleArea(arr[0], arr[1], arr[2]) + m2::GetTriangleArea(arr[2], arr[3], arr[0]),
GetPolygonArea(arr, arr + ARRAY_SIZE(arr)), ());
}
}
// This polygon has self-intersections.
/*
UNIT_TEST(IsPolygonCCW_DataSet2)
{
P arr[] = { P(0.747119766424532, 61.4800033732131), P(0.747098308752385, 61.4800496413187),
P(0.747129489432211, 61.4800647287444), P(0.74715195293274, 61.4800191311911),
P(0.745465178736907, 61.4795420332621), P(0.746959839711849, 61.4802327020841),
P(0.746994373152972, 61.4802085622029), P(0.747182463060312, 61.479815953858),
P(0.747314226578283, 61.479873956628), P(0.747109037588444, 61.480298416205),
P(0.747035947392732, 61.4803450195867), P(0.746934023450081, 61.4803403257209),
P(0.745422933944894, 61.4796322225403), P(0.745465178736907, 61.4795420332621) };
TestPolygonOrReverseCCW(arr, arr + ARRAY_SIZE(arr));
}
*/
/*
UNIT_TEST(IsPolygonCCW_DataSet3)
{
P arr[] = { P(0.738019701780757, 61.1239696304537), P(0.738234278502148, 61.1240028227903),
P(0.738675837161651, 61.1240276332237), P(0.738234278502148, 61.1240028227903),
P(0.738019701780757, 61.1239696304537), P(0.737414528371232, 61.1238241206145) };
TestPolygonOrReverseCCW(arr, arr + ARRAY_SIZE(arr));
}
*/
/*
UNIT_TEST(IsPolygonCCW_DataSet4)
{
P arr[] = { P(37.368060441099174795, 67.293103344080122952),
P(37.368017525754879671, 67.292797572252140981), P(37.367990703664702323, 67.292969568905391498),
P(37.368060441099174795, 67.293103344080122952), P(37.368017525754879671, 67.292797572252140981),
P(37.368097992025411713, 67.292830094036474975), P(37.368216009222180674, 67.292969568905391498) };
TestPolygonOrReverseCCW(arr, arr + ARRAY_SIZE(arr));
}
*/
} // namespace polygon_test

View file

@ -0,0 +1,58 @@
#include "testing/testing.hpp"
#include "geometry/point2d.hpp"
#include "geometry/polyline2d.hpp"
#include "geometry/rect2d.hpp"
#include "base/math.hpp"
#include <vector>
namespace polyline_tests
{
double constexpr kEps = 1e-5;
void TestClosest(std::vector<m2::PointD> const & points, m2::PointD const & point, double expectedSquaredDist,
uint32_t expectedIndex)
{
auto const closestByPoints = m2::CalcMinSquaredDistance(points.begin(), points.end(), point);
TEST_ALMOST_EQUAL_ABS(closestByPoints.first, expectedSquaredDist, kEps, ());
TEST_EQUAL(closestByPoints.second, expectedIndex, ());
m2::PolylineD const poly(points);
auto const closestByPoly = poly.CalcMinSquaredDistance(m2::PointD(point));
TEST_ALMOST_EQUAL_ABS(closestByPoly.first, expectedSquaredDist, kEps, ());
TEST_EQUAL(closestByPoly.second, expectedIndex, ());
}
UNIT_TEST(Rect_PolylineSmokeTest)
{
m2::PolylineD poly = {{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}};
TEST_EQUAL(poly.GetSize(), 3, ());
TEST_ALMOST_EQUAL_ABS(poly.GetLength(), 2.0, kEps, ());
auto const limitRect = poly.GetLimitRect();
TEST_ALMOST_EQUAL_ABS(limitRect.LeftBottom(), m2::PointD(0.0, 0.0), kEps, ());
TEST(AlmostEqualAbs(limitRect.RightTop(), m2::PointD(1.0, 1.0), kEps), ());
poly.PopBack();
TEST_EQUAL(poly.GetSize(), 2, ());
}
UNIT_TEST(Rect_PolylineMinDistanceTest)
{
// 1 | |
// | |
// 0----1----2----3
std::vector<m2::PointD> const poly = {{0.0, 1.0}, {0.0, 0.0}, {1.0, 0.0}, {2.0, 0.0}, {3.0, 0.0}, {3.0, 1.0}};
TestClosest(poly, m2::PointD(0.0, 1.0), 0.0 /* expectedSquaredDist */, 0 /* expectedIndex */);
TestClosest(poly, m2::PointD(0.0, 0.0), 0.0 /* expectedSquaredDist */, 0 /* expectedIndex */);
TestClosest(poly, m2::PointD(0.1, 0.0), 0.0 /* expectedSquaredDist */, 1 /* expectedIndex */);
TestClosest(poly, m2::PointD(0.5, 0.2), 0.2 * 0.2 /* expectedSquaredDist */, 1 /* expectedIndex */);
TestClosest(poly, m2::PointD(1.5, 1.0), 1.0 /* expectedSquaredDist */, 2 /* expectedIndex */);
TestClosest(poly, m2::PointD(1.5, -5.0), 5.0 * 5.0 /* expectedSquaredDist */, 2 /* expectedIndex */);
TestClosest(poly, m2::PointD(1.5, 5.0), 4.0 * 4.0 + 1.5 * 1.5 /* expectedSquaredDist */, 0 /* expectedIndex */);
TestClosest(poly, m2::PointD(3.0, 1.0), 0.0 /* expectedSquaredDist */, 4 /* expectedIndex */);
}
} // namespace polyline_tests

View file

@ -0,0 +1,21 @@
#include "geometry/rect2d.hpp"
#include "testing/testing.hpp"
UNIT_TEST(Rect_Intersect)
{
m2::RectD r(0, 0, 100, 100);
m2::RectD r1(10, 10, 20, 20);
TEST(r1.IsIntersect(r), ());
TEST(r.IsIntersect(r1), ());
m2::RectD r2(-100, -100, -50, -50);
TEST(!r2.IsIntersect(r), ());
TEST(!r.IsIntersect(r2), ());
m2::RectD r3(-10, -10, 10, 10);
TEST(r3.IsIntersect(r), ());
TEST(r.IsIntersect(r3), ());
}

View file

@ -0,0 +1,240 @@
#include "testing/testing.hpp"
#include "geometry/geometry_tests/test_regions.hpp"
#include "geometry/region2d/binary_operators.hpp"
#include "base/macros.hpp"
#include <vector>
namespace region2d_binary_op_test
{
using namespace std;
using P = m2::PointI;
using R = m2::RegionI;
UNIT_TEST(RegionIntersect_Smoke)
{
{
P arr1[] = {P(-2, 1), P(2, 1), P(2, -1), P(-2, -1)};
P arr2[] = {P(-1, 2), P(1, 2), P(1, -2), P(-1, -2)};
R r1, r2;
r1.Assign(arr1, arr1 + ARRAY_SIZE(arr1));
r2.Assign(arr2, arr2 + ARRAY_SIZE(arr2));
vector<R> res;
m2::IntersectRegions(r1, r2, res);
TEST_EQUAL(res.size(), 1, ());
TEST_EQUAL(res[0].GetRect(), m2::RectI(-1, -1, 1, 1), ());
}
{
P arr1[] = {P(0, 0), P(1, 1), P(2, 0)};
P arr2[] = {P(0, 0), P(1, -1), P(2, 0)};
R r1, r2;
r1.Assign(arr1, arr1 + ARRAY_SIZE(arr1));
r2.Assign(arr2, arr2 + ARRAY_SIZE(arr2));
vector<R> res;
m2::IntersectRegions(r1, r2, res);
TEST_EQUAL(res.size(), 0, ());
}
{
P arr1[] = {P(-10, -10), P(10, -10), P(10, 10), P(-10, 10)};
P arr2[] = {P(-5, -5), P(5, -5), P(5, 5), P(-5, 5)};
R r1, r2;
r1.Assign(arr1, arr1 + ARRAY_SIZE(arr1));
r2.Assign(arr2, arr2 + ARRAY_SIZE(arr2));
vector<R> res;
res.push_back(r1); // do some smoke
m2::IntersectRegions(r1, r2, res);
TEST_EQUAL(res.size(), 2, ());
TEST_EQUAL(res[1].GetRect(), m2::RectI(-5, -5, 5, 5), ());
}
}
UNIT_TEST(RegionDifference_Smoke)
{
{
P arr1[] = {P(-1, 1), P(1, 1), P(1, -1), P(-1, -1)};
P arr2[] = {P(-2, 2), P(2, 2), P(2, -2), P(-2, -2)};
R r1, r2;
r1.Assign(arr1, arr1 + ARRAY_SIZE(arr1));
r2.Assign(arr2, arr2 + ARRAY_SIZE(arr2));
vector<R> res;
m2::DiffRegions(r1, r2, res);
TEST_EQUAL(res.size(), 0, ());
m2::DiffRegions(r2, r1, res);
TEST_EQUAL(res.size(), 1, ());
TEST_EQUAL(res[0].GetRect(), r2.GetRect(), ());
}
{
P arr1[] = {P(0, 1), P(2, 1), P(2, 0), P(0, 0)};
P arr2[] = {P(1, 2), P(2, 2), P(2, -1), P(1, -1)};
R r1, r2;
r1.Assign(arr1, arr1 + ARRAY_SIZE(arr1));
r2.Assign(arr2, arr2 + ARRAY_SIZE(arr2));
vector<R> res;
m2::DiffRegions(r1, r2, res);
TEST_EQUAL(res.size(), 1, ());
TEST_EQUAL(res[0].GetRect(), m2::RectI(0, 0, 1, 1), ());
}
}
UNIT_TEST(AddRegion_Smoke)
{
{
P arr1[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
P arr2[] = {{2, 2}, {2, 3}, {3, 3}, {3, 2}};
R r1, r2;
r1.Assign(arr1, arr1 + ARRAY_SIZE(arr1));
r2.Assign(arr2, arr2 + ARRAY_SIZE(arr2));
m2::MultiRegionI res;
res.push_back(r2);
m2::AddRegion(r1, res);
TEST_EQUAL(res.size(), 2, ());
TEST_EQUAL(m2::Area(res), 2, ());
res = m2::IntersectRegions(r1, {r2});
TEST_EQUAL(res.size(), 0, ());
TEST_EQUAL(m2::Area(res), 0, ());
}
{
P arr1[] = {{0, 0}, {0, 3}, {3, 3}, {3, 0}};
P arr2[] = {{1, 1}, {1, 2}, {2, 2}, {2, 1}};
R r1, r2;
r1.Assign(arr1, arr1 + ARRAY_SIZE(arr1));
r2.Assign(arr2, arr2 + ARRAY_SIZE(arr2));
m2::MultiRegionI res;
res.push_back(r2);
m2::AddRegion(r1, res);
TEST_EQUAL(res.size(), 1, ());
TEST_EQUAL(m2::Area(res), 9, ());
res = m2::IntersectRegions(r1, {r2});
TEST_EQUAL(res.size(), 1, ());
TEST_EQUAL(m2::Area(res), 1, ());
}
}
UNIT_TEST(RegionIntersect_Floats)
{
P arr1[] = {{0, 1}, {2, 3}, {3, 2}, {1, 0}};
P arr2[] = {{0, 2}, {1, 3}, {3, 1}, {2, 0}};
R r1, r2;
r1.Assign(arr1, arr1 + ARRAY_SIZE(arr1));
r2.Assign(arr2, arr2 + ARRAY_SIZE(arr2));
// Moved diamond as a result with sqrt(2) edge's length and nearest integer coordinates.
m2::MultiRegionI res;
m2::IntersectRegions(r1, r2, res);
TEST_EQUAL(m2::Area(res), 2, ());
}
UNIT_TEST(RegionArea_2Directions)
{
P arr[] = {
{1, 1}, {1, 0}, {2, 0}, {2, 1}, {1, 1}, // CCW direction
{1, 1}, {1, 0}, {0, 0}, {0, 1}, {1, 1} // CW direction
};
R r;
r.Assign(arr, arr + ARRAY_SIZE(arr));
// Natural area is 2, but calculated area is 0, because one region with 2 opposite directions.
TEST_EQUAL(r.CalculateArea(), 0, ());
}
/*
UNIT_TEST(RegionDifference_Data1)
{
using namespace geom_test;
vector<R> vec;
vec.push_back(R());
vec.back().Assign(arrB1, arrB1 + ARRAY_SIZE(arrB1));
vec.push_back(R());
vec.back().Assign(arrB2, arrB2 + ARRAY_SIZE(arrB2));
vec.push_back(R());
vec.back().Assign(arrB3, arrB3 + ARRAY_SIZE(arrB3));
vec.push_back(R());
vec.back().Assign(arrB4, arrB4 + ARRAY_SIZE(arrB4));
vec.push_back(R());
vec.back().Assign(arrB5, arrB5 + ARRAY_SIZE(arrB5));
vec.push_back(R());
vec.back().Assign(arrB6, arrB6 + ARRAY_SIZE(arrB6));
vec.push_back(R());
vec.back().Assign(arrB7, arrB7 + ARRAY_SIZE(arrB7));
vec.push_back(R());
vec.back().Assign(arrB8, arrB8 + ARRAY_SIZE(arrB8));
vec.push_back(R());
vec.back().Assign(arrB9, arrB9 + ARRAY_SIZE(arrB9));
vec.push_back(R());
vec.back().Assign(arrB10, arrB10 + ARRAY_SIZE(arrB10));
vec.push_back(R());
vec.back().Assign(arrB11, arrB11 + ARRAY_SIZE(arrB11));
vec.push_back(R());
vec.back().Assign(arrB12, arrB12 + ARRAY_SIZE(arrB12));
vec.push_back(R());
vec.back().Assign(arrB13, arrB13 + ARRAY_SIZE(arrB13));
vec.push_back(R());
vec.back().Assign(arrB14, arrB14 + ARRAY_SIZE(arrB14));
vec.push_back(R());
vec.back().Assign(arrB15, arrB15 + ARRAY_SIZE(arrB15));
vector<R> res;
res.push_back(R());
res.back().Assign(arrMain, arrMain + ARRAY_SIZE(arrMain));
for (size_t i = 0; i < vec.size(); ++i)
{
vector<R> local;
for (size_t j = 0; j < res.size(); ++j)
m2::DiffRegions(res[j], vec[i], local);
local.swap(res);
}
}
*/
} // namespace region2d_binary_op_test

View file

@ -0,0 +1,367 @@
#include "testing/testing.hpp"
#include "base/macros.hpp"
#include "geometry/convex_hull.hpp"
#include "geometry/point2d.hpp"
#include "geometry/region2d.hpp"
#include <iostream>
#include <random>
#include <type_traits>
#include <vector>
namespace region_tests
{
using namespace std;
template <class Region>
struct ContainsChecker
{
ContainsChecker(Region const & region) : m_region(region) {}
void operator()(typename Region::Value const & pt)
{
TEST(m_region.Contains(pt), ("Region should contain all its points"));
}
Region const & m_region;
};
/// Region should have CCW orientation from left, down corner.
template <class Point>
void TestContainsRectangular(Point const * arr)
{
m2::Region<Point> region;
size_t const count = 4;
region.Assign(arr, arr + count);
for (size_t i = 0; i < count; ++i)
{
TEST(region.Contains(arr[i]), ());
if constexpr (std::is_floating_point<typename Point::value_type>::value)
TEST(region.Contains((arr[i] + arr[(i + 1) % count]) / 2), ());
}
Point dx(1, 0);
Point dy(0, 1);
TEST(!region.Contains(arr[0] - dx), ());
TEST(!region.Contains(arr[0] - dy), ());
TEST(!region.Contains(arr[0] - dx - dy), ());
TEST(region.Contains(arr[0] + dx + dy), ());
TEST(!region.Contains(arr[1] + dx), ());
TEST(!region.Contains(arr[1] - dy), ());
TEST(!region.Contains(arr[1] + dx - dy), ());
TEST(region.Contains(arr[1] - dx + dy), ());
TEST(!region.Contains(arr[2] + dx), ());
TEST(!region.Contains(arr[2] + dy), ());
TEST(!region.Contains(arr[2] + dx + dy), ());
TEST(region.Contains(arr[2] - dx - dy), ());
TEST(!region.Contains(arr[3] - dx), ());
TEST(!region.Contains(arr[3] + dy), ());
TEST(!region.Contains(arr[3] - dx + dy), ());
TEST(region.Contains(arr[3] + dx - dy), ());
}
template <class Region>
void TestContains()
{
Region region;
ContainsChecker<Region> checker(region);
// point type
using P = typename Region::Value;
// rectangular polygon
{
P const data[] = {P(1, 1), P(10, 1), P(10, 10), P(1, 10)};
TestContainsRectangular(data);
}
{
P const data[] = {P(-100, -100), P(-50, -100), P(-50, -50), P(-100, -50)};
TestContainsRectangular(data);
}
{
P const data[] = {P(-2000000000, -2000000000), P(-1000000000, -2000000000), P(-1000000000, -1000000000),
P(-2000000000, -1000000000)};
TestContainsRectangular(data);
}
{
P const data[] = {P(1000000000, 1000000000), P(2000000000, 1000000000), P(2000000000, 2000000000),
P(1000000000, 2000000000)};
TestContainsRectangular(data);
}
// triangle
{
P const data[] = {P(0, 0), P(2, 0), P(2, 2)};
region.Assign(data, data + ARRAY_SIZE(data));
}
TEST_EQUAL(region.GetRect(), m2::Rect<typename P::value_type>(0, 0, 2, 2), ());
TEST(region.Contains(P(2, 0)), ());
TEST(region.Contains(P(1, 1)), ("point on diagonal"));
TEST(!region.Contains(P(33, 0)), ());
region.ForEachPoint(checker);
// complex polygon
{
P const data[] = {P(0, 0), P(2, 0), P(2, 2), P(3, 1), P(4, 2), P(5, 2), P(3, 3), P(3, 2), P(2, 4), P(6, 3),
P(7, 4), P(7, 2), P(8, 5), P(8, 7), P(7, 7), P(8, 8), P(5, 9), P(6, 6), P(5, 7), P(4, 6),
P(4, 8), P(3, 7), P(2, 7), P(3, 6), P(4, 4), P(0, 7), P(2, 3), P(0, 2)};
region.Assign(data, data + ARRAY_SIZE(data));
}
TEST_EQUAL(region.GetRect(), m2::Rect<typename P::value_type>(0, 0, 8, 9), ());
TEST(region.Contains(P(0, 0)), ());
TEST(region.Contains(P(3, 7)), ());
TEST(region.Contains(P(1, 2)), ());
TEST(region.Contains(P(1, 1)), ());
TEST(!region.Contains(P(6, 2)), ());
TEST(!region.Contains(P(3, 5)), ());
TEST(!region.Contains(P(5, 8)), ());
region.ForEachPoint(checker);
}
template <class Point>
class PointsSummator
{
public:
PointsSummator(Point & res) : m_res(res) {}
void operator()(Point const & pt) { m_res += pt; }
private:
Point & m_res;
};
UNIT_TEST(Region)
{
typedef m2::PointD P;
P p1[] = {P(0.1, 0.2)};
m2::Region<P> region(p1, p1 + ARRAY_SIZE(p1));
TEST(!region.IsValid(), ());
{
P p2[] = {P(1.0, 2.0), P(55.0, 33.0)};
region.Assign(p2, p2 + ARRAY_SIZE(p2));
}
TEST(!region.IsValid(), ());
region.AddPoint(P(34.4, 33.2));
TEST(region.IsValid(), ());
{
// equality case
{
P const data[] = {P(1, 1), P(0, 4.995), P(1, 4.999996), P(1.000003, 5.000001), P(0.5, 10), P(10, 10), P(10, 1)};
region.Assign(data, data + ARRAY_SIZE(data));
}
TEST(!region.Contains(P(0.9999987, 0.9999938)), ());
TEST(!region.Contains(P(0.999998, 4.9999987)), ());
}
}
UNIT_TEST(Region_Contains_int32)
{
TestContains<m2::RegionI>();
// negative triangle
{
using P = m2::PointI;
m2::Region<P> region;
P const data[] = {P(1, -1), P(-2, -2), P(-3, 1)};
region.Assign(data, data + ARRAY_SIZE(data));
TEST_EQUAL(region.GetRect(), m2::Rect<P::value_type>(-3, -2, 1, 1), ());
TEST(region.Contains(P(-2, -2)), ());
TEST(region.Contains(P(-2, 0)), ());
TEST(!region.Contains(P(0, 0)), ());
}
{
using P = m2::PointI;
m2::Region<P> region;
P const data[] = {P(1, -1), P(3, 0), P(3, 3), P(0, 3), P(0, 2), P(0, 1), P(2, 2)};
region.Assign(data, data + ARRAY_SIZE(data));
TEST_EQUAL(region.GetRect(), m2::Rect<P::value_type>(0, -1, 3, 3), ());
TEST(region.Contains(P(2, 2)), ());
TEST(region.Contains(P(1, 3)), ());
TEST(region.Contains(P(3, 1)), ());
TEST(!region.Contains(P(1, 1)), ());
}
}
UNIT_TEST(Region_Contains_uint32)
{
TestContains<m2::RegionU>();
}
UNIT_TEST(Region_Contains_double)
{
using Region = m2::RegionD;
using Point = Region::Value;
TestContains<Region>();
{
Region region;
Point const data[] = {{0, 7}, {4, 4}, {3, 6}, {8, 6}, {8, 5}, {6, 3}, {2, 2}};
region.Assign(data, data + ARRAY_SIZE(data));
TEST_EQUAL(region.GetRect(), m2::Rect<Point::value_type>(0, 2, 8, 7), ());
TEST(!region.Contains({3, 5}), ());
}
}
UNIT_TEST(Region_ForEachPoint)
{
using P = m2::PointF;
P const points[] = {P(0.0, 1.0), P(1.0, 2.0), P(10.5, 11.5)};
m2::Region<P> region(points, points + ARRAY_SIZE(points));
P res(0, 0);
region.ForEachPoint(PointsSummator<P>(res));
TEST_EQUAL(res, P(11.5, 14.5), ());
}
UNIT_TEST(Region_point_at_border_test)
{
using P = m2::PointF;
P const points[] = {P(0.0, 1.0), P(0.0, 10.0), P(5.0, 7.0), P(10.0, 10.0), P(10.0, 1.0)};
m2::Region<P> region(points, points + ARRAY_SIZE(points));
P p1(0, 0);
P p2(5.0, 5.0);
P p3(0.0, 1.0);
P p4(5.0, 1.0);
P p5(5.0, 1.01);
P p6(5.0, 0.99);
P p7(5.0, 7.0);
P p8(5.0, 6.98);
P p9(5.0, 6.995);
P p10(5.0, 7.01);
TEST(!region.AtBorder(p1, 0.01), ("Point lies outside the border"));
TEST(!region.AtBorder(p2, 0.01), ("Point lies strictly inside the border"));
TEST(region.AtBorder(p3, 0.01), ("Point has same point with the border"));
TEST(region.AtBorder(p4, 0.01), ("Point lies at the border"));
TEST(region.AtBorder(p5, 0.01), ("Point lies at delta interval near the border inside polygon"));
TEST(region.AtBorder(p6, 0.01), ("Point lies at delta interval near the border outside polygon"));
TEST(region.AtBorder(p7, 0.01), ("Point has same point with the border"));
TEST(!region.AtBorder(p8, 0.01), ("Point is too far from border"));
TEST(region.AtBorder(p9, 0.01), ("Point lies at delta interval near the border outside polygon"));
TEST(region.AtBorder(p10, 0.01), ("Point lies at delta interval near the border inside polygon"));
}
UNIT_TEST(Region_border_intersecion_Test)
{
using P = m2::PointF;
P const points[] = {P(0.0, 1.0), P(0.0, 10.0), P(10.0, 10.0), P(10.0, 1.0)};
m2::Region<P> region(points, points + ARRAY_SIZE(points));
P intersection;
TEST(region.FindIntersection(P(5.0, 5.0), P(15.0, 5.0), intersection), ());
TEST(intersection == P(10.0, 5.0), ());
TEST(region.FindIntersection(P(5.0, 5.0), P(15.0, 15.0), intersection), ());
TEST(intersection == P(10.0, 10.0), ());
TEST(region.FindIntersection(P(7.0, 7.0), P(7.0, 10.0), intersection), ());
TEST(intersection == P(7.0, 10.0), ());
TEST(!region.FindIntersection(P(5.0, 5.0), P(2.0, 2.0), intersection), ("This case has no intersection"));
}
UNIT_TEST(Region_Area)
{
using P = m2::PointD;
{
m2::Region<P> region;
TEST_EQUAL(region.CalculateArea(), 0.0, ());
}
{
// Counterclockwise.
P const points[] = {P(0.0, 0.0), P(1.0, 0.0), P(1.0, 1.0), P(0.0, 1.0)};
m2::Region<P> region(points, points + ARRAY_SIZE(points));
TEST_EQUAL(region.CalculateArea(), 1.0, ());
}
{
// Clockwise.
P const points[] = {P(0.0, 0.0), P(0.0, 1.0), P(1.0, 1.0), P(1.0, 0.0)};
m2::Region<P> region(points, points + ARRAY_SIZE(points));
TEST_EQUAL(region.CalculateArea(), 1.0, ());
}
{
// Non-convex.
P const points[] = {P(0.0, 0.0), P(1.0, 0.0), P(1.0, 1.0), P(0.5, 0.5), P(0.0, 1.0)};
m2::Region<P> region(points, points + ARRAY_SIZE(points));
TEST_EQUAL(region.CalculateArea(), 0.75, ());
}
}
UNIT_TEST(Region_GetRandomPoint)
{
using P = m2::PointD;
// Run several iterations of Monte-Carlo and check that areas are similar.
size_t const kNumIterations = 1000;
bool const kNeedPlot = true;
auto testConvexRegion = [&](m2::Region<P> const & region)
{
minstd_rand rng(0);
vector<P> points;
points.reserve(kNumIterations);
for (size_t i = 0; i < kNumIterations; ++i)
points.emplace_back(region.GetRandomPoint(rng));
m2::ConvexHull const hull(points, 1e-9 /* eps */);
auto const hullRegion = m2::Region<P>(hull.Points().begin(), hull.Points().end());
LOG(LINFO, (hullRegion.CalculateArea()));
TEST(AlmostEqualRel(region.CalculateArea(), hullRegion.CalculateArea(), 0.05), ());
if (kNeedPlot)
{
cout << "import matplotlib.pyplot as plt" << endl;
cout << endl;
cout << "x = [";
for (size_t i = 0; i < points.size(); i++)
cout << points[i].x << ",";
cout << "]" << endl;
cout << "y = [";
for (size_t i = 0; i < points.size(); i++)
cout << points[i].y << ",";
cout << "]" << endl;
cout << endl;
cout << "plt.scatter(x, y)" << endl;
cout << "plt.show()" << endl;
}
};
{
P const points[] = {P(0.0, 0.0), P(1.0, 0.0), P(1.0, 1.0), P(0.0, 1.0)};
m2::Region<P> region(points, points + ARRAY_SIZE(points));
testConvexRegion(region);
}
{
P const points[] = {P(0.0, -1.0), P(1.0, 0.0), P(0.0, 1.0), P(-1.0, 0.0)};
m2::Region<P> region(points, points + ARRAY_SIZE(points));
testConvexRegion(region);
}
}
} // namespace region_tests

View file

@ -0,0 +1,179 @@
#include "testing/testing.hpp"
#include "geometry/robust_orientation.hpp"
#include "geometry/segment2d.hpp"
#include "geometry/triangle2d.hpp"
#include <iterator>
namespace robust_test
{
using namespace m2::robust;
using P = m2::PointD;
template <typename TIt>
void CheckSelfIntersections(TIt beg, TIt end, bool res)
{
TEST_EQUAL(CheckPolygonSelfIntersections(beg, end), res, ());
using TRevIt = std::reverse_iterator<TIt>;
TEST_EQUAL(CheckPolygonSelfIntersections(TRevIt(end), TRevIt(beg)), res, ());
}
bool OnSegment(P const & p, P const ps[])
{
return IsPointOnSegment(p, ps[0], ps[1]);
}
bool InsideTriangle(P const & p, P const ps[])
{
return IsPointInsideTriangle(p, ps[0], ps[1], ps[2]);
}
UNIT_TEST(OrientedS_Smoke)
{
P arr[] = {{-1, -1}, {0, 0}, {1, -1}};
TEST(OrientedS(arr[0], arr[2], arr[1]) > 0, ());
TEST(OrientedS(arr[2], arr[0], arr[1]) < 0, ());
}
UNIT_TEST(Segment_Smoke)
{
double constexpr eps = 1.0E-10;
{
P ps[] = {{0, 0}, {1, 0}};
TEST(OnSegment(ps[0], ps), ());
TEST(OnSegment(ps[1], ps), ());
TEST(OnSegment(P(0.5, 0), ps), ());
TEST(OnSegment(P(eps, 0), ps), ());
TEST(OnSegment(P(1.0 - eps, 0), ps), ());
TEST(!OnSegment(P(-eps, 0), ps), ());
TEST(!OnSegment(P(1.0 + eps, 0), ps), ());
TEST(!OnSegment(P(eps, eps), ps), ());
TEST(!OnSegment(P(eps, -eps), ps), ());
}
{
P ps[] = {{10, 10}, {10, 10}};
TEST(OnSegment(ps[0], ps), ());
TEST(OnSegment(ps[1], ps), ());
TEST(!OnSegment(P(10 - eps, 10), ps), ());
TEST(!OnSegment(P(10 + eps, 10), ps), ());
TEST(!OnSegment(P(0, 0), ps), ());
}
}
// This paranoid test doesn' work with Release optimizations (LTO?).
#ifndef NDEBUG
UNIT_TEST(Segment_Paranoid)
{
{
P ps[] = {{0, 0}, {1e100, 1e100}};
TEST(OnSegment(ps[0], ps), ());
TEST(OnSegment(ps[1], ps), ());
#if defined(DEBUG) || __apple_build_version__ < 15000000 // True if __apple_build_version__ is not defined (e.g. Linux)
// TODO(AB): Fails on Mac's clang with any optimization enabled and -fassociative-math
TEST(OnSegment(P(1e50, 1e50), ps), ());
#endif
TEST(!OnSegment(P(1e50, 1.00000000001e50), ps), ());
#if defined(DEBUG) || __apple_build_version__ < 15000000
// TODO(AB): Fails on Mac's clang with any optimization enabled and -fassociative-math
TEST(OnSegment(P(1e-100, 1e-100), ps), ());
#endif
TEST(!OnSegment(P(1e-100, 1e-100 + 1e-115), ps), ());
}
#if defined(DEBUG) || __apple_build_version__ < 15000000
// TODO(AB): Fails on Mac's clang with any optimization enabled and -fassociative-math
{
P ps[] = {{0, 0}, {2e100, 1e100}};
TEST(OnSegment(P(2.0 / 3.0, 1.0 / 3.0), ps), ());
}
#endif
{
P ps[] = {{0, 0}, {1e-15, 1e-15}};
TEST(!OnSegment(P(1e-16, 2.0 * 1e-16), ps), ());
}
}
#endif
UNIT_TEST(Triangle_Smoke)
{
P arr[] = {{0, 0}, {0, 3}, {3, 0}};
TEST(IsPointInsideTriangle(arr[0], arr[0], arr[1], arr[2]), ());
TEST(IsPointInsideTriangle(arr[1], arr[0], arr[1], arr[2]), ());
TEST(IsPointInsideTriangle(arr[2], arr[0], arr[1], arr[2]), ());
TEST(IsPointInsideTriangle({1, 1}, arr[0], arr[1], arr[2]), ());
TEST(IsPointInsideTriangle({1, 2}, arr[0], arr[1], arr[2]), ());
TEST(IsPointInsideTriangle({2, 1}, arr[0], arr[1], arr[2]), ());
double constexpr eps = 1.0E-10;
TEST(!IsPointInsideTriangle({-eps, -eps}, arr[0], arr[1], arr[2]), ());
TEST(!IsPointInsideTriangle({1 + eps, 2}, arr[0], arr[1], arr[2]), ());
TEST(!IsPointInsideTriangle({2, 1 + eps}, arr[0], arr[1], arr[2]), ());
}
UNIT_TEST(Triangle_PointInsideSegment)
{
double constexpr eps = 1.0E-10;
P ps[] = {{0, 0}, {0, 1}, {0, 1}};
TEST(InsideTriangle(ps[0], ps), ());
TEST(InsideTriangle(ps[1], ps), ());
TEST(InsideTriangle(ps[2], ps), ());
TEST(InsideTriangle(P(0, eps), ps), ());
TEST(InsideTriangle(P(0, 1.0 - eps), ps), ());
TEST(!InsideTriangle(P(0, -eps), ps), ());
TEST(!InsideTriangle(P(0, 1.0 + eps), ps), ());
TEST(!InsideTriangle(P(-eps, eps), ps), ());
TEST(!InsideTriangle(P(eps, eps), ps), ());
}
// This paranoid test doesn' work with Release optimizations (LTO?).
#ifndef NDEBUG
UNIT_TEST(Triangle_PointInsidePoint)
{
double constexpr eps = 1.0E-10;
P ps[] = {{0, 0}, {0, 0}, {0, 0}};
TEST(InsideTriangle(ps[0], ps), ());
TEST(InsideTriangle(ps[1], ps), ());
TEST(InsideTriangle(ps[2], ps), ());
TEST(!InsideTriangle(P(0, eps), ps), ());
TEST(!InsideTriangle(P(0, -eps), ps), ());
#if defined(DEBUG) || __apple_build_version__ < 15000000
// TODO(AB): Fail on Mac's clang with any optimization enabled and -fassociative-math
TEST(!InsideTriangle(P(-eps, eps), ps), ());
TEST(!InsideTriangle(P(eps, eps), ps), ());
#endif
}
#endif
UNIT_TEST(PolygonSelfIntersections_IntersectSmoke)
{
{
P arr[] = {P(0, 1), P(2, -1), P(2, 1), P(0, -1)};
CheckSelfIntersections(&arr[0], arr + ARRAY_SIZE(arr), true);
}
}
UNIT_TEST(PolygonSelfIntersections_TangentSmoke)
{
{
P arr[] = {P(0, 1), P(1, 0), P(2, 1), P(2, -1), P(1, 0), P(0, -1)};
CheckSelfIntersections(&arr[0], arr + ARRAY_SIZE(arr), false);
}
{
P arr[] = {P(0, 0), P(2, 0), P(2, 1), P(1, 0), P(0, 1)};
CheckSelfIntersections(&arr[0], arr + ARRAY_SIZE(arr), false);
}
}
} // namespace robust_test

View file

@ -0,0 +1,215 @@
#include "geometry/geometry_tests/equality.hpp"
#include "base/math.hpp"
#include "geometry/screenbase.hpp"
#include "geometry/transformations.hpp"
#include "testing/testing.hpp"
namespace screen_test
{
using test::is_equal;
static void check_set_from_rect(ScreenBase & screen, int width, int height)
{
screen.OnSize(0, 0, width, height);
m2::PointD b1(0.0, 0.0);
m2::PointD b2(300.0, 300.0);
screen.SetFromRect(m2::AnyRectD(m2::RectD(b1, b2)));
b1 = screen.GtoP(b1);
b2 = screen.GtoP(b2);
// check that we are in boundaries.
TEST(math::Between(0, width, math::iround(b1.x)), ());
TEST(math::Between(0, width, math::iround(b2.x)), ());
TEST(math::Between(0, height, math::iround(b1.y)), ());
TEST(math::Between(0, height, math::iround(b2.y)), ());
}
UNIT_TEST(ScreenBase_P2G2P)
{
ScreenBase screen;
check_set_from_rect(screen, 1000, 500);
check_set_from_rect(screen, 500, 1000);
screen.OnSize(0, 0, 640, 480);
screen.SetFromRect(m2::AnyRectD(m2::RectD(-100, -200, 500, 680)));
// checking that PtoG(GtoP(p)) == p
m2::PointD pp(10.0, 20.0);
m2::PointD pg = screen.PtoG(pp);
TEST(is_equal(pp, screen.GtoP(pg)), ());
pg = m2::PointD(550, 440);
pp = screen.GtoP(pg);
TEST(is_equal(pg, screen.PtoG(pp)), ());
}
UNIT_TEST(ScreenBase_3dTransform)
{
ScreenBase screen;
double const rotationAngle = math::pi4;
screen.SetFromRects(m2::AnyRectD(m2::RectD(50, 25, 55, 30)), m2::RectD(0, 0, 200, 400));
screen.ApplyPerspective(rotationAngle, rotationAngle, math::pi / 3.0);
TEST(screen.PixelRectIn3d().SizeX() < screen.PixelRect().SizeX(), ());
TEST(screen.PixelRectIn3d().SizeY() < screen.PixelRect().SizeY(), ());
double const kEps = 1.0e-3;
m2::PointD pp(screen.PixelRect().SizeX() / 2.0, screen.PixelRect().SizeY());
m2::PointD p3d = screen.PtoP3d(pp);
TEST(p3d.EqualDxDy(m2::PointD(screen.PixelRectIn3d().SizeX() / 2.0, screen.PixelRectIn3d().SizeY()), kEps), ());
p3d = m2::PointD(screen.PixelRectIn3d().SizeX() / 2.0, screen.PixelRectIn3d().SizeY() / 2.0);
pp = screen.P3dtoP(p3d);
TEST(
pp.EqualDxDy(m2::PointD(screen.PixelRect().SizeX() / 2.0,
screen.PixelRect().SizeY() - screen.PixelRectIn3d().SizeY() / (2.0 * cos(rotationAngle))),
kEps),
());
p3d = m2::PointD(0, 0);
pp = screen.P3dtoP(p3d);
TEST(pp.x < kEps, ());
p3d = m2::PointD(screen.PixelRectIn3d().SizeX(), 0);
pp = screen.P3dtoP(p3d);
TEST(fabs(pp.x - screen.PixelRect().maxX()) < kEps, ());
}
UNIT_TEST(ScreenBase_P2P3d2P)
{
ScreenBase screen;
screen.SetFromRects(m2::AnyRectD(m2::RectD(50, 25, 55, 30)), m2::RectD(0, 0, 600, 400));
screen.ApplyPerspective(math::pi4, math::pi4, math::pi / 3.0);
double const kEps = 1.0e-3;
// checking that P3dtoP(PtoP3d(p)) == p
m2::PointD pp(500, 300);
m2::PointD p3d = screen.PtoP3d(pp);
TEST(pp.EqualDxDy(screen.P3dtoP(p3d), kEps), ());
// checking that PtoP3(P3dtoP(p)) == p
p3d = m2::PointD(400, 300);
pp = screen.P3dtoP(p3d);
TEST(p3d.EqualDxDy(screen.PtoP3d(pp), kEps), ());
}
UNIT_TEST(ScreenBase_AxisOrientation)
{
ScreenBase screen;
screen.OnSize(0, 0, 300, 200);
screen.SetFromRect(m2::AnyRectD(m2::RectD(0, 0, 300, 200)));
TEST(is_equal(m2::PointD(150, 100), screen.GtoP(m2::PointD(150, 100))), ());
TEST(is_equal(m2::PointD(0, 0), screen.GtoP(m2::PointD(0, 200))), ());
TEST(is_equal(m2::PointD(300, 0), screen.GtoP(m2::PointD(300, 200))), ());
TEST(is_equal(m2::PointD(300, 200), screen.GtoP(m2::PointD(300, 0))), ());
TEST(is_equal(m2::PointD(0, 200), screen.GtoP(m2::PointD(0, 0))), ());
}
UNIT_TEST(ScreenBase_X0Y0)
{
ScreenBase screen;
screen.OnSize(10, 10, 300, 200);
screen.SetFromRect(m2::AnyRectD(m2::RectD(0, 0, 300, 200)));
TEST(is_equal(m2::PointD(10, 210), screen.GtoP(m2::PointD(0, 0))), ());
}
UNIT_TEST(ScreenBase_ChoosingMaxScale)
{
ScreenBase screen;
screen.OnSize(10, 10, 300, 200);
screen.SetFromRect(m2::AnyRectD(m2::RectD(0, 0, 200, 400)));
TEST(is_equal(screen.GtoP(m2::PointD(100, 200)), m2::PointD(160, 110)), ());
TEST(is_equal(screen.GtoP(m2::PointD(0, 0)), m2::PointD(110, 210)), ());
TEST(is_equal(screen.GtoP(m2::PointD(200, 0)), m2::PointD(210, 210)), ());
TEST(is_equal(screen.GtoP(m2::PointD(0, 400)), m2::PointD(110, 10)), ());
TEST(is_equal(screen.GtoP(m2::PointD(200, 400)), m2::PointD(210, 10)), ());
TEST(is_equal(screen.GtoP(m2::PointD(-200, 0)), m2::PointD(10, 210)), ());
}
UNIT_TEST(ScreenBase_CalcTransform)
{
double s = 2;
double a = sqrt(3.) / 2.0;
double dx = 1;
double dy = 2;
double s1, a1, dx1, dy1;
math::Matrix<double, 3, 3> m =
ScreenBase::CalcTransform(m2::PointD(0, 1), m2::PointD(1, 1), m2::PointD(s * sin(a) + dx, s * cos(a) + dy),
m2::PointD(s * cos(a) + s * sin(a) + dx, -s * sin(a) + s * cos(a) + dy),
true /* allow rotate */, true /* allow scale*/);
ScreenBase::ExtractGtoPParams(m, a1, s1, dx1, dy1);
TEST(fabs(s - s1) < 0.00001, (s, s1));
TEST(fabs(a - a1) < 0.00001, (a, a1));
TEST(fabs(dx - dx1) < 0.00001, (dx, dx1));
TEST(fabs(dy - dy1) < 0.00001, (dy, dy1));
}
UNIT_TEST(ScreenBase_Rotate)
{
ScreenBase s;
s.OnSize(0, 0, 100, 200);
s.SetFromRect(m2::AnyRectD(m2::RectD(0, 0, 100, 200)));
s.Rotate(math::pi / 4);
TEST_EQUAL(s.PixelRect(), m2::RectD(0, 0, 100, 200), ());
}
UNIT_TEST(ScreenBase_CombineTransforms)
{
ScreenBase s;
s.OnSize(0, 0, 640, 480);
s.SetFromRect(m2::AnyRectD(m2::RectD(50, 25, 55, 30)));
s.SetAngle(1.0);
m2::PointD g1(40, 50);
m2::PointD g2(60, 70);
m2::PointD p1 = s.GtoP(g1);
m2::PointD p2 = s.GtoP(g2);
m2::PointD const org = s.GtoP(m2::PointD(0, 0));
double const angle = s.GetAngle();
double const scale = s.GetScale();
double const fixedScale = 666.666;
ScreenBase sCopy(s, m2::PointD(0, 0), fixedScale, 0.0);
{
// GtoP matrix for scale only.
math::Matrix<double, 3, 3> m = math::Shift(
math::Scale(math::Identity<double, 3>(), 1.0 / fixedScale, -1.0 / fixedScale), sCopy.PixelRect().Center());
TEST(is_equal(sCopy.GtoP(g1), g1 * m), ());
TEST(is_equal(sCopy.GtoP(g2), g2 * m), ());
}
// GtoP matrix to make full final transformation.
math::Matrix<double, 3, 3> m = math::Shift(
math::Scale(math::Rotate(math::Shift(math::Identity<double, 3>(), -sCopy.PixelRect().Center()), angle),
fixedScale / scale, fixedScale / scale),
org);
m2::PointD pp1 = sCopy.GtoP(g1) * m;
m2::PointD pp2 = sCopy.GtoP(g2) * m;
TEST(is_equal(p1, pp1), (p1, pp1));
TEST(is_equal(p2, pp2), (p2, pp2));
}
} // namespace screen_test

View file

@ -0,0 +1,99 @@
#include "testing/testing.hpp"
#include "geometry/point2d.hpp"
#include "geometry/segment2d.hpp"
namespace segment2d_tests
{
using namespace m2;
double const kEps = 1e-10;
void TestIntersectionResult(IntersectionResult const & result, IntersectionResult::Type expectedType,
PointD const & expectedPoint)
{
TEST_EQUAL(result.m_type, expectedType, ());
TEST(AlmostEqualAbs(result.m_point, expectedPoint, kEps), (result.m_point, expectedPoint, kEps));
}
bool TestSegmentsIntersect(PointD a, PointD b, PointD c, PointD d)
{
bool const res = SegmentsIntersect(a, b, c, d);
TEST_EQUAL(res, SegmentsIntersect(a, b, d, c), (a, b, c, d));
TEST_EQUAL(res, SegmentsIntersect(b, a, c, d), (a, b, c, d));
TEST_EQUAL(res, SegmentsIntersect(b, a, d, c), (a, b, c, d));
TEST_EQUAL(res, SegmentsIntersect(c, d, a, b), (a, b, c, d));
TEST_EQUAL(res, SegmentsIntersect(c, d, b, a), (a, b, c, d));
TEST_EQUAL(res, SegmentsIntersect(d, c, a, b), (a, b, c, d));
TEST_EQUAL(res, SegmentsIntersect(d, c, b, a), (a, b, c, d));
return res;
}
UNIT_TEST(SegmentIntersection_Collinear)
{
TEST(!TestSegmentsIntersect({0.0, 0.0}, {1.0, 1.0}, {2.0, 3.0}, {3.0, 3.0}), ("Far away"));
TEST(TestSegmentsIntersect({0.0, 0.0}, {1.0, 1.0}, {1.0, 1.0}, {3.0, 3.0}), ("Border intersection"));
TEST(TestSegmentsIntersect({0.0, 0.0}, {2.0, 2.0}, {1.0, 1.0}, {3.0, 3.0}), ("Normal intersection"));
TEST(TestSegmentsIntersect({0.0, 0.0}, {2.0, 2.0}, {0.0, 0.0}, {3.0, 3.0}), ("Border inclusion"));
TEST(TestSegmentsIntersect({1.0, 1.0}, {2.0, 2.0}, {0.0, 0.0}, {3.0, 3.0}), ("Normal inclusion"));
}
UNIT_TEST(SegmentIntersection_NoIntersection)
{
TEST(!TestSegmentsIntersect({0.0, 1.0}, {2.0, 5.0}, {7.0, 7.0}, {15.0, 7.0}), ("Far away"));
TEST(!TestSegmentsIntersect({0.0, 0.0}, {2.0, 4.0}, {1.0, 1.0}, {4.0, 3.0}), ("Rect intersect, segments don't"));
TEST(!TestSegmentsIntersect({0.0, 0.0}, {8.0, 8.0}, {1.0, 2.0}, {4.0, 8.0}), ("Only one cross product is 0"));
}
UNIT_TEST(SegmentIntersection_Intersect)
{
TEST(TestSegmentsIntersect({0.0, 0.0}, {2.0, 4.0}, {0.0, 3.0}, {4.0, 0.0}), ("Normal"));
TEST(TestSegmentsIntersect({1.0, 2.0}, {3.0, 4.0}, {1.0, 2.0}, {5.0, 1.0}), ("Fan"));
TEST(TestSegmentsIntersect({1.0, 2.0}, {3.0, 4.0}, {1.0, 2.0}, {3.0, -2.0}), ("Fan"));
TEST(TestSegmentsIntersect({0.0, 0.0}, {2.0, 2.0}, {0.0, 4.0}, {4.0, 0.0}), ("Border"));
}
UNIT_TEST(SegmentIntersection_NoIntersectionPoint)
{
TEST_EQUAL(Intersect(Segment2D({0.0, 0.0}, {1.0, 0.0}), Segment2D({2.0, 0.0}, {4.0, 0.0}), kEps).m_type,
IntersectionResult::Type::Zero, ());
TEST_EQUAL(Intersect(Segment2D({0.0, 0.0}, {1.0, 1.0}), Segment2D({2.0, 0.0}, {4.0, 0.0}), kEps).m_type,
IntersectionResult::Type::Zero, ());
TEST_EQUAL(Intersect(Segment2D({0.0, 0.0}, {1.0, 1.0}), Segment2D({4.0, 4.0}, {2.0, 2.0}), kEps).m_type,
IntersectionResult::Type::Zero, ());
TEST_EQUAL(Intersect(Segment2D({0.0, 0.0}, {4.0, 4.0}), Segment2D({10.0, 0.0}, {6.0, 4.0}), kEps).m_type,
IntersectionResult::Type::Zero, ());
}
UNIT_TEST(SegmentIntersection_OneIntersectionPoint)
{
// Two intersected segments. The intersection point is in the middle of both of them.
{
auto const result = Intersect(Segment2D({-1.0, -1.0}, {1.0, 1.0}), Segment2D({-1.0, 0.0}, {1.0, 0.0}), kEps);
TestIntersectionResult(result, IntersectionResult::Type::One, {0.0, 0.0});
}
{
auto const result = Intersect(Segment2D({0.0, 0.0}, {10.0, 10.0}), Segment2D({10.0, 0.0}, {0.0, 10.0}), kEps);
TestIntersectionResult(result, IntersectionResult::Type::One, {5.0, 5.0});
}
// Two intersected segments. The intersection point is in an end of both of them.
{
auto const result = Intersect(Segment2D({-1.0, -1.0}, {0.0, 0.0}), Segment2D({0.0, 0.0}, {11.0, 0.0}), kEps);
TestIntersectionResult(result, IntersectionResult::Type::One, {0.0, 0.0});
}
}
UNIT_TEST(SegmentIntersection_InfinityIntersectionPoints)
{
TEST_EQUAL(Intersect(Segment2D({0.0, 0.0}, {2.0, 0.0}), Segment2D({1.0, 0.0}, {4.0, 0.0}), kEps).m_type,
IntersectionResult::Type::Infinity, ());
TEST_EQUAL(Intersect(Segment2D({0.0, 0.0}, {2.0, 4.0}), Segment2D({1.0, 2.0}, {2.0, 4.0}), kEps).m_type,
IntersectionResult::Type::Infinity, ());
}
} // namespace segment2d_tests

View file

@ -0,0 +1,150 @@
#include "testing/testing.hpp"
#include "geometry/geometry_tests/large_polygon.hpp"
#include "geometry/parametrized_segment.hpp"
#include "geometry/point2d.hpp"
#include "geometry/simplification.hpp"
#include "base/logging.hpp"
#include "base/macros.hpp"
#include "base/stl_helpers.hpp"
#include <cstdint>
#include <limits>
#include <vector>
namespace simplification_test
{
using namespace std;
using P = m2::PointD;
using DistanceFn = m2::SquaredDistanceFromSegmentToPoint;
using PointOutput = base::BackInsertFunctor<vector<m2::PointD>>;
using SimplifyFn = void (*)(m2::PointD const *, m2::PointD const *, double, DistanceFn, PointOutput);
struct LargePolylineTestData
{
static m2::PointD const * m_Data;
static size_t m_Size;
};
m2::PointD const * LargePolylineTestData::m_Data = LargePolygon::kLargePolygon;
size_t LargePolylineTestData::m_Size = ARRAY_SIZE(LargePolygon::kLargePolygon);
void TestSimplificationSmoke(SimplifyFn simplifyFn)
{
m2::PointD const points[] = {P(0.0, 1.0), P(2.2, 3.6), P(3.2, 3.6)};
double const epsilon = 0.1;
vector<m2::PointD> result, expectedResult(points, points + 3);
simplifyFn(points, points + 3, epsilon, DistanceFn(), base::MakeBackInsertFunctor(result));
TEST_EQUAL(result, expectedResult, (epsilon));
}
void TestSimplificationOfLine(SimplifyFn simplifyFn)
{
m2::PointD const points[] = {P(0.0, 1.0), P(2.2, 3.6)};
for (double epsilon = numeric_limits<double>::denorm_min(); epsilon < 1000; epsilon *= 2)
{
vector<m2::PointD> result, expectedResult(points, points + 2);
simplifyFn(points, points + 2, epsilon, DistanceFn(), base::MakeBackInsertFunctor(result));
TEST_EQUAL(result, expectedResult, (epsilon));
}
}
void TestSimplificationOfPoly(m2::PointD const * points, size_t count, SimplifyFn simplifyFn)
{
for (double epsilon = 0.00001; epsilon < 0.11; epsilon *= 10)
{
vector<m2::PointD> result;
simplifyFn(points, points + count, epsilon, DistanceFn(), base::MakeBackInsertFunctor(result));
// LOG(LINFO, ("eps:", epsilon, "size:", result.size()));
TEST_GREATER(result.size(), 1, ());
TEST_EQUAL(result.front(), points[0], (epsilon));
TEST_EQUAL(result.back(), points[count - 1], (epsilon));
TEST_LESS(result.size(), count, (epsilon));
}
}
void SimplifyNearOptimal10(m2::PointD const * f, m2::PointD const * l, double e, DistanceFn distFn, PointOutput out)
{
SimplifyNearOptimal(10, f, l, e, distFn, out);
}
void SimplifyNearOptimal20(m2::PointD const * f, m2::PointD const * l, double e, DistanceFn distFn, PointOutput out)
{
SimplifyNearOptimal(20, f, l, e, distFn, out);
}
void CheckDPStrict(P const * arr, size_t n, double eps, size_t expectedCount)
{
vector<P> vec;
DistanceFn distFn;
SimplifyDP(arr, arr + n, eps, distFn, AccumulateSkipSmallTrg<DistanceFn, P>(distFn, vec, eps));
TEST_GREATER(vec.size(), 1, ());
TEST_EQUAL(arr[0], vec.front(), ());
TEST_EQUAL(arr[n - 1], vec.back(), ());
if (expectedCount > 0)
TEST_EQUAL(expectedCount, vec.size(), ());
for (size_t i = 2; i < vec.size(); ++i)
{
auto const d = DistanceFn();
TEST_GREATER_OR_EQUAL(d(vec[i - 2], vec[i], vec[i - 1]), eps, ());
}
}
UNIT_TEST(Simplification_TestDataIsCorrect)
{
TEST_GREATER_OR_EQUAL(LargePolylineTestData::m_Size, 3, ());
// This test provides information about the data set size.
TEST_EQUAL(LargePolylineTestData::m_Size, 5539, ());
}
UNIT_TEST(Simplification_DP_Smoke)
{
TestSimplificationSmoke(&SimplifyDP<DistanceFn>);
}
UNIT_TEST(Simplification_DP_Line)
{
TestSimplificationOfLine(&SimplifyDP<DistanceFn>);
}
UNIT_TEST(Simplification_DP_Polyline)
{
TestSimplificationOfPoly(LargePolylineTestData::m_Data, LargePolylineTestData::m_Size, &SimplifyDP<DistanceFn>);
}
UNIT_TEST(Simplification_Opt_Smoke)
{
TestSimplificationSmoke(&SimplifyNearOptimal10);
}
UNIT_TEST(Simplification_Opt_Line)
{
TestSimplificationOfLine(&SimplifyNearOptimal10);
}
UNIT_TEST(Simplification_Opt10_Polyline)
{
TestSimplificationOfPoly(LargePolylineTestData::m_Data, LargePolylineTestData::m_Size, &SimplifyNearOptimal10);
}
UNIT_TEST(Simplification_Opt20_Polyline)
{
TestSimplificationOfPoly(LargePolylineTestData::m_Data, LargePolylineTestData::m_Size, &SimplifyNearOptimal20);
}
UNIT_TEST(Simpfication_DP_DegenerateTrg)
{
P arr1[] = {P(0, 0), P(100, 100), P(100, 500), P(0, 600)};
CheckDPStrict(arr1, ARRAY_SIZE(arr1), 1.0, 4);
P arr2[] = {P(0, 0), P(100, 100), P(100.1, 150), P(100.2, 200), P(100.3, 250), P(100.4, 300),
P(100.3, 350), P(100.2, 400), P(100.1, 450), P(100, 500), P(0, 600)};
CheckDPStrict(arr2, ARRAY_SIZE(arr2), 1.0, 4);
}
} // namespace simplification_test

View file

@ -0,0 +1,248 @@
#include "testing/testing.hpp"
#include "geometry/geometry_tests/equality.hpp"
#include "geometry/spline.hpp"
#include <vector>
namespace spline_test
{
using namespace std;
using m2::PointD;
using m2::Spline;
namespace
{
double constexpr kAlmostZero = 1.0E-16;
void TestEqual(double x, double y)
{
if (fabs(x) < kAlmostZero || fabs(y) < kAlmostZero)
TEST_ALMOST_EQUAL_ABS(x, y, kAlmostZero, ());
else
TEST_ALMOST_EQUAL_ULPS(x, y, ());
}
void TestPointDDir(PointD const & dst, PointD const & src)
{
double const len1 = dst.Length();
double const len2 = src.Length();
if (len1 < kAlmostZero || len2 < kAlmostZero)
{
TestEqual(dst.x, src.x);
TestEqual(dst.y, src.y);
}
else
{
TestEqual(dst.x / len1, src.x / len2);
TestEqual(dst.y / len1, src.y / len2);
}
}
} // namespace
UNIT_TEST(Spline_SmoothedDirections)
{
vector<PointD> path;
path.push_back(PointD(0, 0));
path.push_back(PointD(40, 40));
path.push_back(PointD(80, 0));
Spline spl(path);
double const sqrt2 = sqrt(2.0);
Spline::iterator itr;
PointD dir1(sqrt2 / 2.0, sqrt2 / 2.0);
PointD dir2(sqrt2 / 2.0, -sqrt2 / 2.0);
itr.Attach(spl);
TestPointDDir(itr.m_avrDir, dir1);
itr.Advance(sqrt2 * 30.0);
TestPointDDir(itr.m_avrDir, dir1);
itr.Advance(sqrt2 * 40.0);
TestPointDDir(itr.m_avrDir, dir1 * 0.25 + dir2 * 0.75);
itr.Advance(sqrt2 * 10.0);
TestPointDDir(itr.m_avrDir, dir2);
path.clear();
path.push_back(PointD(0, 0));
path.push_back(PointD(40, 40));
path.push_back(PointD(80, 40));
path.push_back(PointD(120, 0));
PointD dir12(1.0, 0.0);
Spline spl2(path);
itr.Attach(spl2);
TestPointDDir(itr.m_avrDir, dir1);
itr.Advance(sqrt2 * 80.0 + 40.0);
#if defined(DEBUG) || __apple_build_version__ < 15000000
// TODO(AB): Fails on Mac's clang with any optimization enabled and -ffp-contract=fast
TestPointDDir(itr.m_avrDir, dir12);
#endif
itr.Attach(spl2);
itr.Advance(sqrt2 * 40.0);
TestPointDDir(itr.m_avrDir, dir1);
itr.Advance(80.0);
TestPointDDir(itr.m_avrDir, dir12 * 0.5 + dir2 * 0.5);
}
UNIT_TEST(UsualDirections)
{
vector<PointD> path;
path.push_back(PointD(0, 0));
path.push_back(PointD(40, 40));
path.push_back(PointD(80, 0));
Spline spl(path);
double const sqrt2 = sqrtf(2.0);
Spline::iterator itr;
PointD dir1(sqrt2 / 2.0, sqrt2 / 2.0);
PointD dir2(sqrt2 / 2.0, -sqrt2 / 2.0);
itr.Attach(spl);
TestPointDDir(itr.m_dir, dir1);
itr.Advance(sqrt2 * 30.0);
TestPointDDir(itr.m_dir, dir1);
itr.Advance(sqrt2 * 40.0);
TestPointDDir(itr.m_dir, dir2);
path.clear();
path.push_back(PointD(0, 0));
path.push_back(PointD(40, 40));
path.push_back(PointD(80, 40));
path.push_back(PointD(120, 0));
PointD dir12(1.0, 0.0);
Spline spl2(path);
itr.Attach(spl2);
TestPointDDir(itr.m_dir, dir1);
itr.Advance(sqrt2 * 80.0 + 35.0);
TestPointDDir(itr.m_dir, dir2);
itr.Attach(spl2);
itr.Advance(sqrt2 * 45.0);
TestPointDDir(itr.m_dir, dir12);
itr.Advance(80.0);
TestPointDDir(itr.m_dir, dir2);
}
UNIT_TEST(Positions)
{
vector<PointD> path;
path.push_back(PointD(0, 0));
path.push_back(PointD(40, 40));
path.push_back(PointD(80, 0));
Spline spl0(path);
Spline spl4;
spl4 = spl0;
double const sqrt2 = sqrt(2.0);
Spline::iterator itr;
itr.Attach(spl0);
TestPointDDir(itr.m_pos, PointD(0, 0));
itr.Advance(sqrt2 * 40.0);
TestPointDDir(itr.m_pos, PointD(40, 40));
itr.Advance(sqrt2 * 40.0);
#if defined(DEBUG) || __apple_build_version__ < 15000000
// TODO(AB): Fails on Mac's clang with any optimization enabled and -ffp-contract=fast
TestPointDDir(itr.m_pos, PointD(80, 0));
#endif
itr.Attach(spl4);
TestPointDDir(itr.m_pos, PointD(0, 0));
itr.Advance(sqrt2 * 40.0);
TestPointDDir(itr.m_pos, PointD(40, 40));
itr.Advance(sqrt2 * 40.0);
#if defined(DEBUG) || __apple_build_version__ < 15000000
// TODO(AB): Fails on Mac's clang with any optimization enabled and -ffp-contract=fast
TestPointDDir(itr.m_pos, PointD(80, 0));
#endif
path.clear();
path.push_back(PointD(0, 0));
path.push_back(PointD(40, 40));
path.push_back(PointD(80, 40));
path.push_back(PointD(120, 0));
Spline spl2(path);
Spline spl3 = spl2;
itr.Attach(spl3);
TestPointDDir(itr.m_pos, PointD(0, 0));
itr.Advance(sqrt2 * 80.0 + 40.0);
#if defined(DEBUG) || __apple_build_version__ < 15000000
// TODO(AB): Fails on Mac's clang with any optimization enabled and -ffp-contract=fast
TestPointDDir(itr.m_pos, PointD(120, 0));
#endif
itr.Attach(spl2);
itr.Advance(sqrt2 * 40.0);
TestPointDDir(itr.m_pos, PointD(40, 40));
itr.Advance(2.0);
TestPointDDir(itr.m_pos, PointD(42, 40));
itr.Advance(20.0);
TestPointDDir(itr.m_pos, PointD(62, 40));
itr.Advance(18.0);
TestPointDDir(itr.m_pos, PointD(80, 40));
}
UNIT_TEST(BeginAgain)
{
vector<PointD> path;
path.push_back(PointD(0, 0));
path.push_back(PointD(40, 40));
path.push_back(PointD(80, 0));
Spline spl(path);
double const sqrt2 = sqrtf(2.0);
Spline::iterator itr;
PointD dir1(sqrt2 / 2.0, sqrt2 / 2.0);
PointD dir2(sqrt2 / 2.0, -sqrt2 / 2.0);
itr.Attach(spl);
TEST_EQUAL(itr.BeginAgain(), false, ());
itr.Advance(90.0);
TEST_EQUAL(itr.BeginAgain(), false, ());
itr.Advance(90.0);
TEST_EQUAL(itr.BeginAgain(), true, ());
itr.Advance(190.0);
TEST_EQUAL(itr.BeginAgain(), true, ());
path.clear();
path.push_back(PointD(0, 0));
path.push_back(PointD(40, 40));
path.push_back(PointD(80, 40));
path.push_back(PointD(120, 0));
Spline spl2(path);
itr.Attach(spl2);
TEST_EQUAL(itr.BeginAgain(), false, ());
itr.Advance(90.0);
TEST_EQUAL(itr.BeginAgain(), false, ());
itr.Advance(90.0);
TEST_EQUAL(itr.BeginAgain(), true, ());
itr.Advance(190.0);
TEST_EQUAL(itr.BeginAgain(), true, ());
}
UNIT_TEST(Length)
{
vector<PointD> path;
PointD const p1(27.5536633, 64.2492523);
PointD const p2(27.5547638, 64.2474289);
PointD const p3(27.5549412, 64.2471237);
PointD const p4(27.5559044, 64.2456436);
PointD const p5(27.556284, 64.2451782);
path.push_back(p1);
path.push_back(p2);
path.push_back(p3);
path.push_back(p4);
path.push_back(p5);
Spline spl(path);
double len1 = spl.GetLength();
double l1 = p1.Length(p2);
double l2 = p2.Length(p3);
double l3 = p3.Length(p4);
double l4 = p4.Length(p5);
double len2 = l1 + l2 + l3 + l4;
TEST_ALMOST_EQUAL_ULPS(len1, len2, ());
}
} // namespace spline_test

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,42 @@
#include "geometry/transformations.hpp"
#include "base/matrix.hpp"
#include "geometry/point2d.hpp"
#include "testing/testing.hpp"
UNIT_TEST(Transformations_Shift)
{
math::Matrix<double, 3, 3> m = math::Shift(math::Identity<double, 3>(), 200.0, 100.0);
m2::PointD pt = m2::PointD(30, 20) * m;
TEST(pt.EqualDxDy(m2::PointD(230, 120), 1.0E-10), ());
}
UNIT_TEST(Transformations_ShiftScale)
{
math::Matrix<double, 3, 3> m = math::Scale(math::Shift(math::Identity<double, 3>(), 100, 100), 2, 3);
m2::PointD pt = m2::PointD(20, 10) * m;
TEST(pt.EqualDxDy(m2::PointD(240, 330), 1.0E-10), ());
}
UNIT_TEST(Transformations_Rotate)
{
math::Matrix<double, 3, 3> m = math::Rotate(math::Identity<double, 3>(), math::pi / 2);
TEST(m2::PointD(0, 100).EqualDxDy(m2::PointD(100, 0) * m, 1.0E-10), ());
}
UNIT_TEST(Transformations_ShiftScaleRotate)
{
math::Matrix<double, 3, 3> m =
math::Rotate(math::Scale(math::Shift(math::Identity<double, 3>(), 100, 100), 2, 3), -math::pi / 2);
m2::PointD pt = m2::PointD(20, 10) * m;
TEST(pt.EqualDxDy(m2::PointD(330, -240), 1.0E-10), ());
math::Matrix<double, 3, 3> invM = math::Inverse(m);
m2::PointD invPt = m2::PointD(330, -240) * invM;
TEST(invPt.EqualDxDy(m2::PointD(20, 10), 1.0E-10), ());
}

View file

@ -0,0 +1,183 @@
#include "testing/testing.hpp"
#include "geometry/tree4d.hpp"
#include <functional>
namespace tree_test
{
using namespace std;
using R = m2::RectD;
struct traits_t
{
m2::RectD LimitRect(m2::RectD const & r) const { return r; }
};
using Tree = m4::Tree<R, traits_t>;
template <typename T>
bool RTrue(T const &, T const &)
{
return true;
}
template <typename T>
bool RFalse(T const &, T const &)
{
return false;
}
UNIT_TEST(Tree4D_Smoke)
{
Tree theTree;
R arr[] = {R(0, 0, 1, 1), R(1, 1, 2, 2), R(2, 2, 3, 3)};
for (size_t i = 0; i < ARRAY_SIZE(arr); ++i)
theTree.ReplaceAllInRect(arr[i], &RTrue<R>);
vector<R> test;
theTree.ForEach(base::MakeBackInsertFunctor(test));
TEST_EQUAL(3, test.size(), ());
test.clear();
R const searchR(1.5, 1.5, 1.5, 1.5);
theTree.ForEachInRect(searchR, base::MakeBackInsertFunctor(test));
TEST_EQUAL(1, test.size(), ());
TEST_EQUAL(test[0], arr[1], ());
R const replaceR(0.5, 0.5, 2.5, 2.5);
theTree.ReplaceAllInRect(replaceR, &RTrue<R>);
test.clear();
theTree.ForEach(base::MakeBackInsertFunctor(test));
TEST_EQUAL(1, test.size(), ());
TEST_EQUAL(test[0], replaceR, ());
test.clear();
theTree.ForEachInRect(searchR, base::MakeBackInsertFunctor(test));
TEST_EQUAL(1, test.size(), ());
}
UNIT_TEST(Tree4D_ForAnyInRect)
{
Tree theTree;
R arr[] = {R(0, 0, 1, 1), R(0, 0, 5, 5), R(1, 1, 2, 2), R(1, 1, 6.5, 6.5), R(2, 2, 3, 3), R(2, 2, 7, 7)};
for (auto const & r : arr)
theTree.Add(r);
TEST(theTree.ForAnyInRect(R(0, 0, 100, 100), [](R const & rect) { return rect.maxX() > 4; }), ());
TEST(theTree.ForAnyInRect(R(0, 0, 100, 100), [](R const & rect) { return rect.maxX() > 5; }), ());
TEST(theTree.ForAnyInRect(R(0, 0, 100, 100), [](R const & rect) { return rect.maxX() > 0; }), ());
TEST(!theTree.ForAnyInRect(R(0, 0, 100, 100), [](R const & rect) { return rect.maxX() > 10; }), ());
}
UNIT_TEST(Tree4D_ReplaceAllInRect)
{
Tree theTree;
R arr[] = {R(8, 13, 554, 32), R(555, 13, 700, 32), R(8, 33, 554, 52), R(555, 33, 700, 52),
R(8, 54, 554, 73), R(555, 54, 700, 73), R(8, 76, 554, 95), R(555, 76, 700, 95)};
R arr1[] = {R(3, 23, 257, 42), R(600, 23, 800, 42), R(3, 43, 257, 62), R(600, 43, 800, 62),
R(3, 65, 257, 84), R(600, 65, 800, 84), R(3, 87, 257, 106), R(600, 87, 800, 106)};
for (size_t i = 0; i < ARRAY_SIZE(arr); ++i)
{
size_t const count = theTree.GetSize();
theTree.ReplaceAllInRect(arr[i], &RFalse<R>);
TEST_EQUAL(theTree.GetSize(), count + 1, ());
theTree.ReplaceAllInRect(arr1[i], &RFalse<R>);
TEST_EQUAL(theTree.GetSize(), count + 1, ());
}
vector<R> test;
theTree.ForEach(base::MakeBackInsertFunctor(test));
TEST_EQUAL(ARRAY_SIZE(arr), test.size(), ());
for (size_t i = 0; i < test.size(); ++i)
TEST_EQUAL(test[i], arr[i], ());
}
namespace
{
void CheckInRect(R const * arr, size_t count, R const & searchR, size_t expected)
{
Tree theTree;
for (size_t i = 0; i < count; ++i)
theTree.Add(arr[i], arr[i]);
vector<R> test;
theTree.ForEachInRect(searchR, base::MakeBackInsertFunctor(test));
TEST_EQUAL(test.size(), expected, ());
}
} // namespace
UNIT_TEST(Tree4D_ForEachInRect)
{
R arr[] = {R(0, 0, 1, 1), R(5, 5, 10, 10), R(-1, -1, 0, 0), R(-10, -10, -5, -5)};
CheckInRect(arr, ARRAY_SIZE(arr), R(1, 1, 5, 5), 0);
CheckInRect(arr, ARRAY_SIZE(arr), R(-5, -5, -1, -1), 0);
CheckInRect(arr, ARRAY_SIZE(arr), R(3, 3, 3, 3), 0);
CheckInRect(arr, ARRAY_SIZE(arr), R(-3, -3, -3, -3), 0);
CheckInRect(arr, ARRAY_SIZE(arr), R(0.5, 0.5, 0.5, 0.5), 1);
CheckInRect(arr, ARRAY_SIZE(arr), R(8, 8, 8, 8), 1);
CheckInRect(arr, ARRAY_SIZE(arr), R(-0.5, -0.5, -0.5, -0.5), 1);
CheckInRect(arr, ARRAY_SIZE(arr), R(-8, -8, -8, -8), 1);
CheckInRect(arr, ARRAY_SIZE(arr), R(0.5, 0.5, 5.5, 5.5), 2);
CheckInRect(arr, ARRAY_SIZE(arr), R(-5.5, -5.5, -0.5, -0.5), 2);
}
struct TestObj : public m2::RectD
{
int m_id;
TestObj(double minX, double minY, double maxX, double maxY, int id) : m2::RectD(minX, minY, maxX, maxY), m_id(id) {}
bool operator==(TestObj const & r) const { return m_id == r.m_id; }
};
UNIT_TEST(Tree4D_ReplaceEqual)
{
typedef TestObj T;
m4::Tree<T, traits_t> theTree;
T arr[] = {T(0, 0, 1, 1, 1), T(1, 1, 2, 2, 2), T(2, 2, 3, 3, 3)};
// 1.
for (size_t i = 0; i < ARRAY_SIZE(arr); ++i)
theTree.ReplaceEqualInRect(arr[i], equal_to<T>(), &RTrue<T>);
vector<T> test;
theTree.ForEach(base::MakeBackInsertFunctor(test));
TEST_EQUAL(3, test.size(), ());
// 2.
theTree.ReplaceEqualInRect(T(0, 0, 3, 3, 2), equal_to<T>(), &RFalse<T>);
test.clear();
theTree.ForEach(base::MakeBackInsertFunctor(test));
TEST_EQUAL(3, test.size(), ());
auto i = find(test.begin(), test.end(), T(1, 1, 2, 2, 2));
TEST_EQUAL(R(*i), R(1, 1, 2, 2), ());
// 3.
theTree.ReplaceEqualInRect(T(0, 0, 3, 3, 2), equal_to<T>(), &RTrue<T>);
test.clear();
theTree.ForEach(base::MakeBackInsertFunctor(test));
TEST_EQUAL(3, test.size(), ());
i = find(test.begin(), test.end(), T(1, 1, 2, 2, 2));
TEST_EQUAL(R(*i), R(0, 0, 3, 3), ());
}
} // namespace tree_test

View file

@ -0,0 +1,41 @@
#include "testing/testing.hpp"
#include "geometry/avg_vector.hpp"
namespace
{
template <class T, size_t N>
bool EqualArrays(T (&a1)[N], T (&a2)[N])
{
for (size_t i = 0; i < N; ++i)
if (!AlmostEqualULPs(a1[i], a2[i]))
return false;
return true;
}
} // namespace
UNIT_TEST(AvgVector_Smoke)
{
math::AvgVector<double, 3> holder(3);
double ethalon1[] = {5, 5, 5};
double ethalon2[] = {5.5, 5.5, 5.5};
double ethalon3[] = {6, 6, 6};
double arr1[] = {5, 5, 5};
double arr2[] = {6, 6, 6};
double arr3[] = {5, 5, 5};
double arr4[] = {6, 6, 6};
holder.Next(arr1);
TEST(EqualArrays(arr1, ethalon1), ());
holder.Next(arr2);
TEST(EqualArrays(arr2, ethalon2), ());
holder.Next(arr3);
TEST(EqualArrays(arr3, ethalon1), ());
holder.Next(arr4);
TEST(EqualArrays(arr4, ethalon3), ());
}