--- a/src/basics.h Wed Jun 08 20:41:21 2022 +0300 +++ b/src/basics.h Wed Jun 08 22:29:44 2022 +0300 @@ -18,13 +18,13 @@ #pragma once #include <algorithm> -#include <cstdio> -#include <cstdlib> -#include <cstring> #include <cmath> +#include <compare> +#include <memory> #include <optional> #include <type_traits> -#include <QMatrix4x4> +#include <QDataStream> +#include <QDebug> #include <QObject> #include <QPointF> #include <QSet> @@ -32,64 +32,24 @@ #include <QStringList> #include <QVariant> #include <QVector> -#include <QVector3D> #include <glm/glm.hpp> - -using GLRotationMatrix = QMatrix4x4; - -enum Axis -{ - X = 0, - Y = 1, - Z = 2 -}; +#include "geometry.h" +#include "functional.h" -enum Result -{ - Success = 0, - PartialSuccess, - Failure -}; - -constexpr bool failed(Result r) -{ - return r == Failure; -} +template<typename T> +using opt = std::optional<T>; -enum Winding -{ - NoWinding, - Anticlockwise, - Clockwise, -}; +Q_DECLARE_METATYPE(glm::vec3) +Q_DECLARE_METATYPE(glm::mat4) -/* - * Special operator definition that implements the XOR operator for windings. - * However, if either winding is NoWinding, then this function returns NoWinding. - */ -inline Winding operator^(Winding one, Winding other) -{ - if (one == NoWinding or other == NoWinding) - return NoWinding; - else - return static_cast<Winding>(static_cast<int>(one) ^ static_cast<int>(other)); -} - -inline Winding& operator^=(Winding& one, Winding other) -{ - one = one ^ other; - return one; -} - +//! \brief count the amount of elements in a basic array template<typename T, int N> -constexpr int countof(T const (&)[N]) +constexpr int countof(T(&)[N]) { return N; } -/** - * @brief casts @c x to a suitable unsigned integer - */ +//! \brief casts @c x to a suitable unsigned integer template<typename T> constexpr auto unsigned_cast(T x) -> std::enable_if_t<std::is_integral_v<T>, std::make_unsigned_t<T>> @@ -97,9 +57,7 @@ return static_cast<std::make_unsigned_t<T>>(x); } -/** - * @brief casts @c x to a suitable signed integer - */ +//! \brief casts @c x to a suitable signed integer template<typename T> constexpr auto signed_cast(T x) -> std::enable_if_t<std::is_integral_v<T>, std::make_signed_t<T>> @@ -107,86 +65,42 @@ return static_cast<std::make_signed_t<T>>(x); } - -/** -* @brief casts floating point values to float, converting non-floating point values causes an error -* @param[in] x floating point value to cast -* @returns float -*/ +//! \brief casts floating point values to float template<typename T> -auto toFloat(T x) -> std::enable_if_t<std::is_floating_point_v<T>, float> +constexpr auto float_cast(T x) + -> std::enable_if_t<std::is_floating_point_v<T>, float> { return static_cast<float>(x); } -/** -* @brief casts floating point values to double, converting non-floating point values causes an error -* @param[in] x floating point value to cast -* @returns double -*/ +//! \brief casts floating point values to double template<typename T> -auto toDouble(T x) -> std::enable_if_t<std::is_floating_point_v<T>, double> +constexpr auto double_cast(T x) + -> std::enable_if_t<std::is_floating_point_v<T>, double> { return static_cast<double>(x); } -/** -* @brief casts floating point values to qreal, converting non-floating point values causes an error -* @param[in] x floating point value to cast -* @returns qreal -*/ +//! \brief casts floating point values to qreal template<typename T> -auto toQreal(T x) -> std::enable_if_t<std::is_floating_point_v<T>, qreal> +constexpr auto qreal_cast(T x) + -> std::enable_if_t<std::is_floating_point_v<T>, qreal> { return static_cast<qreal>(x); } template<int N, typename T, glm::qualifier Q> -inline QPoint toQPoint(const glm::vec<N, T, Q>& vec) +constexpr QPointF toQPointF(const glm::vec<N, T, Q>& vec) { - return {static_cast<int>(vec.x), static_cast<int>(vec.y)}; + return {double_cast(vec.x), double_cast(vec.y)}; } -template<int N, typename T, glm::qualifier Q> -inline QPointF toQPointF(const glm::vec<N, T, Q>& vec) -{ - return {toDouble(vec.x), toDouble(vec.y)}; -} - -inline glm::vec2 toVec2(const QPoint& point) -{ - return {point.x(), point.y()}; -} - -inline glm::vec2 toVec2(const QPointF& point) +constexpr glm::vec2 toVec2(const QPointF& point) { return {point.x(), point.y()}; } -/* - * coalesce(arg1, arg2, ..., argn) - * Returns the first of the given arguments that evaluates to true. - */ -template<typename T> -T coalesce(T&& arg) -{ - // recursion base: 1 argument - return arg; -} -template<typename T, typename... Rest> -std::common_type_t<T, Rest...> coalesce(T&& arg, Rest&&... rest) -{ - // general case: n arguments - return arg ? arg : coalesce(rest...); -} - -/** - * @brief Finds an element in a map and possibly returns a reference to it if find - * @param map - * @param key - * @returns the value or nullptr if not found - */ template<typename T, typename R, typename K> const R* findInMap(const std::map<T, R>& map, K&& key) { @@ -201,12 +115,6 @@ } } -/** - * @brief Finds an element in a map and possibly returns a reference to it if find - * @param map - * @param key - * @returns the value or no value if not found - */ template<typename T, typename R, typename K> R* findInMap(std::map<T, R>& map, K&& key) { @@ -221,10 +129,178 @@ } } +template<typename T, typename R> +void removeFromMap(std::map<T, R>& map, T&& key) +{ + const auto it = map.find(key); + if (it != map.end()) { + map.erase(it); + } +} + template<typename T = float> constexpr std::enable_if_t<std::is_floating_point_v<T>, T> pi = static_cast<T>(M_PIl); -constexpr double infinity = std::numeric_limits<double>::infinity(); + +inline QSizeF sizeToSizeF(const QSize& size) +{ + return {static_cast<qreal>(size.width()), static_cast<qreal>(size.height())}; +} + +//! \brief Hints to the specified vector that a certain amount of new elements +//! are going to be added. +template<typename T> +void reserveMore(std::vector<T>& vector, std::size_t amount) +{ + vector.reserve(vector.size() + amount); +} + +inline QString vectorToString(const glm::vec2& vec) +{ + return QStringLiteral("(%1, %2)") + .arg(double_cast(vec.x)) + .arg(double_cast(vec.y)); +} + +inline QString vectorToString(const glm::vec3& vec) +{ + return QStringLiteral("(%1, %2, %3)") + .arg(double_cast(vec.x)) + .arg(double_cast(vec.y)) + .arg(double_cast(vec.z)); +} + +inline QString vectorToString(const glm::vec4& vec) +{ + return QStringLiteral("%1, %2, %3, %4)") + .arg(double_cast(vec.x)) + .arg(double_cast(vec.y)) + .arg(double_cast(vec.z)) + .arg(double_cast(vec.w)); +} +template<typename T, typename IdentifierType> +struct TypeValue +{ + T value; + auto operator<=>(TypeValue other) const = default; +}; + +template<typename T, typename R> +int qHash(TypeValue<T, R> value) +{ + return qHash(value.value); +} + +/** + * Iterates a @c glm::mat + */ +template<int X, int Y, typename T, glm::qualifier Q, typename Fn> +void iter_matrix(const glm::mat<X, Y, T, Q>& matrix, Fn&& fn) +{ + for (int i = 0; i < X; ++i) + { + for (int j = 0; j < Y; ++j) + { + fn(i, j, matrix[i][j]); + } + } +} + +inline QDataStream& operator<<(QDataStream& stream, const glm::vec3& vec) +{ + return stream << vec.x << vec.y << vec.z; +} + +inline QDataStream& operator>>(QDataStream& stream, glm::vec3& vec) +{ + return stream >> vec.x >> vec.y >> vec.z; +} -Q_DECLARE_METATYPE(glm::vec3) -Q_DECLARE_METATYPE(glm::mat4) +template<int X, int Y, typename T, glm::qualifier Q> +QDataStream& operator<<(QDataStream& stream, const glm::mat<X, Y, T, Q>& mat) +{ + iter_matrix(mat, [&stream](int, int, float x) + { + stream << x; + }); + return stream; +} + +template<int X, int Y, typename T, glm::qualifier Q> +QDataStream& operator>>(QDataStream& stream, glm::mat<X, Y, T, Q>& mat) +{ + iter_matrix(mat, [&stream](int, int, float x) + { + stream >> x; + }); + return stream; +} + +//! \brief Converts a pointer value to an \c std::optional value. If p has a +//! value, it will be copied into the result value. +template<typename T> +std::optional<T> pointerToOptional(const T* p) +{ + std::optional<T> result; + if (p != nullptr) { + result = *p; + } + return result; +} + +// some magic code from https://en.cppreference.com/w/cpp/utility/variant/visit +// for use with std::visit +template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; +template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; + +// http://stackoverflow.com/a/18204188/3629665 +template<typename T> +constexpr T rotl10(T x) +{ + return (x << 10) | ((x >> 22) & 0x000000ff); +} + +template<typename T> +constexpr T rotl20(T x) +{ + return (x << 20) | ((x >> 12) & 0x000000ff); +} + +inline QString quoted(QString string) +{ + if (string.contains("'")) + { + string.replace("\"", "\\\""); + string = "\"" + string + "\""; + } + else + { + string = "'" + string + "'"; + } + return string; +} + +inline QString vertexToString(const glm::vec3& vertex) +{ + return QStringLiteral("%1 %2 %3").arg(vertex.x).arg(vertex.y).arg(vertex.z); +} + +inline QString vertexToStringParens(const glm::vec3& vertex) +{ + return QStringLiteral("(%1 %2 %3)").arg(vertex.x).arg(vertex.y).arg(vertex.z); +} + +inline QString transformToString(const glm::mat4& matrix) +{ + return QStringLiteral("%1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12") + .arg(matrix[3][0], matrix[3][1], matrix[3][2]) + .arg(matrix[0][0], matrix[1][0], matrix[2][0]) + .arg(matrix[0][1], matrix[1][1], matrix[2][1]) + .arg(matrix[0][2], matrix[1][2], matrix[2][2]); +} + +template<typename T, glm::qualifier Q> +constexpr unsigned int qHash(const glm::vec<3, T, Q>& key) +{ + return qHash(key.x) ^ rotl10(qHash(key.y)) ^ rotl20(qHash(key.z)); +}