PDF SDK Documentation

Comprehensive Guide for Developers: Features, Integration, and API Reference

Loading...
Searching...
No Matches
math.h
1// Copyright (c) 2009-2025 Avanquest Software. All rights reserved.
2
3#ifndef PDFSDK_CXX_MATH_H_INCLUDED_
4#define PDFSDK_CXX_MATH_H_INCLUDED_
5
6#include <algorithm>
7#include <cassert>
8#include <cfloat>
9#include <cmath>
10#include <limits>
11#include <optional>
12#include <vector>
13
14#include <pdfsdk/cxx/exception.h>
15#include <pdfsdk/math_types.h>
16
17namespace PDF {
18
19namespace Math {
20
21inline constexpr float EPSILON = 0.0001f;
22
23inline constexpr bool FloatLt(float a, float b, float epsilon = EPSILON) {
24 return a < b - epsilon;
25}
26inline constexpr bool FloatGt(float a, float b, float epsilon = EPSILON) {
27 return a > b + epsilon;
28}
29inline constexpr bool FloatEq(float a, float b, float epsilon = EPSILON) {
30 return a >= b - epsilon && a <= b + epsilon;
31}
32inline constexpr bool FloatInRange(float value, float lowerBound, float upperBound, float epsilon = EPSILON) {
33 return !FloatLt(value, lowerBound, epsilon) && !FloatGt(value, upperBound, epsilon);
34}
35
36inline constexpr double PI = 3.14159265358979323846;
37
38inline constexpr float RadianToDegree(float radians) {
39 return (radians * static_cast<float>(180.0 / PI));
40}
41inline constexpr double RadianToDegree(double radians) {
42 return (radians * (180.0 / PI));
43}
44
45inline constexpr float DegreeToRadian(float degrees) {
46 return (degrees * static_cast<float>(PI / 180.0));
47}
48inline constexpr double DegreeToRadian(double degrees) {
49 return (degrees * (PI / 180.0));
50}
51}
52
53struct PointI : public PDPointI {
54 PointI() {
55 x = 0;
56 y = 0;
57 }
58
59 PointI(const PointI& that) = default;
60 PointI& operator=(const PointI& that) = default;
61
62 PointI(int X_, int Y_) {
63 x = X_;
64 y = Y_;
65 }
66
67 template<typename XT, typename YT>
68 PointI(XT X_, YT Y_) {
69 static_assert(std::is_integral_v<XT> && std::is_integral_v<YT>,
70 "Template parameters must be integral types");
71 x = static_cast<int>(X_);
72 y = static_cast<int>(Y_);
73 }
74
75 PointI(const PDPointI& that) {
76 x = that.x;
77 y = that.y;
78 }
79
80 bool Equals(const PointI& that) const { return x == that.x && y == that.y; }
81
82 bool operator==(const PointI& that) const { return Equals(that); }
83 bool operator!=(const PointI& that) const { return !Equals(that); }
84
85 PointI operator-() const { return PointI(-x, -y); }
86
87 void Offset(int dx, int dy) {
88 x += dx;
89 y += dy;
90 }
91
92 static PointI MakeOffset(const PointI& point, int dx, int dy) {
93 PointI result = point;
94 result.Offset(dx, dy);
95 return result;
96 }
97
98 void Mul(int num) {
99 x *= num;
100 y *= num;
101 }
102
103 void Div(int num) {
104 x /= num;
105 y /= num;
106 }
107
108 PointI& operator+=(const PointI& that) {
109 Offset(that.x, that.y);
110 return *this;
111 }
112
113 PointI& operator-=(const PointI& that) {
114 Offset(-that.x, -that.y);
115 return *this;
116 }
117
118 PointI& operator*=(int num) {
119 Mul(num);
120 return *this;
121 }
122
123 PointI& operator/=(int num) {
124 Div(num);
125 return *this;
126 }
127
128 PointI operator+(const PointI& that) { return PointI(x + that.x, y + that.y); }
129 PointI operator-(const PointI& that) { return PointI(x - that.x, y - that.y); }
130
131 PointI operator*(int num) const { return PointI(x * num, y * num); }
132 friend PointI operator*(int num, const PointI& point) { return point * num; }
133
134 PointI operator/(int num) const { return PointI(x / num, y / num); }
135
149 static int DistanceManhattan(const PointI& a, const PointI& b) {
150 return std::abs(b.x - a.x) + std::abs(b.y - a.y);
151 }
152};
153
154struct PointF : public PDPointF {
155 PointF() {
156 x = 0.f;
157 y = 0.f;
158 }
159
160 PointF(const PointF& that) = default;
161 PointF& operator=(const PointF& that) = default;
162
163 PointF(float X_, float Y_) {
164 x = X_;
165 y = Y_;
166 }
167
168 PointF(const PDPointF& that) {
169 x = that.x;
170 y = that.y;
171 }
172
173 PointF(const PDPointI& pointi) {
174 x = static_cast<float>(pointi.x);
175 y = static_cast<float>(pointi.y);
176 }
177
178 PointI Round() const { return PointI(static_cast<int>(std::round(x)), static_cast<int>(std::round(y))); }
179 PointI Floor() const { return PointI(static_cast<int>(std::floor(x)), static_cast<int>(std::floor(y))); }
180 PointI Ceil() const { return PointI(static_cast<int>(std::ceil(x)), static_cast<int>(std::ceil(y))); }
181
182 bool Equals(const PointF& that, const float eps = Math::EPSILON) const {
183 if (this == &that)
184 return true;
185 return Math::FloatEq(x, that.x, eps) &&
186 Math::FloatEq(y, that.y, eps);
187 }
188
189 bool operator==(const PointF& that) const { return Equals(that); }
190 bool operator!=(const PointF& that) const { return !Equals(that); }
191
192 PointF operator-() const { return PointF{-x, -y}; }
193
194 void Offset(float dx, float dy) {
195 x += dx;
196 y += dy;
197 }
198
199 static PointF MakeOffset(const PointF& point, float dx, float dy) {
200 PointF result = point;
201 result.Offset(dx, dy);
202 return result;
203 }
204
205 void Mul(float num) {
206 x *= num;
207 y *= num;
208 }
209
210 void Div(float num) {
211 x /= num;
212 y /= num;
213 }
214
215 PointF& operator+=(const PointF& that) {
216 Offset(that.x, that.y);
217 return *this;
218 }
219
220 PointF& operator-=(const PointF& that) {
221 Offset(-that.x, -that.y);
222 return *this;
223 }
224
225 PointF& operator*=(float num) {
226 Mul(num);
227 return *this;
228 }
229
230 PointF& operator/=(float num) {
231 Div(num);
232 return *this;
233 }
234
235 PointF operator+(const PointF& that) const { return PointF(x + that.x, y + that.y); }
236 PointF operator-(const PointF& that) const { return PointF(x - that.x, y - that.y); }
237
238 PointF operator*(float num) const { return PointF(x * num, y * num); }
239 friend PointF operator*(float num, const PointF& point) { return point * num; }
240
241 PointF operator/(float num) const { return PointF(x / num, y / num); }
242
253 static float DistanceSquared(const PointF& a, const PointF& b) {
254 return (b - a).VectorMagnitudeSquared();
255 }
256
267 static float Distance(const PointF& a, const PointF& b) {
268 return (b - a).VectorMagnitude();
269 }
270
281 float DistanceToEdge(const PointF& a, const PointF& b) const {
282 PointF p = *this;
283
284 // сompute squared length of segment AB
285 float d = DistanceSquared(a, b);
286
287 if (Math::FloatEq(d, 0.f))
288 // segment is degenerate (A == B), return distance to point A
289 return Distance(p, a);
290
291 // compute projection factor t of point P onto line AB
292 float t = VectorDotProduct(p - a, b - a) / d;
293
294 if (Math::FloatLt(t, 0.f))
295 // projection falls before point A, return distance to A
296 return Distance(p, a);
297
298 if (Math::FloatGt(t, 1.f))
299 // projection falls after point B, return distance to B
300 return Distance(p, b);
301
302 // projection falls on segment AB, return distance to projected point
303 return Distance(p, a + t * (b - a));
304 }
305
319 static float DistanceManhattan(const PointF& a, const PointF& b) {
320 return std::abs(b.x - a.x) + std::abs(b.y - a.y);
321 }
322
334 float TestSide(const PointF& a, const PointF& b) const { return VectorCrossProduct(b - a, *this - a); }
335
345 static PointF AtCenter(const PointF& a, const PointF& b) { return (a + b) / 2.0f; }
346
354 float VectorMagnitudeSquared() const { return x * x + y * y; }
355
363 float VectorMagnitude() const { return std::sqrt(VectorMagnitudeSquared()); }
364
374 static float VectorDotProduct(const PointF& a, const PointF& b) { return a.x * b.x + a.y * b.y; }
375
385 static float VectorCrossProduct(const PointF& a, const PointF& b) { return a.x * b.y - b.x * a.y; }
386
396 static float VectorAngleBetween(const PointF& a, const PointF& b) {
397 return std::atan2(b.y, b.x) - std::atan2(a.y, a.x);
398 }
399
407 PointF& VectorNormalize() {
408 float mag = VectorMagnitude();
409 if (!Math::FloatEq(mag, 0.f))
410 Div(mag);
411 return *this;
412 }
413
422 static PointF VectorNormalTo(const PointF& v) { return PointF(-v.y, v.x); }
423};
424
425namespace Math {
426
442inline bool EdgeIntersectsEdge(const PointF& a0, const PointF& a1, const PointF& b0, const PointF& b1) {
443 auto va = a1 - a0;
444 auto vb = b1 - b0;
445 float d = PointF::VectorCrossProduct(va, vb);
446 if (Math::FloatEq(d, 0.f))
447 return false;
448
449 auto vc = a0 - b0;
450 float r = PointF::VectorCrossProduct(vb, vc) / d;
451 if (Math::FloatLt(r, 0.f) || Math::FloatGt(r, 1.f))
452 return false;
453 float s = PointF::VectorCrossProduct(va, vc) / d;
454 if (Math::FloatLt(s, 0.f) || Math::FloatGt(s, 1.f))
455 return false;
456
457 return true;
458}
459}
460
461struct SizeI : public PDSizeI {
462 SizeI() {
463 width = 0;
464 height = 0;
465 }
466
467 SizeI(const SizeI& that) = default;
468 SizeI& operator=(const SizeI& that) = default;
469
470 SizeI(int width_, int height_) {
471 width = width_;
472 height = height_;
473 }
474
475 template<typename WT, typename HT>
476 SizeI(WT width_, HT height_) {
477 static_assert(std::is_integral_v<WT> && std::is_integral_v<HT>,
478 "Template parameters must be integral types");
479 width = static_cast<int>(width_);
480 height = static_cast<int>(height_);
481 }
482
483 SizeI(const PDSizeI& sz) {
484 width = sz.width;
485 height = sz.height;
486 }
487
488 bool Equals(const SizeI& that) const {
489 if (this == &that)
490 return true;
491 return width == that.width && height == that.height;
492 }
493
494 bool operator==(const SizeI& that) const { return Equals(that); }
495 bool operator!=(const SizeI& that) const { return !Equals(that); }
496
497 int GetArea() const { return width * height; }
498 bool IsAreaEmpty() const { return width == 0 || height == 0; }
499};
500
501struct SizeF : public PDSizeF {
502 SizeF() {
503 width = 0.f;
504 height = 0.f;
505 }
506
507 SizeF(const SizeF& that) = default;
508 SizeF& operator=(const SizeF& that) = default;
509
510 SizeF(float width_, float height_) {
511 width = width_;
512 height = height_;
513 }
514
515 SizeF(const PDSizeF& that) {
516 width = that.width;
517 height = that.height;
518 }
519
520 SizeF(const PDSizeI& sizei) {
521 width = static_cast<float>(sizei.width);
522 height = static_cast<float>(sizei.height);
523 }
524
525 SizeI Round() const { return SizeI(static_cast<int>(std::round(width)), static_cast<int>(std::round(height))); }
526 SizeI Floor() const { return SizeI(static_cast<int>(std::floor(width)), static_cast<int>(std::floor(height))); }
527 SizeI Ceil() const { return SizeI(static_cast<int>(std::ceil(width)), static_cast<int>(std::ceil(height))); }
528
529 bool Equals(const SizeF& that, const float eps = Math::EPSILON) const {
530 if (this == &that)
531 return true;
532 return Math::FloatEq(width, that.width, eps) &&
533 Math::FloatEq(height, that.height, eps);
534 }
535
536 bool operator==(const SizeF& that) const { return Equals(that); }
537 bool operator!=(const SizeF& that) const { return !Equals(that); }
538
539 float GetArea() const { return width * height; }
540 bool IsAreaEmpty() const { return width == 0.f || height == 0.f; }
541};
542
543struct RectI : public PDRectI {
544 RectI() {
545 left = 0;
546 top = 0;
547 right = 0;
548 bottom = 0;
549 }
550
551 RectI(const RectI& that) = default;
552 RectI& operator=(const RectI& that) = default;
553
554 RectI(int left_, int top_, int right_, int bottom_) {
555 left = left_;
556 top = top_;
557 right = right_;
558 bottom = bottom_;
559 }
560
561 explicit RectI(const SizeI& size) {
562 left = 0;
563 top = 0;
564 right = size.width;
565 bottom = size.height;
566 }
567
568 RectI(const PointI& origin, const SizeI& size) {
569 left = origin.x;
570 top = origin.y;
571 right = origin.x + size.width;
572 bottom = origin.y + size.height;
573 }
574
575 RectI(const PointI& leftTop, const PointI& rightBottom) {
576 left = leftTop.x;
577 top = leftTop.y;
578 right = rightBottom.x;
579 bottom = rightBottom.y;
580 }
581
582 RectI(const PDRectI& that) {
583 left = that.left;
584 top = that.top;
585 right = that.right;
586 bottom = that.bottom;
587 }
588
589 void SortForPageSpace() {
590 if (left > right)
591 std::swap(left, right);
592 if (bottom > top)
593 std::swap(top, bottom);
594 }
595 static RectI MakeSortedForPageSpace(const RectI& rect) {
596 RectI result = rect;
597 result.SortForPageSpace();
598 return result;
599 }
600
601 void SortForDeviceSpace() {
602 if (left > right)
603 std::swap(left, right);
604 if (top > bottom)
605 std::swap(top, bottom);
606 }
607 static RectI MakeSortedForDeviceSpace(const RectI& rect) {
608 RectI result = rect;
609 result.SortForDeviceSpace();
610 return result;
611 }
612
613#ifndef SWIG
614 int& MinX() { return (left <= right ? left : right); }
615 int& MaxX() { return (left > right ? left : right); }
616 int& MinY() { return (top <= bottom ? top : bottom); }
617 int& MaxY() { return (top > bottom ? top : bottom); }
618#endif
619
620 int MinX() const { return (std::min)(left, right); }
621 int MaxX() const { return (std::max)(left, right); }
622 int MinY() const { return (std::min)(top, bottom); }
623 int MaxY() const { return (std::max)(top, bottom); }
624
625 bool Equals(const RectI& that) const {
626 if (this == &that)
627 return true;
628 return MinX() == that.MinX() &&
629 MaxX() == that.MaxX() &&
630 MinY() == that.MinY() &&
631 MaxY() == that.MaxY();
632 }
633
634 bool operator==(const RectI& that) const { return Equals(that); }
635 bool operator!=(const RectI& that) const { return !Equals(that); }
636
637 int GetWidth() const { return std::abs(right - left); }
638 int GetHeight() const { return std::abs(top - bottom); }
639
640 int GetArea() const { return GetWidth() * GetHeight(); }
641 bool IsAreaEmpty() const { return left == right || top == bottom; }
642
643 PointI GetOrigin() const { return PointI(MinX(), MinY()); }
644 SizeI GetSize() const { return SizeI(GetWidth(), GetHeight()); }
645
646 PointI GetLeftTop() const { return PointI(left, top); }
647 PointI GetLeftCenter() const { return PointI(left, MinY() + GetHeight() / 2); }
648 PointI GetLeftBottom() const { return PointI(left, bottom); }
649
650 PointI GetCenterTop() const { return PointI(MinX() + GetWidth() / 2, top); }
651 PointI GetCenter() const { return PointI(MinX() + GetWidth() / 2, MinY() + GetHeight() / 2); }
652 PointI GetCenterBottom() const { return PointI(MinX() + GetWidth() / 2, bottom); }
653
654 PointI GetRightTop() const { return PointI(right, top); }
655 PointI GetRightCenter() const { return PointI(right, MinY() + GetHeight() / 2); }
656 PointI GetRightBottom() const { return PointI(right, bottom); }
657
658 void Offset(int dx, int dy) {
659 left += dx;
660 top += dy;
661 right += dx;
662 bottom += dy;
663 }
664 void Offset(const PointI& delta) { Offset(delta.x, delta.y); }
665 static RectI MakeOffset(const RectI& rect, int dx, int dy) {
666 RectI result = rect;
667 result.Offset(dx, dy);
668 return result;
669 }
670 static RectI MakeOffset(const RectI& rect, const PointI& delta) { return MakeOffset(rect, delta.x, delta.y); }
671
672 RectI& operator+=(const PointI& point) {
673 Offset(point);
674 return *this;
675 }
676 RectI& operator-=(const PointI& point) {
677 Offset(-point);
678 return *this;
679 }
680 RectI operator+(const PointI& point) const { return MakeOffset(*this, point); }
681 RectI operator-(const PointI& point) const { return MakeOffset(*this, -point); }
682
683 void Inflate(int dx, int dy) {
684 auto& minX = MinX();
685 auto& minY = MinY();
686 auto& maxX = MaxX();
687 auto& maxY = MaxY();
688 minX -= dx;
689 maxX += dx;
690 minY -= dy;
691 maxY += dy;
692 }
693 void Inflate(int delta) { return Inflate(delta, delta); }
694 static RectI MakeInflated(const RectI& rect, int dx, int dy) {
695 RectI result = rect;
696 result.Inflate(dx, dy);
697 return result;
698 }
699 static RectI MakeInflated(const RectI& rect, int delta) { return MakeInflated(rect, delta, delta); }
700
701 void Extend(const PointI& point) {
702 MinX() = (std::min)(MinX(), point.x);
703 MinY() = (std::min)(MinY(), point.y);
704 MaxX() = (std::max)(MaxX(), point.x);
705 MaxY() = (std::max)(MaxY(), point.y);
706 }
707 static RectI MakeExtended(const RectI& rect, const PointI& point) {
708 RectI result = rect;
709 result.Extend(point);
710 return result;
711 }
712
713 bool Contains(const PointI& point) const {
714 return point.x >= MinX() && point.x < MaxX() && point.y >= MinY() && point.y < MaxY();
715 }
716 bool Contains(const RectI& rect) const {
717 return MinX() <= rect.MinX() && MaxX() >= rect.MaxX() && MinY() <= rect.MinY() && MaxY() >= rect.MaxY();
718 }
719
720 bool HasIntersection(const RectI& rect) const {
721 return MinX() < rect.MaxX() && MinY() < rect.MaxY() && MaxX() > rect.MinX() && MaxY() > rect.MinY();
722 }
723 static RectI MakeIntersection(const RectI& a, const RectI& b) {
724 if (!a.HasIntersection(b))
725 return RectI();
726
727 RectI i = a;
728 i.MinX() = (std::max)(a.MinX(), b.MinX());
729 i.MinY() = (std::max)(a.MinY(), b.MinY());
730 i.MaxX() = (std::min)(a.MaxX(), b.MaxX());
731 i.MaxY() = (std::min)(a.MaxY(), b.MaxY());
732 return i;
733 }
734
735 static RectI MakeUnion(const RectI& a, const RectI& b) {
736 RectI u = a;
737 u.MinX() = (std::min)(a.MinX(), b.MinX());
738 u.MinY() = (std::min)(a.MinY(), b.MinY());
739 u.MaxX() = (std::max)(a.MaxX(), b.MaxX());
740 u.MaxY() = (std::max)(a.MaxY(), b.MaxY());
741 return u;
742 }
743 static RectI MakeUnionIgnoringEmpty(const RectI& a, const RectI& b) {
744 if (a.IsAreaEmpty())
745 return b;
746 if (b.IsAreaEmpty())
747 return a;
748 return MakeUnion(a, b);
749 }
750};
751
752struct RectF : public PDRectF {
753 RectF() {
754 left = 0.f;
755 top = 0.f;
756 right = 0.f;
757 bottom = 0.f;
758 }
759
760 RectF(const RectF& that) = default;
761 RectF& operator=(const RectF& that) = default;
762
763 RectF(float left_, float top_, float right_, float bottom_) {
764 left = left_;
765 top = top_;
766 right = right_;
767 bottom = bottom_;
768 }
769
770 RectF(const PDRectF& rect) {
771 left = rect.left;
772 top = rect.top;
773 right = rect.right;
774 bottom = rect.bottom;
775 }
776
777 explicit RectF(const SizeF& size) {
778 left = 0.f;
779 top = size.height;
780 right = size.width;
781 bottom = 0.f;
782 }
783
784 RectF(const PointF& origin, const SizeF& size) {
785 left = origin.x;
786 top = origin.y + size.height;
787 right = origin.x + size.width;
788 bottom = origin.y;
789 }
790
791 RectF(const PointF& leftTop, const PointF& rightBottom) {
792 left = leftTop.x;
793 top = leftTop.y;
794 right = rightBottom.x;
795 bottom = rightBottom.y;
796 }
797
798 RectF(const RectI& recti) {
799 left = static_cast<float>(recti.left);
800 top = static_cast<float>(recti.top);
801 right = static_cast<float>(recti.right);
802 bottom = static_cast<float>(recti.bottom);
803 }
804
805#ifndef SWIG
806 template<class PointsIter>
807 static RectF EnclosingPoints(const PointsIter& pointsBegin, const PointsIter& pointsEnd) {
808 RectF rect;
809 auto it = pointsBegin;
810 if (it != pointsEnd) {
811 rect = RectF(*it, *it);
812 while (++it != pointsEnd)
813 rect.Extend(*it);
814 }
815 return rect;
816 }
817
818 template<class PointsContainer>
819 static RectF EnclosingPoints(const PointsContainer& points) {
820 return EnclosingPoints(std::begin(points), std::end(points));
821 }
822
823 static RectF EnclosingPoints(const std::initializer_list<PointF>& points) {
824 return EnclosingPoints(std::begin(points), std::end(points));
825 }
826#endif
827
828 RectI Round() const {
829 return RectI(static_cast<int>(std::round(MinX())),
830 static_cast<int>(std::round(MinY())),
831 static_cast<int>(std::round(MaxX())),
832 static_cast<int>(std::round(MaxY())));
833 }
834 RectI Floor() const {
835 return RectI(static_cast<int>(std::ceil(MinX())),
836 static_cast<int>(std::ceil(MinY())),
837 static_cast<int>(std::floor(MaxX())),
838 static_cast<int>(std::floor(MaxY())));
839 }
840 RectI Ceil() const {
841 return RectI(static_cast<int>(std::floor(MinX())),
842 static_cast<int>(std::floor(MinY())),
843 static_cast<int>(std::ceil(MaxX())),
844 static_cast<int>(std::ceil(MaxY())));
845 }
846
847 void SortForPageSpace() {
848 if (left > right)
849 std::swap(left, right);
850 if (bottom > top)
851 std::swap(top, bottom);
852 }
853 static RectF MakeSortedForPageSpace(const RectF& rect) {
854 RectF result = rect;
855 result.SortForPageSpace();
856 return result;
857 }
858
859 void SortForDeviceSpace() {
860 if (left > right)
861 std::swap(left, right);
862 if (top > bottom)
863 std::swap(top, bottom);
864 }
865 static RectF MakeSortedForDeviceSpace(const RectF& rect) {
866 RectF result = rect;
867 result.SortForDeviceSpace();
868 return result;
869 }
870
871#ifndef SWIG
872 float& MinX() { return (left <= right ? left : right); }
873 float& MaxX() { return (left > right ? left : right); }
874 float& MinY() { return (top <= bottom ? top : bottom); }
875 float& MaxY() { return (top > bottom ? top : bottom); }
876#endif
877
878 float MinX() const { return (std::min)(left, right); }
879 float MaxX() const { return (std::max)(left, right); }
880 float MinY() const { return (std::min)(top, bottom); }
881 float MaxY() const { return (std::max)(top, bottom); }
882
883 bool Equals(const RectF& that, const float eps = Math::EPSILON) const {
884 if (this == &that)
885 return true;
886 return Math::FloatEq(MinX(), that.MinX(), eps) &&
887 Math::FloatEq(MaxX(), that.MaxX(), eps) &&
888 Math::FloatEq(MinY(), that.MinY(), eps) &&
889 Math::FloatEq(MaxY(), that.MaxY(), eps);
890 }
891
892 bool operator==(const RectF& that) const { return Equals(that); }
893 bool operator!=(const RectF& that) const { return !Equals(that); }
894
895 float GetWidth() const { return std::abs(right - left); }
896 float GetHeight() const { return std::abs(top - bottom); }
897
898 float GetArea() const { return GetWidth() * GetHeight(); }
899 bool IsAreaEmpty() const { return Math::FloatEq(left, right) || Math::FloatEq(top, bottom); }
900
901 PointF GetOrigin() const { return PointF(MinX(), MinY()); }
902 SizeF GetSize() const { return SizeF(GetWidth(), GetHeight()); }
903
904 PointF GetLeftTop() const { return PointF(left, top); }
905 PointF GetLeftCenter() const { return PointF::AtCenter(GetLeftBottom(), GetLeftTop()); }
906 PointF GetLeftBottom() const { return PointF(left, bottom); }
907
908 PointF GetCenterTop() const { return PointF::AtCenter(GetLeftTop(), GetRightTop()); }
909 PointF GetCenter() const { return PointF(MinX() + GetWidth() / 2.f, MinY() + GetHeight() / 2.f); }
910 PointF GetCenterBottom() const { return PointF::AtCenter(GetLeftBottom(), GetRightBottom()); }
911
912 PointF GetRightTop() const { return PointF(right, top); }
913 PointF GetRightCenter() const { return PointF::AtCenter(GetRightBottom(), GetRightTop()); }
914 PointF GetRightBottom() const { return PointF(right, bottom); }
915
916 void Offset(float dx, float dy) {
917 left += dx;
918 top += dy;
919 right += dx;
920 bottom += dy;
921 }
922 void Offset(const PointF& delta) { Offset(delta.x, delta.y); }
923 static RectF MakeOffset(const RectF& rect, float dx, float dy) {
924 RectF result = rect;
925 result.Offset(dx, dy);
926 return result;
927 }
928 static RectF MakeOffset(const RectF& rect, const PointF& delta) { return MakeOffset(rect, delta.x, delta.y); }
929
930 void MoveTo(float x, float y) { Offset(x - MinX(), y - MinY()); }
931 void MoveTo(const PointF& point) { MoveTo(point.x, point.y); }
932 static RectF MakeMoved(const RectF& rect, float x, float y) {
933 RectF result = rect;
934 result.MoveTo(x, y);
935 return result;
936 }
937 static RectF MakeMoved(const RectF& rect, const PointF& point) { return MakeMoved(rect, point.x, point.y); }
938
939 RectF& operator+=(const PointF& point) {
940 Offset(point);
941 return *this;
942 }
943 RectF& operator-=(const PointF& point) {
944 Offset(-point);
945 return *this;
946 }
947 RectF operator+(const PointF& point) const { return (RectF(*this) += point); }
948 RectF operator-(const PointF& point) const { return (RectF(*this) -= point); }
949
950 void Inflate(float dx, float dy) {
951 auto& minX = MinX();
952 auto& minY = MinY();
953 auto& maxX = MaxX();
954 auto& maxY = MaxY();
955 minX -= dx;
956 maxX += dx;
957 minY -= dy;
958 maxY += dy;
959 }
960 void Inflate(float delta) { Inflate(delta, delta); }
961 static RectF MakeInflated(const RectF& rect, float dx, float dy) {
962 RectF result = rect;
963 result.Inflate(dx, dy);
964 return result;
965 }
966 static RectF MakeInflated(const RectF& rect, float delta) { return MakeInflated(rect, delta, delta); }
967
968 void Extend(const PointF& point) {
969 float& minX = MinX();
970 float& minY = MinY();
971 float& maxX = MaxX();
972 float& maxY = MaxY();
973 minX = (std::min)(minX, point.x);
974 minY = (std::min)(minY, point.y);
975 maxX = (std::max)(maxX, point.x);
976 maxY = (std::max)(maxY, point.y);
977 }
978 static RectF MakeExtended(const RectF& rect, const PointF& point) {
979 RectF result = rect;
980 result.Extend(point);
981 return result;
982 }
983
984 bool Contains(const PointF& point) const {
985 return point.x >= MinX() && point.x <= MaxX() && point.y >= MinY() && point.y <= MaxY();
986 }
987 bool Contains(const RectF& rect) const {
988 return MinX() <= rect.MinX() && MaxX() >= rect.MaxX() && MinY() <= rect.MinY() && MaxY() >= rect.MaxY();
989 }
990
991 bool HasIntersectionWithEdge(const PointF& a, const PointF& b) const {
992 return (Contains(a) ||
993 Contains(b) ||
994 Math::EdgeIntersectsEdge(GetLeftTop(), GetRightTop(), a, b) ||
995 Math::EdgeIntersectsEdge(GetRightTop(), GetRightBottom(), a, b) ||
996 Math::EdgeIntersectsEdge(GetRightBottom(), GetLeftBottom(), a, b) ||
997 Math::EdgeIntersectsEdge(GetLeftBottom(), GetLeftTop(), a, b));
998 }
999 bool HasIntersection(const RectF& rect) const {
1000 return MinX() < rect.MaxX() && MinY() < rect.MaxY() && MaxX() > rect.MinX() && MaxY() > rect.MinY();
1001 }
1002
1003 static RectF MakeIntersection(const RectF& a, const RectF& b) {
1004 if (!a.HasIntersection(b))
1005 return RectF();
1006
1007 RectF i = a;
1008 i.MinX() = (std::max)(a.MinX(), b.MinX());
1009 i.MinY() = (std::max)(a.MinY(), b.MinY());
1010 i.MaxX() = (std::min)(a.MaxX(), b.MaxX());
1011 i.MaxY() = (std::min)(a.MaxY(), b.MaxY());
1012 return i;
1013 }
1014
1015 static RectF MakeUnion(const RectF& a, const RectF& b) {
1016 RectF u = a;
1017 u.MinX() = (std::min)(a.MinX(), b.MinX());
1018 u.MinY() = (std::min)(a.MinY(), b.MinY());
1019 u.MaxX() = (std::max)(a.MaxX(), b.MaxX());
1020 u.MaxY() = (std::max)(a.MaxY(), b.MaxY());
1021 return u;
1022 }
1023
1024 static RectF MakeUnionIgnoringEmpty(const RectF& a, const RectF& b) {
1025 if (a.IsAreaEmpty())
1026 return b;
1027 if (b.IsAreaEmpty())
1028 return a;
1029 return MakeUnion(a, b);
1030 }
1031};
1032
1033struct Quad : public PDQuad {
1034 Quad() {
1035 topleft = PointF();
1036 topright = PointF();
1037 botleft = PointF();
1038 botright = PointF();
1039 }
1040
1041 Quad(const Quad& that) = default;
1042 Quad& operator=(const Quad& that) = default;
1043
1044 Quad(const PDQuad& that) {
1045 topleft = that.topleft;
1046 topright = that.topright;
1047 botleft = that.botleft;
1048 botright = that.botright;
1049 }
1050
1051 Quad(const PointF& leftTop_, const PointF& rightTop_, const PointF& leftBottom_, const PointF& rightBottom_) {
1052 topleft = leftTop_;
1053 topright = rightTop_;
1054 botleft = leftBottom_;
1055 botright = rightBottom_;
1056 }
1057
1058 explicit Quad(const PDRectF& rect) {
1059 topleft = PointF(rect.left, rect.top);
1060 topright = PointF(rect.right, rect.top);
1061 botleft = PointF(rect.left, rect.bottom);
1062 botright = PointF(rect.right, rect.bottom);
1063 }
1064
1065 explicit Quad(const RectF& rect) {
1066 topleft = PointF(rect.left, rect.top);
1067 topright = PointF(rect.right, rect.top);
1068 botleft = PointF(rect.left, rect.bottom);
1069 botright = PointF(rect.right, rect.bottom);
1070 }
1071
1072 PointF GetLeftTop() const { return topleft; }
1073 PointF GetLeftCenter() const { return PointF::AtCenter(GetLeftBottom(), GetLeftTop()); }
1074 PointF GetLeftBottom() const { return botleft; }
1075
1076 PointF GetCenterTop() const { return PointF::AtCenter(GetLeftTop(), GetRightTop()); }
1077 PointF GetCenter() const { return PointF::AtCenter(PointF::AtCenter(topleft, botright), PointF::AtCenter(topright, botleft)); }
1078 PointF GetCenterBottom() const { return PointF::AtCenter(GetLeftBottom(), GetRightBottom()); }
1079
1080 PointF GetRightTop() const { return topright; }
1081 PointF GetRightBottom() const { return botright; }
1082 PointF GetRightCenter() const { return PointF::AtCenter(GetRightBottom(), GetRightTop()); }
1083
1084 RectF GetBound() const {
1085 auto [minx, maxx] = std::minmax({topleft.x, topright.x, botleft.x, botright.x});
1086 auto [miny, maxy] = std::minmax({topleft.y, topright.y, botleft.y, botright.y});
1087 return RectF(minx, maxy, maxx, miny);
1088 }
1089
1090 float GetRotationAngle() const { return std::atan2(topright.y - topleft.y, topright.x - topleft.x); }
1091
1092 bool IsRectangle() const {
1093 return (std::min)(topleft.x, botright.x) == (std::min)(botleft.x, topright.x) &&
1094 (std::max)(topleft.x, botright.x) == (std::max)(botleft.x, topright.x) &&
1095 (std::min)(topleft.y, botright.y) == (std::min)(botleft.y, topright.y) &&
1096 (std::max)(topleft.y, botright.y) == (std::max)(botleft.y, topright.y);
1097 }
1098
1099 bool Equals(const Quad& that, const float eps = Math::EPSILON) const {
1100 if (this == &that)
1101 return true;
1102 return GetLeftTop().Equals(that.GetLeftTop(), eps) &&
1103 GetRightTop().Equals(that.GetRightTop(), eps) &&
1104 GetLeftBottom().Equals(that.GetLeftBottom(), eps) &&
1105 GetRightBottom().Equals(that.GetRightBottom(), eps);
1106 }
1107
1108 bool operator==(const Quad& that) const { return Equals(that); }
1109 bool operator!=(const Quad& that) const { return !Equals(that); }
1110
1111 void Offset(float dx, float dy) {
1112 topleft.x += dx;
1113 topleft.y += dy;
1114 topright.x += dx;
1115 topright.y += dy;
1116 botleft.x += dx;
1117 botleft.y += dy;
1118 botright.x += dx;
1119 botright.y += dy;
1120 }
1121 void Offset(const PointF& point) { Offset(point.x, point.y); }
1122
1123 static Quad MakeOffset(const Quad& quad, float dx, float dy) {
1124 Quad result = quad;
1125 result.Offset(dx, dy);
1126 return result;
1127 }
1128 static Quad MakeOffset(const Quad& quad, const PointF& delta) { return MakeOffset(quad, delta.x, delta.y); }
1129
1130 Quad& operator+=(const PointF& point) {
1131 Offset(point);
1132 return *this;
1133 }
1134 Quad& operator-=(const PointF& point) {
1135 Offset(-point);
1136 return *this;
1137 }
1138
1139 Quad operator+(const PointF& point) const { return (Quad(*this) += point); }
1140 Quad operator-(const PointF& point) const { return (Quad(*this) -= point); }
1141
1142 bool Contains(const PointF& point) const {
1143 RectF bound = GetBound();
1144 if (!bound.Contains(point))
1145 return false;
1146
1147 if (point == topleft || point == topright || point == botleft || point == botright)
1148 return true;
1149
1150 if (Math::FloatEq(0.f, point.DistanceToEdge(topleft, topright)) ||
1151 Math::FloatEq(0.f, point.DistanceToEdge(topright, botright)) ||
1152 Math::FloatEq(0.f, point.DistanceToEdge(botright, botleft)) ||
1153 Math::FloatEq(0.f, point.DistanceToEdge(botleft, topleft)))
1154 return true;
1155
1156 int winding = 0;
1157 winding += (point.TestSide(topleft, topright) > 0) ? 1 : -1;
1158 winding += (point.TestSide(topright, botright) > 0) ? 1 : -1;
1159 winding += (point.TestSide(botright, botleft) > 0) ? 1 : -1;
1160 winding += (point.TestSide(botleft, topleft) > 0) ? 1 : -1;
1161 return winding != 0;
1162 }
1163
1164 bool Contains(const Quad& that) const {
1165 return Contains(that.GetLeftTop()) &&
1166 Contains(that.GetRightTop()) &&
1167 Contains(that.GetLeftBottom()) &&
1168 Contains(that.GetRightBottom());
1169 }
1170
1171 bool HasIntersectionWithEdge(const PointF& a, const PointF& b) const {
1172 return (Contains(a) ||
1173 Contains(b) ||
1174 Math::EdgeIntersectsEdge(GetLeftTop(), GetRightTop(), a, b) ||
1175 Math::EdgeIntersectsEdge(GetRightTop(), GetRightBottom(), a, b) ||
1176 Math::EdgeIntersectsEdge(GetRightBottom(), GetLeftBottom(), a, b) ||
1177 Math::EdgeIntersectsEdge(GetLeftBottom(), GetLeftTop(), a, b));
1178 }
1179
1180 bool HasIntersection(const RectF& rect) const {
1181 return HasIntersection(Quad{rect});
1182 }
1183
1184 bool HasIntersection(const Quad& quad) const {
1185 return (Contains(quad.GetLeftTop()) ||
1186 Contains(quad.GetRightTop()) ||
1187 Contains(quad.GetRightBottom()) ||
1188 Contains(quad.GetLeftBottom()) ||
1189 quad.Contains(GetLeftTop()) ||
1190 quad.Contains(GetRightTop()) ||
1191 quad.Contains(GetRightBottom()) ||
1192 quad.Contains(GetLeftBottom()) ||
1193 HasIntersectionWithEdge(quad.GetLeftTop(), quad.GetRightTop()) ||
1194 HasIntersectionWithEdge(quad.GetRightTop(), quad.GetRightBottom()) ||
1195 HasIntersectionWithEdge(quad.GetRightBottom(), quad.GetLeftBottom()) ||
1196 HasIntersectionWithEdge(quad.GetLeftBottom(), quad.GetLeftTop()));
1197 }
1198};
1199
1200struct QuadPoints {
1201 QuadPoints() = default;
1202
1203 explicit QuadPoints(std::vector<Quad> Quads_)
1204 : quads(std::move(Quads_)) {
1205 }
1206
1207 RectF GetBound() const {
1208 RectF rect;
1209 if (!quads.empty()) {
1210 rect = quads[0].GetBound();
1211 for (const auto& quad : quads)
1212 rect = RectF::MakeUnion(rect, quad.GetBound());
1213 }
1214 return rect;
1215 }
1216
1217 bool Equals(const QuadPoints& that, const float eps = Math::EPSILON) const {
1218 if (this == &that)
1219 return true;
1220 if (quads.size() != that.quads.size())
1221 return false;
1222 for (size_t i = 0; i < quads.size(); ++i) {
1223 if (!(quads[i].Equals(that.quads[i], eps)))
1224 return false;
1225 }
1226 return true;
1227 }
1228
1229 bool operator==(const QuadPoints& that) const { return Equals(that); }
1230 bool operator!=(const QuadPoints& that) const { return !Equals(that); }
1231
1232 QuadPoints Optimize() const {
1233 if (quads.empty())
1234 return QuadPoints();
1235
1236 std::vector<Quad> merged;
1237
1238 static constexpr auto areLinesCollinear = [](const PointF& p0, const PointF& p1, const PointF& p2) {
1239 return Math::FloatEq(PointF::VectorCrossProduct(p1 - p0, p2 - p0), 0.f);
1240 };
1241
1242 Quad quad;
1243 bool initialized = false;
1244 for (size_t i = 0; i <= quads.size(); ++i) {
1245 if (i == quads.size()) {
1246 merged.emplace_back(quad);
1247 break;
1248 }
1249
1250 auto& newQuad = quads[i];
1251 if (!initialized) {
1252 quad = newQuad;
1253 initialized = true;
1254 } else {
1255 if (areLinesCollinear(quad.GetLeftBottom(), quad.GetRightBottom(), newQuad.GetLeftBottom()) &&
1256 areLinesCollinear(quad.GetLeftTop(), quad.GetRightTop(), newQuad.GetLeftTop()) &&
1257 quad.HasIntersection(newQuad)) {
1258 quad.topright = newQuad.GetRightTop();
1259 quad.botright = newQuad.GetRightBottom();
1260 } else {
1261 merged.emplace_back(quad);
1262 quad = newQuad;
1263 }
1264 }
1265 }
1266
1267 return QuadPoints(std::move(merged));
1268 }
1269
1270 std::vector<Quad> quads;
1271};
1272
1276struct Matrix : public PDMatrix {
1277 Matrix() {
1278 a = 1.f;
1279 b = 0.f;
1280 c = 0.f;
1281 d = 1.f;
1282 e = 0.f;
1283 f = 0.f;
1284 }
1285
1286 Matrix(const Matrix& that) = default;
1287 Matrix& operator=(const Matrix& that) = default;
1288
1289 Matrix(float a_, float b_, float c_, float d_, float e_, float f_) {
1290 a = a_;
1291 b = b_;
1292 c = c_;
1293 d = d_;
1294 e = e_;
1295 f = f_;
1296 }
1297
1298 Matrix(const PDMatrix& that) {
1299 a = that.a;
1300 b = that.b;
1301 c = that.c;
1302 d = that.d;
1303 e = that.e;
1304 f = that.f;
1305 }
1306
1307 bool Equals(const Matrix& that, const float eps = Math::EPSILON) const {
1308 if (this == &that)
1309 return true;
1310 return Math::FloatEq(a, that.a, eps) &&
1311 Math::FloatEq(b, that.b, eps) &&
1312 Math::FloatEq(c, that.c, eps) &&
1313 Math::FloatEq(d, that.d, eps) &&
1314 Math::FloatEq(e, that.e, eps) &&
1315 Math::FloatEq(f, that.f, eps);
1316 }
1317
1318 bool operator==(const Matrix& that) const { return Equals(that); }
1319 bool operator!=(const Matrix& that) const { return !Equals(that); }
1320
1321 bool IsIdentity() const { return *this == Matrix{}; }
1322
1323 static Matrix Concat(const Matrix& lhs, const Matrix& rhs) {
1324 return Matrix(lhs.a * rhs.a + lhs.b * rhs.c,
1325 lhs.a * rhs.b + lhs.b * rhs.d,
1326 lhs.c * rhs.a + lhs.d * rhs.c,
1327 lhs.c * rhs.b + lhs.d * rhs.d,
1328 lhs.e * rhs.a + lhs.f * rhs.c + rhs.e,
1329 lhs.e * rhs.b + lhs.f * rhs.d + rhs.f);
1330 }
1331
1332 Matrix operator*(const Matrix& that) const { return Concat(*this, that); }
1333 Matrix& operator*=(const Matrix& that) { return (*this = *this * that); }
1334
1335 PointF MapPoint(const PointF& point) const {
1336 return PointF(a * point.x + c * point.y + e, b * point.x + d * point.y + f);
1337 }
1338
1339 SizeF MapSize(const SizeF& size) const {
1340 return SizeF(a * size.width + c * size.height, b * size.width + d * size.height);
1341 }
1342
1343 RectF MapRect(const RectF& rect) const {
1344 return MapQuad(Quad{rect}).GetBound();
1345 }
1346
1347 Quad MapQuad(const Quad& quad) const {
1348 return Quad(MapPoint(quad.GetLeftTop()),
1349 MapPoint(quad.GetRightTop()),
1350 MapPoint(quad.GetLeftBottom()),
1351 MapPoint(quad.GetRightBottom()));
1352 }
1353
1354 float GetScalingX() const {
1355 return std::sqrt(a * a + c * c);
1356 }
1357
1358 float GetScalingY() const {
1359 return std::sqrt(b * b + d * d);
1360 }
1361
1362 bool HasRotation() const {
1363 return !Math::FloatEq(b, 0.f) || !Math::FloatEq(c, 0.f);
1364 }
1365
1366 float GetRotation() const {
1367 return std::atan2(b, a);
1368 }
1369
1370 float GetRotationDegrees() const {
1371 return Math::RadianToDegree(GetRotation());
1372 }
1373
1374 double Determinant() const { return static_cast<double>(a) * d - static_cast<double>(b) * c; }
1375
1376 static Matrix Scaling(float value) { return Scaling(value, value); }
1377 static Matrix Scaling(float x, float y) { return Matrix(x, 0.f, 0.f, y, 0.f, 0.f); }
1378 static Matrix Scaling(const SizeF& scaling) { return Scaling(scaling.width, scaling.height); }
1379 static Matrix Scaling(float value, const PointF& origin) { return Scaling(value, value, origin); }
1380 static Matrix Scaling(float x, float y, const PointF& origin) { return Matrix(x, 0.f, 0.f, y, (1.f - x) * origin.x, (1.f - y) * origin.y); }
1381 static Matrix Scaling(const SizeF& scaling, const PointF& origin) { return Scaling(scaling.width, scaling.height, origin); }
1382
1383 void Scale(float value) { *this = Scaling(value) * *this; }
1384 void Scale(float x, float y) { *this = Scaling(x, y) * *this; }
1385 void Scale(const SizeF& scaling) { *this = Scaling(scaling) * *this; }
1386 void Scale(float value, const PointF& origin) { *this = Scaling(value, origin) * *this; }
1387 void Scale(float x, float y, const PointF& origin) { *this = Scaling(x, y, origin) * *this; }
1388 void Scale(const SizeF& scaling, const PointF& origin) { *this = Scaling(scaling, origin) * *this; }
1389
1390 static Matrix Rotation(double radians) {
1391 float sina = static_cast<float>(std::sin(radians));
1392 float cosa = static_cast<float>(std::cos(radians));
1393 return Matrix(cosa, sina, -sina, cosa, 0.f, 0.f);
1394 }
1395 static Matrix Rotation(double radians, const PointF& origin) {
1396 float sina = static_cast<float>(std::sin(radians));
1397 float cosa = static_cast<float>(std::cos(radians));
1398 float dx = origin.x * (1.f - cosa) + origin.y * sina;
1399 float dy = origin.y * (1.f - cosa) - origin.x * sina;
1400 return Matrix(cosa, sina, -sina, cosa, dx, dy);
1401 }
1402
1403 void Rotate(double radians) { *this = Rotation(radians) * *this; }
1404 void Rotate(double radians, const PointF& origin) { *this = Rotation(radians, origin) * *this; }
1405
1406 static Matrix RotationDegree(double degree) {
1407 PointF zero(0.f, 0.f);
1408 return RotationDegree(degree, zero);
1409 }
1410 static Matrix RotationDegree(double degree, const PointF& origin) {
1411 float sina;
1412 float cosa;
1413 if (degree == 0.0) {
1414 sina = 0.f;
1415 cosa = 1.f;
1416 } else if (degree == 90.0) {
1417 sina = 1.f;
1418 cosa = 0.f;
1419 } else if (degree == 180.0) {
1420 sina = 0.f;
1421 cosa = -1.f;
1422 } else if (degree == 270.0) {
1423 sina = -1.f;
1424 cosa = 0.f;
1425 } else {
1426 double radians = Math::DegreeToRadian(degree);
1427 sina = static_cast<float>(std::sin(radians));
1428 cosa = static_cast<float>(std::cos(radians));
1429 }
1430
1431 float dx = origin.x * (1.f - cosa) + origin.y * sina;
1432 float dy = origin.y * (1.f - cosa) - origin.x * sina;
1433 return Matrix(cosa, sina, -sina, cosa, dx, dy);
1434 }
1435
1436 void RotateDegree(double degree) { *this = RotationDegree(degree) * *this; }
1437 void RotateDegree(double degree, const PointF& origin) { *this = RotationDegree(degree, origin) * *this; }
1438
1439 static Matrix ReflectionX(float width = 0.f) { return Matrix(-1.f, 0.f, 0.f, 1.f, 0.f, width); }
1440 static Matrix ReflectionY(float height = 0.f) { return Matrix(1.f, 0.f, 0.f, -1.f, 0.f, height); }
1441
1442 void ReflectX(float width = 0.f) { *this = ReflectionX(width) * *this; }
1443 void ReflectY(float height = 0.f) { *this = ReflectionY(height) * *this; }
1444
1445 bool IsReflected() const {
1446 return a * d < b * c;
1447 }
1448
1449 static Matrix Translation(float x, float y) { return Matrix(1.f, 0.f, 0.f, 1.f, x, y); }
1450 static Matrix Translation(const PointF& v) { return Translation(v.x, v.y); }
1451
1452 void Translate(float x, float y) { *this = Translation(x, y) * *this; }
1453 void Translate(const PointF& v) { *this = Translation(v) * *this; }
1454
1455 bool IsInvertible() const {
1456 double det = Determinant();
1457 static constexpr double EPS = std::numeric_limits<double>::epsilon();
1458 return (det < -EPS || det > EPS);
1459 }
1460
1461#ifndef SWIG
1462 std::optional<Matrix> Inverse() const {
1463 double det = Determinant();
1464
1465 static constexpr double EPS = std::numeric_limits<double>::epsilon();
1466 if (det >= -EPS && det <= EPS)
1467 return std::nullopt;
1468
1469 double invdet = 1.0 / det;
1470 float A = static_cast<float>(d * invdet);
1471 float B = static_cast<float>(-b * invdet);
1472 float C = static_cast<float>(-c * invdet);
1473 float D = static_cast<float>(a * invdet);
1474 float E = static_cast<float>((f * c - e * d) * invdet);
1475 float F = static_cast<float>((e * b - f * a) * invdet);
1476 return Matrix(A, B, C, D, E, F);
1477 }
1478#endif
1479
1480 Matrix InverseOrIdentity() const {
1481 return Inverse().value_or(Matrix{});
1482 }
1483
1484 static Matrix RectToRect(const RectF& source, const RectF& dest) {
1485 float sx = dest.GetWidth() / source.GetWidth();
1486 float sy = dest.GetHeight() / source.GetHeight();
1487 float dx = dest.MinX() - sx * source.MinX();
1488 float dy = dest.MinY() - sy * source.MinY();
1489 return Matrix(sx, 0.f, 0.f, sy, dx, dy);
1490 }
1491
1492 static Matrix RectToRectProportional(const RectF& source, const RectF& dest) {
1493 float sx = dest.GetWidth() / source.GetWidth();
1494 float sy = dest.GetHeight() / source.GetHeight();
1495 float scale = (std::min)(sx, sy);
1496 float dx = dest.MinX() - scale * source.MinX() + (dest.GetWidth() - scale * source.GetWidth()) / 2.0f;
1497 float dy = dest.MinY() - scale * source.MinY() + (dest.GetHeight() - scale * source.GetHeight()) / 2.0f;
1498 return Matrix(scale, 0.f, 0.f, scale, dx, dy);
1499 }
1500
1501 bool DoesPreserveRects() const {
1502 bool A = !Math::FloatEq(a, 0.0f);
1503 bool B = !Math::FloatEq(b, 0.0f);
1504 bool C = !Math::FloatEq(c, 0.0f);
1505 bool D = !Math::FloatEq(d, 0.0f);
1506 return (A == D && B == C && A != B && C != D);
1507 }
1508};
1509
1510} // namespace PDF
1511
1512#endif // PDFSDK_CXX_MATH_H_INCLUDED_
Math types.
Definition math.h:154
float VectorMagnitudeSquared() const
Calculates the squared magnitude (length) of the vector from the origin to this point.
Definition math.h:354
PointF & VectorNormalize()
Normalizes the vector (makes it a unit vector with length 1).
Definition math.h:407
static PointF VectorNormalTo(const PointF &v)
Calculates a vector that is normal (perpendicular) to the given vector.
Definition math.h:422
static PointF AtCenter(const PointF &a, const PointF &b)
Computes the midpoint between two points.
Definition math.h:345
static float Distance(const PointF &a, const PointF &b)
Computes the Euclidean distance between two points.
Definition math.h:267
static float VectorDotProduct(const PointF &a, const PointF &b)
Calculates the dot product of two vectors.
Definition math.h:374
float DistanceToEdge(const PointF &a, const PointF &b) const
Computes the shortest distance from this point to a line segment AB.
Definition math.h:281
static float DistanceSquared(const PointF &a, const PointF &b)
Computes the squared Euclidean distance between two points.
Definition math.h:253
float TestSide(const PointF &a, const PointF &b) const
Determines the relative position of this point with respect to a directed line segment AB.
Definition math.h:334
static float VectorCrossProduct(const PointF &a, const PointF &b)
Calculates the 2D cross product of two vectors.
Definition math.h:385
static float DistanceManhattan(const PointF &a, const PointF &b)
Computes the Manhattan distance between two points.
Definition math.h:319
static float VectorAngleBetween(const PointF &a, const PointF &b)
Calculates the angle between two vectors.
Definition math.h:396
float VectorMagnitude() const
Calculates the magnitude (length) of the vector from the origin to this point.
Definition math.h:363
Definition math.h:53
static int DistanceManhattan(const PointI &a, const PointI &b)
Computes the Manhattan distance between two points.
Definition math.h:149
Definition math.h:1033
Definition math.h:752
Definition math.h:543
Definition math.h:501
Definition math.h:461
Definition math_types.h:60
Definition math_types.h:12
Definition math_types.h:18
Definition math_types.h:52
Definition math_types.h:30
Definition math_types.h:44
Definition math_types.h:24
Definition math_types.h:38