137 PointF(
float X_,
float Y_) {
148 x =
static_cast<float>(pointi.x);
149 y =
static_cast<float>(pointi.y);
152 PointI Round()
const {
return PointI(
static_cast<int>(std::round(x)),
static_cast<int>(std::round(y))); }
153 PointI Floor()
const {
return PointI(
static_cast<int>(std::floor(x)),
static_cast<int>(std::floor(y))); }
154 PointI Ceil()
const {
return PointI(
static_cast<int>(std::ceil(x)),
static_cast<int>(std::ceil(y))); }
156 bool Equals(
const PointF& that)
const {
return Math::FloatEq(x, that.x) && Math::FloatEq(y, that.y); }
158 bool operator==(
const PointF& that)
const {
return Equals(that); }
159 bool operator!=(
const PointF& that)
const {
return !Equals(that); }
163 void Offset(
float dx,
float dy) {
168 static PointF Offset(
const PointF& point,
float dx,
float dy) {
170 result.Offset(dx, dy);
174 void Mul(
float num) {
179 void Div(
float num) {
185 Offset(that.x, that.y);
190 Offset(-that.x, -that.y);
194 PointF& operator*=(
float num) {
199 PointF& operator/=(
float num) {
207 PointF operator*(
float num)
const {
return PointF(x * num, y * num); }
208 friend PointF operator*(
float num,
const PointF& point) {
return point * num; }
210 PointF operator/(
float num)
const {
return PointF(x / num, y / num); }
212 static float DistanceSquared(
const PointF& a,
const PointF& b) {
return (b - a).VectorMagnitudeSquared(); }
213 static float Distance(
const PointF& a,
const PointF& b) {
return (b - a).VectorMagnitude(); }
217 float DistanceToEdge(
const PointF& a,
const PointF& b)
const {
219 float d = DistanceSquared(a, b);
220 if (Math::FloatEq(d, 0.f))
221 return Distance(p, a);
222 float t = VectorDotProduct(p - a, b - a) / d;
223 if (Math::FloatLt(t, 0.f))
224 return Distance(p, a);
225 if (Math::FloatGt(t, 1.f))
226 return Distance(p, b);
227 return Distance(p, a + t * (b - a));
230 float VectorMagnitudeSquared()
const {
return x * x + y * y; }
231 float VectorMagnitude()
const {
return std::sqrt(VectorMagnitudeSquared()); }
241 static float VectorDotProduct(
const PointF& a,
const PointF& b) {
return a.x * b.x + a.y * b.y; }
242 static float VectorCrossProduct(
const PointF& a,
const PointF& b) {
return a.x * b.y - b.x * a.y; }
244 static float VectorAngleBetween(
const PointF& a,
const PointF& b) {
245 return std::atan2(b.y, b.x) - std::atan2(a.y, a.x);
248 PointF& VectorNormalize() {
249 float mag = VectorMagnitude();
250 if (!Math::FloatEq(mag, 0.f))
255 static PointF VectorNormalTo(
const PointF& v) {
return PointF(-v.y, v.x); }
354 RectI(
int left_,
int top_,
int right_,
int bottom_) {
365 bottom = size.height;
371 right = origin.x + size.width;
372 bottom = origin.y + size.height;
378 right = rightBottom.x;
379 bottom = rightBottom.y;
386 bottom = that.bottom;
390 PointI LeftBottom()
const {
return PointI(left, bottom); }
392 PointI RightBottom()
const {
return PointI(right, bottom); }
394 void SortForPageSpace() {
396 std::swap(left, right);
398 std::swap(top, bottom);
401 void SortForDeviceSpace() {
403 std::swap(left, right);
405 std::swap(top, bottom);
409 int& MinX() {
return (left <= right ? left : right); }
410 int& MaxX() {
return (left > right ? left : right); }
411 int& MinY() {
return (top <= bottom ? top : bottom); }
412 int& MaxY() {
return (top > bottom ? top : bottom); }
415 int MinX()
const {
return (std::min)(left, right); }
416 int MaxX()
const {
return (std::max)(left, right); }
417 int MinY()
const {
return (std::min)(top, bottom); }
418 int MaxY()
const {
return (std::max)(top, bottom); }
420 bool Equals(
const RectI& that)
const {
423 return MinX() == that.MinX() && MaxX() == that.MaxX() && MinY() == that.MinY() && MaxY() == that.MaxY();
426 bool operator==(
const RectI& that)
const {
return Equals(that); }
427 bool operator!=(
const RectI& that)
const {
return !Equals(that); }
429 int GetWidth()
const {
return std::abs(right - left); }
430 int GetHeight()
const {
return std::abs(top - bottom); }
431 SizeI GetSize()
const {
return SizeI(GetWidth(), GetHeight()); }
432 PointI GetOrigin()
const {
return PointI(MinX(), MinY()); }
433 PointI GetCenter()
const {
return PointI(MinX() + GetWidth() / 2, MinY() + GetHeight() / 2); }
435 int GetArea()
const {
return GetWidth() * GetHeight(); }
436 bool IsAreaEmpty()
const {
return left == right || top == bottom; }
438 void Offset(
int dx,
int dy) {
445 void Offset(
const PointI& delta) { Offset(delta.x, delta.y); }
447 static RectI Offset(
const RectI& rect,
int dx,
int dy) {
449 result.Offset(dx, dy);
453 static RectI Offset(
const RectI& rect,
const PointI& delta) {
return Offset(rect, delta.x, delta.y); }
465 RectI operator+(
const PointI& point)
const {
return Offset(*
this, point); }
466 RectI operator-(
const PointI& point)
const {
return Offset(*
this, -point); }
468 void Inflate(
int dx,
int dy) {
479 void Inflate(
int delta) {
return Inflate(delta, delta); }
481 static RectI Inflate(
const RectI& rect,
int dx,
int dy) {
483 result.Inflate(dx, dy);
487 static RectI Inflate(
const RectI& rect,
int delta) {
return Inflate(rect, delta, delta); }
489 void Extend(
const PointI& point) {
490 MinX() = (std::min)(MinX(), point.x);
491 MinY() = (std::min)(MinY(), point.y);
492 MaxX() = (std::max)(MaxX(), point.x);
493 MaxY() = (std::max)(MaxY(), point.y);
497 RectI result = *
this;
498 result.Extend(point);
502 bool Contains(
const PointI& point)
const {
503 return point.x >= MinX() && point.x < MaxX() && point.y >= MinY() && point.y < MaxY();
506 bool Contains(
const RectI& rect)
const {
507 return MinX() <= rect.MinX() && MaxX() >= rect.MaxX() && MinY() <= rect.MinY() && MaxY() >= rect.MaxY();
510 bool HasIntersection(
const RectI& rect)
const {
511 return MinX() < rect.MaxX() && MinY() < rect.MaxY() && MaxX() > rect.MinX() && MaxY() > rect.MinY();
515 if (!a.HasIntersection(b))
519 i.MinX() = (std::max)(a.MinX(), b.MinX());
520 i.MinY() = (std::max)(a.MinY(), b.MinY());
521 i.MaxX() = (std::min)(a.MaxX(), b.MaxX());
522 i.MaxY() = (std::min)(a.MaxY(), b.MaxY());
528 u.MinX() = (std::min)(a.MinX(), b.MinX());
529 u.MinY() = (std::min)(a.MinY(), b.MinY());
530 u.MaxX() = (std::max)(a.MaxX(), b.MaxX());
531 u.MaxY() = (std::max)(a.MaxY(), b.MaxY());
552 RectF(
float left_,
float top_,
float right_,
float bottom_) {
563 bottom = rect.bottom;
575 top = origin.y + size.height;
576 right = origin.x + size.width;
583 right = rightBottom.x;
584 bottom = rightBottom.y;
588 left =
static_cast<float>(recti.left);
589 top =
static_cast<float>(recti.top);
590 right =
static_cast<float>(recti.right);
591 bottom =
static_cast<float>(recti.bottom);
595 template<
class Po
intsIter>
596 static RectF EnclosingPoints(
const PointsIter& pointsBegin,
const PointsIter& pointsEnd) {
598 auto it = pointsBegin;
599 if (it != pointsEnd) {
600 rect =
RectF(*it, *it);
601 while (++it != pointsEnd)
607 template<
class Po
intsContainer>
608 static RectF EnclosingPoints(
const PointsContainer& points) {
609 return EnclosingPoints(std::begin(points), std::end(points));
612 static RectF EnclosingPoints(
const std::initializer_list<PointF>& points) {
613 return EnclosingPoints(std::begin(points), std::end(points));
617 RectI Round()
const {
618 return RectI(
static_cast<int>(std::round(MinX())),
619 static_cast<int>(std::round(MinY())),
620 static_cast<int>(std::round(MaxX())),
621 static_cast<int>(std::round(MaxY())));
624 RectI Floor()
const {
625 return RectI(
static_cast<int>(std::ceil(MinX())),
626 static_cast<int>(std::ceil(MinY())),
627 static_cast<int>(std::floor(MaxX())),
628 static_cast<int>(std::floor(MaxY())));
632 return RectI(
static_cast<int>(std::floor(MinX())),
633 static_cast<int>(std::floor(MinY())),
634 static_cast<int>(std::ceil(MaxX())),
635 static_cast<int>(std::ceil(MaxY())));
639 PointF LeftBottom()
const {
return PointF(left, bottom); }
641 PointF RightBottom()
const {
return PointF(right, bottom); }
643 static RectF SortForPageSpace(
const RectF& rect) {
645 result.SortForPageSpace();
648 void SortForPageSpace() {
650 std::swap(left, right);
652 std::swap(top, bottom);
655 static RectF SortForDeviceSpace(
const RectF& rect) {
657 result.SortForDeviceSpace();
660 void SortForDeviceSpace() {
662 std::swap(left, right);
664 std::swap(top, bottom);
668 float& MinX() {
return (left <= right ? left : right); }
669 float& MaxX() {
return (left > right ? left : right); }
670 float& MinY() {
return (top <= bottom ? top : bottom); }
671 float& MaxY() {
return (top > bottom ? top : bottom); }
674 float MinX()
const {
return (std::min)(left, right); }
675 float MaxX()
const {
return (std::max)(left, right); }
676 float MinY()
const {
return (std::min)(top, bottom); }
677 float MaxY()
const {
return (std::max)(top, bottom); }
679 bool Equals(
const RectF& that)
const {
682 return MinX() == that.MinX() && MaxX() == that.MaxX() && MinY() == that.MinY() && MaxY() == that.MaxY();
685 bool operator==(
const RectF& that)
const {
return Equals(that); }
686 bool operator!=(
const RectF& that)
const {
return !Equals(that); }
688 float GetWidth()
const {
return std::abs(right - left); }
689 float GetHeight()
const {
return std::abs(top - bottom); }
690 SizeF GetSize()
const {
return SizeF(GetWidth(), GetHeight()); }
691 PointF GetOrigin()
const {
return PointF(MinX(), MinY()); }
692 PointF GetCenter()
const {
return PointF(MinX() + GetWidth() / 2.f, MinY() + GetHeight() / 2.f); }
694 PointF LeftCenter()
const {
return PointF::AtCenter(LeftBottom(), LeftTop()); }
695 PointF CenterTop()
const {
return PointF::AtCenter(LeftTop(), RightTop()); }
696 PointF RightCenter()
const {
return PointF::AtCenter(RightBottom(), RightTop()); }
697 PointF CenterBottom()
const {
return PointF::AtCenter(LeftBottom(), RightBottom()); }
699 float GetArea()
const {
return GetWidth() * GetHeight(); }
700 bool IsAreaEmpty()
const {
return Math::FloatEq(left, right) || Math::FloatEq(top, bottom); }
702 void Offset(
float dx,
float dy) {
708 void Offset(
const PointF& delta) { Offset(delta.x, delta.y); }
709 static RectF Offset(
const RectF& rect,
float dx,
float dy) {
711 result.Offset(dx, dy);
714 static RectF Offset(
const RectF& rect,
const PointF& delta) {
return Offset(rect, delta.x, delta.y); }
716 void MoveTo(
float x,
float y) { Offset(x - MinX(), y - MinY()); }
717 void MoveTo(
const PointF& point) { MoveTo(point.x, point.y); }
719 static RectF MoveTo(
const RectF& rect,
float x,
float y) {
724 static RectF MoveTo(
const RectF& rect,
const PointF& point) {
return MoveTo(rect, point.x, point.y); }
734 RectF operator+(
const PointF& point)
const {
return (
RectF(*
this) += point); }
735 RectF operator-(
const PointF& point)
const {
return (
RectF(*
this) -= point); }
737 void Inflate(
float dx,
float dy) {
747 void Inflate(
float delta) { Inflate(delta, delta); }
748 static RectF Inflate(
const RectF& rect,
float dx,
float dy) {
750 result.Inflate(dx, dy);
753 static RectF Inflate(
const RectF& rect,
float delta) {
return Inflate(rect, delta, delta); }
755 void Extend(
const PointF& point) {
756 float& minX = MinX();
757 float& minY = MinY();
758 float& maxX = MaxX();
759 float& maxY = MaxY();
760 minX = (std::min)(minX, point.x);
761 minY = (std::min)(minY, point.y);
762 maxX = (std::max)(maxX, point.x);
763 maxY = (std::max)(maxY, point.y);
767 result.Extend(point);
771 bool Contains(
const PointF& point)
const {
772 return point.x >= MinX() && point.x <= MaxX() && point.y >= MinY() && point.y <= MaxY();
774 bool Contains(
const RectF& rect)
const {
775 return MinX() <= rect.MinX() && MaxX() >= rect.MaxX() && MinY() <= rect.MinY() && MaxY() >= rect.MaxY();
778 bool HasIntersectionWithEdge(
const PointF& a,
const PointF& b)
const {
779 return (Contains(a) ||
781 Math::EdgeIntersectsEdge(LeftTop(), RightTop(), a, b) ||
782 Math::EdgeIntersectsEdge(RightTop(), RightBottom(), a, b) ||
783 Math::EdgeIntersectsEdge(RightBottom(), LeftBottom(), a, b) ||
784 Math::EdgeIntersectsEdge(LeftBottom(), LeftTop(), a, b));
786 bool HasIntersection(
const RectF& rect)
const {
787 return MinX() < rect.MaxX() && MinY() < rect.MaxY() && MaxX() > rect.MinX() && MaxY() > rect.MinY();
791 if (!a.HasIntersection(b))
795 i.MinX() = (std::max)(a.MinX(), b.MinX());
796 i.MinY() = (std::max)(a.MinY(), b.MinY());
797 i.MaxX() = (std::min)(a.MaxX(), b.MaxX());
798 i.MaxY() = (std::min)(a.MaxY(), b.MaxY());
804 u.MinX() = (std::min)(a.MinX(), b.MinX());
805 u.MinY() = (std::min)(a.MinY(), b.MinY());
806 u.MaxX() = (std::max)(a.MaxX(), b.MaxX());
807 u.MaxY() = (std::max)(a.MaxY(), b.MaxY());
829 topleft = that.topleft;
830 topright = that.topright;
831 botleft = that.botleft;
832 botright = that.botright;
837 topright = rightTop_;
838 botleft = leftBottom_;
839 botright = rightBottom_;
843 topleft =
PointF(rect.left, rect.top);
844 topright =
PointF(rect.right, rect.top);
845 botleft =
PointF(rect.left, rect.bottom);
846 botright =
PointF(rect.right, rect.bottom);
849 PointF LeftTop()
const {
return topleft; }
850 PointF RightTop()
const {
return topright; }
851 PointF LeftBottom()
const {
return botleft; }
852 PointF RightBottom()
const {
return botright; }
854 PointF CenterTop()
const {
return PointF::AtCenter(topleft, topright); }
855 PointF LeftCenter()
const {
return PointF::AtCenter(topleft, botleft); }
856 PointF CenterBottom()
const {
return PointF::AtCenter(botleft, botright); }
857 PointF RightCenter()
const {
return PointF::AtCenter(botright, topright); }
859 PointF GetCenter()
const {
return PointF::AtCenter(PointF::AtCenter(topleft, botright), PointF::AtCenter(topright, botleft)); }
860 RectF GetBound()
const {
861 auto [minx, maxx] = std::minmax({topleft.x, topright.x, botleft.x, botright.x});
862 auto [miny, maxy] = std::minmax({topleft.y, topright.y, botleft.y, botright.y});
863 return RectF(minx, maxy, maxx, miny);
866 float GetRotationAngle()
const {
return std::atan2(topright.y - topleft.y, topright.x - topleft.x); }
868 bool IsRectangle()
const {
869 return (std::min)(topleft.x, botright.x) == (std::min)(botleft.x, topright.x) &&
870 (std::max)(topleft.x, botright.x) == (std::max)(botleft.x, topright.x) &&
871 (std::min)(topleft.y, botright.y) == (std::min)(botleft.y, topright.y) &&
872 (std::max)(topleft.y, botright.y) == (std::max)(botleft.y, topright.y);
875 bool Equals(
const Quad& that)
const {
878 return (LeftTop() == that.LeftTop()) &&
879 (RightTop() == that.RightTop()) &&
880 (LeftBottom() == that.LeftBottom()) &&
881 (RightBottom() == that.RightBottom());
884 bool operator==(
const Quad& that)
const {
return Equals(that); }
885 bool operator!=(
const Quad& that)
const {
return !Equals(that); }
887 void Offset(
float dx,
float dy) {
898 void Offset(
const PointF& point) { Offset(point.x, point.y); }
900 static Quad Offset(
const Quad& quad,
float dx,
float dy) {
902 result.Offset(dx, dy);
906 static Quad Offset(
const Quad& quad,
const PointF& delta) {
return Offset(quad, delta.x, delta.y); }
918 Quad operator+(
const PointF& point)
const {
return (
Quad(*
this) += point); }
919 Quad operator-(
const PointF& point)
const {
return (
Quad(*
this) -= point); }
921 bool Contains(
const PointF& point)
const {
922 RectF bound = GetBound();
923 if (!bound.Contains(point))
926 if (point == topleft || point == topright || point == botleft || point == botright)
929 if (Math::FloatEq(0.f, point.DistanceToEdge(topleft, topright)) ||
930 Math::FloatEq(0.f, point.DistanceToEdge(topright, botright)) ||
931 Math::FloatEq(0.f, point.DistanceToEdge(botright, botleft)) ||
932 Math::FloatEq(0.f, point.DistanceToEdge(botleft, topleft)))
936 winding += (point.
TestSide(topleft, topright) > 0) ? 1 : -1;
937 winding += (point.
TestSide(topright, botright) > 0) ? 1 : -1;
938 winding += (point.
TestSide(botright, botleft) > 0) ? 1 : -1;
939 winding += (point.
TestSide(botleft, topleft) > 0) ? 1 : -1;
943 bool Contains(
const Quad& that)
const {
944 return Contains(that.LeftTop()) &&
945 Contains(that.RightTop()) &&
946 Contains(that.LeftBottom()) &&
947 Contains(that.RightBottom());
950 bool HasIntersectionWithEdge(
const PointF& a,
const PointF& b)
const {
951 return (Contains(a) ||
953 Math::EdgeIntersectsEdge(LeftTop(), RightTop(), a, b) ||
954 Math::EdgeIntersectsEdge(RightTop(), RightBottom(), a, b) ||
955 Math::EdgeIntersectsEdge(RightBottom(), LeftBottom(), a, b) ||
956 Math::EdgeIntersectsEdge(LeftBottom(), LeftTop(), a, b));
959 bool HasIntersection(
const RectF& rect)
const {
960 return HasIntersection(
Quad{rect});
963 bool HasIntersection(
const Quad& quad)
const {
964 return (Contains(quad.LeftTop()) ||
965 Contains(quad.RightTop()) ||
966 Contains(quad.RightBottom()) ||
967 Contains(quad.LeftBottom()) ||
968 quad.Contains(LeftTop()) ||
969 quad.Contains(RightTop()) ||
970 quad.Contains(RightBottom()) ||
971 quad.Contains(LeftBottom()) ||
972 HasIntersectionWithEdge(quad.LeftTop(), quad.RightTop()) ||
973 HasIntersectionWithEdge(quad.RightTop(), quad.RightBottom()) ||
974 HasIntersectionWithEdge(quad.RightBottom(), quad.LeftBottom()) ||
975 HasIntersectionWithEdge(quad.LeftBottom(), quad.LeftTop()));
983 : quads(std::move(Quads_)) {
986 RectF GetBound()
const {
988 if (!quads.empty()) {
989 rect = quads[0].GetBound();
990 for (
const auto& quad : quads)
991 rect = RectF::Union(rect, quad.GetBound());
997 std::vector<Quad> merged;
999 static constexpr auto areLinesCollinear = [](
const PointF& p0,
const PointF& p1,
const PointF& p2) {
1000 return Math::FloatEq(PointF::VectorCrossProduct(p1 - p0, p2 - p0), 0.f);
1004 bool initialized =
false;
1005 for (
size_t i = 0; i <= quads.size(); ++i) {
1006 if (i == quads.size()) {
1007 merged.emplace_back(quad);
1011 auto& newQuad = quads[i];
1016 if (areLinesCollinear(quad.LeftBottom(), quad.RightBottom(), newQuad.LeftBottom()) &&
1017 areLinesCollinear(quad.LeftTop(), quad.RightTop(), newQuad.LeftTop()) &&
1018 quad.HasIntersection(newQuad)) {
1019 quad.topright = newQuad.RightTop();
1020 quad.botright = newQuad.RightBottom();
1022 merged.emplace_back(quad);
1031 std::vector<Quad> quads;
1047 Matrix(
float a_,
float b_,
float c_,
float d_,
float e_,
float f_) {
1065 bool Equals(
const Matrix& that)
const {
1069 return (Math::FloatEq(a, that.a) && Math::FloatEq(b, that.b) && Math::FloatEq(c, that.c) &&
1070 Math::FloatEq(d, that.d) && Math::FloatEq(e, that.e) && Math::FloatEq(f, that.f));
1073 bool operator==(
const Matrix& that)
const {
return Equals(that); }
1074 bool operator!=(
const Matrix& that)
const {
return !Equals(that); }
1076 bool IsIdentity()
const {
return *
this ==
Matrix{}; }
1079 return Matrix(lhs.a * rhs.a + lhs.b * rhs.c,
1080 lhs.a * rhs.b + lhs.b * rhs.d,
1081 lhs.c * rhs.a + lhs.d * rhs.c,
1082 lhs.c * rhs.b + lhs.d * rhs.d,
1083 lhs.e * rhs.a + lhs.f * rhs.c + rhs.e,
1084 lhs.e * rhs.b + lhs.f * rhs.d + rhs.f);
1087 Matrix operator*(
const Matrix& that)
const {
return Concat(*
this, that); }
1088 Matrix& operator*=(
const Matrix& that) {
return (*
this = *
this * that); }
1091 return PointF(a * point.x + c * point.y + e, b * point.x + d * point.y + f);
1095 return SizeF(a * size.width + c * size.height, b * size.width + d * size.height);
1099 return MapQuad(
Quad{rect}).GetBound();
1102 Quad MapQuad(
const Quad& quad)
const {
1103 return Quad(MapPoint(quad.LeftTop()),
1104 MapPoint(quad.RightTop()),
1105 MapPoint(quad.LeftBottom()),
1106 MapPoint(quad.RightBottom()));
1109 float GetScalingX()
const {
1110 return std::sqrt(a * a + c * c);
1113 float GetScalingY()
const {
1114 return std::sqrt(b * b + d * d);
1117 bool HasRotation()
const {
1118 return !Math::FloatEq(b, 0.f) || !Math::FloatEq(c, 0.f);
1121 float GetRotation()
const {
1122 return std::atan2(b, a);
1125 float GetRotationDegrees()
const {
1126 return Math::RadianToDegree(GetRotation());
1129 double Determinant()
const {
return static_cast<double>(a) * d -
static_cast<double>(b) * c; }
1131 static Matrix Scaling(
float value) {
return Scaling(value, value); }
1132 static Matrix Scaling(
float x,
float y) {
return Matrix(x, 0.f, 0.f, y, 0.f, 0.f); }
1133 static Matrix Scaling(
const SizeF& scaling) {
return Scaling(scaling.width, scaling.height); }
1134 static Matrix Scaling(
float value,
const PointF& origin) {
return Scaling(value, value, origin); }
1135 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); }
1136 static Matrix Scaling(
const SizeF& scaling,
const PointF& origin) {
return Scaling(scaling.width, scaling.height, origin); }
1138 void Scale(
float value) { *
this = Scaling(value) * *
this; }
1139 void Scale(
float x,
float y) { *
this = Scaling(x, y) * *
this; }
1140 void Scale(
const SizeF& scaling) { *
this = Scaling(scaling) * *
this; }
1141 void Scale(
float value,
const PointF& origin) { *
this = Scaling(value, origin) * *
this; }
1142 void Scale(
float x,
float y,
const PointF& origin) { *
this = Scaling(x, y, origin) * *
this; }
1143 void Scale(
const SizeF& scaling,
const PointF& origin) { *
this = Scaling(scaling, origin) * *
this; }
1145 static Matrix Rotation(
double radians) {
1146 float sina =
static_cast<float>(std::sin(radians));
1147 float cosa =
static_cast<float>(std::cos(radians));
1148 return Matrix(cosa, sina, -sina, cosa, 0.f, 0.f);
1151 static Matrix Rotation(
double radians,
const PointF& origin) {
1152 float sina =
static_cast<float>(std::sin(radians));
1153 float cosa =
static_cast<float>(std::cos(radians));
1154 float dx = origin.x * (1.f - cosa) + origin.y * sina;
1155 float dy = origin.y * (1.f - cosa) - origin.x * sina;
1156 return Matrix(cosa, sina, -sina, cosa, dx, dy);
1159 void Rotate(
double radians) { *
this = Rotation(radians) * *
this; }
1160 void Rotate(
double radians,
const PointF& origin) { *
this = Rotation(radians, origin) * *
this; }
1162 static Matrix RotationDegree(
double degree) {
1164 return RotationDegree(degree, zero);
1167 static Matrix RotationDegree(
double degree,
const PointF& origin) {
1170 if (degree == 0.0) {
1173 }
else if (degree == 90.0) {
1176 }
else if (degree == 180.0) {
1179 }
else if (degree == 270.0) {
1183 double radians = Math::DegreeToRadian(degree);
1184 sina =
static_cast<float>(std::sin(radians));
1185 cosa =
static_cast<float>(std::cos(radians));
1188 float dx = origin.x * (1.f - cosa) + origin.y * sina;
1189 float dy = origin.y * (1.f - cosa) - origin.x * sina;
1190 return Matrix(cosa, sina, -sina, cosa, dx, dy);
1193 void RotateDegree(
double degree) { *
this = RotationDegree(degree) * *
this; }
1194 void RotateDegree(
double degree,
const PointF& origin) { *
this = RotationDegree(degree, origin) * *
this; }
1196 static Matrix ReflectionX(
float width = 0.f) {
return Matrix(-1.f, 0.f, 0.f, 1.f, 0.f, width); }
1197 static Matrix ReflectionY(
float height = 0.f) {
return Matrix(1.f, 0.f, 0.f, -1.f, 0.f, height); }
1199 void ReflectX(
float width = 0.f) { *
this = ReflectionX(width) * *
this; }
1200 void ReflectY(
float height = 0.f) { *
this = ReflectionY(height) * *
this; }
1202 bool IsReflected()
const {
1203 return a * d < b * c;
1206 static Matrix Translation(
float x,
float y) {
return Matrix(1.f, 0.f, 0.f, 1.f, x, y); }
1207 static Matrix Translation(
const PointF& v) {
return Translation(v.x, v.y); }
1209 void Translate(
float x,
float y) { *
this = Translation(x, y) * *
this; }
1210 void Translate(
const PointF& v) { *
this = Translation(v) * *
this; }
1212 bool IsInvertible()
const {
1213 double det = Determinant();
1214 static constexpr double EPS = std::numeric_limits<double>::epsilon();
1215 return (det < -EPS || det > EPS);
1219 std::optional<Matrix> Inverse()
const {
1220 double det = Determinant();
1222 static constexpr double EPS = std::numeric_limits<double>::epsilon();
1223 if (det >= -EPS && det <= EPS)
1224 return std::nullopt;
1226 double invdet = 1.0 / det;
1227 float A =
static_cast<float>(d * invdet);
1228 float B =
static_cast<float>(-b * invdet);
1229 float C =
static_cast<float>(-c * invdet);
1230 float D =
static_cast<float>(a * invdet);
1231 float E =
static_cast<float>((f * c - e * d) * invdet);
1232 float F =
static_cast<float>((e * b - f * a) * invdet);
1233 return Matrix(A, B, C, D, E, F);
1237 Matrix InverseOrIdentity()
const {
1238 return Inverse().value_or(
Matrix{});
1242 float sx = dest.GetWidth() / source.GetWidth();
1243 float sy = dest.GetHeight() / source.GetHeight();
1244 float dx = dest.MinX() - sx * source.MinX();
1245 float dy = dest.MinY() - sy * source.MinY();
1246 return Matrix(sx, 0.f, 0.f, sy, dx, dy);
1249 static Matrix RectToRectProportional(
const RectF& source,
const RectF& dest) {
1250 float sx = dest.GetWidth() / source.GetWidth();
1251 float sy = dest.GetHeight() / source.GetHeight();
1252 float scale = (std::min)(sx, sy);
1253 float dx = dest.MinX() - scale * source.MinX() + (dest.GetWidth() - scale * source.GetWidth()) / 2.0f;
1254 float dy = dest.MinY() - scale * source.MinY() + (dest.GetHeight() - scale * source.GetHeight()) / 2.0f;
1255 return Matrix(scale, 0.f, 0.f, scale, dx, dy);
1258 bool DoesPreserveRects()
const {
1259 bool A = !Math::FloatEq(a, 0.0f);
1260 bool B = !Math::FloatEq(b, 0.0f);
1261 bool C = !Math::FloatEq(c, 0.0f);
1262 bool D = !Math::FloatEq(d, 0.0f);
1263 return (A == D && B == C && A != B && C != D);