16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 */ |
17 */ |
18 |
18 |
19 #pragma once |
19 #pragma once |
20 #include <algorithm> |
20 #include <algorithm> |
21 #include <cstdio> |
|
22 #include <cstdlib> |
|
23 #include <cstring> |
|
24 #include <cmath> |
21 #include <cmath> |
|
22 #include <compare> |
|
23 #include <memory> |
25 #include <optional> |
24 #include <optional> |
26 #include <type_traits> |
25 #include <type_traits> |
27 #include <QMatrix4x4> |
26 #include <QDataStream> |
|
27 #include <QDebug> |
28 #include <QObject> |
28 #include <QObject> |
29 #include <QPointF> |
29 #include <QPointF> |
30 #include <QSet> |
30 #include <QSet> |
31 #include <QString> |
31 #include <QString> |
32 #include <QStringList> |
32 #include <QStringList> |
33 #include <QVariant> |
33 #include <QVariant> |
34 #include <QVector> |
34 #include <QVector> |
35 #include <QVector3D> |
|
36 #include <glm/glm.hpp> |
35 #include <glm/glm.hpp> |
37 |
36 #include "geometry.h" |
38 using GLRotationMatrix = QMatrix4x4; |
37 #include "functional.h" |
39 |
38 |
40 enum Axis |
39 template<typename T> |
41 { |
40 using opt = std::optional<T>; |
42 X = 0, |
41 |
43 Y = 1, |
42 Q_DECLARE_METATYPE(glm::vec3) |
44 Z = 2 |
43 Q_DECLARE_METATYPE(glm::mat4) |
45 }; |
44 |
46 |
45 //! \brief count the amount of elements in a basic array |
47 enum Result |
|
48 { |
|
49 Success = 0, |
|
50 PartialSuccess, |
|
51 Failure |
|
52 }; |
|
53 |
|
54 constexpr bool failed(Result r) |
|
55 { |
|
56 return r == Failure; |
|
57 } |
|
58 |
|
59 enum Winding |
|
60 { |
|
61 NoWinding, |
|
62 Anticlockwise, |
|
63 Clockwise, |
|
64 }; |
|
65 |
|
66 /* |
|
67 * Special operator definition that implements the XOR operator for windings. |
|
68 * However, if either winding is NoWinding, then this function returns NoWinding. |
|
69 */ |
|
70 inline Winding operator^(Winding one, Winding other) |
|
71 { |
|
72 if (one == NoWinding or other == NoWinding) |
|
73 return NoWinding; |
|
74 else |
|
75 return static_cast<Winding>(static_cast<int>(one) ^ static_cast<int>(other)); |
|
76 } |
|
77 |
|
78 inline Winding& operator^=(Winding& one, Winding other) |
|
79 { |
|
80 one = one ^ other; |
|
81 return one; |
|
82 } |
|
83 |
|
84 template<typename T, int N> |
46 template<typename T, int N> |
85 constexpr int countof(T const (&)[N]) |
47 constexpr int countof(T(&)[N]) |
86 { |
48 { |
87 return N; |
49 return N; |
88 } |
50 } |
89 |
51 |
90 /** |
52 //! \brief casts @c x to a suitable unsigned integer |
91 * @brief casts @c x to a suitable unsigned integer |
|
92 */ |
|
93 template<typename T> |
53 template<typename T> |
94 constexpr auto unsigned_cast(T x) |
54 constexpr auto unsigned_cast(T x) |
95 -> std::enable_if_t<std::is_integral_v<T>, std::make_unsigned_t<T>> |
55 -> std::enable_if_t<std::is_integral_v<T>, std::make_unsigned_t<T>> |
96 { |
56 { |
97 return static_cast<std::make_unsigned_t<T>>(x); |
57 return static_cast<std::make_unsigned_t<T>>(x); |
98 } |
58 } |
99 |
59 |
100 /** |
60 //! \brief casts @c x to a suitable signed integer |
101 * @brief casts @c x to a suitable signed integer |
|
102 */ |
|
103 template<typename T> |
61 template<typename T> |
104 constexpr auto signed_cast(T x) |
62 constexpr auto signed_cast(T x) |
105 -> std::enable_if_t<std::is_integral_v<T>, std::make_signed_t<T>> |
63 -> std::enable_if_t<std::is_integral_v<T>, std::make_signed_t<T>> |
106 { |
64 { |
107 return static_cast<std::make_signed_t<T>>(x); |
65 return static_cast<std::make_signed_t<T>>(x); |
108 } |
66 } |
109 |
67 |
110 |
68 //! \brief casts floating point values to float |
111 /** |
69 template<typename T> |
112 * @brief casts floating point values to float, converting non-floating point values causes an error |
70 constexpr auto float_cast(T x) |
113 * @param[in] x floating point value to cast |
71 -> std::enable_if_t<std::is_floating_point_v<T>, float> |
114 * @returns float |
|
115 */ |
|
116 template<typename T> |
|
117 auto toFloat(T x) -> std::enable_if_t<std::is_floating_point_v<T>, float> |
|
118 { |
72 { |
119 return static_cast<float>(x); |
73 return static_cast<float>(x); |
120 } |
74 } |
121 |
75 |
122 /** |
76 //! \brief casts floating point values to double |
123 * @brief casts floating point values to double, converting non-floating point values causes an error |
77 template<typename T> |
124 * @param[in] x floating point value to cast |
78 constexpr auto double_cast(T x) |
125 * @returns double |
79 -> std::enable_if_t<std::is_floating_point_v<T>, double> |
126 */ |
|
127 template<typename T> |
|
128 auto toDouble(T x) -> std::enable_if_t<std::is_floating_point_v<T>, double> |
|
129 { |
80 { |
130 return static_cast<double>(x); |
81 return static_cast<double>(x); |
131 } |
82 } |
132 |
83 |
133 /** |
84 //! \brief casts floating point values to qreal |
134 * @brief casts floating point values to qreal, converting non-floating point values causes an error |
85 template<typename T> |
135 * @param[in] x floating point value to cast |
86 constexpr auto qreal_cast(T x) |
136 * @returns qreal |
87 -> std::enable_if_t<std::is_floating_point_v<T>, qreal> |
137 */ |
|
138 template<typename T> |
|
139 auto toQreal(T x) -> std::enable_if_t<std::is_floating_point_v<T>, qreal> |
|
140 { |
88 { |
141 return static_cast<qreal>(x); |
89 return static_cast<qreal>(x); |
142 } |
90 } |
143 |
91 |
144 template<int N, typename T, glm::qualifier Q> |
92 template<int N, typename T, glm::qualifier Q> |
145 inline QPoint toQPoint(const glm::vec<N, T, Q>& vec) |
93 constexpr QPointF toQPointF(const glm::vec<N, T, Q>& vec) |
146 { |
94 { |
147 return {static_cast<int>(vec.x), static_cast<int>(vec.y)}; |
95 return {double_cast(vec.x), double_cast(vec.y)}; |
148 } |
96 } |
149 |
97 |
150 template<int N, typename T, glm::qualifier Q> |
98 constexpr glm::vec2 toVec2(const QPointF& point) |
151 inline QPointF toQPointF(const glm::vec<N, T, Q>& vec) |
|
152 { |
|
153 return {toDouble(vec.x), toDouble(vec.y)}; |
|
154 } |
|
155 |
|
156 inline glm::vec2 toVec2(const QPoint& point) |
|
157 { |
99 { |
158 return {point.x(), point.y()}; |
100 return {point.x(), point.y()}; |
159 } |
101 } |
160 |
102 |
161 inline glm::vec2 toVec2(const QPointF& point) |
103 |
162 { |
|
163 return {point.x(), point.y()}; |
|
164 } |
|
165 |
|
166 /* |
|
167 * coalesce(arg1, arg2, ..., argn) |
|
168 * Returns the first of the given arguments that evaluates to true. |
|
169 */ |
|
170 template<typename T> |
|
171 T coalesce(T&& arg) |
|
172 { |
|
173 // recursion base: 1 argument |
|
174 return arg; |
|
175 } |
|
176 |
|
177 template<typename T, typename... Rest> |
|
178 std::common_type_t<T, Rest...> coalesce(T&& arg, Rest&&... rest) |
|
179 { |
|
180 // general case: n arguments |
|
181 return arg ? arg : coalesce(rest...); |
|
182 } |
|
183 |
|
184 /** |
|
185 * @brief Finds an element in a map and possibly returns a reference to it if find |
|
186 * @param map |
|
187 * @param key |
|
188 * @returns the value or nullptr if not found |
|
189 */ |
|
190 template<typename T, typename R, typename K> |
104 template<typename T, typename R, typename K> |
191 const R* findInMap(const std::map<T, R>& map, K&& key) |
105 const R* findInMap(const std::map<T, R>& map, K&& key) |
192 { |
106 { |
193 auto pair = map.find(key); |
107 auto pair = map.find(key); |
194 if (pair != map.end()) |
108 if (pair != map.end()) |
219 { |
127 { |
220 return nullptr; |
128 return nullptr; |
221 } |
129 } |
222 } |
130 } |
223 |
131 |
|
132 template<typename T, typename R> |
|
133 void removeFromMap(std::map<T, R>& map, T&& key) |
|
134 { |
|
135 const auto it = map.find(key); |
|
136 if (it != map.end()) { |
|
137 map.erase(it); |
|
138 } |
|
139 } |
|
140 |
224 template<typename T = float> |
141 template<typename T = float> |
225 constexpr std::enable_if_t<std::is_floating_point_v<T>, T> pi = static_cast<T>(M_PIl); |
142 constexpr std::enable_if_t<std::is_floating_point_v<T>, T> pi = static_cast<T>(M_PIl); |
226 constexpr double infinity = std::numeric_limits<double>::infinity(); |
143 |
227 |
144 inline QSizeF sizeToSizeF(const QSize& size) |
228 |
145 { |
229 Q_DECLARE_METATYPE(glm::vec3) |
146 return {static_cast<qreal>(size.width()), static_cast<qreal>(size.height())}; |
230 Q_DECLARE_METATYPE(glm::mat4) |
147 } |
|
148 |
|
149 //! \brief Hints to the specified vector that a certain amount of new elements |
|
150 //! are going to be added. |
|
151 template<typename T> |
|
152 void reserveMore(std::vector<T>& vector, std::size_t amount) |
|
153 { |
|
154 vector.reserve(vector.size() + amount); |
|
155 } |
|
156 |
|
157 inline QString vectorToString(const glm::vec2& vec) |
|
158 { |
|
159 return QStringLiteral("(%1, %2)") |
|
160 .arg(double_cast(vec.x)) |
|
161 .arg(double_cast(vec.y)); |
|
162 } |
|
163 |
|
164 inline QString vectorToString(const glm::vec3& vec) |
|
165 { |
|
166 return QStringLiteral("(%1, %2, %3)") |
|
167 .arg(double_cast(vec.x)) |
|
168 .arg(double_cast(vec.y)) |
|
169 .arg(double_cast(vec.z)); |
|
170 } |
|
171 |
|
172 inline QString vectorToString(const glm::vec4& vec) |
|
173 { |
|
174 return QStringLiteral("%1, %2, %3, %4)") |
|
175 .arg(double_cast(vec.x)) |
|
176 .arg(double_cast(vec.y)) |
|
177 .arg(double_cast(vec.z)) |
|
178 .arg(double_cast(vec.w)); |
|
179 } |
|
180 |
|
181 template<typename T, typename IdentifierType> |
|
182 struct TypeValue |
|
183 { |
|
184 T value; |
|
185 auto operator<=>(TypeValue other) const = default; |
|
186 }; |
|
187 |
|
188 template<typename T, typename R> |
|
189 int qHash(TypeValue<T, R> value) |
|
190 { |
|
191 return qHash(value.value); |
|
192 } |
|
193 |
|
194 /** |
|
195 * Iterates a @c glm::mat |
|
196 */ |
|
197 template<int X, int Y, typename T, glm::qualifier Q, typename Fn> |
|
198 void iter_matrix(const glm::mat<X, Y, T, Q>& matrix, Fn&& fn) |
|
199 { |
|
200 for (int i = 0; i < X; ++i) |
|
201 { |
|
202 for (int j = 0; j < Y; ++j) |
|
203 { |
|
204 fn(i, j, matrix[i][j]); |
|
205 } |
|
206 } |
|
207 } |
|
208 |
|
209 inline QDataStream& operator<<(QDataStream& stream, const glm::vec3& vec) |
|
210 { |
|
211 return stream << vec.x << vec.y << vec.z; |
|
212 } |
|
213 |
|
214 inline QDataStream& operator>>(QDataStream& stream, glm::vec3& vec) |
|
215 { |
|
216 return stream >> vec.x >> vec.y >> vec.z; |
|
217 } |
|
218 |
|
219 template<int X, int Y, typename T, glm::qualifier Q> |
|
220 QDataStream& operator<<(QDataStream& stream, const glm::mat<X, Y, T, Q>& mat) |
|
221 { |
|
222 iter_matrix(mat, [&stream](int, int, float x) |
|
223 { |
|
224 stream << x; |
|
225 }); |
|
226 return stream; |
|
227 } |
|
228 |
|
229 template<int X, int Y, typename T, glm::qualifier Q> |
|
230 QDataStream& operator>>(QDataStream& stream, glm::mat<X, Y, T, Q>& mat) |
|
231 { |
|
232 iter_matrix(mat, [&stream](int, int, float x) |
|
233 { |
|
234 stream >> x; |
|
235 }); |
|
236 return stream; |
|
237 } |
|
238 |
|
239 //! \brief Converts a pointer value to an \c std::optional value. If p has a |
|
240 //! value, it will be copied into the result value. |
|
241 template<typename T> |
|
242 std::optional<T> pointerToOptional(const T* p) |
|
243 { |
|
244 std::optional<T> result; |
|
245 if (p != nullptr) { |
|
246 result = *p; |
|
247 } |
|
248 return result; |
|
249 } |
|
250 |
|
251 // some magic code from https://en.cppreference.com/w/cpp/utility/variant/visit |
|
252 // for use with std::visit |
|
253 template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; |
|
254 template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; |
|
255 |
|
256 // http://stackoverflow.com/a/18204188/3629665 |
|
257 template<typename T> |
|
258 constexpr T rotl10(T x) |
|
259 { |
|
260 return (x << 10) | ((x >> 22) & 0x000000ff); |
|
261 } |
|
262 |
|
263 template<typename T> |
|
264 constexpr T rotl20(T x) |
|
265 { |
|
266 return (x << 20) | ((x >> 12) & 0x000000ff); |
|
267 } |
|
268 |
|
269 inline QString quoted(QString string) |
|
270 { |
|
271 if (string.contains("'")) |
|
272 { |
|
273 string.replace("\"", "\\\""); |
|
274 string = "\"" + string + "\""; |
|
275 } |
|
276 else |
|
277 { |
|
278 string = "'" + string + "'"; |
|
279 } |
|
280 return string; |
|
281 } |
|
282 |
|
283 inline QString vertexToString(const glm::vec3& vertex) |
|
284 { |
|
285 return QStringLiteral("%1 %2 %3").arg(vertex.x).arg(vertex.y).arg(vertex.z); |
|
286 } |
|
287 |
|
288 inline QString vertexToStringParens(const glm::vec3& vertex) |
|
289 { |
|
290 return QStringLiteral("(%1 %2 %3)").arg(vertex.x).arg(vertex.y).arg(vertex.z); |
|
291 } |
|
292 |
|
293 inline QString transformToString(const glm::mat4& matrix) |
|
294 { |
|
295 return QStringLiteral("%1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12") |
|
296 .arg(matrix[3][0], matrix[3][1], matrix[3][2]) |
|
297 .arg(matrix[0][0], matrix[1][0], matrix[2][0]) |
|
298 .arg(matrix[0][1], matrix[1][1], matrix[2][1]) |
|
299 .arg(matrix[0][2], matrix[1][2], matrix[2][2]); |
|
300 } |
|
301 |
|
302 template<typename T, glm::qualifier Q> |
|
303 constexpr unsigned int qHash(const glm::vec<3, T, Q>& key) |
|
304 { |
|
305 return qHash(key.x) ^ rotl10(qHash(key.y)) ^ rotl20(qHash(key.z)); |
|
306 } |