summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/collections.h77
-rw-r--r--lib/maths.h168
-rw-r--r--lib/ray.h3
-rw-r--r--lib/sorting.h9
-rw-r--r--lib/stream_support.h12
-rw-r--r--lib/triangle.h48
-rw-r--r--lib/util.h21
7 files changed, 315 insertions, 23 deletions
diff --git a/lib/collections.h b/lib/collections.h
index ea5d5dc..27eff0a 100644
--- a/lib/collections.h
+++ b/lib/collections.h
@@ -3,6 +3,7 @@
#include <algorithm>
#include <array>
#include <cstdint>
+#include <ranges>
#include <span>
#include <tuple>
#include <utility>
@@ -106,6 +107,15 @@ operator+=(std::vector<T...> & in, std::vector<T...> && src)
return in;
}
+template<typename... T>
+constexpr auto
+operator+(std::vector<T...> in1, std::vector<T...> in2)
+{
+ in1.reserve(in1.size() + in2.size());
+ std::move(in2.begin(), in2.end(), std::back_inserter(in1));
+ return in1;
+}
+
template<typename... T, typename Vn>
[[nodiscard]] constexpr auto
operator+(const std::vector<T...> & in, Vn && vn)
@@ -187,6 +197,14 @@ template<typename iter> struct stripiter {
return *this;
}
+ constexpr stripiter
+ operator++(int)
+ {
+ auto out {*this};
+ ++*this;
+ return out;
+ }
+
constexpr stripiter &
operator--()
{
@@ -195,6 +213,14 @@ template<typename iter> struct stripiter {
return *this;
}
+ constexpr stripiter
+ operator--(int)
+ {
+ auto out {*this};
+ --*this;
+ return out;
+ }
+
constexpr auto
operator-(const stripiter & other) const
{
@@ -224,3 +250,54 @@ strip_end(IterableCollection auto & cont)
{
return stripiter {cont.end()};
}
+
+inline constexpr auto dereference = std::views::transform([](const auto & iter) -> decltype(auto) {
+ return *iter;
+});
+
+struct TriangleTriples : public std::ranges::range_adaptor_closure<TriangleTriples> {
+ decltype(auto)
+ operator()(const auto & triangleStrip) const
+ {
+ return std::views::iota(strip_begin(triangleStrip), strip_end(triangleStrip)) | dereference;
+ }
+};
+
+inline constexpr TriangleTriples triangleTriples;
+
+template<typename T, typename Dist, typename Merger>
+void
+mergeClose(std::vector<T> & range, const Dist & dist, const Merger & merger,
+ decltype(dist(range.front(), range.front())) tolerance)
+{
+ using DistanceType = decltype(tolerance);
+ std::vector<DistanceType> 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<std::size_t>(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/lib/maths.h b/lib/maths.h
index 3959896..2049c78 100644
--- a/lib/maths.h
+++ b/lib/maths.h
@@ -1,6 +1,8 @@
#pragma once
#include "config/types.h"
+#include <algorithm>
+#include <array>
#include <cmath>
#include <glm/glm.hpp>
#include <glm/gtc/constants.hpp>
@@ -12,8 +14,11 @@
template<typename T>
concept Arithmetic = std::is_arithmetic_v<T>;
+template<Arithmetic T> using CalcType = std::conditional_t<std::is_floating_point_v<T>, T, int64_t>;
+template<Arithmetic T> using DifferenceType = std::conditional_t<std::is_floating_point_v<T>, T, float>;
+
struct Arc : public std::pair<Angle, Angle> {
- template<glm::length_t Lc, glm::length_t Le, Arithmetic T, glm::qualifier Q>
+ template<glm::length_t Lc, glm::length_t Le, Arithmetic T, glm::qualifier Q = glm::defaultp>
requires(Lc >= 2, Le >= 2)
Arc(const glm::vec<Lc, T, Q> & centre, const glm::vec<Le, T, Q> & e0p, const glm::vec<Le, T, Q> & e1p) :
Arc {RelativePosition2D {e0p.xy() - centre.xy()}, RelativePosition2D {e1p.xy() - centre.xy()}}
@@ -36,6 +41,26 @@ struct Arc : public std::pair<Angle, Angle> {
}
};
+template<typename T, glm::qualifier Q = glm::defaultp> 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<std::pair<glm::vec<2, T, Q>, Angle>> 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};
@@ -79,13 +104,33 @@ operator-(const GlobalPosition<D> & global, const CalcPosition<D> & relative)
return global - GlobalPosition<D>(relative);
}
-template<glm::length_t D, std::integral T, glm::qualifier Q>
-constexpr RelativePosition<D>
+template<glm::length_t D, Arithmetic T, glm::qualifier Q = glm::defaultp>
+using DifferenceVector = glm::vec<D, DifferenceType<T>, Q>;
+
+template<glm::length_t D, Arithmetic T, glm::qualifier Q = glm::defaultp>
+constexpr DifferenceVector<D, T, Q>
difference(const glm::vec<D, T, Q> & globalA, const glm::vec<D, T, Q> & globalB)
{
return globalA - globalB;
}
+template<glm::length_t D, Arithmetic T, glm::qualifier Q = glm::defaultp>
+using CalcVector = glm::vec<D, CalcType<T>, Q>;
+
+template<glm::length_t D, Arithmetic T, glm::qualifier Q = glm::defaultp>
+constexpr CalcVector<D, T, Q>
+calcDifference(const glm::vec<D, T, Q> & globalA, const glm::vec<D, T, Q> & globalB)
+{
+ return globalA - globalB;
+}
+
+template<glm::length_t D, Arithmetic T, glm::qualifier Q = glm::defaultp>
+constexpr auto
+distance(const glm::vec<D, T, Q> & pointA, const glm::vec<D, T, Q> & pointB)
+{
+ return glm::length(difference(pointA, pointB));
+}
+
glm::mat4 flat_orientation(const Rotation3D & diff);
namespace {
@@ -122,7 +167,8 @@ namespace {
}
// Helper to lookup into a matrix given an xy vector coordinate
- template<glm::length_t C, glm::length_t R, Arithmetic T, glm::qualifier Q, std::integral I = glm::length_t>
+ template<glm::length_t C, glm::length_t R, Arithmetic T, glm::qualifier Q = glm::defaultp,
+ std::integral I = glm::length_t>
constexpr auto &
operator^(glm::mat<C, R, T, Q> & matrix, const glm::vec<2, I> rowCol)
{
@@ -130,7 +176,7 @@ namespace {
}
// Create a matrix for the angle, given the targets into the matrix
- template<glm::length_t D, std::floating_point T, glm::qualifier Q, std::integral I = glm::length_t>
+ template<glm::length_t D, std::floating_point T, glm::qualifier Q = glm::defaultp, std::integral I = glm::length_t>
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)
@@ -220,7 +266,7 @@ vector_pitch(const glm::vec<D, T, Q> & diff)
return std::atan(diff.z);
}
-template<std::floating_point T, glm::qualifier Q>
+template<Arithmetic T, glm::qualifier Q = glm::defaultp>
constexpr glm::vec<2, T, Q>
vector_normal(const glm::vec<2, T, Q> & vector)
{
@@ -242,7 +288,7 @@ sq(T value)
return value * value;
}
-template<glm::qualifier Q>
+template<glm::qualifier Q = glm::defaultp>
constexpr glm::vec<3, int64_t, Q>
crossProduct(const glm::vec<3, int64_t, Q> & valueA, const glm::vec<3, int64_t, Q> & valueB)
{
@@ -253,14 +299,14 @@ crossProduct(const glm::vec<3, int64_t, Q> & valueA, const glm::vec<3, int64_t,
};
}
-template<std::integral T, glm::qualifier Q>
+template<std::integral T, glm::qualifier Q = glm::defaultp>
constexpr glm::vec<3, T, Q>
crossProduct(const glm::vec<3, T, Q> & valueA, const glm::vec<3, T, Q> & valueB)
{
return crossProduct<Q>(valueA, valueB);
}
-template<std::floating_point T, glm::qualifier Q>
+template<std::floating_point T, glm::qualifier Q = glm::defaultp>
constexpr glm::vec<3, T, Q>
crossProduct(const glm::vec<3, T, Q> & valueA, const glm::vec<3, T, Q> & valueB)
{
@@ -275,35 +321,35 @@ ratio(const Ta valueA, const Tb valueB)
return static_cast<R>((static_cast<Common>(valueA) / static_cast<Common>(valueB)));
}
-template<Arithmetic R = float, Arithmetic T, glm::qualifier Q>
+template<Arithmetic R = float, Arithmetic T, glm::qualifier Q = glm::defaultp>
constexpr auto
ratio(const glm::vec<2, T, Q> & value)
{
return ratio<R>(value.x, value.y);
}
-template<glm::length_t L = 3, std::floating_point T, glm::qualifier Q>
+template<glm::length_t L = 3, std::floating_point T, glm::qualifier Q = glm::defaultp>
constexpr auto
perspective_divide(const glm::vec<4, T, Q> & value)
{
return value / value.w;
}
-template<glm::length_t L1, glm::length_t L2, Arithmetic T, glm::qualifier Q>
+template<glm::length_t L1, glm::length_t L2, Arithmetic T, glm::qualifier Q = glm::defaultp>
constexpr glm::vec<L1 + L2, T, Q>
operator||(const glm::vec<L1, T, Q> valueA, const glm::vec<L2, T, Q> valueB)
{
return {valueA, valueB};
}
-template<glm::length_t L, Arithmetic T, glm::qualifier Q>
+template<glm::length_t L, Arithmetic T, glm::qualifier Q = glm::defaultp>
constexpr glm::vec<L + 1, T, Q>
operator||(const glm::vec<L, T, Q> valueA, const T valueB)
{
return {valueA, valueB};
}
-template<glm::length_t L, std::floating_point T, glm::qualifier Q>
+template<glm::length_t L, std::floating_point T, glm::qualifier Q = glm::defaultp>
constexpr glm::vec<L, T, Q>
perspectiveMultiply(const glm::vec<L, T, Q> & base, const glm::mat<L + 1, L + 1, T, Q> & mutation)
{
@@ -311,7 +357,7 @@ perspectiveMultiply(const glm::vec<L, T, Q> & base, const glm::mat<L + 1, L + 1,
return mutated / mutated.w;
}
-template<glm::length_t L, std::floating_point T, glm::qualifier Q>
+template<glm::length_t L, std::floating_point T, glm::qualifier Q = glm::defaultp>
constexpr glm::vec<L, T, Q>
perspectiveApply(glm::vec<L, T, Q> & base, const glm::mat<L + 1, L + 1, T, Q> & mutation)
{
@@ -331,8 +377,6 @@ normalize(T ang)
return ang;
}
-template<Arithmetic T> using CalcType = std::conditional_t<std::is_floating_point_v<T>, T, int64_t>;
-
template<Arithmetic T, glm::qualifier Q = glm::defaultp>
[[nodiscard]] constexpr std::optional<glm::vec<2, T, Q>>
linesIntersectAt(const glm::vec<2, T, Q> Aabs, const glm::vec<2, T, Q> Babs, const glm::vec<2, T, Q> Cabs,
@@ -359,7 +403,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<Arithmetic T, glm::qualifier Q>
+template<Arithmetic T, glm::qualifier Q = glm::defaultp>
std::pair<glm::vec<2, T, Q>, bool>
find_arc_centre(glm::vec<2, T, Q> start, Rotation2D startDir, glm::vec<2, T, Q> end, Rotation2D endDir)
{
@@ -372,7 +416,7 @@ find_arc_centre(glm::vec<2, T, Q> start, Rotation2D startDir, glm::vec<2, T, Q>
throw std::runtime_error("no intersection");
}
-template<Arithmetic T, glm::qualifier Q>
+template<Arithmetic T, glm::qualifier Q = glm::defaultp>
std::pair<glm::vec<2, T, Q>, bool>
find_arc_centre(glm::vec<2, T, Q> start, Angle entrys, glm::vec<2, T, Q> end, Angle entrye)
{
@@ -382,7 +426,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<Arithmetic T, glm::qualifier Q>
+template<Arithmetic T, glm::qualifier Q = glm::defaultp>
Angle
find_arcs_radius(glm::vec<2, T, Q> start, Rotation2D ad, glm::vec<2, T, Q> end, Rotation2D bd)
{
@@ -407,7 +451,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<Arithmetic T, glm::qualifier Q>
+template<Arithmetic T, glm::qualifier Q = glm::defaultp>
std::pair<Angle, Angle>
find_arcs_radius(glm::vec<2, T, Q> start, Angle entrys, glm::vec<2, T, Q> end, Angle entrye)
{
@@ -424,6 +468,13 @@ midpoint(const std::pair<T, T> & v)
return std::midpoint(v.first, v.second);
}
+template<glm::length_t D, std::integral T, glm::qualifier Q = glm::defaultp>
+auto
+midpoint(const glm::vec<D, T, Q> & valueA, const glm::vec<D, T, Q> & valueB)
+{
+ return valueA + (valueB - valueA) / 2;
+}
+
// std::pow is not constexpr
template<Arithmetic T>
requires requires(T n) { n *= n; }
@@ -461,3 +512,78 @@ operator"" _degrees(long double degrees)
{
return static_cast<float>(degrees) * degreesToRads;
}
+
+// Late implementations due to dependencies
+template<typename T, glm::qualifier Q>
+constexpr ArcSegment<T, Q>::ArcSegment(PointType centre, PointType ep0, PointType ep1) :
+ Arc {centre, ep0, ep1}, centre {centre}, ep0 {ep0}, ep1 {ep1}, radius {::distance(centre, ep0)}
+{
+}
+
+template<typename T, glm::qualifier Q>
+[[nodiscard]] constexpr std::optional<std::pair<glm::vec<2, T, Q>, Angle>>
+ArcSegment<T, Q>::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<std::pair<RelativePosition2D, Angle>, 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.first - lineRelStart);
+ return !angleWithinArc(point.second) || dot < 0 || dot > drdr;
+ });
+ if (points.begin() == end) {
+ return std::nullopt;
+ }
+ 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);
+}
+
+namespace {
+ template<template<typename> 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<std::greater>;
+constexpr auto pointLeftOfOrOnLine = pointLineOp<std::greater_equal>;
+
+[[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/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<typename PositionType::value_type>::infinity();
}
return static_cast<PositionType::value_type>(glm::abs(glm::dot(n, RelativePosition3D(p1 - start))));
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 <functional>
#include <glm/fwd.hpp>
#include <type_traits>
@@ -30,6 +31,14 @@ template<typename T, auto M> struct PtrMemberSorter : public PtrSorter<T> {
}
};
+template<auto Proj> 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;
diff --git a/lib/stream_support.h b/lib/stream_support.h
index f21622a..5f276fd 100644
--- a/lib/stream_support.h
+++ b/lib/stream_support.h
@@ -4,6 +4,7 @@
#include <glm/glm.hpp>
#include <iostream>
#include <maths.h>
+#include <optional>
#include <source_location>
#include <span>
#include <sstream>
@@ -82,6 +83,16 @@ namespace std {
{
return s << EnumTypeDetails<E>::typeName << "::" << EnumDetails<E>::to_string(e).value();
}
+
+ template<typename T>
+ inline std::ostream &
+ operator<<(std::ostream & s, const std::optional<T> & v)
+ {
+ if (v) {
+ return s << *v;
+ }
+ return s << "nullopt";
+ }
}
template<typename T>
@@ -104,3 +115,4 @@ namespace {
}
#define CLOG(x) clogImpl(x, #x)
+#define CLOGf(...) clogImpl(std::format(__VA_ARGS__), "msg")
diff --git a/lib/triangle.h b/lib/triangle.h
index 812bfab..abd697c 100644
--- a/lib/triangle.h
+++ b/lib/triangle.h
@@ -31,6 +31,13 @@ struct Triangle : public glm::vec<3, glm::vec<Dim, T, Q>> {
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)
@@ -38,6 +45,12 @@ struct Triangle : public glm::vec<3, glm::vec<Dim, T, Q>> {
return crossProduct(sideDifference(1), sideDifference(2));
}
+ [[nodiscard]] constexpr auto
+ height()
+ {
+ return (area() * 2) / ::distance(p(0), p(1));
+ }
+
[[nodiscard]] constexpr Normal3D
nnormal() const
requires(Dim == 3)
@@ -52,6 +65,12 @@ struct Triangle : public glm::vec<3, glm::vec<Dim, T, Q>> {
}
[[nodiscard]] constexpr auto
+ calcSideDifference(glm::length_t side) const
+ {
+ return calcDifference(p(side), p(0));
+ }
+
+ [[nodiscard]] constexpr auto
angle(glm::length_t corner) const
{
return Arc {P(corner), P(corner + 2), P(corner + 1)}.length();
@@ -71,6 +90,14 @@ struct Triangle : public glm::vec<3, glm::vec<Dim, T, Q>> {
}
[[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
{
return Base::operator[](idx);
@@ -105,4 +132,25 @@ struct Triangle : public glm::vec<3, glm::vec<Dim, T, Q>> {
{
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<T>(
+ ((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/lib/util.h b/lib/util.h
index 290492f..cd7971b 100644
--- a/lib/util.h
+++ b/lib/util.h
@@ -3,6 +3,7 @@
#include <algorithm> // IWYU pragma: keep
#include <array>
#include <cstddef>
+#include <tuple>
template<typename T, std::size_t N>
constexpr auto
@@ -12,3 +13,23 @@ transform_array(const std::array<T, N> & in, auto && transform)
std::transform(in.begin(), in.end(), out.begin(), transform);
return out;
}
+
+namespace {
+ template<size_t... N> struct GetNth {
+ decltype(auto)
+ operator()(const auto & tup) const
+ {
+ if constexpr (sizeof...(N) == 1) {
+ return std::get<N...>(tup);
+ }
+ else {
+ return std::tie(std::get<N>(tup)...);
+ }
+ }
+ };
+}
+
+template<size_t... N> inline constexpr auto Nth = GetNth<N...> {};
+inline constexpr auto GetFirst = Nth<0>;
+inline constexpr auto GetSecond = Nth<1>;
+inline constexpr auto GetSwapped = Nth<0, 1>;