#include #include "vde/core/polygon.h" using namespace vde::core; // ── signed_area tests ── TEST(PolygonTest, SignedArea_CCWSquare_Positive) { // Counter-clockwise unit square Polygon2D poly({{0, 0}, {1, 0}, {1, 1}, {0, 1}}); EXPECT_GT(poly.signed_area(), 0.0); EXPECT_NEAR(poly.signed_area(), 1.0, 1e-9); } TEST(PolygonTest, SignedArea_CWSquare_Negative) { // Clockwise unit square Polygon2D poly({{0, 0}, {0, 1}, {1, 1}, {1, 0}}); EXPECT_LT(poly.signed_area(), 0.0); EXPECT_NEAR(poly.signed_area(), -1.0, 1e-9); } TEST(PolygonTest, SignedArea_Triangle) { Polygon2D poly({{0, 0}, {3, 0}, {0, 4}}); EXPECT_NEAR(std::abs(poly.signed_area()), 6.0, 1e-9); } TEST(PolygonTest, SignedArea_Collinear_Degenerate) { // All points on a line → zero area Polygon2D poly({{0, 0}, {1, 1}, {2, 2}}); EXPECT_NEAR(poly.signed_area(), 0.0, 1e-9); } TEST(PolygonTest, SignedArea_Empty) { Polygon2D poly; EXPECT_DOUBLE_EQ(poly.signed_area(), 0.0); } TEST(PolygonTest, Area_AbsoluteValue) { // area() should be absolute value of signed_area() Polygon2D ccw({{0, 0}, {2, 0}, {2, 2}, {0, 2}}); Polygon2D cw({{0, 0}, {0, 2}, {2, 2}, {2, 0}}); EXPECT_NEAR(ccw.area(), 4.0, 1e-9); EXPECT_NEAR(cw.area(), 4.0, 1e-9); } // ── contains (point-in-polygon via ray casting) ── TEST(PolygonTest, Contains_InteriorPoint) { Polygon2D square({{0, 0}, {2, 0}, {2, 2}, {0, 2}}); // Center of the square EXPECT_TRUE(square.contains(Point2D(1.0, 1.0))); } TEST(PolygonTest, Contains_ExteriorPoint) { Polygon2D square({{0, 0}, {2, 0}, {2, 2}, {0, 2}}); // Far outside EXPECT_FALSE(square.contains(Point2D(10.0, 10.0))); // Outside but near EXPECT_FALSE(square.contains(Point2D(-0.1, 1.0))); } TEST(PolygonTest, Contains_BoundaryPoint) { Polygon2D square({{0, 0}, {2, 0}, {2, 2}, {0, 2}}); // On edge — behavior may vary; ray casting treats as intersection // Not strictly "interior" in most implementations Point2D on_edge(1.0, 0.0); // Just verify it doesn't crash; boundary is valid input bool result = square.contains(on_edge); (void)result; // implementation-defined for boundary SUCCEED(); } TEST(PolygonTest, Contains_PointOnVertex) { Polygon2D tri({{0, 0}, {2, 0}, {0, 2}}); // Exact vertex — should not crash bool result = tri.contains(Point2D(0.0, 0.0)); (void)result; SUCCEED(); } TEST(PolygonTest, Contains_ConcavePolygon) { // L-shaped polygon (concave) Polygon2D L({{0, 0}, {3, 0}, {3, 1}, {2, 1}, {2, 3}, {0, 3}}); // Point in the "notch" area (1.5, 2) should be inside EXPECT_TRUE(L.contains(Point2D(1.0, 2.0))); // Point in the concave void EXPECT_FALSE(L.contains(Point2D(2.5, 2.0))); } // ── is_ccw ── TEST(PolygonTest, IsCCW) { Polygon2D ccw({{0, 0}, {1, 0}, {1, 1}, {0, 1}}); Polygon2D cw({{0, 0}, {0, 1}, {1, 1}, {1, 0}}); EXPECT_TRUE(ccw.is_ccw()); EXPECT_FALSE(cw.is_ccw()); } // ── Basic accessors ── TEST(PolygonTest, SizeAndEmpty) { Polygon2D empty; EXPECT_TRUE(empty.empty()); EXPECT_EQ(empty.size(), 0u); Polygon2D tri({{0, 0}, {1, 0}, {0, 1}}); EXPECT_FALSE(tri.empty()); EXPECT_EQ(tri.size(), 3u); } TEST(PolygonTest, VerticesAccessor) { std::vector v = {{0, 0}, {1, 0}, {0, 1}}; Polygon2D poly(v); EXPECT_EQ(poly.vertices().size(), 3u); EXPECT_DOUBLE_EQ(poly.vertices()[0].x(), 0.0); EXPECT_DOUBLE_EQ(poly.vertices()[1].y(), 0.0); } TEST(PolygonTest, DefaultConstructor_Empty) { Polygon2D poly; EXPECT_TRUE(poly.vertices().empty()); EXPECT_TRUE(poly.empty()); } // ── Larger polygon area (regular hexagon) ── TEST(PolygonTest, SignedArea_RegularHexagon) { // Regular hexagon centered at origin, radius 1 // Vertices CCW: (1,0), (0.5,0.866), (-0.5,0.866), (-1,0), (-0.5,-0.866), (0.5,-0.866) Polygon2D hex({ {1.0, 0.0}, {0.5, std::sqrt(3.0) / 2.0}, {-0.5, std::sqrt(3.0) / 2.0}, {-1.0, 0.0}, {-0.5, -std::sqrt(3.0) / 2.0}, {0.5, -std::sqrt(3.0) / 2.0} }); // Area of regular hexagon = (3√3 / 2) * r² ≈ 2.598 EXPECT_NEAR(hex.signed_area(), 3.0 * std::sqrt(3.0) / 2.0, 1e-9); } TEST(PolygonTest, Contains_StarShapedHexagon) { Polygon2D hex({ {1.0, 0.0}, {0.5, 0.866}, {-0.5, 0.866}, {-1.0, 0.0}, {-0.5, -0.866}, {0.5, -0.866} }); EXPECT_TRUE(hex.contains(Point2D(0.0, 0.0))); EXPECT_FALSE(hex.contains(Point2D(2.0, 0.0))); }