summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--game/geoData.cpp218
-rw-r--r--game/geoData.h12
-rw-r--r--game/terrain.cpp2
-rw-r--r--lib/collections.h73
-rw-r--r--lib/geometricPlane.h2
-rw-r--r--lib/ray.h4
-rw-r--r--test/test-geoData.cpp62
-rw-r--r--test/test-lib.cpp20
-rw-r--r--ui/builders/freeExtend.cpp6
-rw-r--r--ui/builders/straight.cpp6
10 files changed, 380 insertions, 25 deletions
diff --git a/game/geoData.cpp b/game/geoData.cpp
index 359e8c0..1f6768a 100644
--- a/game/geoData.cpp
+++ b/game/geoData.cpp
@@ -1,7 +1,10 @@
#include "geoData.h"
+#include "collections.h"
#include <fstream>
#include <glm/gtx/intersect.hpp>
+#include <math.h>
#include <maths.h>
+#include <set>
GeoData
GeoData::loadFromAsciiGrid(const std::filesystem::path & input)
@@ -62,6 +65,8 @@ GeoData::loadFromAsciiGrid(const std::filesystem::path & input)
return mesh;
};
+template<typename T> constexpr static T GRID_SIZE = 10'000;
+
GeoData
GeoData::createFlat(GlobalPosition2D lower, GlobalPosition2D upper, GlobalDistance h)
{
@@ -70,11 +75,29 @@ GeoData::createFlat(GlobalPosition2D lower, GlobalPosition2D upper, GlobalDistan
mesh.lowerExtent = {lower, h};
mesh.upperExtent = {upper, h};
- const auto ll = mesh.add_vertex({lower.x, lower.y, h}), lu = mesh.add_vertex({lower.x, upper.y, h}),
- ul = mesh.add_vertex({upper.x, lower.y, h}), uu = mesh.add_vertex({upper.x, upper.y, h});
+ std::vector<VertexHandle> vertices;
+ for (GlobalDistance row = lower.x; row < upper.x; row += GRID_SIZE<GlobalDistance>) {
+ for (GlobalDistance col = lower.y; col < upper.y; col += GRID_SIZE<GlobalDistance>) {
+ vertices.push_back(mesh.add_vertex({col, row, h}));
+ }
+ }
- mesh.add_face(ll, uu, lu);
- mesh.add_face(ll, ul, uu);
+ const auto nrows = static_cast<size_t>(std::ceil(float(upper.x - lower.x) / GRID_SIZE<RelativeDistance>));
+ const auto ncols = static_cast<size_t>(std::ceil(float(upper.y - lower.y) / GRID_SIZE<RelativeDistance>));
+ for (size_t row = 1; row < nrows; ++row) {
+ for (size_t col = 1; col < ncols; ++col) {
+ mesh.add_face({
+ vertices[ncols * (row - 1) + (col - 1)],
+ vertices[ncols * (row - 0) + (col - 0)],
+ vertices[ncols * (row - 0) + (col - 1)],
+ });
+ mesh.add_face({
+ vertices[ncols * (row - 1) + (col - 1)],
+ vertices[ncols * (row - 1) + (col - 0)],
+ vertices[ncols * (row - 0) + (col - 0)],
+ });
+ }
+ }
mesh.update_vertex_normals_only();
@@ -193,16 +216,16 @@ GeoData::positionAt(const PointFace & p) const
return positionOnTriangle(p.point, triangle<3>(p.face(this)));
}
-[[nodiscard]] std::optional<GlobalPosition3D>
+[[nodiscard]] GeoData::IntersectionResult
GeoData::intersectRay(const Ray<GlobalPosition3D> & ray) const
{
return intersectRay(ray, findPoint(ray.start));
}
-[[nodiscard]] std::optional<GlobalPosition3D>
+[[nodiscard]] GeoData::IntersectionResult
GeoData::intersectRay(const Ray<GlobalPosition3D> & ray, FaceHandle face) const
{
- std::optional<GlobalPosition3D> out;
+ GeoData::IntersectionResult out;
walkUntil(PointFace {ray.start, face},
ray.start.xy() + (ray.direction.xy() * RelativePosition2D(upperExtent.xy() - lowerExtent.xy())),
[&out, &ray, this](FaceHandle face) {
@@ -210,7 +233,7 @@ GeoData::intersectRay(const Ray<GlobalPosition3D> & ray, FaceHandle face) const
RelativeDistance dist {};
const auto t = triangle<3>(face);
if (ray.intersectTriangle(t.x, t.y, t.z, bari, dist)) {
- out = t * bari;
+ out.emplace(t * bari, face);
return true;
}
return false;
@@ -338,3 +361,182 @@ GeoData::update_vertex_normals_only()
this->set_normal(vh, glm::normalize(n));
}
}
+
+bool
+GeoData::triangleOverlapsTriangle(const Triangle<2> & a, const Triangle<2> & b)
+{
+ return triangleContainsPoint(a.x, b) || triangleContainsPoint(a.y, b) || triangleContainsPoint(a.z, b)
+ || triangleContainsPoint(b.x, a) || triangleContainsPoint(b.y, a) || triangleContainsPoint(b.z, a)
+ || linesCross(a.x, a.y, b.x, b.y) || linesCross(a.x, a.y, b.y, b.z) || linesCross(a.x, a.y, b.z, b.x)
+ || linesCross(a.y, a.z, b.x, b.y) || linesCross(a.y, a.z, b.y, b.z) || linesCross(a.y, a.z, b.z, b.x)
+ || linesCross(a.z, a.x, b.x, b.y) || linesCross(a.z, a.x, b.y, b.z) || linesCross(a.z, a.x, b.z, b.x);
+}
+
+bool
+GeoData::triangleContainsTriangle(const Triangle<2> & a, const Triangle<2> & b)
+{
+ return triangleContainsPoint(a.x, b) && triangleContainsPoint(a.y, b) && triangleContainsPoint(a.z, b);
+}
+
+void
+GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip)
+{
+ std::set<EdgeHandle> nosplit;
+ // Create new vertices
+ std::vector<VertexHandle> newVerts;
+ newVerts.reserve(newVerts.size());
+ std::transform(triangleStrip.begin(), triangleStrip.end(), std::back_inserter(newVerts), [this](const auto tsVert) {
+ return add_vertex(tsVert);
+ });
+ // Create new faces
+ std::vector<FaceHandle> newFaces;
+ newFaces.reserve(newVerts.size() - 2);
+ std::transform(strip_begin(newVerts), strip_end(newVerts), std::back_inserter(newFaces),
+ [this, &nosplit](const auto & newVert) {
+ const auto [a, b, c] = newVert;
+ auto faceHandle = add_face(a, b, c);
+ std::copy(fe_begin(faceHandle), fe_end(faceHandle), std::inserter(nosplit, nosplit.end()));
+ return faceHandle;
+ });
+ std::vector<HalfedgeHandle> boundary;
+ boundaryWalk(
+ [out = std::back_inserter(boundary)](const auto boundaryHeh) mutable {
+ out = boundaryHeh;
+ },
+ *voh_begin(newVerts.front()));
+
+ // Extrude corners
+ std::set<VertexHandle> cutpoints;
+ std::vector<std::pair<GlobalPosition3D, GlobalPosition3D>> extrusionExtents;
+ std::vector<VertexHandle> extrusionVertices;
+ std::transform(boundary.begin(), boundary.end(), std::back_inserter(extrusionExtents),
+ [this, &nosplit, &cutpoints, &extrusionVertices](const auto boundaryHeh) {
+ const auto vectorNormal
+ = []<typename T, glm::qualifier Q>(const glm::vec<2, T, Q> & v) -> glm::vec<2, T, Q> {
+ return {-v.y, v.x};
+ };
+
+ const auto p0 = point(from_vertex_handle(prev_halfedge_handle(boundaryHeh)));
+ const auto p1 = point(from_vertex_handle(boundaryHeh));
+ const auto p2 = point(to_vertex_handle(boundaryHeh));
+ const auto e0 = glm::normalize(vectorNormal(RelativePosition2D(p1 - p0)));
+ const auto e1 = glm::normalize(vectorNormal(RelativePosition2D(p2 - p1)));
+ const auto mid = glm::normalize((e0 + e1) / 2.F);
+ const auto doExtrusion = [mid, p1, this, &nosplit, &cutpoints, &extrusionVertices](
+ RelativeDistance vert, GlobalDistance limit) {
+ const auto extrusionDir = glm::normalize(mid || vert);
+
+ if (const auto intersect = intersectRay({p1, extrusionDir})) {
+ auto splitVertex = split(intersect->second, intersect->first);
+ cutpoints.insert(splitVertex);
+ extrusionVertices.push_back(splitVertex);
+ std::copy(ve_begin(splitVertex), ve_end(splitVertex), std::inserter(nosplit, nosplit.end()));
+ }
+
+ const auto extrusion
+ = extrusionDir * std::max(0.F, RelativeDistance(limit - p1.z) / extrusionDir.z);
+ return p1 + extrusion;
+ };
+ return std::make_pair(doExtrusion(-2, lowerExtent.z - 100), doExtrusion(2, upperExtent.z + 100));
+ });
+
+ // Cut existing terrain
+ extrusionExtents.emplace_back(extrusionExtents.front()); // Circular next
+ extrusionVertices.emplace_back(extrusionVertices.front());
+ std::vector<std::vector<VertexHandle>> boundaryFaces;
+ std::transform(boundary.begin(), boundary.end(), std ::back_inserter(boundaryFaces),
+ [ex = extrusionExtents.begin(), exv = extrusionVertices.begin(), this, &nosplit](
+ const auto boundaryHeh) mutable {
+ const auto fromVertex = from_vertex_handle(boundaryHeh);
+ const auto p0 = point(fromVertex);
+ auto toVertex = to_vertex_handle(boundaryHeh);
+ const auto p1 = point(toVertex);
+ const auto nex = ex + 1;
+ const auto nexv = exv + 1;
+ const std::array<Triangle<3>, 4> triangles {{
+ {p0, ex->first, nex->first},
+ {p0, p1, nex->first},
+ {p0, ex->second, nex->second},
+ {p0, p1, nex->second},
+ }};
+
+ std::vector<VertexHandle> sideVerts {fromVertex, *exv};
+ for (auto currentVertex = *exv;
+ std::any_of(voh_begin(currentVertex), voh_end(currentVertex), [&](const auto currentVertexOut) {
+ const auto next = next_halfedge_handle(currentVertexOut);
+ const auto nextVertex = to_vertex_handle(next);
+ const auto startVertex = from_vertex_handle(next);
+ if (nextVertex == *++sideVerts.rbegin()) {
+ // This half edge goes back to the previous vertex
+ return false;
+ }
+ if (nextVertex == *nexv) {
+ // The next half edge goes to the termination point
+ return false;
+ }
+ const auto edge = edge_handle(next);
+ if (nosplit.contains(edge)) {
+ // This edge should not be split (for some reason, maybe no longer applies)
+ return false;
+ }
+ const auto ep0 = point(startVertex);
+ const auto ep1 = point(nextVertex);
+ const auto diff = RelativePosition3D(ep1 - ep0);
+ const auto length = glm::length(diff);
+ const auto dir = diff / length;
+ const Ray r {ep0, dir};
+ return std::any_of(triangles.begin(), triangles.end(), [&](const auto & triangle) {
+ BaryPosition bary;
+ RelativeDistance dist {};
+
+ if (r.intersectTriangle(triangle.x, triangle.y, triangle.z, bary, dist)
+ && dist <= length - 1 && dist >= 1) {
+ const auto splitPos = triangle * bary;
+ currentVertex = sideVerts.emplace_back(split(edge, splitPos));
+ return true;
+ }
+ return false;
+ });
+ });) {
+ ;
+ }
+ sideVerts.emplace_back(*nexv);
+ sideVerts.emplace_back(toVertex);
+ ex = nex;
+ exv++;
+ return sideVerts;
+ });
+
+ // Remove old faces
+ std::set<FaceHandle> visited;
+ auto removeOld = [&](auto & self, const auto face) -> void {
+ if (visited.insert(face).second) {
+ std::vector<FaceHandle> neighbourFaces;
+ std::for_each(fh_begin(face), fh_end(face), [&](const auto fh) {
+ if (std::none_of(boundaryFaces.begin(), boundaryFaces.end(), [fh, this](const auto & bf) {
+ return std::find(bf.begin(), bf.end(), from_vertex_handle(fh)) != bf.end()
+ && std::find(bf.begin(), bf.end(), to_vertex_handle(fh)) != bf.end();
+ })) {
+ neighbourFaces.emplace_back(opposite_face_handle(fh));
+ }
+ });
+
+ delete_face(face, false);
+ std::for_each(neighbourFaces.begin(), neighbourFaces.end(), [&self](const auto nextFace) {
+ if (nextFace.is_valid()) {
+ self(self, nextFace);
+ }
+ });
+ }
+ };
+ removeOld(removeOld, findPoint(triangleStrip.front()));
+
+ std::for_each(boundaryFaces.begin(), boundaryFaces.end(), [&](auto & boundaryFace) {
+ std::reverse(boundaryFace.begin(), boundaryFace.end());
+ add_face(boundaryFace);
+ });
+
+ // Tidy up
+ garbage_collection();
+ update_vertex_normals_only();
+}
diff --git a/game/geoData.h b/game/geoData.h
index 15143e8..5671954 100644
--- a/game/geoData.h
+++ b/game/geoData.h
@@ -65,7 +65,7 @@ public:
operator*(BaryPosition bari) const
{
const auto & t {*this};
- return t[0] + (RelativePosition<Dim>(t[1] - t[0]) * bari.x) + (RelativePosition<Dim>(t[2] - t[1]) * bari.y);
+ return t[0] + (RelativePosition<Dim>(t[1] - t[0]) * bari.x) + (RelativePosition<Dim>(t[2] - t[0]) * bari.y);
}
};
@@ -73,8 +73,10 @@ public:
[[nodiscard]] FaceHandle findPoint(GlobalPosition2D, FaceHandle start) const;
[[nodiscard]] GlobalPosition3D positionAt(const PointFace &) const;
- [[nodiscard]] std::optional<GlobalPosition3D> intersectRay(const Ray<GlobalPosition3D> &) const;
- [[nodiscard]] std::optional<GlobalPosition3D> intersectRay(const Ray<GlobalPosition3D> &, FaceHandle start) const;
+ using IntersectionLocation = std::pair<GlobalPosition3D, FaceHandle>;
+ using IntersectionResult = std::optional<IntersectionLocation>;
+ [[nodiscard]] IntersectionResult intersectRay(const Ray<GlobalPosition3D> &) const;
+ [[nodiscard]] IntersectionResult intersectRay(const Ray<GlobalPosition3D> &, FaceHandle start) const;
void walk(const PointFace & from, const GlobalPosition2D to, const std::function<void(FaceHandle)> & op) const;
void walkUntil(const PointFace & from, const GlobalPosition2D to, const std::function<bool(FaceHandle)> & op) const;
@@ -86,6 +88,8 @@ public:
[[nodiscard]] HalfedgeHandle findEntry(const GlobalPosition2D from, const GlobalPosition2D to) const;
+ void setHeights(const std::span<const GlobalPosition3D> triangleStrip);
+
[[nodiscard]] auto
getExtents() const
{
@@ -102,6 +106,8 @@ protected:
[[nodiscard]] static bool triangleContainsPoint(const GlobalPosition2D, const Triangle<2> &);
[[nodiscard]] bool triangleContainsPoint(const GlobalPosition2D, FaceHandle) const;
+ [[nodiscard]] static bool triangleOverlapsTriangle(const Triangle<2> &, const Triangle<2> &);
+ [[nodiscard]] static bool triangleContainsTriangle(const Triangle<2> &, const Triangle<2> &);
[[nodiscard]] HalfedgeHandle findBoundaryStart() const;
void update_vertex_normals_only();
diff --git a/game/terrain.cpp b/game/terrain.cpp
index d2c8593..7f59b6c 100644
--- a/game/terrain.cpp
+++ b/game/terrain.cpp
@@ -34,7 +34,7 @@ Terrain::generateMeshes()
[this, &vertexIndex](const GeoData::VertexHandle v) {
vertexIndex.emplace(v, vertexIndex.size());
const auto p = geoData->point(v);
- return Vertex {p, p / 10000, geoData->normal(v)};
+ return Vertex {p, RelativePosition2D(p) / 10000.F, geoData->normal(v)};
});
std::for_each(
geoData->faces_begin(), geoData->faces_end(), [this, &vertexIndex, &indices](const GeoData::FaceHandle f) {
diff --git a/lib/collections.h b/lib/collections.h
index 943b986..dd603be 100644
--- a/lib/collections.h
+++ b/lib/collections.h
@@ -2,6 +2,7 @@
#include <algorithm>
#include <array>
+#include <cstdint>
#include <span>
#include <utility>
#include <vector>
@@ -129,18 +130,25 @@ vectorOfN(std::integral auto N, T start = {}, T step = 1)
return v;
}
+template<template<typename...> typename Rtn = std::vector, typename In>
+[[nodiscard]] auto
+materializeRange(const In begin, const In end)
+{
+ return Rtn(begin, end);
+}
+
template<template<typename...> typename Rtn = std::vector, IterableCollection In>
[[nodiscard]] auto
-materializeRange(In && in)
+materializeRange(const In & in)
{
- return Rtn(in.begin(), in.end());
+ return materializeRange<Rtn>(in.begin(), in.end());
}
template<template<typename...> typename Rtn = std::vector, typename In>
[[nodiscard]] auto
materializeRange(const std::pair<In, In> & in)
{
- return Rtn(in.first, in.second);
+ return materializeRange<Rtn>(in.first, in.second);
}
template<typename T> struct pair_range {
@@ -160,3 +168,62 @@ template<typename T> struct pair_range {
};
template<typename T> pair_range(std::pair<T, T>) -> pair_range<T>;
+
+template<typename iter> struct stripiter {
+ [[nodiscard]] constexpr bool
+ operator!=(const stripiter & other) const
+ {
+ return current != other.current;
+ }
+
+ [[nodiscard]] constexpr bool
+ operator==(const stripiter & other) const
+ {
+ return current == other.current;
+ }
+
+ constexpr stripiter &
+ operator++()
+ {
+ ++current;
+ off = 1 - off;
+ return *this;
+ }
+
+ constexpr stripiter &
+ operator--()
+ {
+ --current;
+ off = 1 - off;
+ return *this;
+ }
+
+ constexpr auto
+ operator-(const stripiter & other) const
+ {
+ return current - other.current;
+ }
+
+ constexpr auto
+ operator*() const
+ {
+ return std::tie(*(current - (2 - off)), *(current - off - 1), *current);
+ }
+
+ iter current;
+ uint8_t off {};
+};
+
+template<typename T> struct std::iterator_traits<stripiter<T>> : std::iterator_traits<T> { };
+
+constexpr auto
+strip_begin(IterableCollection auto & cont)
+{
+ return stripiter {cont.begin() + 2};
+}
+
+constexpr auto
+strip_end(IterableCollection auto & cont)
+{
+ return stripiter {cont.end()};
+}
diff --git a/lib/geometricPlane.h b/lib/geometricPlane.h
index 3f95d3c..3b1a0e4 100644
--- a/lib/geometricPlane.h
+++ b/lib/geometricPlane.h
@@ -27,7 +27,7 @@ public:
[[nodiscard]] inline PlaneRelation
getRelation(PositionType point) const
{
- const auto d = glm::dot(normal, point - origin);
+ const auto d = glm::dot(normal, RelativePosition3D(point - origin));
return d < 0.F ? PlaneRelation::Below : d > 0.F ? PlaneRelation::Above : PlaneRelation::On;
}
diff --git a/lib/ray.h b/lib/ray.h
index e1f43c3..67fdd12 100644
--- a/lib/ray.h
+++ b/lib/ray.h
@@ -47,11 +47,11 @@ public:
RelativeDistance & distance) const
{
if constexpr (std::is_floating_point_v<typename PositionType::value_type>) {
- return glm::intersectRayTriangle(start, direction, t0, t1, t2, bary, distance);
+ return glm::intersectRayTriangle(start, direction, t0, t1, t2, bary, distance) && distance >= 0.F;
}
else {
const RelativePosition3D t0r = t0 - start, t1r = t1 - start, t2r = t2 - start;
- return glm::intersectRayTriangle({}, direction, t0r, t1r, t2r, bary, distance);
+ return glm::intersectRayTriangle({}, direction, t0r, t1r, t2r, bary, distance) && distance >= 0.F;
}
}
diff --git a/test/test-geoData.cpp b/test/test-geoData.cpp
index 302cab7..ac1d668 100644
--- a/test/test-geoData.cpp
+++ b/test/test-geoData.cpp
@@ -1,7 +1,12 @@
#define BOOST_TEST_MODULE terrain
+#include "game/terrain.h"
+#include "test/testMainWindow.h"
+#include "test/testRenderOutput.h"
#include "testHelpers.h"
+#include "ui/applicationBase.h"
#include <boost/test/data/test_case.hpp>
#include <boost/test/unit_test.hpp>
+#include <gfx/gl/sceneRenderer.h>
#include <stream_support.h>
#include <game/geoData.h>
@@ -114,7 +119,7 @@ BOOST_DATA_TEST_CASE(findRayIntersect,
}),
p, d, i)
{
- BOOST_CHECK_EQUAL(fixedTerrtain.intersectRay({p, d}).value(), i);
+ BOOST_CHECK_EQUAL(fixedTerrtain.intersectRay({p, d})->first, i);
}
BOOST_AUTO_TEST_CASE(boundaryWalk)
@@ -199,3 +204,58 @@ BOOST_DATA_TEST_CASE(findEntries,
{
BOOST_CHECK_EQUAL(fixedTerrtain.findEntry(from, to).idx(), heh);
}
+
+BOOST_AUTO_TEST_CASE(setTriangle, *boost::unit_test::timeout(5))
+{
+ auto gd = std::make_shared<GeoData>(GeoData::createFlat({0, 0}, {1000000, 1000000}, 100));
+ std::array points {
+ GlobalPosition3D {70100, 123000, 6000},
+ GlobalPosition3D {50100, 52300, 6000},
+ GlobalPosition3D {191000, 283000, 8000},
+ GlobalPosition3D {241000, 123330, -2000},
+ };
+ BOOST_CHECK_NO_THROW(gd->setHeights(points));
+
+ ApplicationBase ab;
+ TestMainWindow tmw;
+ TestRenderOutput tro {{1792, 1024}};
+
+ SceneRenderer ss {tro.size, tro.output};
+ ss.camera.setView({-90000, -90000, 300000}, glm::normalize(glm::vec3 {1, 1, -1.5F}));
+
+ struct TestTerrain : public SceneProvider {
+ explicit TestTerrain(std::shared_ptr<GeoData> gd) : terrain(std::move(gd)) { }
+
+ const Terrain terrain;
+
+ void
+ content(const SceneShader & shader) const override
+ {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ terrain.render(shader);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }
+
+ void
+ environment(const SceneShader &, const SceneRenderer & sr) const override
+ {
+ sr.setAmbientLight({0.1, 0.1, 0.1});
+ sr.setDirectionalLight({1, 1, 1}, south + down, *this);
+ }
+
+ void
+ lights(const SceneShader &) const override
+ {
+ }
+
+ void
+ shadows(const ShadowMapper & shadowMapper) const override
+ {
+ terrain.shadows(shadowMapper);
+ }
+ };
+
+ TestTerrain t {gd};
+ BOOST_CHECK_NO_THROW(ss.render(t));
+ Texture::save(tro.outImage, "/tmp/geoData.tga");
+}
diff --git a/test/test-lib.cpp b/test/test-lib.cpp
index 58b769a..5f0b5e5 100644
--- a/test/test-lib.cpp
+++ b/test/test-lib.cpp
@@ -4,6 +4,7 @@
#include <boost/test/data/test_case.hpp>
#include <boost/test/unit_test.hpp>
+#include <collections.h>
#include <glArrays.h>
#include <glad/gl.h>
#include <set>
@@ -49,3 +50,22 @@ BOOST_AUTO_TEST_CASE(generate_move_and_delete)
}
BOOST_CHECK(active.empty());
}
+
+constexpr std::array TRIANGLE_STRIP_IN {0, 1, 2, 3, 4, 5};
+static_assert(std::distance(strip_begin(TRIANGLE_STRIP_IN), strip_end(TRIANGLE_STRIP_IN)) == 4);
+
+BOOST_AUTO_TEST_CASE(triangle_strip_iter)
+{
+ constexpr std::array TRIANGLE_STRIP_EXPECTED {0, 1, 2, 2, 1, 3, 2, 3, 4, 4, 3, 5};
+
+ std::vector<int> out;
+ std::for_each(strip_begin(TRIANGLE_STRIP_IN), strip_end(TRIANGLE_STRIP_IN), [&out](const auto & t) {
+ const auto [a, b, c] = t;
+ out.push_back(a);
+ out.push_back(b);
+ 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());
+}
diff --git a/ui/builders/freeExtend.cpp b/ui/builders/freeExtend.cpp
index 47356c3..db127e6 100644
--- a/ui/builders/freeExtend.cpp
+++ b/ui/builders/freeExtend.cpp
@@ -19,7 +19,7 @@ BuilderFreeExtend::move(
candidateLinks.objects = network->candidateJoins(*p1, p->pos);
}
else if (const auto p = geoData->intersectRay(ray)) {
- candidateLinks.objects = network->candidateExtend(*p1, *p);
+ candidateLinks.objects = network->candidateExtend(*p1, p->first);
}
else {
candidateLinks.removeAll();
@@ -42,8 +42,8 @@ BuilderFreeExtend::click(
p1 = p->pos;
}
else if (const auto p = geoData->intersectRay(ray)) {
- network->addExtend(*p1, *p);
- p1 = *p;
+ network->addExtend(*p1, p->first);
+ p1 = p->first;
}
}
else {
diff --git a/ui/builders/straight.cpp b/ui/builders/straight.cpp
index 0c4a3e2..43f5ec8 100644
--- a/ui/builders/straight.cpp
+++ b/ui/builders/straight.cpp
@@ -16,7 +16,7 @@ BuilderStraight::move(
{
if (p1) {
if (const auto p = geoData->intersectRay(ray)) {
- candidateLinks.objects = network->candidateStraight(*p1, *p);
+ candidateLinks.objects = network->candidateStraight(*p1, p->first);
}
else {
candidateLinks.removeAll();
@@ -32,12 +32,12 @@ BuilderStraight::click(
case SDL_BUTTON_LEFT:
if (const auto p = geoData->intersectRay(ray)) {
if (p1) {
- create(network, *p1, *p);
+ create(network, *p1, p->first);
candidateLinks.removeAll();
p1.reset();
}
else {
- p1 = p;
+ p1 = p->first;
}
}
return;