1 | // [Blend2D] |
2 | // 2D Vector Graphics Powered by a JIT Compiler. |
3 | // |
4 | // [License] |
5 | // Zlib - See LICENSE.md file in the package. |
6 | |
7 | #ifndef BLEND2D_BLMATRIX_H |
8 | #define BLEND2D_BLMATRIX_H |
9 | |
10 | #include "./blgeometry.h" |
11 | |
12 | BL_DIAGNOSTIC_PUSH(BL_DIAGNOSTIC_NO_SHADOW) |
13 | |
14 | //! \addtogroup blend2d_api_geometry |
15 | //! \{ |
16 | |
17 | // ============================================================================ |
18 | // [Typedefs] |
19 | // ============================================================================ |
20 | |
21 | //! A generic function that can be used to transform an array of points that use |
22 | //! `double` precision coordinates. This function will be 99.99% of time used with |
23 | //! `BLMatrix2D` so the `ctx` would point to a `const BLMatrix2D*` instance. |
24 | typedef BLResult (BL_CDECL* BLMapPointDArrayFunc)(const void* ctx, BLPoint* dst, const BLPoint* src, size_t count) BL_NOEXCEPT; |
25 | |
26 | // ============================================================================ |
27 | // [Constants] |
28 | // ============================================================================ |
29 | |
30 | //! 2D matrix type that can be obtained by calling `BLMatrix2D::type()`. |
31 | //! |
32 | //! ``` |
33 | //! Identity Transl. Scale Swap Affine |
34 | //! [1 0] [1 0] [. 0] [0 .] [. .] |
35 | //! [0 1] [0 1] [0 .] [. 0] [. .] |
36 | //! [0 0] [. .] [. .] [. .] [. .] |
37 | //! ``` |
38 | BL_DEFINE_ENUM(BLMatrix2DType) { |
39 | //! Identity matrix. |
40 | BL_MATRIX2D_TYPE_IDENTITY = 0, |
41 | //! Has translation part (the rest is like identity). |
42 | BL_MATRIX2D_TYPE_TRANSLATE = 1, |
43 | //! Has translation and scaling parts. |
44 | BL_MATRIX2D_TYPE_SCALE = 2, |
45 | //! Has translation and scaling parts, however scaling swaps X/Y. |
46 | BL_MATRIX2D_TYPE_SWAP = 3, |
47 | //! Generic affine matrix. |
48 | BL_MATRIX2D_TYPE_AFFINE = 4, |
49 | //! Invalid/degenerate matrix not useful for transformations. |
50 | BL_MATRIX2D_TYPE_INVALID = 5, |
51 | |
52 | //! Count of matrix types. |
53 | BL_MATRIX2D_TYPE_COUNT = 6 |
54 | }; |
55 | |
56 | //! 2D matrix data index. |
57 | BL_DEFINE_ENUM(BLMatrix2DValue) { |
58 | //! Value at index 0 - M00. |
59 | BL_MATRIX2D_VALUE_00 = 0, |
60 | //! Value at index 1 - M01. |
61 | BL_MATRIX2D_VALUE_01 = 1, |
62 | //! Value at index 2 - M10. |
63 | BL_MATRIX2D_VALUE_10 = 2, |
64 | //! Value at index 3 - M11. |
65 | BL_MATRIX2D_VALUE_11 = 3, |
66 | //! Value at index 4 - M20. |
67 | BL_MATRIX2D_VALUE_20 = 4, |
68 | //! Value at index 5 - M21. |
69 | BL_MATRIX2D_VALUE_21 = 5, |
70 | |
71 | //! Count of `BLMatrix2D` values. |
72 | BL_MATRIX2D_VALUE_COUNT = 6 |
73 | }; |
74 | |
75 | //! 2D matrix operation. |
76 | BL_DEFINE_ENUM(BLMatrix2DOp) { |
77 | //! Reset matrix to identity (argument ignored, should be nullptr). |
78 | BL_MATRIX2D_OP_RESET = 0, |
79 | //! Assign (copy) the other matrix. |
80 | BL_MATRIX2D_OP_ASSIGN = 1, |
81 | |
82 | //! Translate the matrix by [x, y]. |
83 | BL_MATRIX2D_OP_TRANSLATE = 2, |
84 | //! Scale the matrix by [x, y]. |
85 | BL_MATRIX2D_OP_SCALE = 3, |
86 | //! Skew the matrix by [x, y]. |
87 | BL_MATRIX2D_OP_SKEW = 4, |
88 | //! Rotate the matrix by the given angle about [0, 0]. |
89 | BL_MATRIX2D_OP_ROTATE = 5, |
90 | //! Rotate the matrix by the given angle about [x, y]. |
91 | BL_MATRIX2D_OP_ROTATE_PT = 6, |
92 | //! Transform this matrix by other `BLMatrix2D`. |
93 | BL_MATRIX2D_OP_TRANSFORM = 7, |
94 | |
95 | //! Post-translate the matrix by [x, y]. |
96 | BL_MATRIX2D_OP_POST_TRANSLATE = 8, |
97 | //! Post-scale the matrix by [x, y]. |
98 | BL_MATRIX2D_OP_POST_SCALE = 9, |
99 | //! Post-skew the matrix by [x, y]. |
100 | BL_MATRIX2D_OP_POST_SKEW = 10, |
101 | //! Post-rotate the matrix about [0, 0]. |
102 | BL_MATRIX2D_OP_POST_ROTATE = 11, |
103 | //! Post-rotate the matrix about a reference BLPoint. |
104 | BL_MATRIX2D_OP_POST_ROTATE_PT = 12, |
105 | //! Post-transform this matrix by other `BLMatrix2D`. |
106 | BL_MATRIX2D_OP_POST_TRANSFORM = 13, |
107 | |
108 | //! Count of matrix operations. |
109 | BL_MATRIX2D_OP_COUNT = 14 |
110 | }; |
111 | |
112 | // ============================================================================ |
113 | // [BLMatrix2D] |
114 | // ============================================================================ |
115 | |
116 | //! 2D matrix represents an affine transformation matrix that can be used to |
117 | //! transform geometry and images. |
118 | struct BLMatrix2D { |
119 | union { |
120 | //! Matrix values, use `BL_MATRIX2D_VALUE` indexes to get a particular one. |
121 | double m[BL_MATRIX2D_VALUE_COUNT]; |
122 | //! Matrix values that map `m` to named values that can be used directly. |
123 | struct { |
124 | double m00; |
125 | double m01; |
126 | double m10; |
127 | double m11; |
128 | double m20; |
129 | double m21; |
130 | }; |
131 | }; |
132 | |
133 | // -------------------------------------------------------------------------- |
134 | #ifdef __cplusplus |
135 | |
136 | //! \name Construction & Destruction |
137 | //! \{ |
138 | |
139 | //! Creates an uninitialized 2D matrix, you must initialize all members before use. |
140 | BL_INLINE BLMatrix2D() noexcept = default; |
141 | |
142 | //! Creates a new matrix initialized to a copy of `src` matrix. |
143 | constexpr BLMatrix2D(const BLMatrix2D& src) noexcept = default; |
144 | |
145 | //! Creates a new matrix initialized to: |
146 | //! |
147 | //! ``` |
148 | //! [m00 m01] |
149 | //! [m10 m11] |
150 | //! [m20 m21] |
151 | //! ``` |
152 | constexpr BLMatrix2D(double m00, double m01, double m10, double m11, double m20, double m21) noexcept |
153 | : m00(m00), m01(m01), |
154 | m10(m10), m11(m11), |
155 | m20(m20), m21(m21) {} |
156 | |
157 | //! \} |
158 | |
159 | //! \name Static Constructors |
160 | //! \{ |
161 | |
162 | //! Creates a new matrix initialized to identity. |
163 | static constexpr BLMatrix2D makeIdentity() noexcept { return BLMatrix2D(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); } |
164 | |
165 | //! \overload |
166 | static constexpr BLMatrix2D makeTranslation(double x, double y) noexcept { return BLMatrix2D(1.0, 0.0, 0.0, 1.0, x, y); } |
167 | //! Creates a new matrix initialized to translation. |
168 | static constexpr BLMatrix2D makeTranslation(const BLPointI& p) noexcept { return BLMatrix2D(1.0, 0.0, 0.0, 1.0, double(p.x), double(p.y)); } |
169 | //! \overload |
170 | static constexpr BLMatrix2D makeTranslation(const BLPoint& p) noexcept { return BLMatrix2D(1.0, 0.0, 0.0, 1.0, p.x, p.y); } |
171 | |
172 | //! \overload |
173 | static constexpr BLMatrix2D makeScaling(double xy) noexcept { return BLMatrix2D(xy, 0.0, 0.0, xy, 0.0, 0.0); } |
174 | //! \overload |
175 | static constexpr BLMatrix2D makeScaling(double x, double y) noexcept { return BLMatrix2D(x, 0.0, 0.0, y, 0.0, 0.0); } |
176 | //! \overload |
177 | static constexpr BLMatrix2D makeScaling(const BLPointI& p) noexcept { return BLMatrix2D(double(p.x), 0.0, 0.0, double(p.y), 0.0, 0.0); } |
178 | //! Creates a new matrix initialized to scaling. |
179 | static constexpr BLMatrix2D makeScaling(const BLPoint& p) noexcept { return BLMatrix2D(p.x, 0.0, 0.0, p.y, 0.0, 0.0); } |
180 | |
181 | //! Creates a new matrix initialized to rotation. |
182 | static BL_INLINE BLMatrix2D makeRotation(double angle) noexcept { |
183 | BLMatrix2D result; |
184 | result.resetToRotation(angle, 0.0, 0.0); |
185 | return result; |
186 | } |
187 | |
188 | //! \overload |
189 | static BL_INLINE BLMatrix2D makeRotation(double angle, double x, double y) noexcept { |
190 | BLMatrix2D result; |
191 | result.resetToRotation(angle, x, y); |
192 | return result; |
193 | } |
194 | |
195 | //! \overload |
196 | static BL_INLINE BLMatrix2D makeRotation(double angle, const BLPoint& p) noexcept { |
197 | BLMatrix2D result; |
198 | result.resetToRotation(angle, p.x, p.y); |
199 | return result; |
200 | } |
201 | |
202 | //! Create a new skewing matrix. |
203 | static BL_INLINE BLMatrix2D makeSkewing(double x, double y) noexcept { |
204 | BLMatrix2D result; |
205 | result.resetToSkewing(x, y); |
206 | return result; |
207 | } |
208 | //! \overload |
209 | static BL_INLINE BLMatrix2D makeSkewing(const BLPoint& p) noexcept { |
210 | BLMatrix2D result; |
211 | result.resetToSkewing(p.x, p.y); |
212 | return result; |
213 | } |
214 | |
215 | static BL_INLINE BLMatrix2D makeSinCos(double sin, double cos, double tx = 0.0, double ty = 0.0) noexcept { |
216 | return BLMatrix2D(cos, sin, -sin, cos, tx, ty); |
217 | } |
218 | |
219 | static BL_INLINE BLMatrix2D makeSinCos(double sin, double cos, const BLPoint& t) noexcept { |
220 | return makeSinCos(sin, cos, t.x, t.y); |
221 | } |
222 | |
223 | //! \} |
224 | |
225 | //! \name Reset Matrix |
226 | //! \{ |
227 | |
228 | //! Resets matrix to identity. |
229 | BL_INLINE void reset() noexcept { |
230 | reset(1.0, 0.0, |
231 | 0.0, 1.0, |
232 | 0.0, 0.0); |
233 | } |
234 | |
235 | //! Resets matrix to `other` (copy its content to this matrix). |
236 | BL_INLINE void reset(const BLMatrix2D& other) noexcept { |
237 | reset(other.m00, other.m01, |
238 | other.m10, other.m11, |
239 | other.m20, other.m21); |
240 | } |
241 | |
242 | //! Resets matrix to [`m00`, `m01`, `m10`, `m11`, `m20`, `m21`]. |
243 | BL_INLINE void reset(double m00, double m01, double m10, double m11, double m20, double m21) noexcept { |
244 | this->m00 = m00; |
245 | this->m01 = m01; |
246 | this->m10 = m10; |
247 | this->m11 = m11; |
248 | this->m20 = m20; |
249 | this->m21 = m21; |
250 | } |
251 | |
252 | //! Resets matrix to translation. |
253 | BL_INLINE void resetToTranslation(double x, double y) noexcept { reset(1.0, 0.0, 0.0, 1.0, x, y); } |
254 | //! Resets matrix to translation. |
255 | BL_INLINE void resetToTranslation(const BLPointI& p) noexcept { resetToTranslation(BLPoint(p)); } |
256 | //! Resets matrix to translation. |
257 | BL_INLINE void resetToTranslation(const BLPoint& p) noexcept { resetToTranslation(p.x, p.y); } |
258 | |
259 | //! Resets matrix to scaling. |
260 | BL_INLINE void resetToScaling(double xy) noexcept { resetToScaling(xy, xy); } |
261 | //! Resets matrix to scaling. |
262 | BL_INLINE void resetToScaling(double x, double y) noexcept { reset(x, 0.0, 0.0, y, 0.0, 0.0); } |
263 | //! Resets matrix to scaling. |
264 | BL_INLINE void resetToScaling(const BLPointI& p) noexcept { resetToScaling(BLPoint(p)); } |
265 | //! Resets matrix to scaling. |
266 | BL_INLINE void resetToScaling(const BLPoint& p) noexcept { resetToScaling(p.x, p.y); } |
267 | |
268 | //! Resets matrix to skewing. |
269 | BL_INLINE void resetToSkewing(double x, double y) noexcept { blMatrix2DSetSkewing(this, x, y); } |
270 | //! Resets matrix to skewing. |
271 | BL_INLINE void resetToSkewing(const BLPoint& p) noexcept { blMatrix2DSetSkewing(this, p.x, p.y); } |
272 | |
273 | //! Resets matrix to rotation specified by `sin` and `cos` and optional translation `tx` and `ty`. |
274 | BL_INLINE void resetToSinCos(double sin, double cos, double tx = 0.0, double ty = 0.0) noexcept { reset(cos, sin, -sin, cos, tx, ty); } |
275 | //! Resets matrix to rotation specified by `sin` and `cos` and optional translation `t`. |
276 | BL_INLINE void resetToSinCos(double sin, double cos, const BLPoint& t) noexcept { resetToSinCos(sin, cos, t.x, t.y); } |
277 | |
278 | //! Resets matrix to rotation. |
279 | BL_INLINE void resetToRotation(double angle) noexcept { blMatrix2DSetRotation(this, angle, 0.0, 0.0); } |
280 | //! Resets matrix to rotation around a point `[x, y]`. |
281 | BL_INLINE void resetToRotation(double angle, double x, double y) noexcept { blMatrix2DSetRotation(this, angle, x, y); } |
282 | //! Resets matrix to rotation around a point `p`. |
283 | BL_INLINE void resetToRotation(double angle, const BLPoint& p) noexcept { blMatrix2DSetRotation(this, angle, p.x, p.y); } |
284 | |
285 | //! \} |
286 | |
287 | //! \name Overloaded Operators |
288 | //! \{ |
289 | |
290 | BL_INLINE bool operator==(const BLMatrix2D& other) const noexcept { return equals(other); } |
291 | BL_INLINE bool operator!=(const BLMatrix2D& other) const noexcept { return !equals(other); } |
292 | |
293 | //! \} |
294 | |
295 | //! \name Common Functionality |
296 | //! \{ |
297 | |
298 | BL_INLINE bool equals(const BLMatrix2D& other) const noexcept { |
299 | return blEquals(this->m00, other.m00) & |
300 | blEquals(this->m01, other.m01) & |
301 | blEquals(this->m10, other.m10) & |
302 | blEquals(this->m11, other.m11) & |
303 | blEquals(this->m20, other.m20) & |
304 | blEquals(this->m21, other.m21) ; |
305 | } |
306 | |
307 | //! \} |
308 | |
309 | //! \name Matrix Properties |
310 | //! \{ |
311 | |
312 | //! Returns the matrix type, see `BLMatrix2DType`. |
313 | BL_INLINE uint32_t type() const noexcept { return blMatrix2DGetType(this); } |
314 | |
315 | //! Calculates the matrix determinant. |
316 | BL_INLINE double determinant() noexcept { return this->m00 * this->m11 - this->m01 * this->m10; } |
317 | |
318 | //! \} |
319 | |
320 | //! \name Matrix Operations |
321 | //! \{ |
322 | |
323 | BL_INLINE BLResult translate(double x, double y) noexcept { |
324 | this->m20 += x * this->m00 + y * this->m10; |
325 | this->m21 += x * this->m01 + y * this->m11; |
326 | |
327 | return BL_SUCCESS; |
328 | } |
329 | |
330 | BL_INLINE BLResult translate(const BLPointI& p) noexcept { return translate(BLPoint(p)); } |
331 | BL_INLINE BLResult translate(const BLPoint& p) noexcept { return translate(p.x, p.y); } |
332 | |
333 | BL_INLINE BLResult scale(double xy) noexcept { return scale(xy, xy); } |
334 | BL_INLINE BLResult scale(double x, double y) noexcept { |
335 | this->m00 *= x; |
336 | this->m01 *= x; |
337 | this->m10 *= y; |
338 | this->m11 *= y; |
339 | |
340 | return BL_SUCCESS; |
341 | } |
342 | |
343 | BL_INLINE BLResult scale(const BLPointI& p) noexcept { return scale(BLPoint(p)); } |
344 | BL_INLINE BLResult scale(const BLPoint& p) noexcept { return scale(p.x, p.y); } |
345 | |
346 | BL_INLINE BLResult skew(double x, double y) noexcept { return skew(BLPoint(x, y)); } |
347 | BL_INLINE BLResult skew(const BLPoint& p) noexcept { return blMatrix2DApplyOp(this, BL_MATRIX2D_OP_SKEW, &p); } |
348 | |
349 | BL_INLINE BLResult rotate(double angle) noexcept { return blMatrix2DApplyOp(this, BL_MATRIX2D_OP_ROTATE, &angle); } |
350 | BL_INLINE BLResult rotate(double angle, double x, double y) noexcept { |
351 | double opData[3] = { angle, x, y }; |
352 | return blMatrix2DApplyOp(this, BL_MATRIX2D_OP_ROTATE_PT, opData); |
353 | } |
354 | |
355 | BL_INLINE BLResult rotate(double angle, const BLPointI& p) noexcept { return rotate(angle, double(p.x), double(p.y)); } |
356 | BL_INLINE BLResult rotate(double angle, const BLPoint& p) noexcept { return rotate(angle, p.x, p.y); } |
357 | |
358 | BL_INLINE BLResult transform(const BLMatrix2D& m) noexcept { |
359 | return blMatrix2DApplyOp(this, BL_MATRIX2D_OP_TRANSFORM, &m); |
360 | } |
361 | |
362 | BL_INLINE BLResult postTranslate(double x, double y) noexcept { |
363 | this->m20 += x; |
364 | this->m21 += y; |
365 | |
366 | return BL_SUCCESS; |
367 | } |
368 | |
369 | BL_INLINE BLResult postTranslate(const BLPointI& p) noexcept { return postTranslate(BLPoint(p)); } |
370 | BL_INLINE BLResult postTranslate(const BLPoint& p) noexcept { return postTranslate(p.x, p.y); } |
371 | |
372 | BL_INLINE BLResult postScale(double xy) noexcept { return postScale(xy, xy); } |
373 | BL_INLINE BLResult postScale(double x, double y) noexcept { |
374 | this->m00 *= x; |
375 | this->m01 *= y; |
376 | this->m10 *= x; |
377 | this->m11 *= y; |
378 | this->m20 *= x; |
379 | this->m21 *= y; |
380 | |
381 | return BL_SUCCESS; |
382 | } |
383 | BL_INLINE BLResult postScale(const BLPointI& p) noexcept { return postScale(BLPoint(p)); } |
384 | BL_INLINE BLResult postScale(const BLPoint& p) noexcept { return postScale(p.x, p.y); } |
385 | |
386 | BL_INLINE BLResult postSkew(double x, double y) noexcept { return postSkew(BLPoint(x, y)); } |
387 | BL_INLINE BLResult postSkew(const BLPoint& p) noexcept { return blMatrix2DApplyOp(this, BL_MATRIX2D_OP_POST_SKEW, &p); } |
388 | |
389 | BL_INLINE BLResult postRotate(double angle) noexcept { return blMatrix2DApplyOp(this, BL_MATRIX2D_OP_POST_ROTATE, &angle); } |
390 | BL_INLINE BLResult postRotate(double angle, double x, double y) noexcept { |
391 | double params[3] = { angle, x, y }; |
392 | return blMatrix2DApplyOp(this, BL_MATRIX2D_OP_POST_ROTATE_PT, params); |
393 | } |
394 | |
395 | BL_INLINE BLResult postRotate(double angle, const BLPointI& p) noexcept { return postRotate(angle, BLPoint(p)); } |
396 | BL_INLINE BLResult postRotate(double angle, const BLPoint& p) noexcept { return postRotate(angle, p.x, p.y); } |
397 | |
398 | BL_INLINE BLResult postTransform(const BLMatrix2D& m) noexcept { return blMatrix2DApplyOp(this, BL_MATRIX2D_OP_POST_TRANSFORM, &m); } |
399 | |
400 | //! Inverts the matrix, returns `BL_SUCCESS` if the matrix has been inverted successfully. |
401 | BL_INLINE BLResult invert() noexcept { return blMatrix2DInvert(this, this); } |
402 | |
403 | //! \} |
404 | |
405 | //! \name Map Points and Primitives |
406 | //! \{ |
407 | |
408 | BL_INLINE BLPoint mapPoint(double x, double y) const noexcept { return BLPoint(x * m00 + y * m10 + m20, x * m01 + y * m11 + m21); } |
409 | BL_INLINE BLPoint mapPoint(const BLPoint& p) const noexcept { return mapPoint(p.x, p.y); } |
410 | |
411 | BL_INLINE BLPoint mapVector(double x, double y) const noexcept { return BLPoint(x * m00 + y * m10, x * m01 + y * m11); } |
412 | BL_INLINE BLPoint mapVector(const BLPoint& v) const noexcept { return mapVector(v.x, v.y); } |
413 | |
414 | //! \} |
415 | |
416 | //! \name Static Operations |
417 | //! \{ |
418 | |
419 | //! Inverts `src` matrix and stores the result in `dst. |
420 | //! |
421 | //! \overload |
422 | static BL_INLINE BLResult invert(BLMatrix2D& dst, const BLMatrix2D& src) noexcept { return blMatrix2DInvert(&dst, &src); } |
423 | |
424 | //! \} |
425 | |
426 | #endif |
427 | // -------------------------------------------------------------------------- |
428 | }; |
429 | |
430 | #ifdef __cplusplus |
431 | extern "C" { |
432 | #endif |
433 | |
434 | //! Array of functions for transforming points indexed by `BLMatrixType`. Each |
435 | //! function is optimized for the respective type. This is mostly used internally, |
436 | //! but exported for users that can take advantage of Blend2D SIMD optimziations. |
437 | extern BL_API BLMapPointDArrayFunc blMatrix2DMapPointDArrayFuncs[BL_MATRIX2D_TYPE_COUNT]; |
438 | |
439 | #ifdef __cplusplus |
440 | } // {Extern:C} |
441 | #endif |
442 | |
443 | //! \} |
444 | |
445 | BL_DIAGNOSTIC_POP |
446 | |
447 | #endif // BLEND2D_BLMATRIX_H |
448 | |