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