summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2025-05-12 00:23:42 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2025-05-12 00:23:42 +0100
commitb3105c56c277f6e1e27a3e5e5ee5e1b6c3f47afd (patch)
tree2f136cb121ef5aa5a78267ccbfa220ca87b56aac
parentSplit definition creation from link creation (diff)
downloadilt-b3105c56c277f6e1e27a3e5e5ee5e1b6c3f47afd.tar.bz2
ilt-b3105c56c277f6e1e27a3e5e5ee5e1b6c3f47afd.tar.xz
ilt-b3105c56c277f6e1e27a3e5e5ee5e1b6c3f47afd.zip
Split link generation definitions according to terrain
Copies the same imperfect logic from old link addition functions.
-rw-r--r--application/main.cpp9
-rw-r--r--game/network/network.cpp75
-rw-r--r--game/network/network.h5
-rw-r--r--test/test-network.cpp54
-rw-r--r--ui/editNetwork.cpp6
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<Angle> 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 <array>
+#include <collections.h>
#include <game/network/link.h>
#include <gfx/models/texture.h>
#include <glm/gtx/intersect.hpp>
@@ -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<GeoData::WalkStepCurve, SortedBy<&GeoData::WalkStepCurve::angle>> 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<GlobalPosition3D> 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, GenCurveDef> 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 Ray<GlobalPosition3
}
else {
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});
for (const auto & link : candidates) {
network->add(gameState->terrain.get(), link);
}
@@ -46,7 +47,8 @@ EditNetwork::move(const SDL_MouseMotionEvent &, const Ray<GlobalPosition3D> & 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;