From b3105c56c277f6e1e27a3e5e5ee5e1b6c3f47afd Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 12 May 2025 00:23:42 +0100 Subject: Split link generation definitions according to terrain Copies the same imperfect logic from old link addition functions. --- application/main.cpp | 9 +++--- game/network/network.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++++++-- game/network/network.h | 5 +++- test/test-network.cpp | 54 ++++++++++++++++++---------------- ui/editNetwork.cpp | 6 ++-- 5 files changed, 116 insertions(+), 33 deletions(-) diff --git a/application/main.cpp b/application/main.cpp index d96b188..3536cf7 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -64,10 +64,11 @@ public: Link::Ptr l3; for (std::optional previousDir; const auto [fromPos, toPos] : std::views::pairwise(nodes)) { - const auto links = railLinks->create({ - .fromEnd = {.position = fromPos, .direction = previousDir}, - .toEnd = {.position = toPos, .direction = std::nullopt}, - }); + const auto links = railLinks->create(gameState->terrain.get(), + { + .fromEnd = {.position = fromPos, .direction = previousDir}, + .toEnd = {.position = toPos, .direction = std::nullopt}, + }); for (const auto & link : links) { railLinks->add(terrain.get(), link); } diff --git a/game/network/network.cpp b/game/network/network.cpp index 561cafb..1c3a122 100644 --- a/game/network/network.cpp +++ b/game/network/network.cpp @@ -1,6 +1,8 @@ #include "network.h" +#include "game/geoData.h" #include "routeWalker.h" #include +#include #include #include #include @@ -96,6 +98,66 @@ Network::routeFromTo(const Link::End & end, const Node::Ptr & dest) return RouteWalker().findRouteTo(end, dest); } +void +Network::terrainSplitAt(GenLinkDef & previous, GenLinkDef & next, GlobalPosition3D pos) +{ + std::visit( + [pos](auto & typedDefPrevious, auto & typedDefNext) { + std::get<1>(typedDefPrevious) = std::get<0>(typedDefNext) = pos; + }, + previous, next); +} + +GenLinksDef +Network::terrainSplit(const GeoData * geoData, const GenStraightDef & def) const +{ + GenLinksDef out {def}; + const auto [fromPos, toPos] = def; + geoData->walk(fromPos.xy(), toPos, [geoData, &out](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(out.back()); + terrainSplitAt(*out.rbegin(), *++out.rbegin(), surfaceEdgePosition); + } + }); + return out; +} + +GenLinksDef +Network::terrainSplit(const GeoData * geoData, const GenCurveDef & def) const +{ + static constexpr auto MIN_DISTANCE = 2000.F; + auto [cstart, cend, centre] = def; + 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>, MIN_DISTANCE); + GenLinksDef out {def}; + std::ranges::for_each(++points.begin(), --points.end(), [&out](const auto pos) { + out.emplace_back(out.back()); + terrainSplitAt(*out.rbegin(), *++out.rbegin(), pos); + }); + return out; +} + GenCurveDef Network::genCurveDef(const GlobalPosition3D & start, const GlobalPosition3D & end, float startDir) { @@ -144,7 +206,7 @@ Network::genCurveDef(const GlobalPosition3D & start, const GlobalPosition3D & en } Link::Collection -Network::create(const CreationDefinition & def) +Network::create(const GeoData * geoData, const CreationDefinition & def) { // TODO // Where to make a straight to join because angles align? @@ -168,8 +230,17 @@ Network::create(const CreationDefinition & def) // One specific direction, single curve from the other return {genCurveDef(def.toEnd.position, def.fromEnd.position, *def.toEnd.direction)}; }; + const auto splitDefs = [&linkDefs, this, geoData]() { + return std::ranges::fold_left(linkDefs(), GenLinksDef {}, [this, geoData](auto && existing, const auto & def) { + return existing += std::visit( + [this, geoData](const auto & typedDef) { + return this->terrainSplit(geoData, typedDef); + }, + def); + }); + }; Link::Collection links; - std::ranges::transform(linkDefs(), std::back_inserter(links), [this](const auto & def) { + std::ranges::transform(geoData ? splitDefs() : linkDefs(), std::back_inserter(links), [this](const auto & def) { return std::visit( [this](const auto & typedDef) { return this->create(typedDef); diff --git a/game/network/network.h b/game/network/network.h index 153b410..65db016 100644 --- a/game/network/network.h +++ b/game/network/network.h @@ -64,7 +64,7 @@ public: [[nodiscard]] virtual float findNodeDirection(Node::AnyCPtr) const = 0; - [[nodiscard]] Link::Collection create(const CreationDefinition &); + [[nodiscard]] Link::Collection create(const GeoData *, const CreationDefinition &); virtual void add(GeoData *, const Link::Ptr &) = 0; [[nodiscard]] virtual const Surface * getBaseSurface() const = 0; @@ -76,6 +76,9 @@ protected: static std::pair genCurveDef( const GlobalPosition3D & start, const GlobalPosition3D & end, float startDir, float endDir); + [[nodiscard]] GenLinksDef terrainSplit(const GeoData *, const GenStraightDef &) const; + [[nodiscard]] GenLinksDef terrainSplit(const GeoData *, const GenCurveDef &) const; + static void terrainSplitAt(GenLinkDef & previous, GenLinkDef & next, GlobalPosition3D pos); [[nodiscard]] virtual Link::Ptr create(const GenStraightDef &) = 0; [[nodiscard]] virtual Link::Ptr create(const GenCurveDef &) = 0; diff --git a/test/test-network.cpp b/test/test-network.cpp index e6b92f3..05e995e 100644 --- a/test/test-network.cpp +++ b/test/test-network.cpp @@ -269,10 +269,11 @@ BOOST_DATA_TEST_CASE(GenCurveDefs, BOOST_AUTO_TEST_CASE(NetworkCreateStraight) { - const auto link = create(CreationDefinition { - .fromEnd = {.position = {0, 0, 0}, .direction = {}}, - .toEnd = {.position = {0, 1000, 0}, .direction = {}}, - }); + const auto link = create(nullptr, + CreationDefinition { + .fromEnd = {.position = {0, 0, 0}, .direction = {}}, + .toEnd = {.position = {0, 1000, 0}, .direction = {}}, + }); BOOST_REQUIRE_EQUAL(link.size(), 1); BOOST_CHECK(links.empty()); BOOST_CHECK(nodes.empty()); @@ -290,10 +291,11 @@ BOOST_AUTO_TEST_CASE(NetworkCreateStraight) BOOST_AUTO_TEST_CASE(NetworkCreateExtendingCurve) { - const auto link = create(CreationDefinition { - .fromEnd = {.position = {0, 0, 0}, .direction = half_pi}, - .toEnd = {.position = {0, 1000, 0}, .direction = {}}, - }); + const auto link = create(nullptr, + CreationDefinition { + .fromEnd = {.position = {0, 0, 0}, .direction = half_pi}, + .toEnd = {.position = {0, 1000, 0}, .direction = {}}, + }); BOOST_REQUIRE_EQUAL(link.size(), 1); BOOST_CHECK(links.empty()); BOOST_CHECK(nodes.empty()); @@ -315,10 +317,11 @@ BOOST_AUTO_TEST_CASE(NetworkCreateExtendingCurve) BOOST_AUTO_TEST_CASE(NetworkCreateExtendeeCurve) { - const auto link = create(CreationDefinition { - .fromEnd = {.position = {0, 0, 0}, .direction = {}}, - .toEnd = {.position = {0, 1000, 0}, .direction = half_pi}, - }); + const auto link = create(nullptr, + CreationDefinition { + .fromEnd = {.position = {0, 0, 0}, .direction = {}}, + .toEnd = {.position = {0, 1000, 0}, .direction = half_pi}, + }); BOOST_REQUIRE_EQUAL(link.size(), 1); BOOST_CHECK(links.empty()); BOOST_CHECK(nodes.empty()); @@ -343,10 +346,11 @@ BOOST_AUTO_TEST_CASE(NetworkCreateExtendeeCurve) BOOST_AUTO_TEST_CASE(NetworkCreateBiarcPair) { - const auto link = create(CreationDefinition { - .fromEnd = {.position = {0, 0, 0}, .direction = pi}, - .toEnd = {.position = {1000, 1000, 0}, .direction = 0}, - }); + const auto link = create(nullptr, + CreationDefinition { + .fromEnd = {.position = {0, 0, 0}, .direction = pi}, + .toEnd = {.position = {1000, 1000, 0}, .direction = 0}, + }); BOOST_REQUIRE_EQUAL(link.size(), 2); BOOST_CHECK(links.empty()); BOOST_CHECK(nodes.empty()); @@ -382,10 +386,11 @@ BOOST_AUTO_TEST_CASE(NetworkCreateBiarcPair) BOOST_AUTO_TEST_CASE(NetworkCreateBiarcPairEqTan) { // This could be achieved with a single curve, but not there yet - const auto link = create(CreationDefinition { - .fromEnd = {.position = {0, 0, 0}, .direction = 0}, - .toEnd = {.position = {1000, 0, 0}, .direction = 0}, - }); + const auto link = create(nullptr, + CreationDefinition { + .fromEnd = {.position = {0, 0, 0}, .direction = 0}, + .toEnd = {.position = {1000, 0, 0}, .direction = 0}, + }); BOOST_REQUIRE_EQUAL(link.size(), 2); BOOST_CHECK(links.empty()); BOOST_CHECK(nodes.empty()); @@ -402,10 +407,11 @@ BOOST_AUTO_TEST_CASE(NetworkCreateBiarcPairEqTan) BOOST_AUTO_TEST_CASE(NetworkCreateBiarcPairEqTanPerp) { // This creates an equal pair of semi-circle arcs - const auto link = create(CreationDefinition { - .fromEnd = {.position = {0, 0, 0}, .direction = 0}, - .toEnd = {.position = {1000, 0, 0}, .direction = pi}, - }); + const auto link = create(nullptr, + CreationDefinition { + .fromEnd = {.position = {0, 0, 0}, .direction = 0}, + .toEnd = {.position = {1000, 0, 0}, .direction = pi}, + }); BOOST_REQUIRE_EQUAL(link.size(), 2); BOOST_CHECK(links.empty()); BOOST_CHECK(nodes.empty()); diff --git a/ui/editNetwork.cpp b/ui/editNetwork.cpp index cab13f2..98a60d3 100644 --- a/ui/editNetwork.cpp +++ b/ui/editNetwork.cpp @@ -21,7 +21,8 @@ EditNetwork::click(const SDL_MouseButtonEvent & event, const Raycreate(CreationDefinition {.fromEnd = *currentStart, .toEnd = *def}); + candidates = network->create( + gameState->terrain.get(), CreationDefinition {.fromEnd = *currentStart, .toEnd = *def}); for (const auto & link : candidates) { network->add(gameState->terrain.get(), link); } @@ -46,7 +47,8 @@ EditNetwork::move(const SDL_MouseMotionEvent &, const Ray & ra { if (currentStart) { if (const auto def = resolveRay(ray)) { - candidates = network->create(CreationDefinition {.fromEnd = *currentStart, .toEnd = *def}); + candidates = network->create( + gameState->terrain.get(), CreationDefinition {.fromEnd = *currentStart, .toEnd = *def}); } } return false; -- cgit v1.2.3