From f92c378423f81b4058c19f8fc3d7e631ceb8304d Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 15 Dec 2024 14:17:27 +0000 Subject: vector difference works with floating point Makes it a generic wrapper --- lib/maths.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/maths.h b/lib/maths.h index 3959896..b28fe01 100644 --- a/lib/maths.h +++ b/lib/maths.h @@ -79,8 +79,11 @@ operator-(const GlobalPosition & global, const CalcPosition & relative) return global - GlobalPosition(relative); } -template -constexpr RelativePosition +template +using DifferenceVector = glm::vec, T, float>, Q>; + +template +constexpr DifferenceVector difference(const glm::vec & globalA, const glm::vec & globalB) { return globalA - globalB; -- cgit v1.2.3 From 39985f36e807126b2c7b9ec6096bda3922903a54 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 15 Dec 2024 14:38:00 +0000 Subject: 2D triangle area support --- lib/triangle.h | 7 +++++++ test/test-geoData.cpp | 17 ----------------- test/test-maths.cpp | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 17 deletions(-) (limited to 'lib') diff --git a/lib/triangle.h b/lib/triangle.h index 812bfab..5787094 100644 --- a/lib/triangle.h +++ b/lib/triangle.h @@ -31,6 +31,13 @@ struct Triangle : public glm::vec<3, glm::vec> { return glm::length(crossProduct(sideDifference(1), sideDifference(2))) / T {2}; } + [[nodiscard]] constexpr auto + area() const + requires(Dim == 2) + { + return std::abs((sideDifference(1).x * sideDifference(2).y) - (sideDifference(2).x * sideDifference(1).y)) / 2; + } + [[nodiscard]] constexpr Normal3D normal() const requires(Dim == 3) diff --git a/test/test-geoData.cpp b/test/test-geoData.cpp index 5411b61..1ca050d 100644 --- a/test/test-geoData.cpp +++ b/test/test-geoData.cpp @@ -190,23 +190,6 @@ BOOST_DATA_TEST_CASE(walkTerrainUntil, BOOST_CHECK_EQUAL_COLLECTIONS(visited.begin(), visited.end(), visits.begin(), visits.end()); } -BOOST_AUTO_TEST_CASE(triangle_helpers) -{ - constexpr static GeoData::Triangle<3> t {{0, 0, 0}, {5, 0, 0}, {5, 5, 0}}; - - BOOST_CHECK_EQUAL(t.nnormal(), up); - BOOST_CHECK_CLOSE(t.angle(0), quarter_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angleAt({0, 0, 0}), quarter_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angle(1), half_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angleAt({5, 0, 0}), half_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angle(2), quarter_pi, 0.01F); - BOOST_CHECK_CLOSE(t.angleAt({5, 5, 0}), quarter_pi, 0.01F); - - BOOST_CHECK_CLOSE(t.angleAt({0, 1, 0}), 0.F, 0.01F); - - BOOST_CHECK_CLOSE(t.area(), 12.5F, 0.01F); -} - using FindEntiesData = std::tuple; BOOST_DATA_TEST_CASE(findEntries, diff --git a/test/test-maths.cpp b/test/test-maths.cpp index b9d08bb..1278c44 100644 --- a/test/test-maths.cpp +++ b/test/test-maths.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include using vecter_and_angle = std::tuple; @@ -341,3 +342,36 @@ static_assert(linesIntersectAt(GlobalPosition2D {311000100, 491100100}, {3110500 .value() == GlobalPosition2D {311000100, 491100100}); static_assert(!linesIntersectAt(glm::dvec2 {0, 1}, {0, 4}, {1, 8}, {1, 4}).has_value()); + +BOOST_AUTO_TEST_CASE(triangle2d_helpers) +{ + constexpr static Triangle<2, float> t {{0, 0}, {5, 0}, {5, 5}}; + + BOOST_CHECK_CLOSE(t.angle(0), quarter_pi, 0.01F); + BOOST_CHECK_CLOSE(t.angleAt({0, 0}), quarter_pi, 0.01F); + BOOST_CHECK_CLOSE(t.angle(1), half_pi, 0.01F); + BOOST_CHECK_CLOSE(t.angleAt({5, 0}), half_pi, 0.01F); + BOOST_CHECK_CLOSE(t.angle(2), quarter_pi, 0.01F); + BOOST_CHECK_CLOSE(t.angleAt({5, 5}), quarter_pi, 0.01F); + + BOOST_CHECK_CLOSE(t.angleAt({0, 1}), 0.F, 0.01F); + + BOOST_CHECK_CLOSE(t.area(), 12.5F, 0.01F); +} + +BOOST_AUTO_TEST_CASE(triangle3d_helpers) +{ + constexpr static Triangle<3, float> t {{0, 0, 0}, {5, 0, 0}, {5, 5, 0}}; + + BOOST_CHECK_EQUAL(t.nnormal(), up); + BOOST_CHECK_CLOSE(t.angle(0), quarter_pi, 0.01F); + BOOST_CHECK_CLOSE(t.angleAt({0, 0, 0}), quarter_pi, 0.01F); + BOOST_CHECK_CLOSE(t.angle(1), half_pi, 0.01F); + BOOST_CHECK_CLOSE(t.angleAt({5, 0, 0}), half_pi, 0.01F); + BOOST_CHECK_CLOSE(t.angle(2), quarter_pi, 0.01F); + BOOST_CHECK_CLOSE(t.angleAt({5, 5, 0}), quarter_pi, 0.01F); + + BOOST_CHECK_CLOSE(t.angleAt({0, 1, 0}), 0.F, 0.01F); + + BOOST_CHECK_CLOSE(t.area(), 12.5F, 0.01F); +} -- cgit v1.2.3 From ea393a85a572e94f4c76e79cbd16897f9eae55f5 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 15 Dec 2024 14:53:05 +0000 Subject: Triangle height support Point C from the line AB --- lib/triangle.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib') diff --git a/lib/triangle.h b/lib/triangle.h index 5787094..888481d 100644 --- a/lib/triangle.h +++ b/lib/triangle.h @@ -45,6 +45,12 @@ struct Triangle : public glm::vec<3, glm::vec> { return crossProduct(sideDifference(1), sideDifference(2)); } + [[nodiscard]] constexpr auto + height() + { + return (area() * 2) / glm::length(difference(p(0), p(1))); + } + [[nodiscard]] constexpr Normal3D nnormal() const requires(Dim == 3) -- cgit v1.2.3 From 5af54c1584a1ce7797dfcb9e75dfbb26bdfc6338 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 18 Dec 2024 14:50:21 +0000 Subject: 2D vector_normal to work on any arithmetic --- lib/maths.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/maths.h b/lib/maths.h index b28fe01..4223f14 100644 --- a/lib/maths.h +++ b/lib/maths.h @@ -223,7 +223,7 @@ vector_pitch(const glm::vec & diff) return std::atan(diff.z); } -template +template constexpr glm::vec<2, T, Q> vector_normal(const glm::vec<2, T, Q> & vector) { -- cgit v1.2.3 From 15754d6b61b0f00fa142ff55d87e8be09d2041c8 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 18 Dec 2024 17:12:56 +0000 Subject: Midpoint for integral vectors --- lib/maths.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'lib') diff --git a/lib/maths.h b/lib/maths.h index 4223f14..7f2d7b4 100644 --- a/lib/maths.h +++ b/lib/maths.h @@ -427,6 +427,13 @@ midpoint(const std::pair & v) return std::midpoint(v.first, v.second); } +template +auto +midpoint(const glm::vec & valueA, const glm::vec & valueB) +{ + return valueA + (valueB - valueA) / 2; +} + // std::pow is not constexpr template requires requires(T n) { n *= n; } -- cgit v1.2.3 From a007ffeed3cfa6af2cbe1053c330ad11927d58de Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 18 Dec 2024 17:24:02 +0000 Subject: Add sanity checking logic to GeoData --- game/geoData.cpp | 10 ++++++++++ game/geoData.h | 2 ++ lib/triangle.h | 8 ++++++++ test/test-geoData.cpp | 10 +++------- 4 files changed, 23 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/game/geoData.cpp b/game/geoData.cpp index 8e662b3..eef5dc7 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -601,3 +601,13 @@ GeoData::setHeights(const std::span triangleStrip, const updateAllVertexNormals(newOrChangedVerts); } + +void +GeoData::sanityCheck() const +{ + if (!std::ranges::all_of(faces(), [this](const auto face) { + return triangle<2>(face).isUp(); + })) { + throw std::logic_error("Upside down faces detected"); + } +} diff --git a/game/geoData.h b/game/geoData.h index 79924d3..01582a6 100644 --- a/game/geoData.h +++ b/game/geoData.h @@ -98,6 +98,8 @@ public: return property(surface, h); } + void sanityCheck() const; + protected: template [[nodiscard]] Triangle diff --git a/lib/triangle.h b/lib/triangle.h index 888481d..d5547ab 100644 --- a/lib/triangle.h +++ b/lib/triangle.h @@ -83,6 +83,14 @@ struct Triangle : public glm::vec<3, glm::vec> { return 0.F; } + [[nodiscard]] constexpr auto + isUp() const + { + const auto edgeAB = sideDifference(1); + const auto edgeAC = sideDifference(2); + return edgeAB.x * edgeAC.y >= edgeAB.y * edgeAC.x; + } + [[nodiscard]] constexpr auto p(const glm::length_t idx) const { diff --git a/test/test-geoData.cpp b/test/test-geoData.cpp index 5998789..9ec4656 100644 --- a/test/test-geoData.cpp +++ b/test/test-geoData.cpp @@ -29,11 +29,9 @@ BOOST_AUTO_TEST_CASE(loadSuccess) BOOST_CHECK_EQUAL(upper, GlobalPosition3D(319950000, 499950000, 571600)); } -BOOST_AUTO_TEST_CASE(normalsAllPointUp) +BOOST_AUTO_TEST_CASE(sanityCheck) { - BOOST_CHECK(std::ranges::all_of(vertices(), [this](auto && vertex) { - return normal(vertex).z > 0; - })); + BOOST_CHECK_NO_THROW(sanityCheck()); } BOOST_AUTO_TEST_CASE(trianglesContainsPoints) @@ -214,9 +212,7 @@ BOOST_DATA_TEST_CASE(deform, loadFixtureJson("geoData/deform/ surface.colorBias = RGB {0, 0, 1}; auto gd = std::make_shared(GeoData::createFlat({0, 0}, {1000000, 1000000}, 100)); BOOST_CHECK_NO_THROW(gd->setHeights(points, {.surface = surface})); - BOOST_CHECK(std::ranges::all_of(gd->vertices(), [&gd](auto && vertex) { - return gd->normal(vertex).z > 0; - })); + BOOST_CHECK_NO_THROW(gd->sanityCheck()); ApplicationBase ab; TestMainWindow tmw; -- cgit v1.2.3 From 06e64d7ccf053dbb11547edf3ba986d096b50045 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 31 Dec 2024 12:52:10 +0000 Subject: Add ArcSegment Extends Arc, with method for determining intersection point with line segment --- lib/maths.h | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/stream_support.h | 11 +++++++++ test/test-maths.cpp | 31 ++++++++++++++++++++++++ 3 files changed, 110 insertions(+) (limited to 'lib') diff --git a/lib/maths.h b/lib/maths.h index 7f2d7b4..f43321a 100644 --- a/lib/maths.h +++ b/lib/maths.h @@ -1,6 +1,8 @@ #pragma once #include "config/types.h" +#include +#include #include #include #include @@ -36,6 +38,26 @@ struct Arc : public std::pair { } }; +template struct ArcSegment : public Arc { + using PointType = glm::vec<2, T, Q>; + + constexpr ArcSegment(PointType centre, PointType ep0, PointType ep1); + + PointType centre; + PointType ep0; + PointType ep1; + RelativeDistance radius; + + [[nodiscard]] constexpr std::optional> crossesLineAt( + const glm::vec<2, T, Q> & lineStart, const glm::vec<2, T, Q> & lineEnd) const; + + [[nodiscard]] constexpr bool + angleWithinArc(Angle angle) const + { + return first <= angle && angle <= second; + } +}; + constexpr const RelativePosition3D up {0, 0, 1}; // NOLINT(readability-identifier-length) constexpr const RelativePosition3D down {0, 0, -1}; constexpr const RelativePosition3D north {0, 1, 0}; @@ -471,3 +493,49 @@ operator"" _degrees(long double degrees) { return static_cast(degrees) * degreesToRads; } + +// Late implementations due to dependencies +template +constexpr ArcSegment::ArcSegment(PointType centre, PointType ep0, PointType ep1) : + Arc {centre, ep0, ep1}, centre {centre}, ep0 {ep0}, ep1 {ep1}, radius {glm::length(difference(centre, ep0))} +{ +} + +template +[[nodiscard]] constexpr std::optional> +ArcSegment::crossesLineAt(const glm::vec<2, T, Q> & lineStart, const glm::vec<2, T, Q> & lineEnd) const +{ + // Based on formulas from https://mathworld.wolfram.com/Circle-LineIntersection.html + const auto lineDiff = difference(lineEnd, lineStart); + const auto lineLen = glm::length(lineDiff); + const auto lineRelStart = difference(lineStart, centre); + const auto lineRelEnd = difference(lineEnd, centre); + const auto determinant = (lineRelStart.x * lineRelEnd.y) - (lineRelEnd.x * lineRelStart.y); + const auto discriminant = (radius * radius * lineLen * lineLen) - (determinant * determinant); + if (discriminant < 0) { + return std::nullopt; + } + + const auto rootDiscriminant = std::sqrt(discriminant); + const auto drdr = lineLen * lineLen; + const RelativeDistance sgn = (lineDiff.y < 0 ? -1 : 1); + std::array points { + RelativePosition2D {((determinant * lineDiff.y) + sgn * lineDiff.x * rootDiscriminant), + ((-determinant * lineDiff.x) + std::abs(lineDiff.y) * rootDiscriminant)} + / drdr, + RelativePosition2D {((determinant * lineDiff.y) - sgn * lineDiff.x * rootDiscriminant), + ((-determinant * lineDiff.x) - std::abs(lineDiff.y) * rootDiscriminant)} + / drdr, + }; + const auto end + = std::remove_if(points.begin(), points.end(), [this, lineRelStart, lineDiff, drdr](const auto point) { + const auto dot = glm::dot(lineDiff, point - lineRelStart); + return !angleWithinArc(vector_yaw(point)) || dot < 0 || dot > drdr; + }); + if (points.begin() == end) { + return std::nullopt; + } + return centre + *std::ranges::min_element(points.begin(), end, {}, [lineRelStart](const auto point) { + return glm::distance(lineRelStart, point); + }); +} diff --git a/lib/stream_support.h b/lib/stream_support.h index f21622a..f5c5e37 100644 --- a/lib/stream_support.h +++ b/lib/stream_support.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +83,16 @@ namespace std { { return s << EnumTypeDetails::typeName << "::" << EnumDetails::to_string(e).value(); } + + template + inline std::ostream & + operator<<(std::ostream & s, const std::optional & v) + { + if (v) { + return s << *v; + } + return s << "nullopt"; + } } template diff --git a/test/test-maths.cpp b/test/test-maths.cpp index 1278c44..aa2b9c8 100644 --- a/test/test-maths.cpp +++ b/test/test-maths.cpp @@ -375,3 +375,34 @@ BOOST_AUTO_TEST_CASE(triangle3d_helpers) BOOST_CHECK_CLOSE(t.area(), 12.5F, 0.01F); } + +using ArcLineIntersectData = std::tuple>; + +BOOST_DATA_TEST_CASE(arcline_intersection, + boost::unit_test::data::make({ + {{0, 0}, {0, 100}, {100, 0}, {200, 0}, {0, 200}, std::nullopt}, + {{0, 0}, {0, 100}, {100, 0}, {0, 0}, {10, 10}, std::nullopt}, + {{0, 0}, {0, 100}, {100, 0}, {0, 0}, {100, 100}, GlobalPosition2D {71, 71}}, + {{15, 27}, {15, 127}, {115, 27}, {15, 27}, {115, 127}, GlobalPosition2D {86, 98}}, + {{0, 0}, {0, 100}, {100, 0}, {0, 0}, {-100, -100}, std::nullopt}, + {{0, 0}, {0, 100}, {100, 0}, {-10, 125}, {125, -10}, GlobalPosition2D {16, 99}}, + {{0, 0}, {0, 100}, {100, 0}, {125, -10}, {-10, 125}, GlobalPosition2D {99, 16}}, + {{0, 0}, {0, 100}, {100, 0}, {10, 125}, {125, -10}, GlobalPosition2D {38, 93}}, + {{0, 0}, {0, 100}, {100, 0}, {12, 80}, {125, -10}, GlobalPosition2D {99, 10}}, + {{0, 0}, {0, 100}, {100, 0}, {40, 80}, {125, -10}, GlobalPosition2D {98, 18}}, + {{0, 0}, {0, 100}, {100, 0}, {40, 80}, {80, 20}, std::nullopt}, + {{0, 0}, {0, 100}, {100, 0}, {40, 80}, {80, 80}, GlobalPosition2D {60, 80}}, + {{0, 0}, {0, 100}, {100, 0}, {80, 40}, {80, 80}, GlobalPosition2D {80, 60}}, + {{310002000, 490203000}, {310202000, 490203000}, {310002000, 490003000}, {310200000, 490150000}, + {310150000, 490150000}, GlobalPosition2D {310194850, 490150000}}, + }), + centre, arcStart, arcEnd, lineStart, lineEnd, intersection) +{ + const ArcSegment arc {centre, arcStart, arcEnd}; + BOOST_TEST_INFO(arc.first); + BOOST_TEST_INFO(arc.second); + BOOST_TEST_INFO(arc.length()); + + BOOST_CHECK_EQUAL(arc.crossesLineAt(lineStart, lineEnd), intersection); +} -- cgit v1.2.3 From c0d05b1ad0f2d82f9ce94437370648e7dfdd994e Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 1 Jan 2025 16:00:35 +0000 Subject: Return angle of intersection of arc with line --- game/geoData.cpp | 2 +- lib/maths.h | 30 +++++++++++++++--------------- test/test-maths.cpp | 32 +++++++++++++++++++------------- 3 files changed, 35 insertions(+), 29 deletions(-) (limited to 'lib') diff --git a/game/geoData.cpp b/game/geoData.cpp index 37abc4c..45e6590 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -324,7 +324,7 @@ GeoData::walkUntil(const PointFace & from, GlobalPosition2D to, GlobalPosition2D const auto e2 = point(to_vertex_handle(opposite_halfedge_handle(next))); if (const auto intersect = arc.crossesLineAt(e1, e2)) { step.exitHalfedge = next; - step.exitPosition = intersect.value(); + step.exitPosition = intersect.value().first; break; } } diff --git a/lib/maths.h b/lib/maths.h index f43321a..671313d 100644 --- a/lib/maths.h +++ b/lib/maths.h @@ -48,7 +48,7 @@ template struct ArcSegment : publi PointType ep1; RelativeDistance radius; - [[nodiscard]] constexpr std::optional> crossesLineAt( + [[nodiscard]] constexpr std::optional, Angle>> crossesLineAt( const glm::vec<2, T, Q> & lineStart, const glm::vec<2, T, Q> & lineEnd) const; [[nodiscard]] constexpr bool @@ -502,7 +502,7 @@ constexpr ArcSegment::ArcSegment(PointType centre, PointType ep0, PointTyp } template -[[nodiscard]] constexpr std::optional> +[[nodiscard]] constexpr std::optional, Angle>> ArcSegment::crossesLineAt(const glm::vec<2, T, Q> & lineStart, const glm::vec<2, T, Q> & lineEnd) const { // Based on formulas from https://mathworld.wolfram.com/Circle-LineIntersection.html @@ -519,23 +519,23 @@ ArcSegment::crossesLineAt(const glm::vec<2, T, Q> & lineStart, const glm:: const auto rootDiscriminant = std::sqrt(discriminant); const auto drdr = lineLen * lineLen; const RelativeDistance sgn = (lineDiff.y < 0 ? -1 : 1); - std::array points { - RelativePosition2D {((determinant * lineDiff.y) + sgn * lineDiff.x * rootDiscriminant), - ((-determinant * lineDiff.x) + std::abs(lineDiff.y) * rootDiscriminant)} - / drdr, - RelativePosition2D {((determinant * lineDiff.y) - sgn * lineDiff.x * rootDiscriminant), - ((-determinant * lineDiff.x) - std::abs(lineDiff.y) * rootDiscriminant)} - / drdr, - }; + std::array, 2> points; + std::ranges::transform(std::initializer_list {1, -1}, points.begin(), [&](RelativeDistance N) { + const auto point = RelativePosition2D {((determinant * lineDiff.y) + sgn * lineDiff.x * rootDiscriminant * N), + ((-determinant * lineDiff.x) + std::abs(lineDiff.y) * rootDiscriminant * N)} + / drdr; + return std::make_pair(point, vector_yaw(point)); + }); const auto end = std::remove_if(points.begin(), points.end(), [this, lineRelStart, lineDiff, drdr](const auto point) { - const auto dot = glm::dot(lineDiff, point - lineRelStart); - return !angleWithinArc(vector_yaw(point)) || dot < 0 || dot > drdr; - }); + const auto dot = glm::dot(lineDiff, point.first - lineRelStart); + return !angleWithinArc(point.second) || dot < 0 || dot > drdr; + }); if (points.begin() == end) { return std::nullopt; } - return centre + *std::ranges::min_element(points.begin(), end, {}, [lineRelStart](const auto point) { - return glm::distance(lineRelStart, point); + const auto first = *std::ranges::min_element(points.begin(), end, {}, [lineRelStart](const auto point) { + return glm::distance(lineRelStart, point.first); }); + return std::make_pair(centre + first.first, first.second); } diff --git a/test/test-maths.cpp b/test/test-maths.cpp index aa2b9c8..5906757 100644 --- a/test/test-maths.cpp +++ b/test/test-maths.cpp @@ -376,33 +376,39 @@ BOOST_AUTO_TEST_CASE(triangle3d_helpers) BOOST_CHECK_CLOSE(t.area(), 12.5F, 0.01F); } +using ArcLineIntersectExp = std::pair; using ArcLineIntersectData = std::tuple>; + GlobalPosition2D, std::optional>; BOOST_DATA_TEST_CASE(arcline_intersection, boost::unit_test::data::make({ {{0, 0}, {0, 100}, {100, 0}, {200, 0}, {0, 200}, std::nullopt}, {{0, 0}, {0, 100}, {100, 0}, {0, 0}, {10, 10}, std::nullopt}, - {{0, 0}, {0, 100}, {100, 0}, {0, 0}, {100, 100}, GlobalPosition2D {71, 71}}, - {{15, 27}, {15, 127}, {115, 27}, {15, 27}, {115, 127}, GlobalPosition2D {86, 98}}, + {{0, 0}, {0, 100}, {100, 0}, {0, 0}, {100, 100}, ArcLineIntersectExp {{71, 71}, quarter_pi}}, + {{15, 27}, {15, 127}, {115, 27}, {15, 27}, {115, 127}, ArcLineIntersectExp {{86, 98}, quarter_pi}}, {{0, 0}, {0, 100}, {100, 0}, {0, 0}, {-100, -100}, std::nullopt}, - {{0, 0}, {0, 100}, {100, 0}, {-10, 125}, {125, -10}, GlobalPosition2D {16, 99}}, - {{0, 0}, {0, 100}, {100, 0}, {125, -10}, {-10, 125}, GlobalPosition2D {99, 16}}, - {{0, 0}, {0, 100}, {100, 0}, {10, 125}, {125, -10}, GlobalPosition2D {38, 93}}, - {{0, 0}, {0, 100}, {100, 0}, {12, 80}, {125, -10}, GlobalPosition2D {99, 10}}, - {{0, 0}, {0, 100}, {100, 0}, {40, 80}, {125, -10}, GlobalPosition2D {98, 18}}, + {{0, 0}, {0, 100}, {100, 0}, {-10, 125}, {125, -10}, ArcLineIntersectExp {{16, 99}, 0.164F}}, + {{0, 0}, {0, 100}, {100, 0}, {125, -10}, {-10, 125}, ArcLineIntersectExp {{99, 16}, 1.407F}}, + {{0, 0}, {0, 100}, {100, 0}, {10, 125}, {125, -10}, ArcLineIntersectExp {{38, 93}, 0.385F}}, + {{0, 0}, {0, 100}, {100, 0}, {12, 80}, {125, -10}, ArcLineIntersectExp {{99, 10}, 1.467F}}, + {{0, 0}, {0, 100}, {100, 0}, {40, 80}, {125, -10}, ArcLineIntersectExp {{98, 18}, 1.387F}}, {{0, 0}, {0, 100}, {100, 0}, {40, 80}, {80, 20}, std::nullopt}, - {{0, 0}, {0, 100}, {100, 0}, {40, 80}, {80, 80}, GlobalPosition2D {60, 80}}, - {{0, 0}, {0, 100}, {100, 0}, {80, 40}, {80, 80}, GlobalPosition2D {80, 60}}, + {{0, 0}, {0, 100}, {100, 0}, {40, 80}, {80, 80}, ArcLineIntersectExp {{60, 80}, 0.6435F}}, + {{0, 0}, {0, 100}, {100, 0}, {80, 40}, {80, 80}, ArcLineIntersectExp {{80, 60}, 0.9273F}}, {{310002000, 490203000}, {310202000, 490203000}, {310002000, 490003000}, {310200000, 490150000}, - {310150000, 490150000}, GlobalPosition2D {310194850, 490150000}}, + {310150000, 490150000}, ArcLineIntersectExp {{310194850, 490150000}, 1.839F}}, }), - centre, arcStart, arcEnd, lineStart, lineEnd, intersection) + centre, arcStart, arcEnd, lineStart, lineEnd, expected) { const ArcSegment arc {centre, arcStart, arcEnd}; BOOST_TEST_INFO(arc.first); BOOST_TEST_INFO(arc.second); BOOST_TEST_INFO(arc.length()); - BOOST_CHECK_EQUAL(arc.crossesLineAt(lineStart, lineEnd), intersection); + const auto intersection = arc.crossesLineAt(lineStart, lineEnd); + BOOST_REQUIRE_EQUAL(expected.has_value(), intersection.has_value()); + if (expected.has_value()) { + BOOST_CHECK_EQUAL(expected->first, intersection->first); + BOOST_CHECK_CLOSE(expected->second, intersection->second, 1.F); + } } -- cgit v1.2.3 From bc0bbe935942fa6c54f2e9e71f49351c85d7e956 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 5 Jan 2025 18:15:04 +0000 Subject: Add helper for merging close elements in a vector --- lib/collections.h | 38 ++++++++++++++++++++++++++++++++++++++ test/test-lib.cpp | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/collections.h b/lib/collections.h index ea5d5dc..6f26eae 100644 --- a/lib/collections.h +++ b/lib/collections.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -224,3 +225,40 @@ strip_end(IterableCollection auto & cont) { return stripiter {cont.end()}; } + +template +void +mergeClose(std::vector & range, const Dist & dist, const Merger & merger, + decltype(dist(range.front(), range.front())) tolerance) +{ + using DistanceType = decltype(tolerance); + std::vector distances; + distances.reserve(range.size() - 1); + std::ranges::transform(range | std::views::pairwise, std::back_inserter(distances), [&dist](const auto & pair) { + return (std::apply(dist, pair)); + }); + while (distances.size() > 1) { + const auto closestPair = std::ranges::min_element(distances); + if (*closestPair > tolerance) { + return; + } + const auto offset = std::distance(distances.begin(), closestPair); + const auto idx = static_cast(offset); + if (closestPair == distances.begin()) { + // Remove second element + range.erase(range.begin() + 1); + distances.erase(distances.begin()); + } + else if (closestPair == --distances.end()) { + // Remove second from last element + range.erase(range.end() - 2); + distances.erase(distances.end() - 1); + } + else { + range[idx] = merger(range[idx], range[idx + 1]); + range.erase(range.begin() + offset + 1); + distances.erase(distances.begin() + offset); + } + distances[idx] = dist(range[idx], range[idx + 1]); + } +} diff --git a/test/test-lib.cpp b/test/test-lib.cpp index 5f0b5e5..7672acc 100644 --- a/test/test-lib.cpp +++ b/test/test-lib.cpp @@ -1,8 +1,8 @@ #define BOOST_TEST_MODULE test_lib -#include "testHelpers.h" #include #include +#include #include #include @@ -69,3 +69,35 @@ BOOST_AUTO_TEST_CASE(triangle_strip_iter) BOOST_CHECK_EQUAL_COLLECTIONS( out.begin(), out.end(), TRIANGLE_STRIP_EXPECTED.begin(), TRIANGLE_STRIP_EXPECTED.end()); } + +using MergeCloseData = std::tuple, int, std::vector>; + +BOOST_DATA_TEST_CASE(mergeCloseInts, + boost::unit_test::data::make({ + {{0}, 0, {0}}, + {{0, 1}, 0, {0, 1}}, + {{0, 1}, 2, {0, 1}}, + {{0, 1, 2}, 2, {0, 2}}, + {{0, 1, 4}, 2, {0, 4}}, + {{0, 1, 2}, 4, {0, 2}}, + {{0, 4, 8}, 4, {0, 8}}, + {{0, 4, 10, 14}, 4, {0, 14}}, + {{0, 3, 6}, 2, {0, 3, 6}}, + {{0, 3, 4}, 2, {0, 4}}, + {{0, 5, 7, 12}, 4, {0, 6, 12}}, + {{0, 3, 4, 5, 10, 17, 18, 19}, 2, {0, 4, 10, 19}}, + }), + collection, tolerance, expected) +{ + auto mutableCollection {collection}; + BOOST_REQUIRE_NO_THROW((mergeClose( + mutableCollection, + [](int left, int right) { + return std::abs(left - right); + }, + [](int left, int right) { + return (left + right) / 2; + }, + tolerance))); + BOOST_CHECK_EQUAL_COLLECTIONS(mutableCollection.begin(), mutableCollection.end(), expected.begin(), expected.end()); +} -- cgit v1.2.3 From 6d16749c5c4950ca4480bc0908a602f38e21cea0 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 5 Jan 2025 19:18:40 +0000 Subject: Add helper for sorting sorted containers by a projection --- lib/sorting.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'lib') diff --git a/lib/sorting.h b/lib/sorting.h index 777de00..be5a7e2 100644 --- a/lib/sorting.h +++ b/lib/sorting.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -30,6 +31,14 @@ template struct PtrMemberSorter : public PtrSorter { } }; +template struct SortedBy { + auto + operator()(const auto & left, const auto & right) const + { + return (std::invoke(Proj, left) < std::invoke(Proj, right)); + } +}; + struct CompareBy { glm::length_t index; -- cgit v1.2.3 From 8f10ddbfffd0f7f60b6b9488a0d3b0c60d8d5131 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 6 Jan 2025 20:28:16 +0000 Subject: Add default Q = glm::defaultp to maths functions Makes getting a pointer to the function more trivial --- lib/maths.h | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) (limited to 'lib') diff --git a/lib/maths.h b/lib/maths.h index 671313d..17ca795 100644 --- a/lib/maths.h +++ b/lib/maths.h @@ -15,7 +15,7 @@ template concept Arithmetic = std::is_arithmetic_v; struct Arc : public std::pair { - template + template requires(Lc >= 2, Le >= 2) Arc(const glm::vec & centre, const glm::vec & e0p, const glm::vec & e1p) : Arc {RelativePosition2D {e0p.xy() - centre.xy()}, RelativePosition2D {e1p.xy() - centre.xy()}} @@ -101,10 +101,10 @@ operator-(const GlobalPosition & global, const CalcPosition & relative) return global - GlobalPosition(relative); } -template +template using DifferenceVector = glm::vec, T, float>, Q>; -template +template constexpr DifferenceVector difference(const glm::vec & globalA, const glm::vec & globalB) { @@ -147,7 +147,8 @@ namespace { } // Helper to lookup into a matrix given an xy vector coordinate - template + template constexpr auto & operator^(glm::mat & matrix, const glm::vec<2, I> rowCol) { @@ -155,7 +156,7 @@ namespace { } // Create a matrix for the angle, given the targets into the matrix - template + template constexpr auto rotation(const T angle, const glm::vec<2, I> cos1, const glm::vec<2, I> sin1, const glm::vec<2, I> cos2, const glm::vec<2, I> negSin1) @@ -245,7 +246,7 @@ vector_pitch(const glm::vec & diff) return std::atan(diff.z); } -template +template constexpr glm::vec<2, T, Q> vector_normal(const glm::vec<2, T, Q> & vector) { @@ -267,7 +268,7 @@ sq(T value) return value * value; } -template +template constexpr glm::vec<3, int64_t, Q> crossProduct(const glm::vec<3, int64_t, Q> & valueA, const glm::vec<3, int64_t, Q> & valueB) { @@ -278,14 +279,14 @@ crossProduct(const glm::vec<3, int64_t, Q> & valueA, const glm::vec<3, int64_t, }; } -template +template constexpr glm::vec<3, T, Q> crossProduct(const glm::vec<3, T, Q> & valueA, const glm::vec<3, T, Q> & valueB) { return crossProduct(valueA, valueB); } -template +template constexpr glm::vec<3, T, Q> crossProduct(const glm::vec<3, T, Q> & valueA, const glm::vec<3, T, Q> & valueB) { @@ -300,35 +301,35 @@ ratio(const Ta valueA, const Tb valueB) return static_cast((static_cast(valueA) / static_cast(valueB))); } -template +template constexpr auto ratio(const glm::vec<2, T, Q> & value) { return ratio(value.x, value.y); } -template +template constexpr auto perspective_divide(const glm::vec<4, T, Q> & value) { return value / value.w; } -template +template constexpr glm::vec operator||(const glm::vec valueA, const glm::vec valueB) { return {valueA, valueB}; } -template +template constexpr glm::vec operator||(const glm::vec valueA, const T valueB) { return {valueA, valueB}; } -template +template constexpr glm::vec perspectiveMultiply(const glm::vec & base, const glm::mat & mutation) { @@ -336,7 +337,7 @@ perspectiveMultiply(const glm::vec & base, const glm::mat +template constexpr glm::vec perspectiveApply(glm::vec & base, const glm::mat & mutation) { @@ -384,7 +385,7 @@ linesIntersectAt(const glm::vec<2, T, Q> Aabs, const glm::vec<2, T, Q> Babs, con return Aabs + CVec {(b1 * c2) / -determinant, (a1 * c2) / determinant}; } -template +template std::pair, bool> find_arc_centre(glm::vec<2, T, Q> start, Rotation2D startDir, glm::vec<2, T, Q> end, Rotation2D endDir) { @@ -397,7 +398,7 @@ find_arc_centre(glm::vec<2, T, Q> start, Rotation2D startDir, glm::vec<2, T, Q> throw std::runtime_error("no intersection"); } -template +template std::pair, bool> find_arc_centre(glm::vec<2, T, Q> start, Angle entrys, glm::vec<2, T, Q> end, Angle entrye) { @@ -407,7 +408,7 @@ find_arc_centre(glm::vec<2, T, Q> start, Angle entrys, glm::vec<2, T, Q> end, An return find_arc_centre(start, sincos(entrys + half_pi), end, sincos(entrye - half_pi)); } -template +template Angle find_arcs_radius(glm::vec<2, T, Q> start, Rotation2D ad, glm::vec<2, T, Q> end, Rotation2D bd) { @@ -432,7 +433,7 @@ find_arcs_radius(glm::vec<2, T, Q> start, Rotation2D ad, glm::vec<2, T, Q> end, / (2 * (sq(X) - 2 * X * Z + sq(Z) + sq(Y) - 2 * Y * W + sq(W) - 4)); } -template +template std::pair find_arcs_radius(glm::vec<2, T, Q> start, Angle entrys, glm::vec<2, T, Q> end, Angle entrye) { @@ -449,7 +450,7 @@ midpoint(const std::pair & v) return std::midpoint(v.first, v.second); } -template +template auto midpoint(const glm::vec & valueA, const glm::vec & valueB) { -- cgit v1.2.3 From 1aba027462a861f2c1155672792dbbe555d7dc0a Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 13 Jan 2025 01:45:43 +0000 Subject: Add distance helper Works with integer positions, first template param allows forcing to N dimensions --- game/geoData.cpp | 7 ++++--- game/geoData.h | 2 +- game/network/network.cpp | 4 ++-- game/network/network.impl.h | 4 ++-- game/network/rail.cpp | 6 +++--- lib/maths.h | 9 ++++++++- lib/ray.h | 3 +-- lib/triangle.h | 2 +- test/test-network.cpp | 6 +++--- 9 files changed, 25 insertions(+), 18 deletions(-) (limited to 'lib') diff --git a/game/geoData.cpp b/game/geoData.cpp index 1a4cd3b..448ff67 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -405,10 +405,11 @@ GeoData::difference(const HalfedgeHandle heh) const return ::difference(point(to_vertex_handle(heh)), point(from_vertex_handle(heh))); } +template [[nodiscard]] RelativeDistance GeoData::length(const HalfedgeHandle heh) const { - return glm::length(difference(heh)); + return ::distance(point(to_vertex_handle(heh)), point(from_vertex_handle(heh))); } [[nodiscard]] GlobalPosition3D @@ -468,7 +469,7 @@ GeoData::setHeights(const std::span triangleStrip, const const auto vertexDistFrom = [this](GlobalPosition2D p) { return [p, this](const VertexHandle v) { - return std::make_pair(v, glm::length(::difference(p, this->point(v).xy()))); + return std::make_pair(v, ::distance(p, this->point(v).xy())); }; }; const auto vertexDistFromE = [this](GlobalPosition2D p) { @@ -614,7 +615,7 @@ GeoData::setHeights(const std::span triangleStrip, const todoOutHalfEdges(toVertex); } else if (!toTriangle) { // point without the new strip, adjust vertically by limit - const auto maxOffset = static_cast(opts.maxSlope * glm::length(difference(heh).xy())); + const auto maxOffset = static_cast(opts.maxSlope * length<2>(heh)); const auto newHeight = std::clamp(toPoint.z, fromPoint.z - maxOffset, fromPoint.z + maxOffset); if (newHeight != toPoint.z) { toPoint.z = newHeight; diff --git a/game/geoData.h b/game/geoData.h index 7e4c28f..390a443 100644 --- a/game/geoData.h +++ b/game/geoData.h @@ -136,7 +136,7 @@ protected: [[nodiscard]] HalfedgeHandle findBoundaryStart() const; [[nodiscard]] RelativePosition3D difference(const HalfedgeHandle) const; - [[nodiscard]] RelativeDistance length(const HalfedgeHandle) const; + template [[nodiscard]] RelativeDistance length(const HalfedgeHandle) const; [[nodiscard]] GlobalPosition3D centre(const HalfedgeHandle) const; void updateAllVertexNormals(); diff --git a/game/network/network.cpp b/game/network/network.cpp index 1666c4d..e67942f 100644 --- a/game/network/network.cpp +++ b/game/network/network.cpp @@ -121,8 +121,8 @@ Network::genCurveDef(const GlobalPosition3D & start, const GlobalPosition3D & en endDir += pi; const auto flatStart {start.xy()}, flatEnd {end.xy()}; auto midheight = [&](auto mid) { - const auto sm = glm::length(RelativePosition2D(flatStart - mid)), - em = glm::length(RelativePosition2D(flatEnd - mid)); + const auto sm = ::distance<2>(flatStart, mid); + const auto em = ::distance<2>(flatEnd, mid); return start.z + GlobalDistance(RelativeDistance(end.z - start.z) * (sm / (sm + em))); }; if (const auto radii = find_arcs_radius(flatStart, startDir, flatEnd, endDir); radii.first < radii.second) { diff --git a/game/network/network.impl.h b/game/network/network.impl.h index ff29088..33b0a86 100644 --- a/game/network/network.impl.h +++ b/game/network/network.impl.h @@ -52,7 +52,7 @@ template Link::CCollection NetworkOf::candidateJoins(GlobalPosition3D start, GlobalPosition3D end) { - if (glm::length(RelativePosition3D(start - end)) < 2000.F) { + if (::distance(start, end) < 2000.F) { return {}; } const auto defs = genCurveDef( @@ -81,7 +81,7 @@ template Link::CCollection NetworkOf::addJoins(GlobalPosition3D start, GlobalPosition3D end) { - if (glm::length(RelativePosition3D(start - end)) < 2000.F) { + if (::distance(start, end) < 2000.F) { return {}; } const auto defs = genCurveDef(start, end, findNodeDirection(nodeAt(start)), findNodeDirection(nodeAt(end))); diff --git a/game/network/rail.cpp b/game/network/rail.cpp index 2820cca..d7de231 100644 --- a/game/network/rail.cpp +++ b/game/network/rail.cpp @@ -40,8 +40,8 @@ RailLinks::addLinksBetween(GlobalPosition3D start, GlobalPosition3D end) const auto flatStart {start.xy()}, flatEnd {end.xy()}; if (node2ins.second == NodeIs::InNetwork) { auto midheight = [&](auto mid) { - const auto sm = glm::length(RelativePosition2D(flatStart - mid)), - em = glm::length(RelativePosition2D(flatEnd - mid)); + const auto sm = ::distance<2>(flatStart, mid); + const auto em = ::distance<2>(flatEnd, mid); return start.z + GlobalDistance(RelativeDistance(end.z - start.z) * (sm / (sm + em))); }; const float dir2 = pi + findNodeDirection(node2ins.first); @@ -117,7 +117,7 @@ RailLinkStraight::RailLinkStraight( RailLinkCurve::RailLinkCurve( NetworkLinkHolder & instances, const Node::Ptr & a, const Node::Ptr & b, GlobalPosition2D c) : - RailLinkCurve(instances, a, b, c || a->pos.z, glm::length(difference(a->pos.xy(), c)), {c, a->pos, b->pos}) + RailLinkCurve(instances, a, b, c || a->pos.z, ::distance<2>(a->pos.xy(), c), {c, a->pos, b->pos}) { } diff --git a/lib/maths.h b/lib/maths.h index 17ca795..3d4f440 100644 --- a/lib/maths.h +++ b/lib/maths.h @@ -111,6 +111,13 @@ difference(const glm::vec & globalA, const glm::vec & globalB) return globalA - globalB; } +template +constexpr auto +distance(const glm::vec & pointA, const glm::vec & pointB) +{ + return glm::length(difference(pointA, pointB)); +} + glm::mat4 flat_orientation(const Rotation3D & diff); namespace { @@ -498,7 +505,7 @@ operator"" _degrees(long double degrees) // Late implementations due to dependencies template constexpr ArcSegment::ArcSegment(PointType centre, PointType ep0, PointType ep1) : - Arc {centre, ep0, ep1}, centre {centre}, ep0 {ep0}, ep1 {ep1}, radius {glm::length(difference(centre, ep0))} + Arc {centre, ep0, ep1}, centre {centre}, ep0 {ep0}, ep1 {ep1}, radius {::distance(centre, ep0)} { } diff --git a/lib/ray.h b/lib/ray.h index a831270..793e21e 100644 --- a/lib/ray.h +++ b/lib/ray.h @@ -27,8 +27,7 @@ public: const auto n2 = crossProduct(direction, n); const auto c1 = p1 + PositionType((glm::dot(RelativePosition3D(start - p1), n2) / glm::dot(d1, n2)) * d1); const auto difflength = glm::length(diff); - if (glm::length(RelativePosition3D(c1 - p1)) > difflength - || glm::length(RelativePosition3D(c1 - e1)) > difflength) { + if (::distance(c1, p1) > difflength || ::distance(c1, e1) > difflength) { return std::numeric_limits::infinity(); } return static_cast(glm::abs(glm::dot(n, RelativePosition3D(p1 - start)))); diff --git a/lib/triangle.h b/lib/triangle.h index d5547ab..e430653 100644 --- a/lib/triangle.h +++ b/lib/triangle.h @@ -48,7 +48,7 @@ struct Triangle : public glm::vec<3, glm::vec> { [[nodiscard]] constexpr auto height() { - return (area() * 2) / glm::length(difference(p(0), p(1))); + return (area() * 2) / ::distance(p(0), p(1)); } [[nodiscard]] constexpr Normal3D diff --git a/test/test-network.cpp b/test/test-network.cpp index 5373dd5..e7419b5 100644 --- a/test/test-network.cpp +++ b/test/test-network.cpp @@ -241,7 +241,7 @@ BOOST_FIXTURE_TEST_CASE(test_rail_network, RailLinks) // -------- auto l0 = addLinksBetween(p000, p100); BOOST_CHECK(dynamic_cast(l0.get())); - BOOST_CHECK_EQUAL(l0->length, glm::length(difference(p000, p100))); + BOOST_CHECK_EQUAL(l0->length, ::distance(p000, p100)); BOOST_CHECK_CLOSE(l0->ends[0].dir, half_pi, 0.1F); BOOST_CHECK_CLOSE(l0->ends[1].dir, -half_pi, 0.1F); BOOST_CHECK(l0->ends[0].nexts.empty()); @@ -249,7 +249,7 @@ BOOST_FIXTURE_TEST_CASE(test_rail_network, RailLinks) auto l1 = addLinksBetween(p200, p100); BOOST_CHECK(dynamic_cast(l1.get())); - BOOST_CHECK_EQUAL(l1->length, glm::length(difference(p200, p100))); + BOOST_CHECK_EQUAL(l1->length, ::distance(p200, p100)); BOOST_CHECK_CLOSE(l1->ends[0].dir, half_pi, 0.1F); BOOST_CHECK_CLOSE(l1->ends[1].dir, -half_pi, 0.1F); BOOST_CHECK(l0->ends[0].nexts.empty()); @@ -261,7 +261,7 @@ BOOST_FIXTURE_TEST_CASE(test_rail_network, RailLinks) auto l2 = addLinksBetween(p200, p300); BOOST_CHECK(dynamic_cast(l2.get())); - BOOST_CHECK_EQUAL(l2->length, glm::length(difference(p200, p300))); + BOOST_CHECK_EQUAL(l2->length, ::distance(p200, p300)); BOOST_CHECK_CLOSE(l2->ends[0].dir, half_pi, 0.1F); BOOST_CHECK_CLOSE(l2->ends[1].dir, -half_pi, 0.1F); BOOST_CHECK(l0->ends[0].nexts.empty()); -- cgit v1.2.3 From 62fd9391bbfde47177fb36434d9664e47f4cf656 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 9 Feb 2025 13:02:09 +0000 Subject: Initial commit setting terrain during network construction This is all in the wrong place, it shouldn't be part of the network interface. --- game/network/network.h | 17 +++++++------ game/network/network.impl.h | 61 ++++++++++++++++++++++++++++++++++++++------- game/terrain.cpp | 2 ++ lib/collections.h | 9 +++++++ ui/builders/freeExtend.cpp | 14 ++++++----- ui/builders/freeExtend.h | 4 +-- ui/builders/join.cpp | 8 +++--- ui/builders/join.h | 2 +- ui/builders/straight.cpp | 6 ++--- ui/builders/straight.h | 2 +- 10 files changed, 91 insertions(+), 34 deletions(-) (limited to 'lib') diff --git a/game/network/network.h b/game/network/network.h index f8739b8..291c4ec 100644 --- a/game/network/network.h +++ b/game/network/network.h @@ -14,7 +14,8 @@ #include class SceneShader; -class Surface; +struct Surface; +class GeoData; template class Ray; template using GenDef = std::tuple...>; @@ -42,9 +43,9 @@ public: virtual Link::CCollection candidateStraight(GlobalPosition3D, GlobalPosition3D) = 0; virtual Link::CCollection candidateJoins(GlobalPosition3D, GlobalPosition3D) = 0; virtual Link::CCollection candidateExtend(GlobalPosition3D, GlobalPosition3D) = 0; - virtual Link::CCollection addStraight(GlobalPosition3D, GlobalPosition3D) = 0; - virtual Link::CCollection addJoins(GlobalPosition3D, GlobalPosition3D) = 0; - virtual Link::CCollection addExtend(GlobalPosition3D, GlobalPosition3D) = 0; + virtual Link::CCollection addStraight(const GeoData *, GlobalPosition3D, GlobalPosition3D) = 0; + virtual Link::CCollection addJoins(const GeoData *, GlobalPosition3D, GlobalPosition3D) = 0; + virtual Link::CCollection addExtend(const GeoData *, GlobalPosition3D, GlobalPosition3D) = 0; [[nodiscard]] virtual float findNodeDirection(Node::AnyCPtr) const = 0; @@ -106,12 +107,12 @@ public: Link::CCollection candidateStraight(GlobalPosition3D n1, GlobalPosition3D n2) override; Link::CCollection candidateJoins(GlobalPosition3D, GlobalPosition3D) override; Link::CCollection candidateExtend(GlobalPosition3D, GlobalPosition3D) override; - Link::CCollection addStraight(GlobalPosition3D n1, GlobalPosition3D n2) override; - Link::CCollection addJoins(GlobalPosition3D, GlobalPosition3D) override; - Link::CCollection addExtend(GlobalPosition3D, GlobalPosition3D) override; + Link::CCollection addStraight(const GeoData *, GlobalPosition3D n1, GlobalPosition3D n2) override; + Link::CCollection addJoins(const GeoData *, GlobalPosition3D, GlobalPosition3D) override; + Link::CCollection addExtend(const GeoData *, GlobalPosition3D, GlobalPosition3D) override; [[nodiscard]] float findNodeDirection(Node::AnyCPtr) const override; protected: - Link::CCollection addJoins(); + Link::CCollection addCurve(const GeoData *, const GenCurveDef &); }; diff --git a/game/network/network.impl.h b/game/network/network.impl.h index 33b0a86..c683378 100644 --- a/game/network/network.impl.h +++ b/game/network/network.impl.h @@ -1,4 +1,6 @@ +#include "collections.h" #include "network.h" +#include #include #include @@ -72,28 +74,69 @@ NetworkOf::candidateExtend(GlobalPosition3D start, GlobalPosition3D template Link::CCollection -NetworkOf::addStraight(GlobalPosition3D n1, GlobalPosition3D n2) +NetworkOf::addStraight(const GeoData * geoData, GlobalPosition3D n1, GlobalPosition3D n2) { - return {addLink(n1, n2)}; + Link::CCollection out; + geoData->walk(n1.xy(), n2, [geoData, &out, this, &n1](const GeoData::WalkStep & step) { + if (step.previous.is_valid() && geoData->getSurface(step.current) != geoData->getSurface(step.previous)) { + const auto surfaceEdgePosition = geoData->positionAt(GeoData::PointFace(step.exitPosition, step.current)); + out.emplace_back(addLink(n1, surfaceEdgePosition)); + n1 = surfaceEdgePosition; + } + }); + out.emplace_back(addLink(n1, n2)); + return out; +} + +template +Link::CCollection +NetworkOf::addCurve(const GeoData * geoData, const GenCurveDef & curve) +{ + auto [cstart, cend, centre] = curve; + Link::CCollection out; + std::set> breaks; + const auto radiusMid = ::distance(cstart.xy(), centre); + for (const auto radiusOffset : {-getBaseWidth() / 2.F, 0.F, getBaseWidth() / 2.F}) { + const auto radius = radiusOffset + radiusMid; + const auto start = centre + (difference(cstart.xy(), centre) * radius) / radiusMid; + const auto end = centre + (difference(cend.xy(), centre) * radius) / radiusMid; + geoData->walk(start, end, centre, [geoData, &breaks](const GeoData::WalkStepCurve & step) { + if (step.previous.is_valid() && geoData->getSurface(step.current) != geoData->getSurface(step.previous)) { + breaks.insert(step); + } + }); + } + std::vector points; + points.reserve(breaks.size() + 2); + points.push_back(cstart); + std::ranges::transform( + breaks, std::back_inserter(points), [geoData, centre, radiusMid](const GeoData::WalkStepCurve & step) { + return (centre + (sincos(step.angle) * radiusMid)) + || geoData->positionAt(GeoData::PointFace(step.exitPosition, step.current)).z; + }); + points.push_back(cend); + mergeClose(points, ::distance<3, GlobalDistance>, ::midpoint<3, GlobalDistance>, 2'000.F); + std::ranges::transform(points | std::views::pairwise, std::back_inserter(out), [this, centre](const auto pair) { + const auto [a, b] = pair; + return addLink(a, b, centre); + }); + return out; } template Link::CCollection -NetworkOf::addJoins(GlobalPosition3D start, GlobalPosition3D end) +NetworkOf::addJoins(const GeoData * geoData, GlobalPosition3D start, GlobalPosition3D end) { if (::distance(start, end) < 2000.F) { return {}; } const auto defs = genCurveDef(start, end, findNodeDirection(nodeAt(start)), findNodeDirection(nodeAt(end))); - const auto & [c1s, c1e, c1c] = defs.first; - const auto & [c2s, c2e, c2c] = defs.second; - return {addLink(c1s, c1e, c1c), addLink(c2s, c2e, c2c)}; + return addCurve(geoData, defs.first) + addCurve(geoData, defs.second); } template Link::CCollection -NetworkOf::addExtend(GlobalPosition3D start, GlobalPosition3D end) +NetworkOf::addExtend(const GeoData * geoData, GlobalPosition3D start, GlobalPosition3D end) { - const auto [cstart, cend, centre] = genCurveDef(start, end, findNodeDirection(nodeAt(start))); - return {addLink(cstart, cend, centre)}; + return addCurve(geoData, genCurveDef(start, end, findNodeDirection(nodeAt(start)))); } diff --git a/game/terrain.cpp b/game/terrain.cpp index 39aa99a..c834379 100644 --- a/game/terrain.cpp +++ b/game/terrain.cpp @@ -77,7 +77,9 @@ Terrain::render(const SceneShader & shader) const { shader.landmass.use(); grass->bind(); + // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); meshes.apply(&Mesh::Draw); + // glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } void diff --git a/lib/collections.h b/lib/collections.h index 6f26eae..fcd65d3 100644 --- a/lib/collections.h +++ b/lib/collections.h @@ -107,6 +107,15 @@ operator+=(std::vector & in, std::vector && src) return in; } +template +constexpr auto +operator+(std::vector in1, std::vector in2) +{ + in1.reserve(in1.size() + in2.size()); + std::move(in2.begin(), in2.end(), std::back_inserter(in1)); + return in1; +} + template [[nodiscard]] constexpr auto operator+(const std::vector & in, Vn && vn) diff --git a/ui/builders/freeExtend.cpp b/ui/builders/freeExtend.cpp index fa08af6..ab5a998 100644 --- a/ui/builders/freeExtend.cpp +++ b/ui/builders/freeExtend.cpp @@ -38,11 +38,11 @@ BuilderFreeExtend::click( case SDL_BUTTON_LEFT: if (p1) { if (const auto p = network->intersectRayNodes(ray)) { - createJoin(network, *p1, p->pos); + createJoin(network, geoData, *p1, p->pos); p1 = p->pos; } else if (const auto p = geoData->intersectRay(ray)) { - createExtend(network, *p1, p->first); + createExtend(network, geoData, *p1, p->first); p1 = p->first; } } @@ -59,17 +59,19 @@ BuilderFreeExtend::click( } Link::CCollection -BuilderFreeExtend::createJoin(Network * network, GlobalPosition3D p1, GlobalPosition3D p2) const +BuilderFreeExtend::createJoin( + Network * network, const GeoData * geoData, GlobalPosition3D p1, GlobalPosition3D p2) const { - const auto links = network->addJoins(p1, p2); + const auto links = network->addJoins(geoData, p1, p2); setHeightsFor(network, links); return links; } Link::CCollection -BuilderFreeExtend::createExtend(Network * network, GlobalPosition3D p1, GlobalPosition3D p2) const +BuilderFreeExtend::createExtend( + Network * network, const GeoData * geoData, GlobalPosition3D p1, GlobalPosition3D p2) const { - const auto links = network->addExtend(p1, p2); + const auto links = network->addExtend(geoData, p1, p2); setHeightsFor(network, links); return links; } diff --git a/ui/builders/freeExtend.h b/ui/builders/freeExtend.h index 8e30ef4..6f28493 100644 --- a/ui/builders/freeExtend.h +++ b/ui/builders/freeExtend.h @@ -13,8 +13,8 @@ private: const Ray & ray) override; public: - Link::CCollection createJoin(Network * network, GlobalPosition3D, GlobalPosition3D) const; - Link::CCollection createExtend(Network * network, GlobalPosition3D, GlobalPosition3D) const; + Link::CCollection createJoin(Network * network, const GeoData *, GlobalPosition3D, GlobalPosition3D) const; + Link::CCollection createExtend(Network * network, const GeoData *, GlobalPosition3D, GlobalPosition3D) const; private: std::optional p1; diff --git a/ui/builders/join.cpp b/ui/builders/join.cpp index ee14d63..6941e23 100644 --- a/ui/builders/join.cpp +++ b/ui/builders/join.cpp @@ -25,13 +25,13 @@ BuilderJoin::move(Network * network, const GeoData *, const SDL_MouseMotionEvent void BuilderJoin::click( - Network * network, const GeoData *, const SDL_MouseButtonEvent & e, const Ray & ray) + Network * network, const GeoData * geoData, const SDL_MouseButtonEvent & e, const Ray & ray) { switch (e.button) { case SDL_BUTTON_LEFT: if (const auto p = network->intersectRayNodes(ray)) { if (p1) { - create(network, p1, p); + create(network, geoData, p1, p); p1.reset(); candidateLinks.removeAll(); } @@ -47,9 +47,9 @@ BuilderJoin::click( } Link::CCollection -BuilderJoin::create(Network * network, const Node::Ptr & p1, const Node::Ptr & p2) const +BuilderJoin::create(Network * network, const GeoData * geoData, const Node::Ptr & p1, const Node::Ptr & p2) const { - const auto links = network->addJoins(p1->pos, p2->pos); + const auto links = network->addJoins(geoData, p1->pos, p2->pos); setHeightsFor(network, links); return links; } diff --git a/ui/builders/join.h b/ui/builders/join.h index d92037c..326d23d 100644 --- a/ui/builders/join.h +++ b/ui/builders/join.h @@ -12,7 +12,7 @@ private: void move(Network * network, const GeoData * geoData, const SDL_MouseMotionEvent & e, const Ray & ray) override; - Link::CCollection create(Network * network, const Node::Ptr & p1, const Node::Ptr & p2) const; + Link::CCollection create(Network * network, const GeoData *, const Node::Ptr & p1, const Node::Ptr & p2) const; Node::Ptr p1; }; diff --git a/ui/builders/straight.cpp b/ui/builders/straight.cpp index b9f1831..338aa8a 100644 --- a/ui/builders/straight.cpp +++ b/ui/builders/straight.cpp @@ -33,7 +33,7 @@ BuilderStraight::click( case SDL_BUTTON_LEFT: if (const auto p = geoData->intersectRay(ray)) { if (p1) { - create(network, *p1, p->first); + create(network, geoData, *p1, p->first); candidateLinks.removeAll(); p1.reset(); } @@ -50,9 +50,9 @@ BuilderStraight::click( } Link::CCollection -BuilderStraight::create(Network * network, GlobalPosition3D p1, GlobalPosition3D p2) const +BuilderStraight::create(Network * network, const GeoData * geoData, GlobalPosition3D p1, GlobalPosition3D p2) const { - const auto links = network->addStraight(p1, p2); + const auto links = network->addStraight(geoData, p1, p2); setHeightsFor(network, links); return links; } diff --git a/ui/builders/straight.h b/ui/builders/straight.h index 1717cad..0a6f290 100644 --- a/ui/builders/straight.h +++ b/ui/builders/straight.h @@ -13,7 +13,7 @@ private: const Ray & ray) override; public: - Link::CCollection create(Network * network, GlobalPosition3D p1, GlobalPosition3D p2) const; + Link::CCollection create(Network * network, const GeoData *, GlobalPosition3D p1, GlobalPosition3D p2) const; private: std::optional p1; -- cgit v1.2.3 From ecbb621171af0f20751bbc590453d00a7bd38320 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 9 Feb 2025 15:18:35 +0000 Subject: Move lots of geoData helpers into lib --- game/geoData.cpp | 41 ----------------------------------------- lib/maths.h | 46 +++++++++++++++++++++++++++++++++++++++++++--- lib/triangle.h | 27 +++++++++++++++++++++++++++ test/test-maths.cpp | 25 +++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 44 deletions(-) (limited to 'lib') diff --git a/game/geoData.cpp b/game/geoData.cpp index 816ce03..3b94564 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -142,47 +142,6 @@ GeoData::PointFace::face(const GeoData * mesh) const } namespace { - template typename Op> - [[nodiscard]] constexpr inline auto - pointLineOp(const GlobalPosition2D p, const GlobalPosition2D e1, const GlobalPosition2D e2) - { - return Op {}(CalcDistance(e2.x - e1.x) * CalcDistance(p.y - e1.y), - CalcDistance(e2.y - e1.y) * CalcDistance(p.x - e1.x)); - } - - constexpr auto pointLeftOfLine = pointLineOp; - constexpr auto pointLeftOfOrOnLine = pointLineOp; - - static_assert(pointLeftOfLine({1, 2}, {1, 1}, {2, 2})); - static_assert(pointLeftOfLine({2, 1}, {2, 2}, {1, 1})); - static_assert(pointLeftOfLine({2, 2}, {1, 2}, {2, 1})); - static_assert(pointLeftOfLine({1, 1}, {2, 1}, {1, 2})); - static_assert(pointLeftOfOrOnLine({310000000, 490000000}, {310000000, 490000000}, {310050000, 490050000})); - static_assert(pointLeftOfOrOnLine({310000000, 490000000}, {310050000, 490050000}, {310000000, 490050000})); - static_assert(pointLeftOfOrOnLine({310000000, 490000000}, {310000000, 490050000}, {310000000, 490000000})); - - [[nodiscard]] constexpr inline bool - linesCross( - const GlobalPosition2D a1, const GlobalPosition2D a2, const GlobalPosition2D b1, const GlobalPosition2D b2) - { - return (pointLeftOfLine(a2, b1, b2) == pointLeftOfLine(a1, b2, b1)) - && (pointLeftOfLine(b1, a1, a2) == pointLeftOfLine(b2, a2, a1)); - } - - static_assert(linesCross({1, 1}, {2, 2}, {1, 2}, {2, 1})); - static_assert(linesCross({2, 2}, {1, 1}, {1, 2}, {2, 1})); - - [[nodiscard]] constexpr inline bool - linesCrossLtR( - const GlobalPosition2D a1, const GlobalPosition2D a2, const GlobalPosition2D b1, const GlobalPosition2D b2) - { - return pointLeftOfLine(a2, b1, b2) && pointLeftOfLine(a1, b2, b1) && pointLeftOfLine(b1, a1, a2) - && pointLeftOfLine(b2, a2, a1); - } - - static_assert(linesCrossLtR({1, 1}, {2, 2}, {1, 2}, {2, 1})); - static_assert(!linesCrossLtR({2, 2}, {1, 1}, {1, 2}, {2, 1})); - constexpr GlobalPosition3D positionOnTriangle(const GlobalPosition2D point, const GeoData::Triangle<3> & t) { diff --git a/lib/maths.h b/lib/maths.h index 3d4f440..2049c78 100644 --- a/lib/maths.h +++ b/lib/maths.h @@ -14,6 +14,9 @@ template concept Arithmetic = std::is_arithmetic_v; +template using CalcType = std::conditional_t, T, int64_t>; +template using DifferenceType = std::conditional_t, T, float>; + struct Arc : public std::pair { template requires(Lc >= 2, Le >= 2) @@ -102,7 +105,7 @@ operator-(const GlobalPosition & global, const CalcPosition & relative) } template -using DifferenceVector = glm::vec, T, float>, Q>; +using DifferenceVector = glm::vec, Q>; template constexpr DifferenceVector @@ -111,6 +114,16 @@ difference(const glm::vec & globalA, const glm::vec & globalB) return globalA - globalB; } +template +using CalcVector = glm::vec, Q>; + +template +constexpr CalcVector +calcDifference(const glm::vec & globalA, const glm::vec & globalB) +{ + return globalA - globalB; +} + template constexpr auto distance(const glm::vec & pointA, const glm::vec & pointB) @@ -364,8 +377,6 @@ normalize(T ang) return ang; } -template using CalcType = std::conditional_t, T, int64_t>; - template [[nodiscard]] constexpr std::optional> linesIntersectAt(const glm::vec<2, T, Q> Aabs, const glm::vec<2, T, Q> Babs, const glm::vec<2, T, Q> Cabs, @@ -547,3 +558,32 @@ ArcSegment::crossesLineAt(const glm::vec<2, T, Q> & lineStart, const glm:: }); return std::make_pair(centre + first.first, first.second); } + +namespace { + template typename Op> + [[nodiscard]] constexpr auto + pointLineOp(const GlobalPosition2D point, const GlobalPosition2D end1, const GlobalPosition2D end2) + { + return Op {}(CalcDistance(end2.x - end1.x) * CalcDistance(point.y - end1.y), + CalcDistance(end2.y - end1.y) * CalcDistance(point.x - end1.x)); + } +} + +constexpr auto pointLeftOfLine = pointLineOp; +constexpr auto pointLeftOfOrOnLine = pointLineOp; + +[[nodiscard]] constexpr bool +linesCross(const GlobalPosition2D lineAend1, const GlobalPosition2D lineAend2, const GlobalPosition2D lineBend1, + const GlobalPosition2D lineBend2) +{ + return (pointLeftOfLine(lineAend2, lineBend1, lineBend2) == pointLeftOfLine(lineAend1, lineBend2, lineBend1)) + && (pointLeftOfLine(lineBend1, lineAend1, lineAend2) == pointLeftOfLine(lineBend2, lineAend2, lineAend1)); +} + +[[nodiscard]] constexpr bool +linesCrossLtR(const GlobalPosition2D lineAend1, const GlobalPosition2D lineAend2, const GlobalPosition2D lineBend1, + const GlobalPosition2D lineBend2) +{ + return pointLeftOfLine(lineAend2, lineBend1, lineBend2) && pointLeftOfLine(lineAend1, lineBend2, lineBend1) + && pointLeftOfLine(lineBend1, lineAend1, lineAend2) && pointLeftOfLine(lineBend2, lineAend2, lineAend1); +} diff --git a/lib/triangle.h b/lib/triangle.h index e430653..abd697c 100644 --- a/lib/triangle.h +++ b/lib/triangle.h @@ -64,6 +64,12 @@ struct Triangle : public glm::vec<3, glm::vec> { return difference(p(side), p(0)); } + [[nodiscard]] constexpr auto + calcSideDifference(glm::length_t side) const + { + return calcDifference(p(side), p(0)); + } + [[nodiscard]] constexpr auto angle(glm::length_t corner) const { @@ -126,4 +132,25 @@ struct Triangle : public glm::vec<3, glm::vec> { { return begin() + 3; } + + [[nodiscard]] + constexpr auto + positionOnPlane(const glm::vec<2, T, Q> coord2d) const + requires(Dim == 3) + { + const auto edgeCrossProduct = crossProduct(calcSideDifference(1), calcSideDifference(2)); + return coord2d + || static_cast( + ((edgeCrossProduct.x * p(0).x) + (edgeCrossProduct.y * p(0).y) + (edgeCrossProduct.z * p(0).z) + - (edgeCrossProduct.x * coord2d.x) - (edgeCrossProduct.y * coord2d.y)) + / edgeCrossProduct.z); + } + + [[nodiscard]] + constexpr bool + containsPoint(const GlobalPosition2D coord) const + { + return pointLeftOfOrOnLine(coord, p(0), p(1)) && pointLeftOfOrOnLine(coord, p(1), p(2)) + && pointLeftOfOrOnLine(coord, p(2), p(0)); + } }; diff --git a/test/test-maths.cpp b/test/test-maths.cpp index 5906757..a6b780a 100644 --- a/test/test-maths.cpp +++ b/test/test-maths.cpp @@ -412,3 +412,28 @@ BOOST_DATA_TEST_CASE(arcline_intersection, BOOST_CHECK_CLOSE(expected->second, intersection->second, 1.F); } } + +static_assert(pointLeftOfLine({1, 2}, {1, 1}, {2, 2})); +static_assert(pointLeftOfLine({2, 1}, {2, 2}, {1, 1})); +static_assert(pointLeftOfLine({2, 2}, {1, 2}, {2, 1})); +static_assert(pointLeftOfLine({1, 1}, {2, 1}, {1, 2})); +static_assert(pointLeftOfOrOnLine({310000000, 490000000}, {310000000, 490000000}, {310050000, 490050000})); +static_assert(pointLeftOfOrOnLine({310000000, 490000000}, {310050000, 490050000}, {310000000, 490050000})); +static_assert(pointLeftOfOrOnLine({310000000, 490000000}, {310000000, 490050000}, {310000000, 490000000})); + +static_assert(linesCross({1, 1}, {2, 2}, {1, 2}, {2, 1})); +static_assert(linesCross({2, 2}, {1, 1}, {1, 2}, {2, 1})); + +static_assert(linesCrossLtR({1, 1}, {2, 2}, {1, 2}, {2, 1})); +static_assert(!linesCrossLtR({2, 2}, {1, 1}, {1, 2}, {2, 1})); + +static_assert(Triangle<3, GlobalDistance> {{1, 2, 3}, {1, 0, 1}, {-2, 1, 0}}.positionOnPlane({7, -2}) + == GlobalPosition3D {7, -2, 3}); +static_assert(Triangle<3, GlobalDistance> { + {310000000, 490000000, 32800}, {310050000, 490050000, 33000}, {310000000, 490050000, 32700}} + .positionOnPlane({310000000, 490000000}) + == GlobalPosition3D {310000000, 490000000, 32800}); +static_assert(Triangle<3, GlobalDistance> { + {310750000, 490150000, 58400}, {310800000, 490200000, 55500}, {310750000, 490200000, 57600}} + .positionOnPlane({310751000, 490152000}) + == GlobalPosition3D {310751000, 490152000, 58326}); -- cgit v1.2.3 From 98afc8feaed67a0c6d450d27ded25145476ac8ff Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 14 Feb 2025 02:33:20 +0000 Subject: Add utility class to easily get nth field of tuple/pair for any types --- lib/util.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'lib') diff --git a/lib/util.h b/lib/util.h index 290492f..2674eaf 100644 --- a/lib/util.h +++ b/lib/util.h @@ -3,6 +3,7 @@ #include // IWYU pragma: keep #include #include +#include template constexpr auto @@ -12,3 +13,23 @@ transform_array(const std::array & in, auto && transform) std::transform(in.begin(), in.end(), out.begin(), transform); return out; } + +namespace { + template struct GetNth { + decltype(auto) + operator()(const auto & tup) const + { + if constexpr (sizeof...(N) == 1) { + return std::get(tup); + } + else { + return std::tie(std::get(tup)...); + } + } + }; +} + +template constexpr auto Nth = GetNth {}; +constexpr auto GetFirst = Nth<0>; +constexpr auto GetSecond = Nth<1>; +constexpr auto GetSwapped = Nth<0, 1>; -- cgit v1.2.3 From f26559dcd25b649d37a1a30e087b70b95f530954 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 14 Feb 2025 23:53:46 +0000 Subject: Range adaptor to make triangle strip triples --- game/geoData.cpp | 10 ++++------ lib/collections.h | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/game/geoData.cpp b/game/geoData.cpp index 552d2ba..cfaf44b 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -344,12 +344,10 @@ GeoData::setHeights(const std::span triangleStrip, const std::ranges::for_each(newVerts, addVertexForNormalUpdate); // Create temporary triangles from triangleStrip - std::vector> strip; - std::transform( - strip_begin(triangleStrip), strip_end(triangleStrip), std::back_inserter(strip), [](const auto & newVert) { - const auto [a, b, c] = newVert; - return Triangle<3> {a, b, c}; - }); + const auto strip + = materializeRange(triangleStrip | TriangleTriples | std::views::transform([](const auto & newVert) { + return std::make_from_tuple>(*newVert); + })); auto getTriangle = [&strip](const auto point) -> const Triangle<3> * { if (const auto t = std::ranges::find_if(strip, [point](const auto & triangle) { diff --git a/lib/collections.h b/lib/collections.h index fcd65d3..c81bede 100644 --- a/lib/collections.h +++ b/lib/collections.h @@ -197,6 +197,14 @@ template struct stripiter { return *this; } + constexpr stripiter + operator++(int) + { + auto out {*this}; + ++*this; + return out; + } + constexpr stripiter & operator--() { @@ -205,6 +213,14 @@ template struct stripiter { return *this; } + constexpr stripiter + operator--(int) + { + auto out {*this}; + --*this; + return out; + } + constexpr auto operator-(const stripiter & other) const { @@ -235,6 +251,16 @@ strip_end(IterableCollection auto & cont) return stripiter {cont.end()}; } +struct TriangleTriplesImpl : public std::ranges::range_adaptor_closure { + decltype(auto) + operator()(const auto & triangleStrip) const + { + return std::views::iota(strip_begin(triangleStrip), strip_end(triangleStrip)); + } +}; + +constexpr TriangleTriplesImpl TriangleTriples; + template void mergeClose(std::vector & range, const Dist & dist, const Merger & merger, -- cgit v1.2.3 From 619983e949fde226cc7a3de0bc198fdcee3fe3b4 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 15 Feb 2025 14:46:48 +0000 Subject: Fixes and tests to new range helpers --- game/geoData.cpp | 4 ++-- lib/collections.h | 10 +++++++--- lib/util.h | 8 ++++---- test/test-lib.cpp | 15 ++++++++++++--- 4 files changed, 25 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/game/geoData.cpp b/game/geoData.cpp index cfaf44b..fa96a33 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -345,8 +345,8 @@ GeoData::setHeights(const std::span triangleStrip, const // Create temporary triangles from triangleStrip const auto strip - = materializeRange(triangleStrip | TriangleTriples | std::views::transform([](const auto & newVert) { - return std::make_from_tuple>(*newVert); + = materializeRange(triangleStrip | triangleTriples | std::views::transform([](const auto & newVert) { + return std::make_from_tuple>(newVert); })); auto getTriangle = [&strip](const auto point) -> const Triangle<3> * { if (const auto t = std::ranges::find_if(strip, diff --git a/lib/collections.h b/lib/collections.h index c81bede..27eff0a 100644 --- a/lib/collections.h +++ b/lib/collections.h @@ -251,15 +251,19 @@ strip_end(IterableCollection auto & cont) return stripiter {cont.end()}; } -struct TriangleTriplesImpl : public std::ranges::range_adaptor_closure { +inline constexpr auto dereference = std::views::transform([](const auto & iter) -> decltype(auto) { + return *iter; +}); + +struct TriangleTriples : public std::ranges::range_adaptor_closure { decltype(auto) operator()(const auto & triangleStrip) const { - return std::views::iota(strip_begin(triangleStrip), strip_end(triangleStrip)); + return std::views::iota(strip_begin(triangleStrip), strip_end(triangleStrip)) | dereference; } }; -constexpr TriangleTriplesImpl TriangleTriples; +inline constexpr TriangleTriples triangleTriples; template void diff --git a/lib/util.h b/lib/util.h index 2674eaf..cd7971b 100644 --- a/lib/util.h +++ b/lib/util.h @@ -29,7 +29,7 @@ namespace { }; } -template constexpr auto Nth = GetNth {}; -constexpr auto GetFirst = Nth<0>; -constexpr auto GetSecond = Nth<1>; -constexpr auto GetSwapped = Nth<0, 1>; +template inline constexpr auto Nth = GetNth {}; +inline constexpr auto GetFirst = Nth<0>; +inline constexpr auto GetSecond = Nth<1>; +inline constexpr auto GetSwapped = Nth<0, 1>; diff --git a/test/test-lib.cpp b/test/test-lib.cpp index 7672acc..17c0f63 100644 --- a/test/test-lib.cpp +++ b/test/test-lib.cpp @@ -1,5 +1,6 @@ #define BOOST_TEST_MODULE test_lib +#include "testHelpers.h" #include #include #include @@ -66,8 +67,16 @@ BOOST_AUTO_TEST_CASE(triangle_strip_iter) out.push_back(c); }); BOOST_REQUIRE_EQUAL(out.size(), (TRIANGLE_STRIP_IN.size() - 2) * 3); - BOOST_CHECK_EQUAL_COLLECTIONS( - out.begin(), out.end(), TRIANGLE_STRIP_EXPECTED.begin(), TRIANGLE_STRIP_EXPECTED.end()); + BOOST_CHECK_EQUAL_COLCOL(out, TRIANGLE_STRIP_EXPECTED); +} + +BOOST_AUTO_TEST_CASE(triangle_strip_range_adapter) +{ + using TriTuple = std::tuple; + std::vector outRange; + std::ranges::copy(TRIANGLE_STRIP_IN | triangleTriples, std::back_inserter(outRange)); + constexpr std::array TRIANGLE_STRIP_EXPECTED_TUPLES {{{0, 1, 2}, {2, 1, 3}, {2, 3, 4}, {4, 3, 5}}}; + BOOST_CHECK_EQUAL_COLCOL(outRange, TRIANGLE_STRIP_EXPECTED_TUPLES); } using MergeCloseData = std::tuple, int, std::vector>; @@ -99,5 +108,5 @@ BOOST_DATA_TEST_CASE(mergeCloseInts, return (left + right) / 2; }, tolerance))); - BOOST_CHECK_EQUAL_COLLECTIONS(mutableCollection.begin(), mutableCollection.end(), expected.begin(), expected.end()); + BOOST_CHECK_EQUAL_COLCOL(mutableCollection, expected); } -- cgit v1.2.3 From 127cbcfafd91dca1c4211cbb15a881d782747214 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 23 Feb 2025 23:55:58 +0000 Subject: Add CLOGf for formatting logger --- lib/stream_support.h | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/stream_support.h b/lib/stream_support.h index f5c5e37..5f276fd 100644 --- a/lib/stream_support.h +++ b/lib/stream_support.h @@ -115,3 +115,4 @@ namespace { } #define CLOG(x) clogImpl(x, #x) +#define CLOGf(...) clogImpl(std::format(__VA_ARGS__), "msg") -- cgit v1.2.3