Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
49
libs/geometry/geometry_tests/CMakeLists.txt
Normal file
49
libs/geometry/geometry_tests/CMakeLists.txt
Normal 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)
|
||||
118
libs/geometry/geometry_tests/algorithm_test.cpp
Normal file
118
libs/geometry/geometry_tests/algorithm_test.cpp
Normal 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
|
||||
109
libs/geometry/geometry_tests/angle_test.cpp
Normal file
109
libs/geometry/geometry_tests/angle_test.cpp
Normal 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),
|
||||
());
|
||||
}
|
||||
93
libs/geometry/geometry_tests/anyrect_test.cpp
Normal file
93
libs/geometry/geometry_tests/anyrect_test.cpp
Normal 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
|
||||
28
libs/geometry/geometry_tests/area_on_earth_tests.cpp
Normal file
28
libs/geometry/geometry_tests/area_on_earth_tests.cpp
Normal 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, ());
|
||||
}
|
||||
33
libs/geometry/geometry_tests/bounding_box_tests.cpp
Normal file
33
libs/geometry/geometry_tests/bounding_box_tests.cpp
Normal 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
|
||||
73
libs/geometry/geometry_tests/calipers_box_tests.cpp
Normal file
73
libs/geometry/geometry_tests/calipers_box_tests.cpp
Normal 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
|
||||
240
libs/geometry/geometry_tests/cellid_test.cpp
Normal file
240
libs/geometry/geometry_tests/cellid_test.cpp
Normal 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
|
||||
73
libs/geometry/geometry_tests/circle_on_earth_tests.cpp
Normal file
73
libs/geometry/geometry_tests/circle_on_earth_tests.cpp
Normal 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 */);
|
||||
}
|
||||
278
libs/geometry/geometry_tests/clipping_test.cpp
Normal file
278
libs/geometry/geometry_tests/clipping_test.cpp
Normal 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
|
||||
39
libs/geometry/geometry_tests/common_test.cpp
Normal file
39
libs/geometry/geometry_tests/common_test.cpp
Normal 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)), ());
|
||||
}
|
||||
46
libs/geometry/geometry_tests/convex_hull_tests.cpp
Normal file
46
libs/geometry/geometry_tests/convex_hull_tests.cpp
Normal 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
|
||||
136
libs/geometry/geometry_tests/covering_test.cpp
Normal file
136
libs/geometry/geometry_tests/covering_test.cpp
Normal 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
|
||||
50
libs/geometry/geometry_tests/diamond_box_tests.cpp
Normal file
50
libs/geometry/geometry_tests/diamond_box_tests.cpp
Normal 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
|
||||
24
libs/geometry/geometry_tests/distance_on_sphere_test.cpp
Normal file
24
libs/geometry/geometry_tests/distance_on_sphere_test.cpp
Normal 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, ());
|
||||
}
|
||||
49
libs/geometry/geometry_tests/equality.hpp
Normal file
49
libs/geometry/geometry_tests/equality.hpp
Normal 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
|
||||
177
libs/geometry/geometry_tests/intersect_test.cpp
Normal file
177
libs/geometry/geometry_tests/intersect_test.cpp
Normal 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);
|
||||
}
|
||||
93
libs/geometry/geometry_tests/intersection_score_tests.cpp
Normal file
93
libs/geometry/geometry_tests/intersection_score_tests.cpp
Normal 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), ());
|
||||
}
|
||||
}
|
||||
5549
libs/geometry/geometry_tests/large_polygon.hpp
Normal file
5549
libs/geometry/geometry_tests/large_polygon.hpp
Normal file
File diff suppressed because it is too large
Load diff
13
libs/geometry/geometry_tests/latlon_test.cpp
Normal file
13
libs/geometry/geometry_tests/latlon_test.cpp
Normal 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, ());
|
||||
}
|
||||
48
libs/geometry/geometry_tests/line2d_tests.cpp
Normal file
48
libs/geometry/geometry_tests/line2d_tests.cpp
Normal 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
|
||||
81
libs/geometry/geometry_tests/mercator_test.cpp
Normal file
81
libs/geometry/geometry_tests/mercator_test.cpp
Normal 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)));
|
||||
}
|
||||
146
libs/geometry/geometry_tests/nearby_points_sweeper_test.cpp
Normal file
146
libs/geometry/geometry_tests/nearby_points_sweeper_test.cpp
Normal 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
|
||||
71
libs/geometry/geometry_tests/oblate_spheroid_tests.cpp
Normal file
71
libs/geometry/geometry_tests/oblate_spheroid_tests.cpp
Normal 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);
|
||||
}
|
||||
77
libs/geometry/geometry_tests/packer_test.cpp
Normal file
77
libs/geometry/geometry_tests/packer_test.cpp
Normal 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)), ());
|
||||
}
|
||||
69
libs/geometry/geometry_tests/parametrized_segment_tests.cpp
Normal file
69
libs/geometry/geometry_tests/parametrized_segment_tests.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
100
libs/geometry/geometry_tests/point3d_tests.cpp
Normal file
100
libs/geometry/geometry_tests/point3d_tests.cpp
Normal 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
|
||||
88
libs/geometry/geometry_tests/point_test.cpp
Normal file
88
libs/geometry/geometry_tests/point_test.cpp
Normal 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.)),
|
||||
());
|
||||
}
|
||||
200
libs/geometry/geometry_tests/polygon_test.cpp
Normal file
200
libs/geometry/geometry_tests/polygon_test.cpp
Normal 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
|
||||
58
libs/geometry/geometry_tests/polyline_tests.cpp
Normal file
58
libs/geometry/geometry_tests/polyline_tests.cpp
Normal 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
|
||||
21
libs/geometry/geometry_tests/rect_test.cpp
Normal file
21
libs/geometry/geometry_tests/rect_test.cpp
Normal 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), ());
|
||||
}
|
||||
240
libs/geometry/geometry_tests/region2d_binary_op_test.cpp
Normal file
240
libs/geometry/geometry_tests/region2d_binary_op_test.cpp
Normal 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
|
||||
367
libs/geometry/geometry_tests/region_tests.cpp
Normal file
367
libs/geometry/geometry_tests/region_tests.cpp
Normal 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
|
||||
179
libs/geometry/geometry_tests/robust_test.cpp
Normal file
179
libs/geometry/geometry_tests/robust_test.cpp
Normal 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
|
||||
215
libs/geometry/geometry_tests/screen_test.cpp
Normal file
215
libs/geometry/geometry_tests/screen_test.cpp
Normal 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
|
||||
99
libs/geometry/geometry_tests/segment2d_tests.cpp
Normal file
99
libs/geometry/geometry_tests/segment2d_tests.cpp
Normal 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
|
||||
150
libs/geometry/geometry_tests/simplification_test.cpp
Normal file
150
libs/geometry/geometry_tests/simplification_test.cpp
Normal 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
|
||||
248
libs/geometry/geometry_tests/spline_test.cpp
Normal file
248
libs/geometry/geometry_tests/spline_test.cpp
Normal 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
|
||||
2060
libs/geometry/geometry_tests/test_regions.hpp
Normal file
2060
libs/geometry/geometry_tests/test_regions.hpp
Normal file
File diff suppressed because it is too large
Load diff
42
libs/geometry/geometry_tests/transformations_test.cpp
Normal file
42
libs/geometry/geometry_tests/transformations_test.cpp
Normal 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), ());
|
||||
}
|
||||
183
libs/geometry/geometry_tests/tree_test.cpp
Normal file
183
libs/geometry/geometry_tests/tree_test.cpp
Normal 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
|
||||
41
libs/geometry/geometry_tests/vector_test.cpp
Normal file
41
libs/geometry/geometry_tests/vector_test.cpp
Normal 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), ());
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue