diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/chronology.cpp | 12 | ||||
| -rw-r--r-- | lib/chronology.h | 2 | ||||
| -rw-r--r-- | lib/collection.h | 263 | ||||
| -rw-r--r-- | lib/collections.h | 92 | ||||
| -rw-r--r-- | lib/embed-glsl.cpp.m4 | 6 | ||||
| -rw-r--r-- | lib/enumDetails.h | 102 | ||||
| -rw-r--r-- | lib/filesystem.cpp | 2 | ||||
| -rw-r--r-- | lib/filesystem.h | 2 | ||||
| -rw-r--r-- | lib/glAllocator.cpp | 28 | ||||
| -rw-r--r-- | lib/glAllocator.h | 163 | ||||
| -rw-r--r-- | lib/glArrays.cpp | 21 | ||||
| -rw-r--r-- | lib/glArrays.h | 135 | ||||
| -rw-r--r-- | lib/glContainer.h | 497 | ||||
| -rw-r--r-- | lib/glMappedBufferSpan.h | 51 | ||||
| -rw-r--r-- | lib/gl_traits.h | 110 | ||||
| -rw-r--r-- | lib/jsonParse-persistence.h | 3 | ||||
| -rw-r--r-- | lib/jsonParse.ll | 9 | ||||
| -rw-r--r-- | lib/manyPtr.h | 86 | ||||
| -rw-r--r-- | lib/maths.cpp | 105 | ||||
| -rw-r--r-- | lib/maths.h | 529 | ||||
| -rw-r--r-- | lib/msgException.h | 23 | ||||
| -rw-r--r-- | lib/persistence.h | 3 | ||||
| -rw-r--r-- | lib/ray.h | 50 | ||||
| -rw-r--r-- | lib/sorting.h | 9 | ||||
| -rw-r--r-- | lib/stdTypeDefs.h | 12 | ||||
| -rw-r--r-- | lib/stream_support.h | 52 | ||||
| -rw-r--r-- | lib/triangle.h | 156 | ||||
| -rw-r--r-- | lib/util.h | 21 | ||||
| -rw-r--r-- | lib/worker.cpp | 5 |
29 files changed, 1605 insertions, 944 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, ¶ms...](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, ¶ms...](auto && op) { + std::invoke(m, op, std::forward<Params>(params)...); + }); + return std::distance(begin, end); + } + else { + return std::count_if(begin, end, [&m, ¶ms...](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, ¶ms...](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, ¶ms...](auto && op) { + return std::invoke(m, op, std::forward<Params>(params)...); + }); + } + else { + return std::find_if(begin, end, [&m, ¶ms...](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..3c80125 100644 --- a/lib/collections.h +++ b/lib/collections.h @@ -3,16 +3,15 @@ #include <algorithm> #include <array> #include <cstdint> +#include <ranges> #include <span> #include <tuple> #include <utility> #include <vector> template<typename T, typename E> -concept SequentialCollection = requires(T c) { - { c.size() } -> std::integral; - { c.data() } -> std::same_as<const E *>; -}; +concept SequentialCollection + = std::ranges::contiguous_range<T> && std::is_same_v<const E, const typename T::value_type>; template<typename T> concept IterableCollection = std::is_same_v<decltype(std::declval<T>().begin()), decltype(std::declval<T>().end())>; @@ -44,6 +43,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 +106,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 +138,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 +196,14 @@ template<typename iter> struct stripiter { return *this; } + constexpr stripiter + operator++(int) + { + auto out {*this}; + ++*this; + return out; + } + constexpr stripiter & operator--() { @@ -195,6 +212,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 +249,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/embed-glsl.cpp.m4 b/lib/embed-glsl.cpp.m4 index 9fe0b41..e8d285c 100644 --- a/lib/embed-glsl.cpp.m4 +++ b/lib/embed-glsl.cpp.m4 @@ -1,9 +1,9 @@ changecom() dnl // NAME #include "gfx/gl/shader.h" -#include "substr(TYPE,1)-NAME.h" +#include "NAME-substr(TYPE,1).h" #include <glad/gl.h> constexpr Shader NAME`_'substr(TYPE,1) { - R"GLSL-EMBED(dnl - include(SOURCE))GLSL-EMBED", GLTYPE }; + R"GLSL-EMBED(// SOURCE +include(SOURCE))GLSL-EMBED", GLTYPE }; 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/glAllocator.cpp b/lib/glAllocator.cpp new file mode 100644 index 0000000..633f7ab --- /dev/null +++ b/lib/glAllocator.cpp @@ -0,0 +1,28 @@ +#include "glAllocator.h" + +namespace Detail { + std::pair<void *, GLuint> + allocateBuffer(size_t count, size_t objSize) + { + constexpr static GLbitfield MAPPING_FLAGS + = GL_MAP_WRITE_BIT | GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT; + constexpr static GLbitfield STORAGE_FLAGS = GL_DYNAMIC_STORAGE_BIT | MAPPING_FLAGS; + GLuint name = 0; + glCreateBuffers(1, &name); + const auto size = static_cast<GLsizeiptr>(count * objSize); + glNamedBufferStorage(name, size, nullptr, STORAGE_FLAGS); + const auto data = (glMapNamedBufferRange(name, 0, size, MAPPING_FLAGS)); + if (!data) { + glDeleteBuffers(1, &name); + throw std::bad_alloc(); + } + return {data, name}; + } + + void + deallocateBuffer(GLuint name) + { + glUnmapNamedBuffer(name); + glDeleteBuffers(1, &name); + } +} diff --git a/lib/glAllocator.h b/lib/glAllocator.h new file mode 100644 index 0000000..02690c3 --- /dev/null +++ b/lib/glAllocator.h @@ -0,0 +1,163 @@ +#include "special_members.h" +#include <glad/gl.h> +#include <iterator> +#include <memory> +#include <vector> + +namespace Detail { + template<typename T> class glPointer { + public: + constexpr glPointer(const glPointer<std::remove_const_t<T>> & other) + requires(std::is_const_v<T>) + : ptr {other.get()}, name {other.bufferName()} + { + } + + DEFAULT_MOVE_COPY(glPointer); + + constexpr glPointer() : ptr {nullptr}, name {0} { } + + constexpr glPointer(T * ptr, GLuint name) : ptr {ptr}, name {name} { } + + auto operator<=>(const glPointer &) const noexcept = default; + + operator T *() const noexcept + { + return ptr; + } + + operator bool() const noexcept + { + return ptr; + } + + std::ptrdiff_t + operator-(const glPointer & other) const noexcept + { + return ptr - other.ptr; + } + + T * + get() const noexcept + { + return ptr; + } + + T & + operator*() const noexcept + { + return *ptr; + } + + [[nodiscard]] + T & + operator[](std::unsigned_integral auto index) const noexcept + { + return ptr[index]; + } + + T * + operator->() const noexcept + { + return ptr; + } + + glPointer<T> & + operator++() noexcept + { + ++ptr; + return *this; + } + + glPointer<T> & + operator--() noexcept + { + --ptr; + return *this; + } + + [[nodiscard]] glPointer<T> + operator+(std::integral auto offset) const noexcept + { + return {ptr + offset, name}; + } + + [[nodiscard]] glPointer<T> + operator-(std::integral auto offset) const noexcept + { + return {ptr - offset, name}; + } + + [[nodiscard]] glPointer<T> + operator+=(std::integral auto offset) const noexcept + { + return {ptr += offset, name}; + } + + [[nodiscard]] glPointer<T> + operator-=(std::integral auto offset) const noexcept + { + return {ptr -= offset, name}; + } + + [[nodiscard]] GLuint + bufferName() const noexcept + { + return name; + } + + private: + T * ptr; + GLuint name; + }; + + std::pair<void *, GLuint> allocateBuffer(size_t count, size_t objSize); + void deallocateBuffer(GLuint name); + + template<typename T> class glAllocator { + public: + // NOLINTBEGIN(readability-identifier-naming) - STL like + using pointer = glPointer<T>; + using const_pointer = glPointer<const T>; + using value_type = T; + + // NOLINTEND(readability-identifier-naming) + + pointer + allocate(size_t count) + { + auto allocated = allocateBuffer(count, sizeof(T)); + return {static_cast<T *>(allocated.first), allocated.second}; + } + +#if (__cpp_lib_allocate_at_least >= 202302L) + std::allocation_result<pointer> + allocate_at_least(size_t count) + { + count = std::min(count, 32ZU); + return {allocate(count), count}; + } +#endif + + void + deallocate(pointer ptr, size_t) + { + deallocateBuffer(ptr.bufferName()); + } + + using is_always_equal = std::true_type; + }; +} + +template<typename T> struct std::iterator_traits<Detail::glPointer<T>> { + using iterator_category = std::random_access_iterator_tag; + using iterator_concept = std::contiguous_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using reference = T &; + using pointer = T *; +}; + +template<typename T> +// NOLINTNEXTLINE(readability-identifier-naming) - OpenGL like +using glVector = std::vector<T, typename std::allocator_traits<Detail::glAllocator<T>>::allocator_type>; diff --git a/lib/glArrays.cpp b/lib/glArrays.cpp deleted file mode 100644 index 7c5b2ea..0000000 --- a/lib/glArrays.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "glArrays.h" -#include <type_traits> - -// Base -static_assert(!std::is_default_constructible_v<glArraysBase<1>>); -static_assert(!std::is_copy_constructible_v<glArraysBase<1>>); -static_assert(!std::is_copy_assignable_v<glArraysBase<1>>); -static_assert(std::is_nothrow_move_constructible_v<glArraysBase<1>>); -static_assert(std::is_nothrow_move_assignable_v<glArraysBase<1>>); - -// Specialisations (glBuffer is an example of the typedef) -static_assert(std::is_nothrow_default_constructible_v<glBuffer>); -static_assert(!std::is_trivially_default_constructible_v<glBuffer>); -static_assert(std::is_nothrow_destructible_v<glBuffer>); -static_assert(!std::is_trivially_destructible_v<glBuffer>); -static_assert(std::is_default_constructible_v<glBuffer>); -static_assert(!std::is_copy_constructible_v<glBuffer>); -static_assert(!std::is_copy_assignable_v<glBuffer>); -static_assert(std::is_nothrow_move_constructible_v<glBuffer>); -static_assert(std::is_nothrow_move_assignable_v<glBuffer>); -static_assert(sizeof(glBuffer) == sizeof(GLuint)); diff --git a/lib/glArrays.h b/lib/glArrays.h index 787ea17..7f5a10b 100644 --- a/lib/glArrays.h +++ b/lib/glArrays.h @@ -5,77 +5,108 @@ #include <cstddef> #include <glad/gl.h> #include <special_members.h> +#include <utility> -template<size_t N> class glArraysBase { - static_assert(N > 0); +namespace Detail { + class glNamed; -public: - ~glArraysBase() = default; - NO_COPY(glArraysBase); - CUSTOM_MOVE(glArraysBase); + template<typename Named> + concept IsglNamed = sizeof(Named) == sizeof(GLuint) && std::is_base_of_v<Detail::glNamed, Named>; +} + +template<Detail::IsglNamed, size_t, auto, auto> struct glManagedArray; + +namespace Detail { + class glNamed { + public: + glNamed() = default; + ~glNamed() = default; + DEFAULT_MOVE_NO_COPY(glNamed); + + GLuint + operator*() const noexcept + { + return name; + } + + // NOLINTNEXTLINE(hicpp-explicit-conversions) + operator GLuint() const noexcept + { + return name; + } - // NOLINTNEXTLINE(hicpp-explicit-conversions) - inline - operator GLuint() const + protected: + GLuint name {}; + template<Detail::IsglNamed, size_t, auto, auto> friend struct ::glManagedArray; + }; +} + +template<Detail::IsglNamed Named, auto Gen, auto Del> struct glManagedSingle : public Named { + glManagedSingle() noexcept { - static_assert(N == 1, "Implicit cast only if N == 1"); - return ids.front(); + (*Gen)(1, &this->name); } - inline auto - operator[](size_t n) const + glManagedSingle(glManagedSingle && src) noexcept { - return ids[n]; + this->name = std::exchange(src.name, 0); } - constexpr static auto size {N}; + ~glManagedSingle() noexcept + { + if (this->name) { + (*Del)(1, &this->name); + } + } -protected: - glArraysBase() noexcept = default; - std::array<GLuint, N> ids {}; -}; + NO_COPY(glManagedSingle); -template<size_t N> inline glArraysBase<N>::glArraysBase(glArraysBase<N> && src) noexcept : ids {src.ids} -{ - std::fill(src.ids.begin(), src.ids.end(), 0); -} + glManagedSingle & + operator=(glManagedSingle && src) noexcept + { + if (this->name) { + (*Del)(1, &this->name); + } + this->name = std::exchange(src.name, 0); + return *this; + } +}; -template<size_t N> -inline glArraysBase<N> & -glArraysBase<N>::operator=(glArraysBase<N> && src) noexcept -{ - ids = src.ids; - std::fill(src.ids.begin(), src.ids.end(), 0); - return *this; -} +template<Detail::IsglNamed Named, size_t N, auto Gen, auto Del> struct glManagedArray : public std::array<Named, N> { + using Arr = std::array<Named, N>; -template<size_t N, auto Gen, auto Del> class glArrays : public glArraysBase<N> { -public: - using glArraysBase<N>::glArraysBase; - using glArraysBase<N>::operator=; + glManagedArray() noexcept + { + (*Gen)(N, &Arr::front().name); + } - DEFAULT_MOVE_COPY(glArrays); + glManagedArray(glManagedArray && src) noexcept + { + Arr::operator=(std::exchange(static_cast<Arr &>(src), {})); + } - inline glArrays() noexcept + ~glManagedArray() noexcept { - (*Gen)(N, this->ids.data()); + if (Arr::front().name) { + (*Del)(N, &Arr::front().name); + } } - inline ~glArrays() noexcept + NO_COPY(glManagedArray); + + glManagedArray & + operator=(glManagedArray && src) noexcept { - if (this->ids.front()) { - (*Del)(N, this->ids.data()); + if (Arr::front().name) { + (*Del)(N, &Arr::front().name); } + Arr::operator=(std::exchange(static_cast<Arr &>(src), {})); + return *this; } -}; -template<size_t N> using glVertexArrays = glArrays<N, &glGenVertexArrays, &glDeleteVertexArrays>; -using glVertexArray = glVertexArrays<1>; -template<size_t N> using glBuffers = glArrays<N, &glGenBuffers, &glDeleteBuffers>; -using glBuffer = glBuffers<1>; -template<size_t N> using glTextures = glArrays<N, &glGenTextures, &glDeleteTextures>; -using glTexture = glTextures<1>; -template<size_t N> using glFrameBuffers = glArrays<N, &glGenFramebuffers, &glDeleteFramebuffers>; -using glFrameBuffer = glFrameBuffers<1>; -template<size_t N> using glRenderBuffers = glArrays<N, &glGenRenderbuffers, &glDeleteRenderbuffers>; -using glRenderBuffer = glRenderBuffers<1>; + [[nodiscard]] static constexpr size_t + size() noexcept + { + return N; + } +}; diff --git a/lib/glContainer.h b/lib/glContainer.h deleted file mode 100644 index 5cbb038..0000000 --- a/lib/glContainer.h +++ /dev/null @@ -1,497 +0,0 @@ -#pragma once - -#include "glArrays.h" -#include <cassert> -#include <span> -#include <stdexcept> -#include <utility> -#include <vector> - -static_assert(GL_READ_ONLY < GL_READ_WRITE); -static_assert(GL_WRITE_ONLY < GL_READ_WRITE); - -template<typename T> class glContainer { -public: - using span = std::span<T>; - using const_span = std::span<const T>; - using value_type = T; - using reference_type = T &; - using const_reference_type = const T &; - using pointer_type = T *; - using const_pointer_type = const T *; - using size_type = std::size_t; - using iterator = span::iterator; - using const_iterator = const_span::iterator; - using reverse_iterator = span::reverse_iterator; - using const_reverse_iterator = const_span::reverse_iterator; - static constexpr bool is_trivial_dest = std::is_trivially_destructible_v<T>; - - explicit glContainer(GLenum target = GL_ARRAY_BUFFER) : target_ {target} - { - allocBuffer(1); - } - - ~glContainer() - requires(is_trivial_dest) - = default; - - ~glContainer() - requires(!is_trivial_dest) - { - clear(); - } - - template<template<typename, typename...> typename C> - explicit glContainer(const C<T> & src, GLenum target = GL_ARRAY_BUFFER) : target_ {target} - { - reserve(src.size()); - std::copy(src.begin(), src.end(), std::back_inserter(*this)); - } - - DEFAULT_MOVE_NO_COPY(glContainer); - - [[nodiscard]] iterator - begin() - { - map(GL_READ_WRITE); - return mkspan().begin(); - } - - [[nodiscard]] iterator - end() - { - map(GL_READ_WRITE); - return mkspan().end(); - } - - [[nodiscard]] const_iterator - begin() const - { - map(GL_READ_ONLY); - return mkcspan().begin(); - } - - [[nodiscard]] const_iterator - end() const - { - map(GL_READ_ONLY); - return mkcspan().end(); - } - - [[nodiscard]] const_iterator - cbegin() const - { - map(GL_READ_ONLY); - return mkcspan().begin(); - } - - [[nodiscard]] const_iterator - cend() const - { - map(GL_READ_ONLY); - return mkcspan().end(); - } - - [[nodiscard]] reverse_iterator - rbegin() - { - map(GL_READ_WRITE); - return mkspan().rbegin(); - } - - [[nodiscard]] reverse_iterator - rend() - { - map(GL_READ_WRITE); - return mkspan().rend(); - } - - [[nodiscard]] const_reverse_iterator - rbegin() const - { - map(GL_READ_ONLY); - return mkcspan().rbegin(); - } - - [[nodiscard]] const_reverse_iterator - rend() const - { - map(GL_READ_ONLY); - return mkcspan().rend(); - } - - [[nodiscard]] const_reverse_iterator - crbegin() const - { - map(GL_READ_ONLY); - return mkcspan().rbegin(); - } - - [[nodiscard]] const_reverse_iterator - crend() const - { - map(GL_READ_ONLY); - return mkcspan().rend(); - } - - [[nodiscard]] const auto & - bufferName() const - { - return buffer_; - } - - [[nodiscard]] size_type - size() const - { - return size_; - } - - void - at(size_type pos, const T & value) - { - if (pos >= size()) { - throw std::out_of_range {__FUNCTION__}; - } - if (data_) { - mkspan()[pos] = value; - } - else { - glBindBuffer(target_, buffer_); - glBufferSubData(target_, static_cast<GLintptr>(pos * sizeof(T)), sizeof(value), &value); - glBindBuffer(target_, 0); - } - } - - [[nodiscard]] reference_type - at(size_type pos) - { - if (pos >= size()) { - throw std::out_of_range {__FUNCTION__}; - } - map(GL_READ_WRITE); - return mkspan()[pos]; - } - - [[nodiscard]] const_reference_type - at(size_type pos) const - { - if (pos >= size()) { - throw std::out_of_range {__FUNCTION__}; - } - map(GL_READ_ONLY); - return mkcspan()[pos]; - } - - [[nodiscard]] reference_type - operator[](size_type pos) - { - map(GL_READ_WRITE); - return mkspan()[pos]; - } - - [[nodiscard]] const_reference_type - operator[](size_type pos) const - { - map(GL_READ_ONLY); - return mkcspan()[pos]; - } - - [[nodiscard]] pointer_type - data() - { - map(GL_READ_WRITE); - return data_; - } - - [[nodiscard]] const_pointer_type - data() const - { - map(GL_READ_ONLY); - return data_; - } - - [[nodiscard]] reference_type - front() - { - map(GL_READ_WRITE); - return mkspan().front(); - } - - [[nodiscard]] reference_type - back() - { - map(GL_READ_WRITE); - return mkspan().back(); - } - - [[nodiscard]] const_reference_type - front() const - { - map(GL_READ_ONLY); - return mkcspan().front(); - } - - [[nodiscard]] const_reference_type - back() const - { - map(GL_READ_ONLY); - return mkcspan().back(); - } - - [[nodiscard]] bool - empty() const - { - return !size(); - } - - [[nodiscard]] size_type - capacity() const - { - return capacity_; - } - - void - unmap() const - { - if (data_) { - glBindBuffer(target_, buffer_); - glUnmapBuffer(target_); - glBindBuffer(target_, 0); - data_ = {}; - access_ = {}; - } - } - - void - reserve(size_type newCapacity) - { - if (newCapacity <= capacity_) { - return; - } - newCapacity = std::max(newCapacity, capacity_ * 2); - - std::vector<T> existing; - existing.reserve(size_); - std::move(begin(), end(), std::back_inserter(existing)); - allocBuffer(newCapacity); - std::move(existing.begin(), existing.end(), begin()); - } - - void - resize(size_type newSize) - requires std::is_default_constructible_v<T> - { - if (newSize == size_) { - return; - } - - if (const auto maintain = std::min(newSize, size_)) { - std::vector<T> existing; - const auto maintaind = static_cast<typename decltype(existing)::difference_type>(maintain); - existing.reserve(maintain); - std::move(begin(), end(), std::back_inserter(existing)); - allocBuffer(newSize); - mapForAdd(); - std::move(existing.begin(), existing.begin() + maintaind, begin()); - } - else { - allocBuffer(newSize); - mapForAdd(); - } - if (newSize > size_) { - for (auto & uninitialised : mkspan().subspan(size_, newSize - size_)) { - new (&uninitialised) T {}; - } - } - setSize(newSize); - } - - void - shrink_to_fit() - { - if (capacity_ <= size_) { - return; - } - - std::vector<T> existing; - existing.reserve(size_); - map(is_trivial_dest ? GL_READ_ONLY : GL_READ_WRITE); - std::move(begin(), end(), std::back_inserter(existing)); - allocBuffer(size_); - map(GL_READ_WRITE); - std::move(existing.begin(), existing.end(), begin()); - } - - void - clear() noexcept(is_trivial_dest) - { - if constexpr (!is_trivial_dest) { - map(GL_READ_WRITE); - std::for_each(begin(), end(), [](auto && v) { - v.~T(); - }); - } - setSize(0); - } - - template<typename... P> - reference_type - emplace_back(P &&... ps) - requires std::is_constructible_v<T, P...> - { - auto newSize = size_ + 1; - reserve(newSize); - mapForAdd(); - new (&*end()) T {std::forward<P>(ps)...}; - setSize(newSize); - return back(); - } - - template<typename... P> - iterator - emplace(iterator pos, P &&... ps) - requires std::is_nothrow_constructible_v<T, P...> - { - auto newSize = size_ + 1; - const auto idx = pos - begin(); - reserve(newSize); - mapForAdd(); - pos = begin() + idx; - std::move_backward(pos, end(), end() + 1); - pos->~T(); - new (&*pos) T {std::forward<P>(ps)...}; - setSize(newSize); - return pos; - } - - reference_type - push_back(T p) - requires std::is_move_constructible_v<T> - { - auto newSize = size_ + 1; - reserve(newSize); - mapForAdd(); - new (&*end()) T {std::move(p)}; - setSize(newSize); - return back(); - } - - iterator - insert(iterator pos, T p) - requires std::is_nothrow_move_constructible_v<T> - { - auto newSize = size_ + 1; - const auto idx = pos - begin(); - reserve(newSize); - mapForAdd(); - pos = begin() + idx; - std::move_backward(pos, end(), end() + 1); - pos->~T(); - new (&*pos) T {std::move(p)}; - setSize(newSize); - return pos; - } - - void - pop_back() - { - if constexpr (!is_trivial_dest) { - map(GL_READ_WRITE); - back().~T(); - } - --size_; - } - - void - erase(iterator pos) - { - erase(pos, pos + 1); - } - - void - erase(iterator pos, iterator to) - { - const auto eraseSize = to - pos; - map(GL_READ_WRITE); - std::move(to, end(), pos); - if constexpr (!is_trivial_dest) { - std::for_each(end() - eraseSize, end(), [](auto && v) { - v.~T(); - }); - } - setSize(size_ - static_cast<size_type>(eraseSize)); - } - -protected: - void - setSize(size_type s) - { - size_ = s; - } - - void - allocBuffer(size_type newCapacity) - { - if (newCapacity == 0) { - return allocBuffer(1); - } - glBindBuffer(target_, buffer_); - glBufferData(target_, static_cast<GLsizeiptr>(sizeof(T) * newCapacity), nullptr, GL_DYNAMIC_DRAW); - glBindBuffer(target_, 0); - capacity_ = newCapacity; - data_ = {}; - access_ = {}; - } - - void - map(GLenum access) const - { - if (size_ > 0) { - mapMode(access); - } - } - - void - mapForAdd() const - { - if (!data_) { - mapMode(GL_READ_WRITE); - } - } - - void - mapMode(GLenum access) const - { - if (data_ && access_ < access) { - unmap(); - } - if (!data_) { - glBindBuffer(target_, buffer_); - data_ = static_cast<T *>(glMapBuffer(target_, access)); - glBindBuffer(target_, 0); - assert(data_); - access_ = access; - } - } - - span - mkspan() const - { - assert(!size_ || data_); - return span {data_, size_}; - } - - const_span - mkcspan() const - { - assert(!size_ || data_); - return const_span {data_, size_}; - } - - glBuffer buffer_; - GLenum target_; - std::size_t capacity_ {}; - std::size_t size_ {}; - mutable T * data_; - mutable GLenum access_ {}; -}; diff --git a/lib/glMappedBufferSpan.h b/lib/glMappedBufferSpan.h new file mode 100644 index 0000000..d30c911 --- /dev/null +++ b/lib/glMappedBufferSpan.h @@ -0,0 +1,51 @@ +#pragma once + +#include "special_members.h" +#include <cstddef> +#include <glad/gl.h> +#include <span> +#include <utility> + +template<typename T> class glMappedBufferSpan : public std::span<T> { +public: + glMappedBufferSpan(GLuint buffer, size_t count, GLenum access, bool reinit) : + std::span<T> {[&]() { + if (reinit) { + glNamedBufferData( + buffer, static_cast<GLsizeiptr>(sizeof(T) * count), nullptr, GL_DYNAMIC_DRAW); + } + return static_cast<T *>(glMapNamedBuffer(buffer, access)); + }(), + count}, + buffer {buffer} + { + } + + ~glMappedBufferSpan() + { + if (buffer) { + glUnmapNamedBuffer(buffer); + } + } + + glMappedBufferSpan(glMappedBufferSpan && other) noexcept : + std::span<T> {other}, buffer {std::exchange(other.buffer, 0)} + { + } + + glMappedBufferSpan & + operator=(glMappedBufferSpan && other) noexcept + { + std::span<T>::span = other; + if (buffer) { + glUnmapBuffer(buffer); + } + buffer = std::exchange(other.buffer, 0); + return *this; + } + + NO_COPY(glMappedBufferSpan); + +private: + GLuint buffer; +}; diff --git a/lib/gl_traits.h b/lib/gl_traits.h index b3c6909..eff9304 100644 --- a/lib/gl_traits.h +++ b/lib/gl_traits.h @@ -14,27 +14,33 @@ struct gl_traits_base { }; struct gl_traits_float : public gl_traits_base { - static constexpr auto vertexAttribFunc { - [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) -> GLuint { - glVertexAttribPointer(index, size, type, GL_FALSE, stride, pointer); - return 1; - }}; + template<GLenum type, GLint size> + static GLuint + vertexAttribFormatFunc(GLuint vao, GLuint index, GLuint offset) + { + glVertexArrayAttribFormat(vao, index, size, type, GL_FALSE, offset); + return 1; + } }; struct gl_traits_longfloat : public gl_traits_base { - static constexpr auto vertexAttribFunc { - [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) -> GLuint { - glVertexAttribLPointer(index, size, type, stride, pointer); - return 1; - }}; + template<GLenum type, GLint size> + static GLuint + vertexAttribFormatFunc(GLuint vao, GLuint index, GLuint offset) + { + glVertexArrayAttribLFormat(vao, index, size, type, offset); + return 1; + } }; struct gl_traits_integer : public gl_traits_base { - static constexpr auto vertexAttribFunc { - [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) -> GLuint { - glVertexAttribIPointer(index, size, type, stride, pointer); - return 1; - }}; + template<GLenum type, GLint size> + static GLuint + vertexAttribFormatFunc(GLuint vao, GLuint index, GLuint offset) + { + glVertexArrayAttribIFormat(vao, index, size, type, offset); + return 1; + } }; template<> struct gl_traits<glm::f32> : public gl_traits_float { @@ -42,36 +48,43 @@ template<> struct gl_traits<glm::f32> : public gl_traits_float { static constexpr auto glUniformFunc {&glUniform1f}; static constexpr std::array glUniformvFunc {&glUniform1fv, &glUniform2fv, &glUniform3fv, &glUniform4fv}; static constexpr std::array glUniformmFunc {&glUniformMatrix2fv, &glUniformMatrix3fv, &glUniformMatrix4fv}; - static constexpr auto glTexParameterFunc {&glTexParameterf}; - static constexpr auto glTexParameterfFunc {&glTexParameterfv}; + static constexpr auto glTextureParameterFunc {&glTextureParameterf}; + static constexpr auto glTextureParametervFunc {&glTextureParameterfv}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>}; }; template<> struct gl_traits<glm::f64> : public gl_traits_longfloat { static constexpr GLenum type {GL_DOUBLE}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>}; }; template<> struct gl_traits<glm::int8> : public gl_traits_integer { static constexpr GLenum type {GL_BYTE}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>}; }; template<> struct gl_traits<glm::int16> : public gl_traits_integer { static constexpr GLenum type {GL_SHORT}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>}; }; template<> struct gl_traits<glm::int32> : public gl_traits_integer { static constexpr GLenum type {GL_INT}; static constexpr auto glUniformFunc {&glUniform1i}; static constexpr std::array glUniformvFunc {&glUniform1iv, &glUniform2iv, &glUniform3iv, &glUniform4iv}; - static constexpr auto glTexParameterFunc {&glTexParameteri}; - static constexpr auto glTexParameterfFunc {&glTexParameteriv}; + static constexpr auto glTextureParameterFunc {&glTextureParameteri}; + static constexpr auto glTextureParametervFunc {&glTextureParameteriv}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>}; }; template<> struct gl_traits<glm::uint8> : public gl_traits_integer { static constexpr GLenum type {GL_UNSIGNED_BYTE}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>}; }; template<> struct gl_traits<glm::uint16> : public gl_traits_integer { static constexpr GLenum type {GL_UNSIGNED_SHORT}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>}; }; template<> struct gl_traits<glm::uint32> : public gl_traits_integer { @@ -79,35 +92,44 @@ template<> struct gl_traits<glm::uint32> : public gl_traits_integer { static constexpr auto glUniformFunc {&glUniform1ui}; static constexpr std::array<decltype(&glUniform1uiv), 5> glUniformvFunc { &glUniform1uiv, &glUniform2uiv, &glUniform3uiv, &glUniform4uiv}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>}; }; template<typename T, std::size_t S> struct gl_traits<std::array<T, S>> : public gl_traits<T> { static constexpr GLint size {S * gl_traits<T>::size}; - static constexpr auto vertexAttribFunc { - [](GLuint index, GLint, GLenum type, GLsizei stride, const void * pointer) -> GLuint { - const auto base = static_cast<const T *>(pointer); - for (GLuint e = 0; e < S; e++) { - glVertexAttribPointer(index + e, gl_traits<T>::size, type, GL_FALSE, stride, base + e); - } - return S; - }}; + static constexpr auto vertexArrayAttribFormat {[](GLuint vao, GLuint index, GLuint offset) { + if constexpr (std::is_pod_v<T>) { + return gl_traits<T>::template vertexAttribFormatFunc<gl_traits<T>::type, S>(vao, index, offset); + } + else { + GLuint used = 0; + for (GLuint e = 0; e < S; e++) { + used += gl_traits<T>::template vertexAttribFormatFunc<gl_traits<T>::type, 1>( + vao, index + e, offset + (e * sizeof(T))); + } + return used; + } + }}; }; template<glm::length_t L, typename T, glm::qualifier Q> struct gl_traits<glm::vec<L, T, Q>> : public gl_traits<T> { static constexpr GLint size {L}; + static constexpr auto vertexArrayAttribFormat {[](GLuint vao, GLuint index, GLuint offset) { + return gl_traits<T>::template vertexAttribFormatFunc<gl_traits<T>::type, L>(vao, index, offset); + }}; }; template<glm::length_t C, glm::length_t R, typename T, glm::qualifier Q> struct gl_traits<glm::mat<C, R, T, Q>> : public gl_traits<T> { static constexpr GLint size {C * R}; - static constexpr auto vertexAttribFunc { - [](GLuint index, GLint, GLenum type, GLsizei stride, const void * pointer) -> GLuint { - const auto base = static_cast<const T *>(pointer); - for (GLuint r = 0; r < R; r++) { - glVertexAttribPointer(index + r, C, type, GL_FALSE, stride, base + (r * C)); - } - return R; - }}; + static constexpr auto vertexArrayAttribFormat {[](GLuint vao, GLuint index, GLuint offset) { + GLuint used = 0; + for (GLuint row = 0; row < R; row++) { + used += gl_traits<T>::template vertexAttribFormatFunc<gl_traits<T>::type, C>( + vao, index + row, offset + (C * row * static_cast<GLuint>(sizeof(T)))); + } + return used; + }}; }; template<typename T> @@ -117,9 +139,9 @@ concept has_glUniformNv = requires { gl_traits<T>::glUniformvFunc; }; template<typename T> concept has_glUniformMatrixNv = requires { gl_traits<T>::glUniformmFunc; }; template<typename T> -concept has_glTexParameter = requires { gl_traits<T>::glTexParameterFunc; }; +concept has_glTextureParameter = requires { gl_traits<T>::glTextureParameterFunc; }; template<typename T> -concept has_glTexParameterf = requires { gl_traits<T>::glTexParameterfFunc; }; +concept has_glTextureParameterv = requires { gl_traits<T>::glTextureParametervFunc; }; template<has_glUniform1 T> void @@ -163,17 +185,3 @@ glUniform(GLint location, std::span<const glm::mat<L, L, T, Q>> v) (*gl_traits<T>::glUniformmFunc[L - 2])( location, static_cast<GLsizei>(v.size()), GL_FALSE, glm::value_ptr(v.front())); } - -template<has_glTexParameter T> -void -glTexParameter(GLenum target, GLenum pname, T param) -{ - (*gl_traits<T>::glTexParameterFunc)(target, pname, param); -} - -template<glm::length_t L, has_glTexParameterf T, glm::qualifier Q> -void -glTexParameter(GLenum target, GLenum pname, const glm::vec<L, T, Q> & param) -{ - (*gl_traits<T>::glTexParameterfFunc)(target, pname, glm::value_ptr(param)); -} 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..4a835d3 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,353 @@ 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 + +// GLM round is not constexpr :( And we can use lround to convert at the same time +template<glm::length_t D, std::floating_point T, glm::qualifier Q> +[[nodiscard]] constexpr glm::vec<D, long, Q> +lround(const glm::vec<D, T, Q> & value) +{ + glm::vec<D, long, Q> out {}; + for (glm::length_t axis = 0; axis < D; ++axis) { + out[axis] = std::lround(value[axis]); + } + return out; +} 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 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 g - GlobalPosition<D>(r); + 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 rotation<D, T, Q>(angle, {0, 0}, {0, 1}, {1, 1}, {1, 0}); +} + +// 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) { - return sincosf(a, &s, &c); + return rotation<D, T, Q>(angle, {0, 0}, {1, 0}, {1, 1}, {0, 1}); } -inline Rotation2D -sincosf(float a) +// 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) { - Rotation2D sc; - sincosf(a, sc.x, sc.y); - return sc; + return rotation<D, T, Q>(angle, {0, 0}, {2, 0}, {2, 2}, {0, 2}); } -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 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}); +} -float vector_yaw(const Direction2D & diff); -float vector_pitch(const Direction3D & diff); +// 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 rotate_yaw<D>(angles.y) * rotate_pitch<D>(angles.x) * rotate_roll<D>(angles.z); +} -template<typename T, glm::qualifier Q> -glm::vec<2, T, Q> -vector_normal(const glm::vec<2, T, Q> & v) +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 {-v.y, v.x}; + 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>(a, b); + return crossProduct<Q>(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<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 glm::cross(a, b); + return glm::cross(valueA, valueB); } -template<typename R = float, typename Ta, typename Tb> -inline constexpr auto -ratio(Ta a, Tb b) +template<Arithmetic R = float, Arithmetic Ta, Arithmetic Tb> +constexpr auto +ratio(const Ta valueA, const Tb valueB) { - return (static_cast<R>(a) / static_cast<R>(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 T, glm::qualifier Q> -inline constexpr auto -ratio(glm::vec<2, T, Q> v) +template<Arithmetic R = float, Arithmetic T, glm::qualifier Q = glm::defaultp> +constexpr auto +ratio(const glm::vec<2, T, Q> & value) { - return ratio<R>(v.x, v.y); + return ratio<R>(value.x, value.y); } -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 L = 3, std::floating_point T, glm::qualifier Q = glm::defaultp> +constexpr auto +perspective_divide(const glm::vec<4, T, Q> & value) { - return v / v.w; + return value / value.w; } -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 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 {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, 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, 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> +perspectiveMultiply(const 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; + 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> -perspectiveApply(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) { - return p = perspectiveMultiply(p, mutation); + return base = perspectiveMultiply(base, mutation); } -float normalize(float ang); +template<std::floating_point T> +constexpr T +normalize(T ang) +{ + while (ang > glm::pi<T>()) { + ang -= glm::two_pi<T>(); + } + while (ang <= -glm::pi<T>()) { + ang += glm::two_pi<T>(); + } + return ang; +} -template<typename T, glm::qualifier Q> +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<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 +428,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 +463,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/msgException.h b/lib/msgException.h new file mode 100644 index 0000000..660738d --- /dev/null +++ b/lib/msgException.h @@ -0,0 +1,23 @@ +#pragma once + +#include <optional> +#include <string> + +template<typename BaseException> class MsgException : public BaseException { +protected: + using BaseException::BaseException; + [[nodiscard]] virtual std::string getMsg() const noexcept = 0; + +public: + [[nodiscard]] const char * + what() const noexcept override + { + if (!msg) { + msg.emplace(getMsg()); + } + return msg->c_str(); + } + +private: + mutable std::optional<std::string> msg; +}; 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 {}; @@ -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/stdTypeDefs.h b/lib/stdTypeDefs.h index beab630..38ebe0b 100644 --- a/lib/stdTypeDefs.h +++ b/lib/stdTypeDefs.h @@ -28,6 +28,18 @@ template<typename T> struct AnyPtr { return *ptr; } + // NOLINTNEXTLINE(hicpp-explicit-conversions) + operator bool() const + { + return ptr != nullptr; + } + + bool + operator!() const + { + return ptr == nullptr; + } + private: T * ptr; }; 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)); + } +}; @@ -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() |
