diff --git a/TEST_Geometry2D.cpp b/TEST_Geometry2D.cpp index 21160af..88e2dc6 100644 --- a/TEST_Geometry2D.cpp +++ b/TEST_Geometry2D.cpp @@ -84,6 +84,12 @@ class Test_Geometry2D : public olc::PixelGameEngine olc::vf2d points[2]; // origin, direction }; + struct Polygon + { + std::vector points; + Polygon(std::initializer_list list) : points(list) {} + }; + // Create desired shapes using a sequence of points static auto make_internal(const Point& p) { return p.points[0]; } static auto make_internal(const Line& p) { return line{ p.points[0], p.points[1] }; } @@ -91,9 +97,10 @@ class Test_Geometry2D : public olc::PixelGameEngine static auto make_internal(const Circle& p) { return circle{ p.points[0], (p.points[1]-p.points[0]).mag() }; } static auto make_internal(const Triangle& p) { return triangle{ p.points[0], p.points[1], p.points[2] }; } static auto make_internal(const Ray& p) { return ray{ p.points[0], (p.points[1]-p.points[0]).norm() }; } + static auto make_internal(const Polygon& p) { return olc::utils::geom2d::createPolygon(p.points); } // The clever bit (and a bit new to me - jx9) - using ShapeWrap = std::variant; + using ShapeWrap = std::variant; @@ -102,10 +109,10 @@ class Test_Geometry2D : public olc::PixelGameEngine const auto dispatch = overloads{ [](const auto& lhs, const auto& rhs) { - return overlaps(make_internal(lhs), make_internal(rhs)); + return overlaps(make_internal(lhs), make_internal(rhs)); }, - // Any combination of 'Ray' does not work because 'overlaps' is not implemented for it. + // any combination of 'ray' does not work because 'overlaps' is not implemented for it. [](const Ray&, const auto&) { return false; }, [](const auto&, const Ray&) { return false; }, [](const Ray&, const Ray&) { return false; } @@ -214,6 +221,31 @@ class Test_Geometry2D : public olc::PixelGameEngine DrawLine(t.origin, t.origin+t.direction * 1000.0f, col, 0xF0F0F0F0); } + void draw_internal(const Polygon& p, const olc::Pixel col) + { + const auto t = make_internal(p); + + for (size_t i = 0; i < t.pos.size(); i++) + { + if (i == t.pos.size() - 1) + { + DrawLine(t.pos[i], t.pos[0], col); + } + else + { + DrawLine(t.pos[i], t.pos[i + 1], col); + } + } + + if (bShowPolygonTriangles == true) + { + for (auto& triangle : t.triangles) + { + DrawTriangle(triangle.pos[0], triangle.pos[1], triangle.pos[2], col); + } + } + } + void DrawShape(const ShapeWrap& shape, const olc::Pixel col = olc::WHITE) { std::visit([&](const auto& x) @@ -226,6 +258,7 @@ class Test_Geometry2D : public olc::PixelGameEngine size_t nSelectedShapeIndex = -1; olc::vi2d vOldMousePos; + bool bShowPolygonTriangles = false; public: bool OnUserCreate() override @@ -243,6 +276,7 @@ class Test_Geometry2D : public olc::PixelGameEngine vecShapes.push_back({ Triangle{{ {50.0f, 100.0f}, {10.0f, 150.0f}, {90.0f, 150.0f}} }}); vecShapes.push_back({ Triangle{{ {350.0f, 200.0f}, {500.0f, 150.0f}, {450.0f, 400.0f}} }}); + vecShapes.push_back({ Polygon{{ {60.0f, 420.0f}, {10.0f, 370.0f}, {160.0f, 320.0f}, {210.f, 420.0f}, {160.0f, 470.0f}, {30.0f, 470.0f} }} }); return true; } @@ -251,6 +285,11 @@ class Test_Geometry2D : public olc::PixelGameEngine { Clear(olc::VERY_DARK_BLUE); + if (GetKey(olc::Key::SPACE).bPressed == true) + { + bShowPolygonTriangles = !bShowPolygonTriangles; + } + olc::vf2d vMouseDelta = GetMousePos() - vOldMousePos; vOldMousePos = GetMousePos(); @@ -260,7 +299,6 @@ class Test_Geometry2D : public olc::PixelGameEngine // Check for mouse hovered shapes ShapeWrap mouse{ Point{olc::vf2d(GetMousePos())} }; - if (nSelectedShapeIndex < vecShapes.size() && GetMouse(0).bHeld) { // Visit the selected shape and offset. @@ -391,9 +429,6 @@ class Test_Geometry2D : public olc::PixelGameEngine for (size_t i = 0; i < vecShapes.size(); i++) { - // Dont check against origin shape - if (i == last_hit_index) continue; - const auto& vTargetShape = vecShapes[i]; auto hit = CheckReflect(ray_laser, vTargetShape); if (hit.has_value()) @@ -412,6 +447,7 @@ class Test_Geometry2D : public olc::PixelGameEngine { DrawLine(ray_laser.origin, ray_reflected.origin, olc::Pixel(rand() % 155 + 100, 0, 0)); ray_laser = ray_reflected; + ray_laser.origin += ray_reflected.direction * 0.01f; ray_stop = false; last_hit_index = closest_hit_index; nBounces--; diff --git a/olcUTIL_Geometry2D.h b/olcUTIL_Geometry2D.h index 0482473..ac1d11c 100644 --- a/olcUTIL_Geometry2D.h +++ b/olcUTIL_Geometry2D.h @@ -785,6 +785,11 @@ namespace olc::utils::geom2d struct polygon { std::vector> pos; + + // triangles that make up the polygon + std::vector> triangles; + + std::vector> edges; }; @@ -891,6 +896,15 @@ namespace olc::utils::geom2d return p; } + // closest(p,p) + // Returns closest point on polygon to point + template + inline olc::v_2d closest(const polygon& p, const olc::v_2d& point) + { + // TODO: + return {}; + } + // Closest location on [SHAPE] to Line @@ -930,6 +944,15 @@ namespace olc::utils::geom2d return {}; } + // closest(p,l) + // Returns closest point on polygon to line + template + inline olc::v_2d closest(const polygon& p, const line& l) + { + // TODO: + return {}; + } + // Closest location on [SHAPE] to Circle @@ -968,6 +991,15 @@ namespace olc::utils::geom2d return {}; } + // closest(p,c) + // Returns closest point on polygon to circle + template + inline olc::v_2d closest(const polygon& p, const circle& c) + { + // TODO: + return {}; + } + // Closest location on [SHAPE] to Triangle @@ -983,7 +1015,7 @@ namespace olc::utils::geom2d // closest(r,t) // Returns closest point on rectangle to triangle template - inline olc::v_2d closest(const rect& r, const triangle& l) + inline olc::v_2d closest(const rect& r, const triangle& t) { // TODO: return {}; @@ -992,7 +1024,7 @@ namespace olc::utils::geom2d // closest(c,t) // Returns closest point on circle to triangle template - inline olc::v_2d closest(const circle& c, const triangle& l) + inline olc::v_2d closest(const circle& c, const triangle& t) { // TODO: return {}; @@ -1001,13 +1033,67 @@ namespace olc::utils::geom2d // closest(t,t) // Returns closest point on triangle to triangle template - inline olc::v_2d closest(const triangle& r, const triangle& l) + inline olc::v_2d closest(const triangle& r, const triangle& t) { // TODO: return {}; } - + // closest(p,t) + // Returns closest point on polygon to triangle + template + inline olc::v_2d closest(const polygon& p, const triangle& t) + { + // TODO: + return {}; + } + + // Closest location on [SHAPE] to Polygon + + // closest(l,p) + // Returns closest point on line to polygon + template + inline olc::v_2d closest(const line& l, const polygon& p) + { + // TODO: + return {}; + } + + // closest(r,p) + // Returns closest point on rectangle to polygon + template + inline olc::v_2d closest(const rect& r, const polygon& p) + { + // TODO: + return {}; + } + + // closest(c,p) + // Returns closest point on circle to polygon + template + inline olc::v_2d closest(const circle& c, const polygon& p) + { + // TODO: + return {}; + } + + // closest(p,p) + // Returns closest point on triangle to polygon + template + inline olc::v_2d closest(const triangle& r, const polygon& p) + { + // TODO: + return {}; + } + + // closest(p,p) + // Returns closest point on polygon to polygon + template + inline olc::v_2d closest(const polygon& r, const polygon& p) + { + // TODO: + return {}; + } @@ -1095,6 +1181,22 @@ namespace olc::utils::geom2d return distance < epsilon; } + // contains(t,p) + // Checks if triangle contains a point + template + inline constexpr bool contains(const polygon& p, const olc::v_2d& point) + { + for (auto& triangle : p.triangles) + { + if (contains(triangle, point) == true) + { + return true; + } + } + + return false; + } + // overlaps(p,p) // Check if point overlaps with point (analogous to contains()) template @@ -1135,6 +1237,14 @@ namespace olc::utils::geom2d return contains(t, p); } + // overlaps(t,p) + // Checks if polygon overlaps with point + template + inline constexpr bool overlaps(const polygon& p, const olc::v_2d& point) + { + return contains(p, point); + } + @@ -1196,6 +1306,19 @@ namespace olc::utils::geom2d } + // intersects(t,p) + // Get intersection points where polygon intersects with point + template + inline std::vector> intersects(const polygon& p, const olc::v_2d& point) + { + for(auto& t : p.triangles) + if (contains(t, p)) + return { point }; + + return {}; + + } + @@ -1250,6 +1373,22 @@ namespace olc::utils::geom2d return contains(t, l.start) && contains(t, l.end); } + // contains(p,l) + // Check if polygon contains line segment + template + inline constexpr bool contains(const polygon& p, const line& l) + { + for (auto& edge : p.edges) + { + if (overlaps(l, edge) == true) + { + return false; + } + } + + return contains(p, l.start) && contains(p, l.end); + } + // overlaps(p,l) @@ -1300,6 +1439,22 @@ namespace olc::utils::geom2d return overlaps(t, l.start) || overlaps(t.side(0), l) || overlaps(t.side(1), l) || overlaps(t.side(2), l); } + // overlaps(p,l) + // Check if polygon overlaps line segment + template + inline constexpr bool overlaps(const polygon& p, const line& l) + { + for (auto& triangle : p.triangles) + { + if (overlaps(triangle, l) == true) + { + return true; + } + } + + return false; + } + // intersects(p,l) @@ -1430,6 +1585,22 @@ namespace olc::utils::geom2d return internal::filter_duplicate_points(intersections); } + // intersects(p,l) + // Get intersection points where polygon intersects with line segment + template + inline std::vector> intersects(const polygon& p, const line& l) + { + std::vector> intersections; + + for (auto& edge : p.edges) + { + auto v = intersects(l, edge); + intersections.insert(intersections.end(), v.begin(), v.end()); + } + + return internal::filter_duplicate_points(intersections); + } + @@ -1491,6 +1662,17 @@ namespace olc::utils::geom2d && contains(t, olc::v_2d{ r.pos.x, r.pos.y + r.size.y }); } + // contains(p,r) + // Check if polygon contains rect + template + inline constexpr bool contains(const polygon& p, const rect& r) + { + return contains(p, r.pos) + && contains(p, r.pos + r.size) + && contains(p, olc::v_2d{ r.pos.x + r.size.x, r.pos.y }) + && contains(p, olc::v_2d{ r.pos.x, r.pos.y + r.size.y }); + } + // overlaps(p,r) @@ -1543,6 +1725,22 @@ namespace olc::utils::geom2d || contains(r, t.pos[0]); } + // overlaps(p,r) + // Check if polygon overlaps rectangle + template + inline constexpr bool overlaps(const polygon& p, const rect& r) + { + for (auto& triangle : p.triangles) + { + if (overlaps(triangle, r) == true) + { + return true; + } + } + + return false; + } + // intersects(p,r) @@ -1607,7 +1805,21 @@ namespace olc::utils::geom2d return internal::filter_duplicate_points(intersections); } + // intersects(p,r) + // Get intersection points where polygon intersects with rectangle + template + inline std::vector> intersects(const polygon& p, const rect& r) + { + std::vector> intersections; + for (auto& edge : p.edges) + { + auto v = intersects(r, edge); + intersections.insert(intersections.end(), v.begin(), v.end()); + } + + return internal::filter_duplicate_points(intersections); + } @@ -1667,6 +1879,22 @@ namespace olc::utils::geom2d } + // contains(p,l) + // Check if polygon contains circle + template + inline constexpr bool contains(const polygon& p, const circle& c) + { + for (auto& edge : p.edges) + { + if (overlaps(c, edge) == true) + { + return false; + } + } + + return contains(p, c.pos); + } + // overlaps(p,c) // Check if point overlaps circle @@ -1708,6 +1936,22 @@ namespace olc::utils::geom2d return contains(t, c.pos) || (c.pos - closest(t, c.pos)).mag2() <= c.radius * c.radius; } + // overlaps(p,c) + // Check if triangle overlaps circle + template + inline constexpr bool overlaps(const polygon& p, const circle& c) + { + for (auto& triangle : p.triangles) + { + if (overlaps(triangle, c) == true) + { + return true; + } + } + + return false; + } + // intersects(p,c) @@ -1795,6 +2039,22 @@ namespace olc::utils::geom2d + // intersects(p,c) + // Get intersection points where polygon intersects with circle + template + inline std::vector> intersects(const polygon& p, const circle& c) + { + std::vector> intersections; + + for (auto& edge : p.edges) + { + auto v = intersects(c, edge); + intersections.insert(intersections.end(), v.begin(), v.end()); + } + + return internal::filter_duplicate_points(intersections); + } + @@ -1854,6 +2114,16 @@ namespace olc::utils::geom2d && contains(t1, t2.pos[2]); } + // contains(p,t) + // Check if polygon contains triangle + template + inline constexpr bool contains(const polygon& p, const triangle& t2) + { + return contains(p, t2.pos[0]) + && contains(p, t2.pos[1]) + && contains(p, t2.pos[2]); + } + // overlaps(p,t) @@ -1899,6 +2169,22 @@ namespace olc::utils::geom2d || overlaps(t2, t1.pos[0]); } + // overlaps(p,t) + // Check if polygon overlaps triangle + template + inline constexpr bool overlaps(const polygon& p, const triangle& t) + { + for (auto& triangle : p.triangles) + { + if (overlaps(triangle, t) == true) + { + return true; + } + } + + return false; + } + // intersects(p,t) @@ -1949,6 +2235,23 @@ namespace olc::utils::geom2d } + + // intersects(p,t) + // Get intersection points where polygon intersects with triangle + template + inline std::vector> intersects(const polygon& p, const triangle& t) + { + std::vector> intersections; + for (auto& edge : p.edges) + { + auto v = intersects(t, edge); + intersections.insert(intersections.end(), v.begin(), v.end()); + } + + return internal::filter_duplicate_points(intersections); + } + + // envelope_c(c) // Return circle that fully encapsulates a point template @@ -2545,6 +2848,424 @@ namespace olc::utils::geom2d return internal::filter_duplicate_points(intersections); } + + // ================================================================================================================ + // POLYGON ========================================================================================================= + template + inline polygon createPolygon(std::vector> points) + { + polygon returnPolygon; + + // Less than three points so return empty polygpon + if (points.size() < 3) + { + return returnPolygon; + } + + std::vector indexList; + + float area = 0.0; + + for (size_t i = 0; i < points.size(); i++) + { + indexList.push_back(i); + returnPolygon.pos.push_back(points[i]); + size_t j = (i + 1) % points.size(); + area += points[i].x * points[j].y - points[j].x * points[i].y; + + line edge; + + if (i == points.size() - 1) + { + edge.start = points[i]; + edge.end = points[0]; + } + else + { + edge.start = points[i]; + edge.end = points[i + 1]; + } + + returnPolygon.edges.push_back(edge); + } + + if (area < 0.0f) + { + std::reverse(returnPolygon.pos.begin(), returnPolygon.pos.end()); + std::reverse(returnPolygon.edges.begin(), returnPolygon.edges.end()); + } + + uint32_t loopCounter = 1000; + + while (indexList.size() > 3 && loopCounter > 0) + { + for (size_t i = 0; i < indexList.size(); i++) + { + size_t a = indexList[i]; + size_t b = indexList[i == 0 ? indexList.size() - 1 : i - 1]; + size_t c = indexList[i == indexList.size() - 1 ? 0 : i + 1]; + + triangle thisTriangle; + thisTriangle.pos[0] = returnPolygon.pos[a]; + thisTriangle.pos[1] = returnPolygon.pos[b]; + thisTriangle.pos[2] = returnPolygon.pos[c]; + + vf2d pb_pa = returnPolygon.pos[b] - returnPolygon.pos[a]; + vf2d pc_pa = returnPolygon.pos[c] - returnPolygon.pos[a]; + + if (pb_pa.x * pc_pa.y - pb_pa.y * pc_pa.x > 0.0f) + { + continue; + } + + bool isEar = true; + + for (size_t j = 0; j < returnPolygon.pos.size(); j++) + { + if (j == a || j == b || j == c) + { + continue; + } + + triangle fTriangle(thisTriangle.pos[0], thisTriangle.pos[1], thisTriangle.pos[2]); + if (contains(fTriangle, vf2d(returnPolygon.pos[j])) == true) + { + isEar = false; + break; + } + } + + if (isEar == true) + { + returnPolygon.triangles.push_back(thisTriangle); + + indexList.erase(indexList.begin() + i); + break; + } + } + + loopCounter--; + } + + triangle lastTriangle; + lastTriangle.pos[0] = returnPolygon.pos[indexList[0]]; + lastTriangle.pos[1] = returnPolygon.pos[indexList[1]]; + lastTriangle.pos[2] = returnPolygon.pos[indexList[2]]; + + returnPolygon.triangles.push_back(lastTriangle); + + return returnPolygon; + } + + // overlaps(p,p) + // Check if point overlaps with polygon (analogous to contains()) + template + inline constexpr bool overlaps(const olc::v_2d& p1, const polygon& p2) + { + return contains(p1, p2); + } + + // overlaps(l,p) + // Checks if line segment overlaps with polygon + template + inline constexpr bool overlaps(const line& l, const polygon& p) + { + for (auto& triangle : p.triangles) + { + if (overlaps(l, triangle) == true) + { + return true; + } + } + + return false; + } + + // overlaps(r,p) + // Checks if rectangle overlaps with polygon + template + inline constexpr bool overlaps(const rect& r, const polygon& p) + { + for (auto& triangle : p.triangles) + { + if (overlaps(r, triangle) == true) + { + return true; + } + } + + return false; + } + + // overlaps(c,p) + // Checks if circle overlaps with polygon + template + inline constexpr bool overlaps(const circle& c, const polygon& p) + { + for (auto& triangle : p.triangles) + { + if (overlaps(c, triangle) == true) + { + return true; + } + } + + return false; + } + + // overlaps(t,p) + // Checks if triangle overlaps with polygon + template + inline constexpr bool overlaps(const triangle& t, const polygon& p) + { + for (auto& triangle : p.triangles) + { + if (overlaps(t, triangle) == true) + { + return true; + } + } + + return false; + } + + // overlaps(t,p) + // Checks if polygon overlaps with polygon + template + inline constexpr bool overlaps(const polygon& p1, const polygon& p2) + { + for (auto& triangle : p1.triangles) + { + if (overlaps(triangle, p2) == true) + { + return true; + } + } + + return false; + } + + // contains(p,p) + // Check if point contains polygon (analogous to contains()) + template + inline constexpr bool contains(const olc::v_2d& p1, const polygon& p2) + { + return false; + } + + // contains(l,p) + // Checks if line segment contains polygon + template + inline constexpr bool contains(const line& l, const polygon& p) + { + return false; + } + + // contains(r,p) + // Checks if rectangle contains polygon + template + inline constexpr bool contains(const rect& r, const polygon& p) + { + for (auto& triangle : p.triangles) + { + if (contains(r, triangle) == false) + { + return false; + } + } + + return true; + } + + // contains(c,p) + // Checks if circle contains polygon + template + inline constexpr bool contains(const circle& c, const polygon& p) + { + for (auto& triangle : p.triangles) + { + if (contains(c, triangle) == false) + { + return false; + } + } + + return true; + } + + // contains(t,p) + // Checks if triangle contains polygon + template + inline constexpr bool contains(const triangle& t, const polygon& p) + { + for (auto& triangle : p.triangles) + { + if (contains(t, triangle) == false) + { + return false; + } + } + + return true; + } + + // contains(p,p) + // Checks if polygon contains polygon + template + inline constexpr bool contains(const polygon& p1, const polygon& p2) + { + for (auto& triangle : p1.triangles) + { + if (contains(p2, triangle) == false) + { + return false; + } + } + + return true; + } + + // intersects(p,p) + // Get intersection points where a point intersects a polygon + template + inline std::vector> intersects(const olc::v_2d& p1, const polygon& p2) + { + std::vector> intersections; + + for (auto& triangle : p2.triangles) + { + auto v = intersects(triangle, p1); + intersections.insert(intersections.end(), v.begin(), v.end()); + } + + return internal::filter_duplicate_points(intersections); + } + + // intersects(l,p) + // Get intersection points where a line intersects a polygon + template + inline std::vector> intersects(const line l, const polygon& p) + { + std::vector> intersections; + for (auto& edge : p.edges) + { + auto v = intersects(l, edge); + intersections.insert(intersections.end(), v.begin(), v.end()); + } + + return internal::filter_duplicate_points(intersections); + } + + // intersects(r,p) + // Get intersection points where a rectangle intersects a rectangle + template + inline std::vector> intersects(const rect r, const polygon& p) + { + std::vector> intersections; + for (auto& edge : p.edges) + { + auto v = intersects(r, edge); + intersections.insert(intersections.end(), v.begin(), v.end()); + } + + return internal::filter_duplicate_points(intersections); + } + + // intersects(c,p) + // Get intersection points where a circle intersects a rectangle + template + inline std::vector> intersects(const circle c, const polygon& p) + { + std::vector> intersections; + for (auto& edge : p.edges) + { + auto v = intersects(c, edge); + intersections.insert(intersections.end(), v.begin(), v.end()); + } + + return internal::filter_duplicate_points(intersections); + } + + // intersects(t,p) + // Get intersection points where a triangle intersects a polygon + template + inline std::vector> intersects(const triangle t, const polygon& p) + { + std::vector> intersections; + for (auto& edge : p.edges) + { + auto v = intersects(t, edge); + intersections.insert(intersections.end(), v.begin(), v.end()); + } + + return internal::filter_duplicate_points(intersections); + } + + // intersects(p,p) + // Get intersection points where a polygon intersects a polygon + template + inline std::vector> intersects(const polygon p1, const polygon& p2) + { + std::vector> intersections; + for (auto& edge : p2.edges) + { + auto v = intersects(p1, edge); + intersections.insert(intersections.end(), v.begin(), v.end()); + } + + return internal::filter_duplicate_points(intersections); + } + + + + // intersects(r,p) + // Get intersection points where a ray intersects a polygon + template + inline std::vector> intersects(const ray& r, const polygon& p) + { + std::vector> intersections; + for (auto& edge : p.edges) + { + auto v = intersects(r, edge); + intersections.insert(intersections.end(), v.begin(), v.end()); + } + + return internal::filter_duplicate_points(intersections); + } + + + + // reflect(r,p) + // optionally returns a ray reflected off a circle if collision occurs + template + inline std::optional> reflect(const ray& r, const polygon& p) + { + line l2; + T1 length = std::numeric_limits().max(); + std::optional> returnValue = std::nullopt; + + for (size_t i = 0; i < p.pos.size(); i++) + { + if (i == p.pos.size() - 1) + { + l2 = { p.pos[i], p.pos[0] }; + } + else + { + l2 = { p.pos[i], p.pos[i + 1] }; + } + + auto v = reflect(r, l2); + + if (v.has_value() && (v.value().origin - r.origin).mag() < length) + { + length = (v.value().origin - r.origin).mag(); + returnValue = v; + } + } + + return returnValue; + } } #endif // PGE_VER