summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/chronology.cpp12
-rw-r--r--lib/chronology.h2
-rw-r--r--lib/collection.h263
-rw-r--r--lib/collections.h86
-rw-r--r--lib/enumDetails.h102
-rw-r--r--lib/filesystem.cpp2
-rw-r--r--lib/filesystem.h2
-rw-r--r--lib/glArrays.h26
-rw-r--r--lib/glMappedBufferWriter.cpp4
-rw-r--r--lib/glMappedBufferWriter.h78
-rw-r--r--lib/jsonParse-persistence.h3
-rw-r--r--lib/jsonParse.ll9
-rw-r--r--lib/manyPtr.h86
-rw-r--r--lib/maths.cpp105
-rw-r--r--lib/maths.h517
-rw-r--r--lib/persistence.h3
-rw-r--r--lib/ray.h50
-rw-r--r--lib/sorting.h9
-rw-r--r--lib/stream_support.h52
-rw-r--r--lib/triangle.h156
-rw-r--r--lib/util.h21
-rw-r--r--lib/worker.cpp5
22 files changed, 1271 insertions, 322 deletions
diff --git a/lib/chronology.cpp b/lib/chronology.cpp
new file mode 100644
index 0000000..8707bba
--- /dev/null
+++ b/lib/chronology.cpp
@@ -0,0 +1,12 @@
+#include "chronology.h"
+
+time_t
+operator""_time_t(const char * iso, size_t)
+{
+ struct tm tm {};
+
+ if (const auto end = strptime(iso, "%FT%T", &tm); !end || *end) {
+ throw std::invalid_argument("Invalid date");
+ }
+ return mktime(&tm);
+}
diff --git a/lib/chronology.h b/lib/chronology.h
index 1980116..688a1f7 100644
--- a/lib/chronology.h
+++ b/lib/chronology.h
@@ -1,5 +1,7 @@
#pragma once
#include <chrono>
+#include <ctime>
using TickDuration = std::chrono::duration<float, std::chrono::seconds::period>;
+time_t operator""_time_t(const char * iso, size_t);
diff --git a/lib/collection.h b/lib/collection.h
index 6802bcb..1c77e1c 100644
--- a/lib/collection.h
+++ b/lib/collection.h
@@ -3,29 +3,62 @@
#include <algorithm>
#include <functional>
#include <memory>
+#include <special_members.h>
#include <type_traits>
#include <vector>
-template<typename Object, bool shared = true> class Collection {
+template<typename Ptr, typename... Others> class Collection {
public:
+ Collection() = default;
virtual ~Collection() = default;
- using Ptr = std::conditional_t<shared, std::shared_ptr<Object>, std::unique_ptr<Object>>;
+ DEFAULT_MOVE_NO_COPY(Collection);
+
+ using Object = Ptr::element_type;
using Objects = std::vector<Ptr>;
- Objects objects;
+ template<typename T> using OtherObjects = std::vector<T *>;
+
+ Collection &
+ operator=(Objects && other)
+ {
+ objects = std::move(other);
+ ((std::get<OtherObjects<Others>>(otherObjects).clear()), ...);
+ for (const auto & other : objects) {
+ addOthersPtr(other.get());
+ }
+ return *this;
+ }
+
+ const Ptr &
+ operator[](size_t idx) const
+ {
+ return objects[idx];
+ }
+
+ template<typename T = Object>
+ requires(std::is_same_v<T, Object> || (std::is_base_of_v<Others, T> || ...))
+ [[nodiscard]] auto
+ size() const noexcept
+ {
+ return containerFor<T>().size();
+ }
template<typename T = Object, typename... Params>
auto
create(Params &&... params)
requires std::is_base_of_v<Object, T>
{
- if constexpr (shared) {
+ if constexpr (requires(Ptr ptr) { ptr = std::make_shared<T>(std::forward<Params>(params)...); }) {
auto obj = std::make_shared<T>(std::forward<Params>(params)...);
objects.emplace_back(obj);
+ addOthersType<T>(obj.get());
return obj;
}
else {
- return static_cast<T *>(objects.emplace_back(std::make_unique<T>(std::forward<Params>(params)...)).get());
+ auto obj = static_cast<T *>(
+ objects.emplace_back(std::make_unique<T>(std::forward<Params>(params)...)).get());
+ addOthersType<T>(obj);
+ return obj;
}
}
@@ -33,12 +66,19 @@ public:
T *
find()
{
- if (auto i = std::find_if(objects.begin(), objects.end(),
- [](auto && o) {
- return (dynamic_cast<T *>(o.get()));
- });
- i != objects.end()) {
- return static_cast<T *>(i->get());
+ const auto & srcObjects = containerFor<T>();
+ if constexpr (std::is_convertible_v<typename std::remove_reference_t<decltype(srcObjects)>::value_type, T *>) {
+ if (srcObjects.empty()) {
+ return nullptr;
+ }
+ return srcObjects.front();
+ }
+ else if (auto i = std::find_if(srcObjects.begin(), srcObjects.end(),
+ [](auto && o) {
+ return dynamic_cast<T *>(std::to_address(o)) != nullptr;
+ });
+ i != srcObjects.end()) {
+ return static_cast<T *>(std::to_address(*i));
}
return nullptr;
}
@@ -58,74 +98,229 @@ public:
auto
apply(const auto & m, Params &&... params) const
{
- return apply_internal<T>(objects.begin(), objects.end(), m, std::forward<Params>(params)...);
+ const auto & srcObjects = containerFor<T>();
+ return apply_internal<T>(srcObjects.begin(), srcObjects.end(), m, std::forward<Params>(params)...);
}
template<typename T = Object, typename... Params>
auto
rapply(const auto & m, Params &&... params) const
{
- return apply_internal<T>(objects.rbegin(), objects.rend(), m, std::forward<Params>(params)...);
+ const auto & srcObjects = containerFor<T>();
+ return apply_internal<T>(srcObjects.rbegin(), srcObjects.rend(), m, std::forward<Params>(params)...);
}
template<typename T = Object, typename... Params>
auto
applyOne(const auto & m, Params &&... params) const
{
- return applyOne_internal<T>(objects.begin(), objects.end(), m, std::forward<Params>(params)...);
+ const auto & srcObjects = containerFor<T>();
+ return applyOne_internal<T>(srcObjects.begin(), srcObjects.end(), m, std::forward<Params>(params)...);
}
template<typename T = Object, typename... Params>
auto
rapplyOne(const auto & m, Params &&... params) const
{
- return applyOne_internal<T>(objects.rbegin(), objects.rend(), m, std::forward<Params>(params)...);
+ const auto & srcObjects = containerFor<T>();
+ return applyOne_internal<T>(srcObjects.rbegin(), srcObjects.rend(), m, std::forward<Params>(params)...);
}
- template<typename T = Object>
+ template<typename T>
+ requires std::is_base_of_v<Object, T>
auto
removeAll()
{
- return std::erase_if(objects, [](auto && op) {
- return dynamic_cast<T *>(op.get());
- });
+ auto removeAllFrom = [](auto & container) {
+ if constexpr (std::is_base_of_v<T, std::decay_t<decltype(*container.front())>>) {
+ const auto size = container.size();
+ container.clear();
+ return size;
+ }
+ else {
+ return std::erase_if(container, [](auto && objPtr) -> bool {
+ return dynamic_cast<const T *>(std::to_address(objPtr));
+ });
+ }
+ };
+ (removeAllFrom(std::get<OtherObjects<Others>>(otherObjects)), ...);
+ return removeAllFrom(objects);
+ }
+
+ void
+ clear()
+ {
+ ((std::get<OtherObjects<Others>>(otherObjects).clear()), ...);
+ objects.clear();
}
- auto
+ [[nodiscard]] auto
+ begin() const
+ {
+ return objects.begin();
+ }
+
+ [[nodiscard]] auto
end() const
{
return objects.end();
}
- auto
+ [[nodiscard]] auto
+ rbegin() const
+ {
+ return objects.rbegin();
+ }
+
+ [[nodiscard]] auto
rend() const
{
return objects.rend();
}
+ [[nodiscard]] bool
+ empty() const
+ {
+ return objects.empty();
+ }
+
+ decltype(auto)
+ emplace(Ptr && ptr)
+ {
+ const auto & object = objects.emplace_back(std::move(ptr));
+ addOthersPtr(object.get());
+ return object;
+ }
+
protected:
+ Objects objects;
+ std::tuple<OtherObjects<Others>...> otherObjects;
+
+ template<typename T>
+ void
+ addOthersType(T * obj)
+ {
+ applyToOthersType<T>(
+ [](auto & others, auto ptr) {
+ others.emplace_back(ptr);
+ },
+ obj);
+ }
+
+ void
+ addOthersPtr(Object * obj)
+ {
+ applyToOthersPtr(
+ [](auto & others, auto ptr) {
+ others.emplace_back(ptr);
+ },
+ obj);
+ }
+
+ template<typename T, typename... Params>
+ requires(sizeof...(Others) == 0)
+ void
+ applyToOthersType(const auto &, Params...)
+ {
+ }
+
+ void
+ applyToOthersPtr(const auto &, Object *)
+ requires(sizeof...(Others) == 0)
+ {
+ }
+
+ template<typename T, typename... Params>
+ requires(sizeof...(Others) > 0)
+ void
+ applyToOthersType(const auto & func, Params &&... params)
+ {
+ (
+ [&]() {
+ if constexpr (std::is_convertible_v<T *, Others *>) {
+ std::invoke(
+ func, std::get<OtherObjects<Others>>(otherObjects), std::forward<Params>(params)...);
+ }
+ }(),
+ ...);
+ }
+
+ void
+ applyToOthersPtr(const auto & func, Object * obj)
+ requires(sizeof...(Others) > 0)
+ {
+ (
+ [&]() {
+ if (auto ptr = dynamic_cast<Others *>(obj)) {
+ std::invoke(func, std::get<OtherObjects<Others>>(otherObjects), ptr);
+ }
+ }(),
+ ...);
+ }
+
+ template<typename T>
+ requires((std::is_base_of_v<Others, T> || ...))
+ [[nodiscard]] consteval static size_t
+ idx()
+ {
+ size_t typeIdx = 0;
+ auto found = ((++typeIdx && std::is_base_of_v<Others, T>) || ...);
+ return typeIdx - found;
+ }
+
+ template<typename T>
+ [[nodiscard]]
+ constexpr const auto &
+ containerFor() const
+ {
+ if constexpr ((std::is_base_of_v<Others, T> || ...)) {
+ return std::get<idx<T>()>(otherObjects);
+ }
+ else {
+ return objects;
+ }
+ }
+
template<typename T = Object, typename... Params>
auto
apply_internal(const auto begin, const auto end, const auto & m, Params &&... params) const
{
- return std::count_if(begin, end, [&m, &params...](auto && op) {
- if (auto o = dynamic_cast<T *>(op.get())) {
- std::invoke(m, o, std::forward<Params>(params)...);
- return true;
- }
- return false;
- });
+ if constexpr (std::is_convertible_v<decltype(std::to_address(*begin)), T *>) {
+ std::for_each(begin, end, [&m, &params...](auto && op) {
+ std::invoke(m, op, std::forward<Params>(params)...);
+ });
+ return std::distance(begin, end);
+ }
+ else {
+ return std::count_if(begin, end, [&m, &params...](auto && op) {
+ if (auto o = dynamic_cast<T *>(std::to_address(op))) {
+ std::invoke(m, o, std::forward<Params>(params)...);
+ return true;
+ }
+ return false;
+ });
+ }
}
template<typename T = Object, typename... Params>
auto
applyOne_internal(const auto begin, const auto end, const auto & m, Params &&... params) const
{
- return std::find_if(begin, end, [&m, &params...](auto && op) {
- if (auto o = dynamic_cast<T *>(op.get())) {
- return std::invoke(m, o, std::forward<Params>(params)...);
- }
- return false;
- });
+ if constexpr (std::is_convertible_v<decltype(std::to_address(*begin)), T *>) {
+ return std::find_if(begin, end, [&m, &params...](auto && op) {
+ return std::invoke(m, op, std::forward<Params>(params)...);
+ });
+ }
+ else {
+ return std::find_if(begin, end, [&m, &params...](auto && op) {
+ if (auto o = dynamic_cast<T *>(std::to_address(op))) {
+ return std::invoke(m, o, std::forward<Params>(params)...);
+ }
+ return false;
+ });
+ }
}
};
+
+template<typename T, typename... Others> using SharedCollection = Collection<std::shared_ptr<T>, Others...>;
+template<typename T, typename... Others> using UniqueCollection = Collection<std::unique_ptr<T>, Others...>;
diff --git a/lib/collections.h b/lib/collections.h
index ea5d5dc..e182af5 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>
@@ -44,6 +45,7 @@ operator*(const std::array<T, first> & a, const std::array<V, second> & b)
template<typename T, std::size_t N>
[[nodiscard]] constexpr auto
operator*(const std::array<T, N> & in, auto && f)
+ requires requires { f(in.front()); }
{
std::array<decltype(f(in[0])), N> out;
@@ -106,6 +108,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)
@@ -129,16 +140,16 @@ vectorOfN(std::integral auto N, T start = {}, T step = 1)
template<template<typename...> typename Rtn = std::vector, typename In>
[[nodiscard]] auto
-materializeRange(const In begin, const In end)
+materializeRange(In && begin, In && end)
{
- return Rtn(begin, end);
+ return Rtn(std::forward<In>(begin), std::forward<In>(end));
}
template<template<typename...> typename Rtn = std::vector, IterableCollection In>
[[nodiscard]] auto
-materializeRange(const In & in)
+materializeRange(In && in)
{
- return materializeRange<Rtn>(in.begin(), in.end());
+ return materializeRange<Rtn>(std::forward<In>(in).begin(), std::forward<In>(in).end());
}
template<template<typename...> typename Rtn = std::vector, typename In>
@@ -187,6 +198,14 @@ template<typename iter> struct stripiter {
return *this;
}
+ constexpr stripiter
+ operator++(int)
+ {
+ auto out {*this};
+ ++*this;
+ return out;
+ }
+
constexpr stripiter &
operator--()
{
@@ -195,6 +214,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 +251,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/enumDetails.h b/lib/enumDetails.h
index dfae082..49906d4 100644
--- a/lib/enumDetails.h
+++ b/lib/enumDetails.h
@@ -9,11 +9,11 @@
/// EnumDetailsBase
// Shared helpers
struct EnumDetailsBase {
- template<size_t len>
+ template<size_t Len>
constexpr static auto
strArr(auto input, auto start, auto end)
{
- std::array<char, len> out;
+ std::array<char, Len> out;
input.copy(out.begin(), end - start, start);
return out;
}
@@ -33,22 +33,22 @@ protected:
return std::string_view {__PRETTY_FUNCTION__};
};
- constexpr static auto typeNameStart {typeraw().find(SEARCH_TYPE) + SEARCH_TYPE.length()};
- constexpr static auto typeNameEnd {typeraw().find_first_of("];", typeNameStart)};
- constexpr static auto typeNameLen {typeNameEnd - typeNameStart};
- constexpr static auto typeNameArr {strArr<typeNameLen>(typeraw(), typeNameStart, typeNameEnd)};
+ constexpr static auto TYPE_NAME_START {typeraw().find(SEARCH_TYPE) + SEARCH_TYPE.length()};
+ constexpr static auto TYPE_NAME_END {typeraw().find_first_of("];", TYPE_NAME_START)};
+ constexpr static auto TYPE_NAME_LEN {TYPE_NAME_END - TYPE_NAME_START};
+ constexpr static auto TYPE_NAME_ARR {strArr<TYPE_NAME_LEN>(typeraw(), TYPE_NAME_START, TYPE_NAME_END)};
public:
- constexpr static std::string_view typeName {typeNameArr.data(), typeNameArr.size()};
+ constexpr static std::string_view TYPE_NAME {TYPE_NAME_ARR.data(), TYPE_NAME_ARR.size()};
};
/// EnumValueDetails
// Extracts the value name and constructs string_views of the parts
-template<auto value> struct EnumValueDetails : public EnumTypeDetails<decltype(value)> {
+template<auto Value> struct EnumValueDetails : public EnumTypeDetails<decltype(Value)> {
#ifndef ENUM_PROBE
private:
#endif
- using T = EnumTypeDetails<decltype(value)>;
+ using T = EnumTypeDetails<decltype(Value)>;
constexpr static auto
raw()
@@ -56,26 +56,23 @@ private:
return std::string_view {__PRETTY_FUNCTION__};
};
- constexpr static auto nameStart {raw().find_last_of(": ") + 1};
- constexpr static auto nameEnd {raw().find_first_of("];", nameStart)};
- constexpr static auto nameLen {nameEnd - nameStart};
- constexpr static auto nameArr {EnumValueDetails::template strArr<nameLen>(raw(), nameStart, nameEnd)};
+ constexpr static auto NAME_START {raw().find_last_of(": ") + 1};
+ constexpr static auto NAME_END {raw().find_first_of("];", NAME_START)};
+ constexpr static auto NAME_LEN {NAME_END - NAME_START};
+ constexpr static auto NAME_ARR {EnumValueDetails::template strArr<NAME_LEN>(raw(), NAME_START, NAME_END)};
public:
- constexpr static std::string_view valueName {nameArr.data(), nameArr.size()};
- constexpr static auto valid {valueName.back() < '0' || valueName.back() > '9'};
-#pragma GCC diagnostic push
-#ifdef __clang__
-# pragma GCC diagnostic ignored "-Wenum-constexpr-conversion"
-#endif
- constexpr static auto raw_value {value};
-#pragma GCC diagnostic pop
+ constexpr static std::string_view VALUE_NAME {NAME_ARR.data(), NAME_ARR.size()};
+ constexpr static auto VALID {VALUE_NAME.back() < '0' || VALUE_NAME.back() > '9'};
+ constexpr static auto RAW_VALUE {Value};
};
/// EnumValueCollection
// Customisation point for specifying the range of underlying values your enum can have
template<typename E> struct EnumValueCollection {
- using Vs = std::make_integer_sequence<int, 256>;
+ using Underlying = std::underlying_type_t<E>;
+ static constexpr Underlying UPPER = std::numeric_limits<Underlying>::max();
+ using Vs = std::make_integer_sequence<Underlying, UPPER>;
};
/// EnumDetails
@@ -84,45 +81,36 @@ template<typename E> struct EnumDetails {
#ifndef ENUM_PROBE
private:
#endif
- template<auto... n>
+ using Underlying = std::underlying_type_t<E>;
+
+ template<auto... N>
constexpr static auto
- get_valids(std::integer_sequence<int, n...>)
+ getValids(std::integer_sequence<Underlying, N...>)
{
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wconversion"
-#ifdef __clang__
-# pragma GCC diagnostic ignored "-Wenum-constexpr-conversion"
-#endif
- return std::array {EnumValueDetails<static_cast<E>(n)>::valid...};
-#pragma GCC diagnostic pop
+ return std::array {EnumValueDetails<static_cast<E>(N)>::VALID...};
}
- template<auto... n>
+ template<auto... N>
constexpr static auto
- get_values(std::integer_sequence<int, n...>)
+ getValues(std::integer_sequence<Underlying, N...>)
{
-#pragma GCC diagnostic push
-#ifdef __clang__
-# pragma GCC diagnostic ignored "-Wenum-constexpr-conversion"
-#endif
- return std::array {EnumValueDetails<static_cast<E>(n)>::raw_value...};
-#pragma GCC diagnostic pop
+ return std::array {EnumValueDetails<static_cast<E>(N)>::RAW_VALUE...};
}
- template<auto... n>
+ template<auto... N>
constexpr static auto
- get_valueNames(std::integer_sequence<int, n...>)
+ getValueNames(std::integer_sequence<int, N...>)
{
- return std::array {EnumValueDetails<values[n]>::valueName...};
+ return std::array {EnumValueDetails<VALUES[N]>::VALUE_NAME...};
}
using EVC = EnumValueCollection<E>;
- constexpr static auto valid_flags {get_valids(typename EVC::Vs {})};
- constexpr static auto valid_count {std::count_if(valid_flags.begin(), valid_flags.end(), std::identity {})};
+ constexpr static auto VALID_FLAGS {getValids(typename EVC::Vs {})};
+ constexpr static auto VALID_COUNT {std::count_if(VALID_FLAGS.begin(), VALID_FLAGS.end(), std::identity {})};
constexpr static auto
- lookup(const auto key, const auto & search,
- const auto & out) -> std::optional<typename std::decay_t<decltype(out)>::value_type>
+ lookup(const auto key, const auto & search, const auto & out)
+ -> std::optional<typename std::decay_t<decltype(out)>::value_type>
{
if (const auto itr = std::find(search.begin(), search.end(), key); itr != search.end()) {
return out[static_cast<std::size_t>(std::distance(search.begin(), itr))];
@@ -131,32 +119,32 @@ private:
}
public:
- constexpr static auto values {[]() {
- constexpr auto values {get_values(typename EVC::Vs {})};
- static_assert(std::is_sorted(values.begin(), values.end()), "Candidate values must be sorted");
- std::array<E, valid_count> out;
- std::copy_if(values.begin(), values.end(), out.begin(), [valid = valid_flags.begin()](auto) mutable {
+ constexpr static auto VALUES {[]() {
+ constexpr auto VALUES {getValues(typename EVC::Vs {})};
+ static_assert(std::ranges::is_sorted(VALUES), "Candidate values must be sorted");
+ std::array<E, VALID_COUNT> out;
+ std::copy_if(VALUES.begin(), VALUES.end(), out.begin(), [valid = VALID_FLAGS.begin()](auto) mutable {
return *valid++;
});
return out;
}()};
- constexpr static auto names {get_valueNames(std::make_integer_sequence<int, valid_count> {})};
+ constexpr static auto NAMES {getValueNames(std::make_integer_sequence<int, VALID_COUNT> {})};
constexpr static bool
- is_valid(E value) noexcept
+ isValid(E value) noexcept
{
- return std::binary_search(values.begin(), values.end(), value);
+ return std::binary_search(VALUES.begin(), VALUES.end(), value);
}
constexpr static std::optional<E>
parse(std::string_view name) noexcept
{
- return lookup(name, names, values);
+ return lookup(name, NAMES, VALUES);
}
constexpr static std::optional<std::string_view>
- to_string(E value) noexcept
+ toString(E value) noexcept
{
- return lookup(value, values, names);
+ return lookup(value, VALUES, NAMES);
}
};
diff --git a/lib/filesystem.cpp b/lib/filesystem.cpp
index 7e8ab9c..5c0c6f8 100644
--- a/lib/filesystem.cpp
+++ b/lib/filesystem.cpp
@@ -37,7 +37,7 @@ namespace filesystem {
}
// NOLINTNEXTLINE(hicpp-vararg)
- fh::fh(const char * path, int flags, int mode) : h {open(path, flags, mode)}
+ fh::fh(const char * path, int flags, mode_t mode) : h {open(path, flags, mode)}
{
if (h == -1) {
throw_filesystem_error("open", errno, path);
diff --git a/lib/filesystem.h b/lib/filesystem.h
index b076f43..92dc08d 100644
--- a/lib/filesystem.h
+++ b/lib/filesystem.h
@@ -30,7 +30,7 @@ namespace filesystem {
class [[nodiscard]] fh final {
public:
- fh(const char * path, int flags, int mode);
+ fh(const char * path, int flags, mode_t mode);
~fh();
NO_MOVE(fh);
NO_COPY(fh);
diff --git a/lib/glArrays.h b/lib/glArrays.h
index 787ea17..842a593 100644
--- a/lib/glArrays.h
+++ b/lib/glArrays.h
@@ -6,6 +6,7 @@
#include <glad/gl.h>
#include <special_members.h>
+// NOLINTNEXTLINE(readability-identifier-naming)
template<size_t N> class glArraysBase {
static_assert(N > 0);
@@ -15,20 +16,30 @@ public:
CUSTOM_MOVE(glArraysBase);
// NOLINTNEXTLINE(hicpp-explicit-conversions)
- inline
operator GLuint() const
+ requires(N == 1)
{
- static_assert(N == 1, "Implicit cast only if N == 1");
return ids.front();
}
- inline auto
+ GLuint
+ operator*() const
+ requires(N == 1)
+ {
+ return ids.front();
+ }
+
+ const auto &
operator[](size_t n) const
{
return ids[n];
}
- constexpr static auto size {N};
+ constexpr static auto
+ size()
+ {
+ return N;
+ }
protected:
glArraysBase() noexcept = default;
@@ -49,6 +60,7 @@ glArraysBase<N>::operator=(glArraysBase<N> && src) noexcept
return *this;
}
+// NOLINTNEXTLINE(readability-identifier-naming)
template<size_t N, auto Gen, auto Del> class glArrays : public glArraysBase<N> {
public:
using glArraysBase<N>::glArraysBase;
@@ -56,12 +68,12 @@ public:
DEFAULT_MOVE_COPY(glArrays);
- inline glArrays() noexcept
+ glArrays() noexcept
{
(*Gen)(N, this->ids.data());
}
- inline ~glArrays() noexcept
+ ~glArrays() noexcept
{
if (this->ids.front()) {
(*Del)(N, this->ids.data());
@@ -69,6 +81,7 @@ public:
}
};
+// NOLINTBEGIN(readability-identifier-naming)
template<size_t N> using glVertexArrays = glArrays<N, &glGenVertexArrays, &glDeleteVertexArrays>;
using glVertexArray = glVertexArrays<1>;
template<size_t N> using glBuffers = glArrays<N, &glGenBuffers, &glDeleteBuffers>;
@@ -79,3 +92,4 @@ template<size_t N> using glFrameBuffers = glArrays<N, &glGenFramebuffers, &glDel
using glFrameBuffer = glFrameBuffers<1>;
template<size_t N> using glRenderBuffers = glArrays<N, &glGenRenderbuffers, &glDeleteRenderbuffers>;
using glRenderBuffer = glRenderBuffers<1>;
+// NOLINTEND(readability-identifier-naming)
diff --git a/lib/glMappedBufferWriter.cpp b/lib/glMappedBufferWriter.cpp
new file mode 100644
index 0000000..cc3c413
--- /dev/null
+++ b/lib/glMappedBufferWriter.cpp
@@ -0,0 +1,4 @@
+#include "glMappedBufferWriter.h"
+#include <iterator>
+
+static_assert(std::weakly_incrementable<glMappedBufferWriter<int>>);
diff --git a/lib/glMappedBufferWriter.h b/lib/glMappedBufferWriter.h
new file mode 100644
index 0000000..f97d7e1
--- /dev/null
+++ b/lib/glMappedBufferWriter.h
@@ -0,0 +1,78 @@
+#pragma once
+
+#include "special_members.h"
+#include <cstddef>
+#include <glad/gl.h>
+#include <utility>
+
+template<typename T> class glMappedBufferWriter {
+public:
+ using difference_type = std::ptrdiff_t;
+
+ glMappedBufferWriter(GLenum target, GLuint buffer, size_t count, GLenum usage = GL_STATIC_DRAW) :
+ target {target}, data {[&]() {
+ glBindBuffer(target, buffer);
+ glBufferData(target, static_cast<GLsizeiptr>(sizeof(T) * count), nullptr, usage);
+ return static_cast<T *>(glMapBuffer(target, GL_WRITE_ONLY));
+ }()}
+ {
+ }
+
+ ~glMappedBufferWriter()
+ {
+ if (target) {
+ glUnmapBuffer(target);
+ }
+ }
+
+ glMappedBufferWriter(glMappedBufferWriter && other) noexcept :
+ target {std::exchange(other.target, 0)}, data {std::exchange(other.data, nullptr)}
+ {
+ }
+
+ glMappedBufferWriter &
+ operator=(glMappedBufferWriter && other) noexcept
+ {
+ if (target) {
+ glUnmapBuffer(target);
+ }
+ target = std::exchange(other.target, 0);
+ data = std::exchange(other.data, nullptr);
+ return *this;
+ }
+
+ NO_COPY(glMappedBufferWriter);
+
+ glMappedBufferWriter &
+ operator++()
+ {
+ data++;
+ return *this;
+ }
+
+ glMappedBufferWriter &
+ operator++(int)
+ {
+ glMappedBufferWriter rtn {data};
+ data++;
+ return rtn;
+ }
+
+ T &
+ operator*()
+ {
+ return *data;
+ }
+
+ T *
+ operator->() const
+ {
+ return data;
+ }
+
+private:
+ explicit glMappedBufferWriter(T * data) : target {0}, data {data} { }
+
+ GLenum target;
+ T * data;
+};
diff --git a/lib/jsonParse-persistence.h b/lib/jsonParse-persistence.h
index 6edebc7..e4e64c0 100644
--- a/lib/jsonParse-persistence.h
+++ b/lib/jsonParse-persistence.h
@@ -15,6 +15,9 @@ namespace Persistence {
inline T
loadState(std::istream & in)
{
+ if (!in.good()) {
+ throw std::runtime_error("Input stream not in good state");
+ }
T t {};
stk.push(std::make_unique<SelectionT<T>>(std::ref(t)));
loadState(in);
diff --git a/lib/jsonParse.ll b/lib/jsonParse.ll
index 100bc46..abcd070 100644
--- a/lib/jsonParse.ll
+++ b/lib/jsonParse.ll
@@ -149,7 +149,10 @@ text [^\\\"]*
<*>. {
LexerError("Unexpected input");
- // Make iwyu think unistd.h is required
- [[maybe_unused]]static constexpr auto x=getpid;
- [[maybe_unused]]static constexpr auto y=printf;
}
+
+%%
+
+// Make iwyu think unistd.h is required
+[[maybe_unused]]static constexpr auto x=getpid;
+[[maybe_unused]]static constexpr auto y=printf;
diff --git a/lib/manyPtr.h b/lib/manyPtr.h
new file mode 100644
index 0000000..9e08452
--- /dev/null
+++ b/lib/manyPtr.h
@@ -0,0 +1,86 @@
+#pragma once
+
+#include <memory>
+#include <tuple>
+
+template<typename Primary, typename... Others> class ManyPtr : Primary {
+public:
+ using element_type = typename Primary::element_type;
+
+ template<typename... Params> ManyPtr(Params &&... params) : Primary {std::forward<Params>(params)...}
+ {
+ updatePtrs();
+ }
+
+ using Primary::operator->;
+ using Primary::operator*;
+ using Primary::operator bool;
+ using Primary::get;
+
+ template<typename... Params>
+ void
+ reset(Params &&... params)
+ {
+ Primary::reset(std::forward<Params>(params)...);
+ updatePtrs();
+ }
+
+ template<typename Other>
+ [[nodiscard]] consteval static bool
+ couldBe()
+ {
+ return (std::is_convertible_v<Others *, Other *> || ...);
+ }
+
+ template<typename Other>
+ requires(couldBe<Other>())
+ [[nodiscard]] auto
+ getAs() const
+ {
+ return std::get<idx<Other>()>(others);
+ }
+
+ template<typename Other>
+ requires(!couldBe<Other>() && requires { std::dynamic_pointer_cast<Other>(std::declval<Primary>()); })
+ [[nodiscard]] auto
+ dynamicCast() const
+ {
+ return std::dynamic_pointer_cast<Other>(*this);
+ }
+
+ template<typename Other>
+ requires(!couldBe<Other>() && !requires { std::dynamic_pointer_cast<Other>(std::declval<Primary>()); })
+ [[nodiscard]] auto
+ dynamicCast() const
+ {
+ return dynamic_cast<Other *>(get());
+ }
+
+private:
+ using OtherPtrs = std::tuple<Others *...>;
+
+ template<typename Other>
+ requires(couldBe<Other>())
+ [[nodiscard]] consteval static bool
+ idx()
+ {
+ size_t typeIdx = 0;
+ return ((typeIdx++ && std::is_convertible_v<Others *, Other *>) || ...);
+ }
+
+ void
+ updatePtrs()
+ {
+ if (*this) {
+ others = {dynamic_cast<Others *>(get())...};
+ }
+ else {
+ others = {};
+ }
+ }
+
+ OtherPtrs others;
+};
+
+template<typename Primary, typename... Others> using ManySharedPtr = ManyPtr<std::shared_ptr<Primary>, Others...>;
+template<typename Primary, typename... Others> using ManyUniquePtr = ManyPtr<std::unique_ptr<Primary>, Others...>;
diff --git a/lib/maths.cpp b/lib/maths.cpp
index 51e27fe..12e0681 100644
--- a/lib/maths.cpp
+++ b/lib/maths.cpp
@@ -19,105 +19,26 @@ flat_orientation(const Direction3D & diff)
return (std::isnan(e[0][0])) ? oneeighty : e;
}
-// Helper to lookup into a matrix given an xy vector coordinate
-template<typename M, typename I>
-inline auto &
-operator^(M & m, glm::vec<2, I> xy)
-{
- return m[xy.x][xy.y];
-}
-
-// Create a matrix for the angle, given the targets into the matrix
-template<typename M, typename I>
-inline auto
-rotation(typename M::value_type a, glm::vec<2, I> c1, glm::vec<2, I> s1, glm::vec<2, I> c2, glm::vec<2, I> ms2)
-{
- M m(1);
- sincosf(a, m ^ s1, m ^ c1);
- m ^ c2 = m ^ c1;
- m ^ ms2 = -(m ^ s1);
- return m;
-}
-
-// Create a flat (2D) transformation matrix
-glm::mat2
-rotate_flat(float a)
-{
- return rotation<glm::mat2, glm::length_t>(a, {0, 0}, {0, 1}, {1, 1}, {1, 0});
-}
-
-// Create a yaw transformation matrix
-glm::mat4
-rotate_yaw(float a)
-{
- return rotation<glm::mat4, glm::length_t>(a, {0, 0}, {1, 0}, {1, 1}, {0, 1});
-}
-
-// Create a roll transformation matrix
-glm::mat4
-rotate_roll(float a)
-{
- return rotation<glm::mat4, glm::length_t>(a, {0, 0}, {2, 0}, {2, 2}, {0, 2});
-}
-
-// Create a pitch transformation matrix
-glm::mat4
-rotate_pitch(float a)
-{
- return rotation<glm::mat4, glm::length_t>(a, {1, 1}, {1, 2}, {2, 2}, {2, 1});
-}
-
-// Create a combined yaw, pitch, roll transformation matrix
-glm::mat4
-rotate_ypr(Rotation3D a)
-{
- return rotate_yaw(a.y) * rotate_pitch(a.x) * rotate_roll(a.z);
-}
-
-glm::mat4
-rotate_yp(Rotation2D a)
-{
- return rotate_yaw(a.y) * rotate_pitch(a.x);
-}
-
-float
-vector_yaw(const Direction2D & diff)
-{
- return std::atan2(diff.x, diff.y);
-}
-
-float
-vector_pitch(const Direction3D & diff)
-{
- return std::atan(diff.z);
-}
-
-float
-round_frac(const float & v, const float & frac)
-{
- return std::round(v / frac) * frac;
-}
-
-float
-normalize(float ang)
-{
- while (ang > pi) {
- ang -= two_pi;
- }
- while (ang <= -pi) {
- ang += two_pi;
- }
- return ang;
-}
+static_assert(pow(1, 0) == 1);
+static_assert(pow(1, 1) == 1);
+static_assert(pow(1, 2) == 1);
+static_assert(pow(2, 0) == 1);
+static_assert(pow(2, 1) == 2);
+static_assert(pow(2, 2) == 4);
+static_assert(pow(2, 3) == 8);
+static_assert(pow(3, 0) == 1);
+static_assert(pow(3, 1) == 3);
+static_assert(pow(3, 2) == 9);
+static_assert(pow(pi, 3) == 31.006278991699219F);
float
-operator"" _mph(const long double v)
+operator""_mph(const long double v)
{
return static_cast<float>(mph_to_ms(v));
}
float
-operator"" _kph(const long double v)
+operator""_kph(const long double v)
{
return static_cast<float>(kph_to_ms(v));
}
diff --git a/lib/maths.h b/lib/maths.h
index 018ef0e..43d6dcd 100644
--- a/lib/maths.h
+++ b/lib/maths.h
@@ -1,15 +1,24 @@
#pragma once
#include "config/types.h"
+#include <algorithm>
+#include <array>
#include <cmath>
#include <glm/glm.hpp>
#include <glm/gtc/constants.hpp>
#include <numeric>
+#include <optional>
#include <stdexcept>
#include <utility>
+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, typename 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()}}
@@ -17,22 +26,42 @@ struct Arc : public std::pair<Angle, Angle> {
}
Arc(const RelativePosition2D & dir0, const RelativePosition2D & dir1);
- Arc(const Angle angb, const Angle anga);
+ Arc(Angle anga, Angle angb);
auto
- operator[](bool i) const
+ operator[](bool getSecond) const
{
- return i ? second : first;
+ return getSecond ? second : first;
}
- [[nodiscard]] constexpr inline float
+ [[nodiscard]] constexpr float
length() const
{
return second - first;
}
};
-constexpr const RelativePosition3D up {0, 0, 1};
+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};
constexpr const RelativePosition3D south {0, -1, 0};
@@ -40,158 +69,341 @@ constexpr const RelativePosition3D east {1, 0, 0};
constexpr const RelativePosition3D west {-1, 0, 0};
constexpr auto half_pi {glm::half_pi<float>()};
constexpr auto quarter_pi {half_pi / 2};
-constexpr auto pi {glm::pi<float>()};
+constexpr auto pi {glm::pi<float>()}; // NOLINT(readability-identifier-length)
constexpr auto two_pi {glm::two_pi<float>()};
+constexpr auto degreesToRads = pi / 180.F;
+
+constexpr auto earthMeanRadius = 6371.01F; // In km
+constexpr auto astronomicalUnit = 149597890.F; // In km
template<glm::length_t D>
-constexpr inline GlobalPosition<D>
-operator+(const GlobalPosition<D> & g, const RelativePosition<D> & r)
+constexpr GlobalPosition<D>
+operator+(const GlobalPosition<D> & global, const RelativePosition<D> & relative)
{
- return g + GlobalPosition<D>(glm::round(r));
+ return global + GlobalPosition<D>(glm::round(relative));
}
template<glm::length_t D>
-constexpr inline GlobalPosition<D>
-operator+(const GlobalPosition<D> & g, const CalcPosition<D> & r)
+constexpr GlobalPosition<D>
+operator+(const GlobalPosition<D> & global, const CalcPosition<D> & relative)
{
- return g + GlobalPosition<D>(r);
+ return global + GlobalPosition<D>(relative);
}
template<glm::length_t D>
-constexpr inline GlobalPosition<D>
-operator-(const GlobalPosition<D> & g, const RelativePosition<D> & r)
+constexpr GlobalPosition<D>
+operator-(const GlobalPosition<D> & global, const RelativePosition<D> & relative)
{
- return g - GlobalPosition<D>(glm::round(r));
+ return global - GlobalPosition<D>(glm::round(relative));
}
template<glm::length_t D>
-constexpr inline GlobalPosition<D>
-operator-(const GlobalPosition<D> & g, const CalcPosition<D> & r)
+constexpr GlobalPosition<D>
+operator-(const GlobalPosition<D> & global, const CalcPosition<D> & relative)
{
- return g - GlobalPosition<D>(r);
+ return global - GlobalPosition<D>(relative);
+}
+
+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);
-// C++ wrapper for C's sincosf, but with references, not pointers
-inline auto
-sincosf(float a, float & s, float & c)
+namespace {
+ // Helpers
+ // C++ wrapper for C's sincosf, but with references, not pointers
+ template<std::floating_point T>
+ constexpr void
+ sincos(T angle, T & sinOut, T & cosOut)
+ {
+ if consteval {
+ sinOut = std::sin(angle);
+ cosOut = std::cos(angle);
+ }
+ else {
+ if constexpr (std::is_same_v<T, float>) {
+ ::sincosf(angle, &sinOut, &cosOut);
+ }
+ else if constexpr (std::is_same_v<T, double>) {
+ ::sincos(angle, &sinOut, &cosOut);
+ }
+ else if constexpr (std::is_same_v<T, long double>) {
+ ::sincosl(angle, &sinOut, &cosOut);
+ }
+ }
+ }
+
+ template<std::floating_point T, glm::qualifier Q = glm::qualifier::defaultp>
+ constexpr auto
+ sincos(const T angle)
+ {
+ glm::vec<2, T, Q> sincosOut {};
+ sincos(angle, sincosOut.x, sincosOut.y);
+ return sincosOut;
+ }
+
+ // 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 = glm::defaultp,
+ std::integral I = glm::length_t>
+ constexpr auto &
+ operator^(glm::mat<C, R, T, Q> & matrix, const glm::vec<2, I> rowCol)
+ {
+ return matrix[rowCol.x][rowCol.y];
+ }
+
+ // Create a matrix for the angle, given the targets into the matrix
+ 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)
+ {
+ glm::mat<D, D, T, Q> out(1);
+ sincos(angle, out ^ sin1, out ^ cos1);
+ out ^ cos2 = out ^ cos1;
+ out ^ negSin1 = -(out ^ sin1);
+ return out;
+ }
+}
+
+// Create a flat transformation matrix
+template<glm::length_t D = 2, glm::qualifier Q = glm::qualifier::defaultp, std::floating_point T>
+ requires(D >= 2)
+constexpr auto
+rotate_flat(const T angle)
{
- return sincosf(a, &s, &c);
+ return rotation<D, T, Q>(angle, {0, 0}, {0, 1}, {1, 1}, {1, 0});
}
-inline Rotation2D
-sincosf(float a)
+// Create a yaw transformation matrix
+template<glm::length_t D = 3, glm::qualifier Q = glm::qualifier::defaultp, std::floating_point T>
+ requires(D >= 2)
+constexpr auto
+rotate_yaw(const T angle)
{
- Rotation2D sc;
- sincosf(a, sc.x, sc.y);
- return sc;
+ return rotation<D, T, Q>(angle, {0, 0}, {1, 0}, {1, 1}, {0, 1});
}
-glm::mat2 rotate_flat(float);
-glm::mat4 rotate_roll(float);
-glm::mat4 rotate_yaw(float);
-glm::mat4 rotate_pitch(float);
-glm::mat4 rotate_yp(Rotation2D);
-glm::mat4 rotate_ypr(Rotation3D);
+// Create a roll transformation matrix
+template<glm::length_t D = 3, glm::qualifier Q = glm::qualifier::defaultp, std::floating_point T>
+ requires(D >= 3)
+constexpr auto
+rotate_roll(const T angle)
+{
+ return rotation<D, T, Q>(angle, {0, 0}, {2, 0}, {2, 2}, {0, 2});
+}
-float vector_yaw(const Direction2D & diff);
-float vector_pitch(const Direction3D & diff);
+// Create a pitch transformation matrix
+template<glm::length_t D = 3, glm::qualifier Q = glm::qualifier::defaultp, std::floating_point T>
+ requires(D >= 3)
+constexpr auto
+rotate_pitch(const T angle)
+{
+ return rotation<D, T, Q>(angle, {1, 1}, {1, 2}, {2, 2}, {2, 1});
+}
-template<typename T, glm::qualifier Q>
-glm::vec<2, T, Q>
-vector_normal(const glm::vec<2, T, Q> & v)
+// Create a combined yaw, pitch, roll transformation matrix
+template<glm::length_t D = 3, glm::qualifier Q = glm::qualifier::defaultp, std::floating_point T>
+ requires(D >= 3)
+constexpr auto
+rotate_ypr(const glm::vec<3, T, Q> & angles)
{
- return {-v.y, v.x};
+ return rotate_yaw<D>(angles.y) * rotate_pitch<D>(angles.x) * rotate_roll<D>(angles.z);
+}
+
+template<glm::length_t D = 3, glm::qualifier Q = glm::qualifier::defaultp, std::floating_point T>
+ requires(D >= 3)
+constexpr auto
+rotate_yp(const T yaw, const T pitch)
+{
+ return rotate_yaw<D>(yaw) * rotate_pitch<D>(pitch);
+}
+
+template<glm::length_t D = 3, glm::qualifier Q = glm::qualifier::defaultp, std::floating_point T>
+ requires(D >= 3)
+constexpr auto
+rotate_yp(const glm::vec<2, T, Q> & angles)
+{
+ return rotate_yp<D>(angles.y, angles.x);
+}
+
+template<glm::length_t D, glm::qualifier Q = glm::qualifier::defaultp, std::floating_point T>
+ requires(D >= 2)
+constexpr auto
+vector_yaw(const glm::vec<D, T, Q> & diff)
+{
+ return std::atan2(diff.x, diff.y);
+}
+
+template<glm::length_t D, glm::qualifier Q = glm::qualifier::defaultp, std::floating_point T>
+ requires(D >= 3)
+constexpr auto
+vector_pitch(const glm::vec<D, T, Q> & diff)
+{
+ return std::atan(diff.z);
+}
+
+template<Arithmetic T, glm::qualifier Q = glm::defaultp>
+constexpr glm::vec<2, T, Q>
+vector_normal(const glm::vec<2, T, Q> & vector)
+{
+ return {-vector.y, vector.x};
};
-float round_frac(const float & v, const float & frac);
+template<std::floating_point T>
+constexpr auto
+round_frac(const T value, const T frac)
+{
+ return std::round(value / frac) * frac;
+}
-template<typename T>
-inline constexpr auto
-sq(T v)
+template<Arithmetic T>
+ requires requires(T value) { value * value; }
+constexpr auto
+sq(T value)
{
- return v * v;
+ return value * value;
}
-template<glm::qualifier Q>
-inline constexpr glm::vec<3, int64_t, Q>
-crossProduct(const glm::vec<3, int64_t, Q> a, const glm::vec<3, int64_t, Q> b)
+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)
{
return {
- (a.y * b.z) - (a.z * b.y),
- (a.z * b.x) - (a.x * b.z),
- (a.x * b.y) - (a.y * b.x),
+ (valueA.y * valueB.z) - (valueA.z * valueB.y),
+ (valueA.z * valueB.x) - (valueA.x * valueB.z),
+ (valueA.x * valueB.y) - (valueA.y * valueB.x),
};
}
-template<std::integral T, glm::qualifier Q>
-inline constexpr glm::vec<3, T, Q>
-crossProduct(const glm::vec<3, T, Q> a, const glm::vec<3, T, Q> b)
+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 = 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>(a, b);
+ return glm::cross(valueA, valueB);
}
-template<std::floating_point T, glm::qualifier Q>
-inline constexpr glm::vec<3, T, Q>
-crossProduct(const glm::vec<3, T, Q> a, const glm::vec<3, T, Q> b)
+template<Arithmetic R = float, Arithmetic Ta, Arithmetic Tb>
+constexpr auto
+ratio(const Ta valueA, const Tb valueB)
{
- return glm::cross(a, b);
+ using Common = std::common_type_t<Ta, Ta, R>;
+ return static_cast<R>((static_cast<Common>(valueA) / static_cast<Common>(valueB)));
}
-template<typename R = float, typename Ta, typename Tb>
-inline constexpr auto
-ratio(Ta a, Tb b)
+template<Arithmetic R = float, Arithmetic T, glm::qualifier Q = glm::defaultp>
+constexpr auto
+ratio(const glm::vec<2, T, Q> & value)
{
- return (static_cast<R>(a) / static_cast<R>(b));
+ return ratio<R>(value.x, value.y);
}
-template<typename R = float, typename T, glm::qualifier Q>
-inline constexpr auto
-ratio(glm::vec<2, T, Q> v)
+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 ratio<R>(v.x, v.y);
+ return value / value.w;
}
-template<glm::length_t L = 3, typename T, glm::qualifier Q>
-inline constexpr glm::vec<L, T, Q>
-perspective_divide(glm::vec<4, T, Q> v)
+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 v / v.w;
+ return {valueA, valueB};
}
-template<glm::length_t L1, glm::length_t L2, typename T, glm::qualifier Q>
-inline constexpr glm::vec<L1 + L2, T, Q>
-operator||(const glm::vec<L1, T, Q> v1, const glm::vec<L2, T, Q> v2)
+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 {v1, v2};
+ return {valueA, valueB};
}
-template<glm::length_t L, typename T, glm::qualifier Q>
-inline constexpr glm::vec<L + 1, T, Q>
-operator||(const glm::vec<L, T, Q> v1, const T v2)
+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)
{
- return {v1, v2};
+ const auto mutated = mutation * (base || T(1));
+ return mutated / mutated.w;
}
-template<glm::length_t L, typename T, glm::qualifier Q>
-inline constexpr glm::vec<L, T, Q>
-perspectiveMultiply(const glm::vec<L, T, Q> & p, const glm::mat<L + 1, L + 1, T, Q> & mutation)
+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)
{
- const auto p2 = mutation * (p || T(1));
- return p2 / p2.w;
+ return base = perspectiveMultiply(base, mutation);
}
-template<glm::length_t L, typename T, glm::qualifier Q>
-inline constexpr glm::vec<L, T, Q>
-perspectiveApply(glm::vec<L, T, Q> & p, const glm::mat<L + 1, L + 1, T, Q> & mutation)
+template<std::floating_point T>
+constexpr T
+normalize(T ang)
{
- return p = perspectiveMultiply(p, mutation);
+ while (ang > glm::pi<T>()) {
+ ang -= glm::two_pi<T>();
+ }
+ while (ang <= -glm::pi<T>()) {
+ ang += glm::two_pi<T>();
+ }
+ return ang;
}
-float normalize(float ang);
+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,
+ const glm::vec<2, T, Q> Dabs)
+{
+ using CT = CalcType<T>;
+ using CVec = glm::vec<2, CT, Q>;
+ // Line AB represented as a1x + b1y = c1
+ const CVec Brel = Babs - Aabs;
+ const CT a1 = Brel.y;
+ const CT b1 = -Brel.x;
+
+ // Line CD represented as a2x + b2y = c2
+ const CVec Crel = Cabs - Aabs, Del = Dabs - Aabs;
+ const CT a2 = Del.y - Crel.y;
+ const CT b2 = Crel.x - Del.x;
+ const CT c2 = (a2 * Crel.x) + (b2 * Crel.y);
+
+ const auto determinant = (a1 * b2) - (a2 * b1);
+
+ if (determinant == 0) {
+ return std::nullopt;
+ }
+ return Aabs + CVec {(b1 * c2) / -determinant, (a1 * c2) / determinant};
+}
-template<typename 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)
{
@@ -204,17 +416,17 @@ find_arc_centre(glm::vec<2, T, Q> start, Rotation2D startDir, glm::vec<2, T, Q>
throw std::runtime_error("no intersection");
}
-template<typename 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)
{
if (start == end) {
return {start, false};
}
- return find_arc_centre(start, sincosf(entrys + half_pi), end, sincosf(entrye - half_pi));
+ return find_arc_centre(start, sincos(entrys + half_pi), end, sincos(entrye - half_pi));
}
-template<typename 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)
{
@@ -239,38 +451,139 @@ 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<typename 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)
{
const auto getrad = [&](auto leftOrRight) {
- return find_arcs_radius(start, sincosf(entrys + leftOrRight), end, sincosf(entrye + leftOrRight));
+ return find_arcs_radius(start, sincos(entrys + leftOrRight), end, sincos(entrye + leftOrRight));
};
return {getrad(-half_pi), getrad(half_pi)};
}
-template<typename T>
+template<Arithmetic T>
auto
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; }
+constexpr T
+pow(const T base, std::integral auto exp)
+{
+ T res {1};
+ while (exp--) {
+ res *= base;
+ }
+ return res;
+}
+
// Conversions
-template<typename T>
-inline constexpr auto
+template<Arithmetic T>
+constexpr auto
mph_to_ms(T v)
{
return v / 2.237L;
}
-template<typename T>
-inline constexpr auto
+template<Arithmetic T>
+constexpr auto
kph_to_ms(T v)
{
return v / 3.6L;
}
// ... literals are handy for now, probably go away when we load stuff externally
-float operator"" _mph(const long double v);
-float operator"" _kph(const long double v);
+float operator""_mph(const long double v);
+float operator""_kph(const long double v);
+
+constexpr float
+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/persistence.h b/lib/persistence.h
index e385b20..75bcea6 100644
--- a/lib/persistence.h
+++ b/lib/persistence.h
@@ -1,5 +1,6 @@
#pragma once
+#include "manyPtr.h"
#include <charconv>
#include <format>
#include <functional>
@@ -200,7 +201,7 @@ namespace Persistence {
}
[[nodiscard]] virtual NameActionSelection setName(const std::string_view key, SelectionFactory &&) = 0;
- virtual void selHandler() {};
+ virtual void selHandler() { };
virtual void setType(const std::string_view, const Persistable *) = 0;
SelectionPtr sel {};
diff --git a/lib/ray.h b/lib/ray.h
index a831270..642cd4d 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))));
@@ -54,34 +53,55 @@ public:
}
}
- bool
- intersectTriangle(const PositionType t0, const PositionType t1, const PositionType t2, BaryPosition & bary,
- RelativeDistance & distance) const
+ struct IntersectTriangleResult {
+ BaryPosition bary;
+ RelativeDistance distance;
+ };
+
+ std::optional<IntersectTriangleResult>
+ intersectTriangle(const PositionType t0, const PositionType t1, const PositionType t2) const
{
+ IntersectTriangleResult out;
if constexpr (std::is_floating_point_v<typename PositionType::value_type>) {
- return glm::intersectRayTriangle(start, direction, t0, t1, t2, bary, distance) && distance >= 0.F;
+ if (glm::intersectRayTriangle(start, direction, t0, t1, t2, out.bary, out.distance)
+ && out.distance >= 0.F) {
+ return out;
+ }
}
else {
const RelativePosition3D t0r = t0 - start, t1r = t1 - start, t2r = t2 - start;
- return glm::intersectRayTriangle({}, direction, t0r, t1r, t2r, bary, distance) && distance >= 0.F;
+ if (glm::intersectRayTriangle({}, direction, t0r, t1r, t2r, out.bary, out.distance)
+ && out.distance >= 0.F) {
+ return out;
+ }
}
+ return std::nullopt;
}
- bool
- intersectSphere(const PositionType centre, const PositionType::value_type size, PositionType & position,
- Normal3D & normal) const
+ struct IntersectSphereResult {
+ PositionType position;
+ Normal3D normal;
+ };
+
+ std::optional<IntersectSphereResult>
+ intersectSphere(const PositionType centre, const PositionType::value_type size) const
{
+ IntersectSphereResult out;
if constexpr (std::is_floating_point_v<typename PositionType::value_type>) {
- return glm::intersectRaySphere(start, direction, centre, size, position, normal);
+ if (glm::intersectRaySphere(start, direction, centre, size, out.position, out.normal)) {
+ return out;
+ }
}
else {
const RelativePosition3D cr = centre - start;
RelativePosition3D positionF {};
- const auto r = glm::intersectRaySphere(
- {}, direction, cr, static_cast<RelativeDistance>(size), positionF, normal);
- position = GlobalPosition3D(positionF) + start;
- return r;
+ if (glm::intersectRaySphere(
+ {}, direction, cr, static_cast<RelativeDistance>(size), positionF, out.normal)) {
+ out.position = GlobalPosition3D(positionF) + start;
+ return out;
+ }
}
+ return std::nullopt;
}
};
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 57d82a1..7f1df96 100644
--- a/lib/stream_support.h
+++ b/lib/stream_support.h
@@ -4,8 +4,11 @@
#include <glm/glm.hpp>
#include <iostream>
#include <maths.h>
+#include <optional>
+#include <source_location>
#include <span>
#include <sstream>
+#include <tuple>
#include <type_traits>
template<typename S>
@@ -16,14 +19,14 @@ concept NonStringIterableCollection
namespace std {
std::ostream &
- operator<<(std::ostream & s, const NonStringIterableCollection auto & v)
+ operator<<(std::ostream & s, const NonStringIterableCollection auto & collection)
{
s << '(';
- for (const auto & i : v) {
- if (&i != &*v.begin()) {
+ for (size_t nth {}; const auto & element : collection) {
+ if (nth++) {
s << ", ";
}
- s << i;
+ s << element;
}
return s << ')';
}
@@ -49,6 +52,22 @@ namespace std {
return (s << '(' << v.first << ", " << v.second << ')');
}
+ namespace {
+ template<typename... T, size_t... Idx>
+ std::ostream &
+ printTuple(std::ostream & s, const std::tuple<T...> & v, std::integer_sequence<size_t, Idx...>)
+ {
+ return ((s << (Idx ? ", " : "") << std::get<Idx>(v)), ...);
+ }
+ }
+
+ template<typename... T>
+ std::ostream &
+ operator<<(std::ostream & s, const std::tuple<T...> & v)
+ {
+ return printTuple(s << '{', v, std::make_index_sequence<sizeof...(T)>()) << '}';
+ }
+
inline std::ostream &
operator<<(std::ostream & s, const Arc & arc)
{
@@ -62,7 +81,17 @@ namespace std {
inline std::ostream &
operator<<(std::ostream & s, const E & e)
{
- return s << EnumTypeDetails<E>::typeName << "::" << EnumDetails<E>::to_string(e).value();
+ return s << EnumTypeDetails<E>::TYPE_NAME << "::" << EnumDetails<E>::toString(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";
}
}
@@ -75,4 +104,15 @@ streamed_string(const T & v)
return std::move(ss).str();
}
-#define CLOG(x) std::cerr << __LINE__ << " : " #x " : " << x << "\n";
+namespace {
+ template<typename T>
+ void
+ clogImpl(const T & value, const std::string_view name,
+ const std::source_location loc = std::source_location::current())
+ {
+ std::cerr << loc.line() << " : " << name << " : " << value << "\n";
+ }
+}
+
+#define CLOG(x) clogImpl(x, #x)
+#define CLOGf(...) clogImpl(std::format(__VA_ARGS__), "msg")
diff --git a/lib/triangle.h b/lib/triangle.h
new file mode 100644
index 0000000..abd697c
--- /dev/null
+++ b/lib/triangle.h
@@ -0,0 +1,156 @@
+#pragma once
+
+#include "config/types.h"
+#include "maths.h"
+#include <glm/glm.hpp>
+
+template<glm::length_t Dim, Arithmetic T, glm::qualifier Q = glm::defaultp>
+struct Triangle : public glm::vec<3, glm::vec<Dim, T, Q>> {
+ using Point = glm::vec<Dim, T, Q>;
+ using Base = glm::vec<3, glm::vec<Dim, T, Q>>;
+ using Base::Base;
+
+ [[nodiscard]] constexpr Point
+ operator*(BaryPosition bari) const
+ {
+ return p(0) + (sideDifference(1) * bari.x) + (sideDifference(2) * bari.y);
+ }
+
+ [[nodiscard]] constexpr Point
+ centroid() const
+ {
+ return [this]<glm::length_t... Axis>(std::integer_sequence<glm::length_t, Axis...>) {
+ return Point {(p(0)[Axis] + p(1)[Axis] + p(2)[Axis]) / 3 ...};
+ }(std::make_integer_sequence<glm::length_t, Dim>());
+ }
+
+ [[nodiscard]] constexpr auto
+ area() const
+ requires(Dim == 3)
+ {
+ 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)
+ {
+ 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)
+ {
+ return glm::normalize(normal());
+ }
+
+ [[nodiscard]] constexpr auto
+ sideDifference(glm::length_t side) const
+ {
+ 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
+ {
+ return Arc {P(corner), P(corner + 2), P(corner + 1)}.length();
+ }
+
+ template<glm::length_t D = Dim>
+ [[nodiscard]] constexpr auto
+ angleAt(const glm::vec<D, T, Q> pos) const
+ requires(D <= Dim)
+ {
+ for (glm::length_t i {}; i < 3; ++i) {
+ if (glm::vec<D, T, Q> {p(i)} == pos) {
+ return angle(i);
+ }
+ }
+ 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
+ {
+ return Base::operator[](idx);
+ }
+
+ [[nodiscard]] constexpr auto
+ P(const glm::length_t idx) const
+ {
+ return Base::operator[](idx % 3);
+ }
+
+ [[nodiscard]] constexpr Point *
+ begin()
+ {
+ return &(Base::x);
+ }
+
+ [[nodiscard]] constexpr const Point *
+ begin() const
+ {
+ return &(Base::x);
+ }
+
+ [[nodiscard]] constexpr Point *
+ end()
+ {
+ return begin() + 3;
+ }
+
+ [[nodiscard]] constexpr const Point *
+ end() const
+ {
+ 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>;
diff --git a/lib/worker.cpp b/lib/worker.cpp
index 45fb6df..9b2b83c 100644
--- a/lib/worker.cpp
+++ b/lib/worker.cpp
@@ -10,6 +10,11 @@ Worker::Worker() : todoLen {0}
std::generate_n(std::back_inserter(threads), std::thread::hardware_concurrency(), [this]() {
return std::jthread {&Worker::worker, this};
});
+ if constexpr (requires { pthread_setname_np(std::declval<std::jthread>().native_handle(), ""); }) {
+ for (auto & thread : threads) {
+ pthread_setname_np(thread.native_handle(), "ilt-worker");
+ }
+ }
}
Worker::~Worker()