1 /*
2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
3
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23 #include "vmatrix.h"
24 #include <vglobal.h>
25 #include <cassert>
26 #include <cmath>
27
28 V_BEGIN_NAMESPACE
29
30 /* m11 m21 mtx
31 * m12 m22 mty
32 * m13 m23 m33
33 */
34
determinant() const35 inline float VMatrix::determinant() const
36 {
37 return m11 * (m33 * m22 - mty * m23) - m21 * (m33 * m12 - mty * m13) +
38 mtx * (m23 * m12 - m22 * m13);
39 }
40
isAffine() const41 bool VMatrix::isAffine() const
42 {
43 return type() < MatrixType::Project;
44 }
45
isIdentity() const46 bool VMatrix::isIdentity() const
47 {
48 return type() == MatrixType::None;
49 }
50
isInvertible() const51 bool VMatrix::isInvertible() const
52 {
53 return !vIsZero(determinant());
54 }
55
isScaling() const56 bool VMatrix::isScaling() const
57 {
58 return type() >= MatrixType::Scale;
59 }
isRotating() const60 bool VMatrix::isRotating() const
61 {
62 return type() >= MatrixType::Rotate;
63 }
64
isTranslating() const65 bool VMatrix::isTranslating() const
66 {
67 return type() >= MatrixType::Translate;
68 }
69
operator *=(float num)70 VMatrix &VMatrix::operator*=(float num)
71 {
72 if (num == 1.) return *this;
73
74 m11 *= num;
75 m12 *= num;
76 m13 *= num;
77 m21 *= num;
78 m22 *= num;
79 m23 *= num;
80 mtx *= num;
81 mty *= num;
82 m33 *= num;
83 if (dirty < MatrixType::Scale) dirty = MatrixType::Scale;
84
85 return *this;
86 }
87
operator /=(float div)88 VMatrix &VMatrix::operator/=(float div)
89 {
90 if (div == 0) return *this;
91
92 div = 1 / div;
93 return operator*=(div);
94 }
95
type() const96 VMatrix::MatrixType VMatrix::type() const
97 {
98 if (dirty == MatrixType::None || dirty < mType) return mType;
99
100 switch (dirty) {
101 case MatrixType::Project:
102 if (!vIsZero(m13) || !vIsZero(m23) || !vIsZero(m33 - 1)) {
103 mType = MatrixType::Project;
104 break;
105 }
106 VECTOR_FALLTHROUGH
107 case MatrixType::Shear:
108 case MatrixType::Rotate:
109 if (!vIsZero(m12) || !vIsZero(m21)) {
110 const float dot = m11 * m12 + m21 * m22;
111 if (vIsZero(dot))
112 mType = MatrixType::Rotate;
113 else
114 mType = MatrixType::Shear;
115 break;
116 }
117 VECTOR_FALLTHROUGH
118 case MatrixType::Scale:
119 if (!vIsZero(m11 - 1) || !vIsZero(m22 - 1)) {
120 mType = MatrixType::Scale;
121 break;
122 }
123 VECTOR_FALLTHROUGH
124 case MatrixType::Translate:
125 if (!vIsZero(mtx) || !vIsZero(mty)) {
126 mType = MatrixType::Translate;
127 break;
128 }
129 VECTOR_FALLTHROUGH
130 case MatrixType::None:
131 mType = MatrixType::None;
132 break;
133 }
134
135 dirty = MatrixType::None;
136 return mType;
137 }
138
translate(float dx,float dy)139 VMatrix &VMatrix::translate(float dx, float dy)
140 {
141 if (dx == 0 && dy == 0) return *this;
142
143 switch (type()) {
144 case MatrixType::None:
145 mtx = dx;
146 mty = dy;
147 break;
148 case MatrixType::Translate:
149 mtx += dx;
150 mty += dy;
151 break;
152 case MatrixType::Scale:
153 mtx += dx * m11;
154 mty += dy * m22;
155 break;
156 case MatrixType::Project:
157 m33 += dx * m13 + dy * m23;
158 VECTOR_FALLTHROUGH
159 case MatrixType::Shear:
160 case MatrixType::Rotate:
161 mtx += dx * m11 + dy * m21;
162 mty += dy * m22 + dx * m12;
163 break;
164 }
165 if (dirty < MatrixType::Translate) dirty = MatrixType::Translate;
166 return *this;
167 }
168
scale(float sx,float sy)169 VMatrix &VMatrix::scale(float sx, float sy)
170 {
171 if (sx == 1 && sy == 1) return *this;
172
173 switch (type()) {
174 case MatrixType::None:
175 case MatrixType::Translate:
176 m11 = sx;
177 m22 = sy;
178 break;
179 case MatrixType::Project:
180 m13 *= sx;
181 m23 *= sy;
182 VECTOR_FALLTHROUGH
183 case MatrixType::Rotate:
184 case MatrixType::Shear:
185 m12 *= sx;
186 m21 *= sy;
187 VECTOR_FALLTHROUGH
188 case MatrixType::Scale:
189 m11 *= sx;
190 m22 *= sy;
191 break;
192 }
193 if (dirty < MatrixType::Scale) dirty = MatrixType::Scale;
194 return *this;
195 }
196
shear(float sh,float sv)197 VMatrix &VMatrix::shear(float sh, float sv)
198 {
199 if (sh == 0 && sv == 0) return *this;
200
201 switch (type()) {
202 case MatrixType::None:
203 case MatrixType::Translate:
204 m12 = sv;
205 m21 = sh;
206 break;
207 case MatrixType::Scale:
208 m12 = sv * m22;
209 m21 = sh * m11;
210 break;
211 case MatrixType::Project: {
212 float tm13 = sv * m23;
213 float tm23 = sh * m13;
214 m13 += tm13;
215 m23 += tm23;
216 VECTOR_FALLTHROUGH
217 }
218 case MatrixType::Rotate:
219 case MatrixType::Shear: {
220 float tm11 = sv * m21;
221 float tm22 = sh * m12;
222 float tm12 = sv * m22;
223 float tm21 = sh * m11;
224 m11 += tm11;
225 m12 += tm12;
226 m21 += tm21;
227 m22 += tm22;
228 break;
229 }
230 }
231 if (dirty < MatrixType::Shear) dirty = MatrixType::Shear;
232 return *this;
233 }
234
235 static const float deg2rad = float(0.017453292519943295769); // pi/180
236 static const float inv_dist_to_plane = 1. / 1024.;
237
rotate(float a,Axis axis)238 VMatrix &VMatrix::rotate(float a, Axis axis)
239 {
240 if (a == 0) return *this;
241
242 float sina = 0;
243 float cosa = 0;
244 if (a == 90. || a == -270.)
245 sina = 1.;
246 else if (a == 270. || a == -90.)
247 sina = -1.;
248 else if (a == 180.)
249 cosa = -1.;
250 else {
251 float b = deg2rad * a; // convert to radians
252 sina = std::sin(b); // fast and convenient
253 cosa = std::cos(b);
254 }
255
256 if (axis == Axis::Z) {
257 switch (type()) {
258 case MatrixType::None:
259 case MatrixType::Translate:
260 m11 = cosa;
261 m12 = sina;
262 m21 = -sina;
263 m22 = cosa;
264 break;
265 case MatrixType::Scale: {
266 float tm11 = cosa * m11;
267 float tm12 = sina * m22;
268 float tm21 = -sina * m11;
269 float tm22 = cosa * m22;
270 m11 = tm11;
271 m12 = tm12;
272 m21 = tm21;
273 m22 = tm22;
274 break;
275 }
276 case MatrixType::Project: {
277 float tm13 = cosa * m13 + sina * m23;
278 float tm23 = -sina * m13 + cosa * m23;
279 m13 = tm13;
280 m23 = tm23;
281 VECTOR_FALLTHROUGH
282 }
283 case MatrixType::Rotate:
284 case MatrixType::Shear: {
285 float tm11 = cosa * m11 + sina * m21;
286 float tm12 = cosa * m12 + sina * m22;
287 float tm21 = -sina * m11 + cosa * m21;
288 float tm22 = -sina * m12 + cosa * m22;
289 m11 = tm11;
290 m12 = tm12;
291 m21 = tm21;
292 m22 = tm22;
293 break;
294 }
295 }
296 if (dirty < MatrixType::Rotate) dirty = MatrixType::Rotate;
297 } else {
298 VMatrix result;
299 if (axis == Axis::Y) {
300 result.m11 = cosa;
301 result.m13 = -sina * inv_dist_to_plane;
302 } else {
303 result.m22 = cosa;
304 result.m23 = -sina * inv_dist_to_plane;
305 }
306 result.mType = MatrixType::Project;
307 *this = result * *this;
308 }
309
310 return *this;
311 }
312
operator *(const VMatrix & m) const313 VMatrix VMatrix::operator*(const VMatrix &m) const
314 {
315 const MatrixType otherType = m.type();
316 if (otherType == MatrixType::None) return *this;
317
318 const MatrixType thisType = type();
319 if (thisType == MatrixType::None) return m;
320
321 VMatrix t;
322 MatrixType type = vMax(thisType, otherType);
323 switch (type) {
324 case MatrixType::None:
325 break;
326 case MatrixType::Translate:
327 t.mtx = mtx + m.mtx;
328 t.mty += mty + m.mty;
329 break;
330 case MatrixType::Scale: {
331 float m11v = m11 * m.m11;
332 float m22v = m22 * m.m22;
333
334 float m31v = mtx * m.m11 + m.mtx;
335 float m32v = mty * m.m22 + m.mty;
336
337 t.m11 = m11v;
338 t.m22 = m22v;
339 t.mtx = m31v;
340 t.mty = m32v;
341 break;
342 }
343 case MatrixType::Rotate:
344 case MatrixType::Shear: {
345 float m11v = m11 * m.m11 + m12 * m.m21;
346 float m12v = m11 * m.m12 + m12 * m.m22;
347
348 float m21v = m21 * m.m11 + m22 * m.m21;
349 float m22v = m21 * m.m12 + m22 * m.m22;
350
351 float m31v = mtx * m.m11 + mty * m.m21 + m.mtx;
352 float m32v = mtx * m.m12 + mty * m.m22 + m.mty;
353
354 t.m11 = m11v;
355 t.m12 = m12v;
356 t.m21 = m21v;
357 t.m22 = m22v;
358 t.mtx = m31v;
359 t.mty = m32v;
360 break;
361 }
362 case MatrixType::Project: {
363 float m11v = m11 * m.m11 + m12 * m.m21 + m13 * m.mtx;
364 float m12v = m11 * m.m12 + m12 * m.m22 + m13 * m.mty;
365 float m13v = m11 * m.m13 + m12 * m.m23 + m13 * m.m33;
366
367 float m21v = m21 * m.m11 + m22 * m.m21 + m23 * m.mtx;
368 float m22v = m21 * m.m12 + m22 * m.m22 + m23 * m.mty;
369 float m23v = m21 * m.m13 + m22 * m.m23 + m23 * m.m33;
370
371 float m31v = mtx * m.m11 + mty * m.m21 + m33 * m.mtx;
372 float m32v = mtx * m.m12 + mty * m.m22 + m33 * m.mty;
373 float m33v = mtx * m.m13 + mty * m.m23 + m33 * m.m33;
374
375 t.m11 = m11v;
376 t.m12 = m12v;
377 t.m13 = m13v;
378 t.m21 = m21v;
379 t.m22 = m22v;
380 t.m23 = m23v;
381 t.mtx = m31v;
382 t.mty = m32v;
383 t.m33 = m33v;
384 }
385 }
386
387 t.dirty = type;
388 t.mType = type;
389
390 return t;
391 }
392
operator *=(const VMatrix & o)393 VMatrix &VMatrix::operator*=(const VMatrix &o)
394 {
395 const MatrixType otherType = o.type();
396 if (otherType == MatrixType::None) return *this;
397
398 const MatrixType thisType = type();
399 if (thisType == MatrixType::None) return operator=(o);
400
401 MatrixType t = vMax(thisType, otherType);
402 switch (t) {
403 case MatrixType::None:
404 break;
405 case MatrixType::Translate:
406 mtx += o.mtx;
407 mty += o.mty;
408 break;
409 case MatrixType::Scale: {
410 float m11v = m11 * o.m11;
411 float m22v = m22 * o.m22;
412
413 float m31v = mtx * o.m11 + o.mtx;
414 float m32v = mty * o.m22 + o.mty;
415
416 m11 = m11v;
417 m22 = m22v;
418 mtx = m31v;
419 mty = m32v;
420 break;
421 }
422 case MatrixType::Rotate:
423 case MatrixType::Shear: {
424 float m11v = m11 * o.m11 + m12 * o.m21;
425 float m12v = m11 * o.m12 + m12 * o.m22;
426
427 float m21v = m21 * o.m11 + m22 * o.m21;
428 float m22v = m21 * o.m12 + m22 * o.m22;
429
430 float m31v = mtx * o.m11 + mty * o.m21 + o.mtx;
431 float m32v = mtx * o.m12 + mty * o.m22 + o.mty;
432
433 m11 = m11v;
434 m12 = m12v;
435 m21 = m21v;
436 m22 = m22v;
437 mtx = m31v;
438 mty = m32v;
439 break;
440 }
441 case MatrixType::Project: {
442 float m11v = m11 * o.m11 + m12 * o.m21 + m13 * o.mtx;
443 float m12v = m11 * o.m12 + m12 * o.m22 + m13 * o.mty;
444 float m13v = m11 * o.m13 + m12 * o.m23 + m13 * o.m33;
445
446 float m21v = m21 * o.m11 + m22 * o.m21 + m23 * o.mtx;
447 float m22v = m21 * o.m12 + m22 * o.m22 + m23 * o.mty;
448 float m23v = m21 * o.m13 + m22 * o.m23 + m23 * o.m33;
449
450 float m31v = mtx * o.m11 + mty * o.m21 + m33 * o.mtx;
451 float m32v = mtx * o.m12 + mty * o.m22 + m33 * o.mty;
452 float m33v = mtx * o.m13 + mty * o.m23 + m33 * o.m33;
453
454 m11 = m11v;
455 m12 = m12v;
456 m13 = m13v;
457 m21 = m21v;
458 m22 = m22v;
459 m23 = m23v;
460 mtx = m31v;
461 mty = m32v;
462 m33 = m33v;
463 }
464 }
465
466 dirty = t;
467 mType = t;
468
469 return *this;
470 }
471
adjoint() const472 VMatrix VMatrix::adjoint() const
473 {
474 float h11, h12, h13, h21, h22, h23, h31, h32, h33;
475 h11 = m22 * m33 - m23 * mty;
476 h21 = m23 * mtx - m21 * m33;
477 h31 = m21 * mty - m22 * mtx;
478 h12 = m13 * mty - m12 * m33;
479 h22 = m11 * m33 - m13 * mtx;
480 h32 = m12 * mtx - m11 * mty;
481 h13 = m12 * m23 - m13 * m22;
482 h23 = m13 * m21 - m11 * m23;
483 h33 = m11 * m22 - m12 * m21;
484
485 VMatrix res;
486 res.m11 = h11;
487 res.m12 = h12;
488 res.m13 = h13;
489 res.m21 = h21;
490 res.m22 = h22;
491 res.m23 = h23;
492 res.mtx = h31;
493 res.mty = h32;
494 res.m33 = h33;
495 res.mType = MatrixType::None;
496 res.dirty = MatrixType::Project;
497
498 return res;
499 }
500
inverted(bool * invertible) const501 VMatrix VMatrix::inverted(bool *invertible) const
502 {
503 VMatrix invert;
504 bool inv = true;
505
506 switch (type()) {
507 case MatrixType::None:
508 break;
509 case MatrixType::Translate:
510 invert.mtx = -mtx;
511 invert.mty = -mty;
512 break;
513 case MatrixType::Scale:
514 inv = !vIsZero(m11);
515 inv &= !vIsZero(m22);
516 if (inv) {
517 invert.m11 = 1.0f / m11;
518 invert.m22 = 1.0f / m22;
519 invert.mtx = -mtx * invert.m11;
520 invert.mty = -mty * invert.m22;
521 }
522 break;
523 default:
524 // general case
525 float det = determinant();
526 inv = !vIsZero(det);
527 if (inv) invert = (adjoint() /= det);
528 // TODO Test above line
529 break;
530 }
531
532 if (invertible) *invertible = inv;
533
534 if (inv) {
535 // inverting doesn't change the type
536 invert.mType = mType;
537 invert.dirty = dirty;
538 }
539
540 return invert;
541 }
542
operator ==(const VMatrix & o) const543 bool VMatrix::operator==(const VMatrix &o) const
544 {
545 return fuzzyCompare(o);
546 }
547
operator !=(const VMatrix & o) const548 bool VMatrix::operator!=(const VMatrix &o) const
549 {
550 return !operator==(o);
551 }
552
fuzzyCompare(const VMatrix & o) const553 bool VMatrix::fuzzyCompare(const VMatrix &o) const
554 {
555 return vCompare(m11, o.m11) && vCompare(m12, o.m12) &&
556 vCompare(m21, o.m21) && vCompare(m22, o.m22) &&
557 vCompare(mtx, o.mtx) && vCompare(mty, o.mty);
558 }
559
560 #define V_NEAR_CLIP 0.000001f
561 #ifdef MAP
562 #undef MAP
563 #endif
564 #define MAP(x, y, nx, ny) \
565 do { \
566 float FX_ = x; \
567 float FY_ = y; \
568 switch (t) { \
569 case MatrixType::None: \
570 nx = FX_; \
571 ny = FY_; \
572 break; \
573 case MatrixType::Translate: \
574 nx = FX_ + mtx; \
575 ny = FY_ + mty; \
576 break; \
577 case MatrixType::Scale: \
578 nx = m11 * FX_ + mtx; \
579 ny = m22 * FY_ + mty; \
580 break; \
581 case MatrixType::Rotate: \
582 case MatrixType::Shear: \
583 case MatrixType::Project: \
584 nx = m11 * FX_ + m21 * FY_ + mtx; \
585 ny = m12 * FX_ + m22 * FY_ + mty; \
586 if (t == MatrixType::Project) { \
587 float w = (m13 * FX_ + m23 * FY_ + m33); \
588 if (w < V_NEAR_CLIP) w = V_NEAR_CLIP; \
589 w = 1. / w; \
590 nx *= w; \
591 ny *= w; \
592 } \
593 } \
594 } while (0)
595
map(const VRect & rect) const596 VRect VMatrix::map(const VRect &rect) const
597 {
598 VMatrix::MatrixType t = type();
599 if (t <= MatrixType::Translate)
600 return rect.translated(std::lround(mtx), std::lround(mty));
601
602 if (t <= MatrixType::Scale) {
603 int x = std::lround(m11 * rect.x() + mtx);
604 int y = std::lround(m22 * rect.y() + mty);
605 int w = std::lround(m11 * rect.width());
606 int h = std::lround(m22 * rect.height());
607 if (w < 0) {
608 w = -w;
609 x -= w;
610 }
611 if (h < 0) {
612 h = -h;
613 y -= h;
614 }
615 return {x, y, w, h};
616 } else if (t < MatrixType::Project) {
617 // see mapToPolygon for explanations of the algorithm.
618 float x = 0, y = 0;
619 MAP(rect.left(), rect.top(), x, y);
620 float xmin = x;
621 float ymin = y;
622 float xmax = x;
623 float ymax = y;
624 MAP(rect.right() + 1, rect.top(), x, y);
625 xmin = vMin(xmin, x);
626 ymin = vMin(ymin, y);
627 xmax = vMax(xmax, x);
628 ymax = vMax(ymax, y);
629 MAP(rect.right() + 1, rect.bottom() + 1, x, y);
630 xmin = vMin(xmin, x);
631 ymin = vMin(ymin, y);
632 xmax = vMax(xmax, x);
633 ymax = vMax(ymax, y);
634 MAP(rect.left(), rect.bottom() + 1, x, y);
635 xmin = vMin(xmin, x);
636 ymin = vMin(ymin, y);
637 xmax = vMax(xmax, x);
638 ymax = vMax(ymax, y);
639 return VRect(std::lround(xmin), std::lround(ymin),
640 std::lround(xmax) - std::lround(xmin),
641 std::lround(ymax) - std::lround(ymin));
642 } else {
643 // Not supported
644 assert(0);
645 return {};
646 }
647 }
648
map(const VPointF & p) const649 VPointF VMatrix::map(const VPointF &p) const
650 {
651 float fx = p.x();
652 float fy = p.y();
653
654 float x = 0, y = 0;
655
656 VMatrix::MatrixType t = type();
657 switch (t) {
658 case MatrixType::None:
659 x = fx;
660 y = fy;
661 break;
662 case MatrixType::Translate:
663 x = fx + mtx;
664 y = fy + mty;
665 break;
666 case MatrixType::Scale:
667 x = m11 * fx + mtx;
668 y = m22 * fy + mty;
669 break;
670 case MatrixType::Rotate:
671 case MatrixType::Shear:
672 case MatrixType::Project:
673 x = m11 * fx + m21 * fy + mtx;
674 y = m12 * fx + m22 * fy + mty;
675 if (t == MatrixType::Project) {
676 float w = 1.0f / (m13 * fx + m23 * fy + m33);
677 x *= w;
678 y *= w;
679 }
680 }
681 return {x, y};
682 }
683
684 V_END_NAMESPACE
685