138 PointF(
float X_,
float Y_) {
149 x =
static_cast<float>(pointi.x);
150 y =
static_cast<float>(pointi.y);
153 PointI Round()
const {
return PointI(
static_cast<int>(std::round(x)),
static_cast<int>(std::round(y))); }
154 PointI Floor()
const {
return PointI(
static_cast<int>(std::floor(x)),
static_cast<int>(std::floor(y))); }
155 PointI Ceil()
const {
return PointI(
static_cast<int>(std::ceil(x)),
static_cast<int>(std::ceil(y))); }
157 bool Equals(
const PointF& that)
const {
return Math::FloatEq(x, that.x) && Math::FloatEq(y, that.y); }
159 bool operator==(
const PointF& that)
const {
return Equals(that); }
160 bool operator!=(
const PointF& that)
const {
return !Equals(that); }
164 void Offset(
float dx,
float dy) {
169 static PointF Offset(
const PointF& point,
float dx,
float dy) {
171 result.Offset(dx, dy);
175 void Mul(
float num) {
180 void Div(
float num) {
186 Offset(that.x, that.y);
191 Offset(-that.x, -that.y);
195 PointF& operator*=(
float num) {
200 PointF& operator/=(
float num) {
208 PointF operator*(
float num)
const {
return PointF(x * num, y * num); }
209 friend PointF operator*(
float num,
const PointF& point) {
return point * num; }
211 PointF operator/(
float num)
const {
return PointF(x / num, y / num); }
213 static float DistanceSquared(
const PointF& a,
const PointF& b) {
return (b - a).VectorMagnitudeSquared(); }
214 static float Distance(
const PointF& a,
const PointF& b) {
return (b - a).VectorMagnitude(); }
218 float DistanceToEdge(
const PointF& a,
const PointF& b)
const {
220 float d = DistanceSquared(a, b);
221 if (Math::FloatEq(d, 0.f))
222 return Distance(p, a);
223 float t = VectorDotProduct(p - a, b - a) / d;
224 if (Math::FloatLt(t, 0.f))
225 return Distance(p, a);
226 if (Math::FloatGt(t, 1.f))
227 return Distance(p, b);
228 return Distance(p, a + t * (b - a));
231 float VectorMagnitudeSquared()
const {
return x * x + y * y; }
232 float VectorMagnitude()
const {
return std::sqrt(VectorMagnitudeSquared()); }
242 static float VectorDotProduct(
const PointF& a,
const PointF& b) {
return a.x * b.x + a.y * b.y; }
243 static float VectorCrossProduct(
const PointF& a,
const PointF& b) {
return a.x * b.y - b.x * a.y; }
245 static float VectorAngleBetween(
const PointF& a,
const PointF& b) {
246 return std::atan2(b.y, b.x) - std::atan2(a.y, a.x);
249 PointF& VectorNormalize() {
250 float mag = VectorMagnitude();
251 if (!Math::FloatEq(mag, 0.f))
256 static PointF VectorNormalTo(
const PointF& v) {
return PointF(-v.y, v.x); }
355 RectI(
int left_,
int top_,
int right_,
int bottom_) {
366 bottom = size.height;
372 right = origin.x + size.width;
373 bottom = origin.y + size.height;
379 right = rightBottom.x;
380 bottom = rightBottom.y;
387 bottom = that.bottom;
391 PointI LeftBottom()
const {
return PointI(left, bottom); }
393 PointI RightBottom()
const {
return PointI(right, bottom); }
395 void SortForPageSpace() {
397 std::swap(left, right);
399 std::swap(top, bottom);
402 void SortForDeviceSpace() {
404 std::swap(left, right);
406 std::swap(top, bottom);
410 int& MinX() {
return (left <= right ? left : right); }
411 int& MaxX() {
return (left > right ? left : right); }
412 int& MinY() {
return (top <= bottom ? top : bottom); }
413 int& MaxY() {
return (top > bottom ? top : bottom); }
416 int MinX()
const {
return (std::min)(left, right); }
417 int MaxX()
const {
return (std::max)(left, right); }
418 int MinY()
const {
return (std::min)(top, bottom); }
419 int MaxY()
const {
return (std::max)(top, bottom); }
421 bool Equals(
const RectI& that)
const {
424 return MinX() == that.MinX() && MaxX() == that.MaxX() && MinY() == that.MinY() && MaxY() == that.MaxY();
427 bool operator==(
const RectI& that)
const {
return Equals(that); }
428 bool operator!=(
const RectI& that)
const {
return !Equals(that); }
430 int GetWidth()
const {
return std::abs(right - left); }
431 int GetHeight()
const {
return std::abs(top - bottom); }
432 SizeI GetSize()
const {
return SizeI(GetWidth(), GetHeight()); }
433 PointI GetOrigin()
const {
return PointI(MinX(), MinY()); }
434 PointI GetCenter()
const {
return PointI(MinX() + GetWidth() / 2, MinY() + GetHeight() / 2); }
436 int GetArea()
const {
return GetWidth() * GetHeight(); }
437 bool IsAreaEmpty()
const {
return left == right || top == bottom; }
439 void Offset(
int dx,
int dy) {
446 void Offset(
const PointI& delta) { Offset(delta.x, delta.y); }
448 static RectI Offset(
const RectI& rect,
int dx,
int dy) {
450 result.Offset(dx, dy);
454 static RectI Offset(
const RectI& rect,
const PointI& delta) {
return Offset(rect, delta.x, delta.y); }
466 RectI operator+(
const PointI& point)
const {
return Offset(*
this, point); }
467 RectI operator-(
const PointI& point)
const {
return Offset(*
this, -point); }
469 void Inflate(
int dx,
int dy) {
480 void Inflate(
int delta) {
return Inflate(delta, delta); }
482 static RectI Inflate(
const RectI& rect,
int dx,
int dy) {
484 result.Inflate(dx, dy);
488 static RectI Inflate(
const RectI& rect,
int delta) {
return Inflate(rect, delta, delta); }
490 void Extend(
const PointI& point) {
491 MinX() = (std::min)(MinX(), point.x);
492 MinY() = (std::min)(MinY(), point.y);
493 MaxX() = (std::max)(MaxX(), point.x);
494 MaxY() = (std::max)(MaxY(), point.y);
498 RectI result = *
this;
499 result.Extend(point);
503 bool Contains(
const PointI& point)
const {
504 return point.x >= MinX() && point.x < MaxX() && point.y >= MinY() && point.y < MaxY();
507 bool Contains(
const RectI& rect)
const {
508 return MinX() <= rect.MinX() && MaxX() >= rect.MaxX() && MinY() <= rect.MinY() && MaxY() >= rect.MaxY();
511 bool HasIntersection(
const RectI& rect)
const {
512 return MinX() < rect.MaxX() && MinY() < rect.MaxY() && MaxX() > rect.MinX() && MaxY() > rect.MinY();
516 if (!a.HasIntersection(b))
520 i.MinX() = (std::max)(a.MinX(), b.MinX());
521 i.MinY() = (std::max)(a.MinY(), b.MinY());
522 i.MaxX() = (std::min)(a.MaxX(), b.MaxX());
523 i.MaxY() = (std::min)(a.MaxY(), b.MaxY());
529 u.MinX() = (std::min)(a.MinX(), b.MinX());
530 u.MinY() = (std::min)(a.MinY(), b.MinY());
531 u.MaxX() = (std::max)(a.MaxX(), b.MaxX());
532 u.MaxY() = (std::max)(a.MaxY(), b.MaxY());
553 RectF(
float left_,
float top_,
float right_,
float bottom_) {
564 bottom = rect.bottom;
576 top = origin.y + size.height;
577 right = origin.x + size.width;
584 right = rightBottom.x;
585 bottom = rightBottom.y;
589 left =
static_cast<float>(recti.left);
590 top =
static_cast<float>(recti.top);
591 right =
static_cast<float>(recti.right);
592 bottom =
static_cast<float>(recti.bottom);
596 template<
class Po
intsIter>
597 static RectF EnclosingPoints(
const PointsIter& pointsBegin,
const PointsIter& pointsEnd) {
599 auto it = pointsBegin;
600 if (it != pointsEnd) {
601 rect =
RectF(*it, *it);
602 while (++it != pointsEnd)
608 template<
class Po
intsContainer>
609 static RectF EnclosingPoints(
const PointsContainer& points) {
610 return EnclosingPoints(std::begin(points), std::end(points));
613 static RectF EnclosingPoints(
const std::initializer_list<PointF>& points) {
614 return EnclosingPoints(std::begin(points), std::end(points));
618 RectI Round()
const {
619 return RectI(
static_cast<int>(std::round(MinX())),
620 static_cast<int>(std::round(MinY())),
621 static_cast<int>(std::round(MaxX())),
622 static_cast<int>(std::round(MaxY())));
625 RectI Floor()
const {
626 return RectI(
static_cast<int>(std::ceil(MinX())),
627 static_cast<int>(std::ceil(MinY())),
628 static_cast<int>(std::floor(MaxX())),
629 static_cast<int>(std::floor(MaxY())));
633 return RectI(
static_cast<int>(std::floor(MinX())),
634 static_cast<int>(std::floor(MinY())),
635 static_cast<int>(std::ceil(MaxX())),
636 static_cast<int>(std::ceil(MaxY())));
640 PointF LeftBottom()
const {
return PointF(left, bottom); }
642 PointF RightBottom()
const {
return PointF(right, bottom); }
644 static RectF SortForPageSpace(
const RectF& rect) {
646 result.SortForPageSpace();
649 void SortForPageSpace() {
651 std::swap(left, right);
653 std::swap(top, bottom);
656 static RectF SortForDeviceSpace(
const RectF& rect) {
658 result.SortForDeviceSpace();
661 void SortForDeviceSpace() {
663 std::swap(left, right);
665 std::swap(top, bottom);
669 float& MinX() {
return (left <= right ? left : right); }
670 float& MaxX() {
return (left > right ? left : right); }
671 float& MinY() {
return (top <= bottom ? top : bottom); }
672 float& MaxY() {
return (top > bottom ? top : bottom); }
675 float MinX()
const {
return (std::min)(left, right); }
676 float MaxX()
const {
return (std::max)(left, right); }
677 float MinY()
const {
return (std::min)(top, bottom); }
678 float MaxY()
const {
return (std::max)(top, bottom); }
680 bool Equals(
const RectF& that)
const {
683 return MinX() == that.MinX() && MaxX() == that.MaxX() && MinY() == that.MinY() && MaxY() == that.MaxY();
686 bool operator==(
const RectF& that)
const {
return Equals(that); }
687 bool operator!=(
const RectF& that)
const {
return !Equals(that); }
689 float GetWidth()
const {
return std::abs(right - left); }
690 float GetHeight()
const {
return std::abs(top - bottom); }
691 SizeF GetSize()
const {
return SizeF(GetWidth(), GetHeight()); }
692 PointF GetOrigin()
const {
return PointF(MinX(), MinY()); }
693 PointF GetCenter()
const {
return PointF(MinX() + GetWidth() / 2.f, MinY() + GetHeight() / 2.f); }
695 PointF LeftCenter()
const {
return PointF::AtCenter(LeftBottom(), LeftTop()); }
696 PointF CenterTop()
const {
return PointF::AtCenter(LeftTop(), RightTop()); }
697 PointF RightCenter()
const {
return PointF::AtCenter(RightBottom(), RightTop()); }
698 PointF CenterBottom()
const {
return PointF::AtCenter(LeftBottom(), RightBottom()); }
700 float GetArea()
const {
return GetWidth() * GetHeight(); }
701 bool IsAreaEmpty()
const {
return Math::FloatEq(left, right) || Math::FloatEq(top, bottom); }
703 void Offset(
float dx,
float dy) {
709 void Offset(
const PointF& delta) { Offset(delta.x, delta.y); }
710 static RectF Offset(
const RectF& rect,
float dx,
float dy) {
712 result.Offset(dx, dy);
715 static RectF Offset(
const RectF& rect,
const PointF& delta) {
return Offset(rect, delta.x, delta.y); }
717 void MoveTo(
float x,
float y) { Offset(x - MinX(), y - MinY()); }
718 void MoveTo(
const PointF& point) { MoveTo(point.x, point.y); }
720 static RectF MoveTo(
const RectF& rect,
float x,
float y) {
725 static RectF MoveTo(
const RectF& rect,
const PointF& point) {
return MoveTo(rect, point.x, point.y); }
735 RectF operator+(
const PointF& point)
const {
return (
RectF(*
this) += point); }
736 RectF operator-(
const PointF& point)
const {
return (
RectF(*
this) -= point); }
738 void Inflate(
float dx,
float dy) {
748 void Inflate(
float delta) { Inflate(delta, delta); }
749 static RectF Inflate(
const RectF& rect,
float dx,
float dy) {
751 result.Inflate(dx, dy);
754 static RectF Inflate(
const RectF& rect,
float delta) {
return Inflate(rect, delta, delta); }
756 void Extend(
const PointF& point) {
757 float& minX = MinX();
758 float& minY = MinY();
759 float& maxX = MaxX();
760 float& maxY = MaxY();
761 minX = (std::min)(minX, point.x);
762 minY = (std::min)(minY, point.y);
763 maxX = (std::max)(maxX, point.x);
764 maxY = (std::max)(maxY, point.y);
768 result.Extend(point);
772 bool Contains(
const PointF& point)
const {
773 return point.x >= MinX() && point.x <= MaxX() && point.y >= MinY() && point.y <= MaxY();
775 bool Contains(
const RectF& rect)
const {
776 return MinX() <= rect.MinX() && MaxX() >= rect.MaxX() && MinY() <= rect.MinY() && MaxY() >= rect.MaxY();
779 bool HasIntersectionWithEdge(
const PointF& a,
const PointF& b)
const {
780 return (Contains(a) ||
782 Math::EdgeIntersectsEdge(LeftTop(), RightTop(), a, b) ||
783 Math::EdgeIntersectsEdge(RightTop(), RightBottom(), a, b) ||
784 Math::EdgeIntersectsEdge(RightBottom(), LeftBottom(), a, b) ||
785 Math::EdgeIntersectsEdge(LeftBottom(), LeftTop(), a, b));
787 bool HasIntersection(
const RectF& rect)
const {
788 return MinX() < rect.MaxX() && MinY() < rect.MaxY() && MaxX() > rect.MinX() && MaxY() > rect.MinY();
792 if (!a.HasIntersection(b))
796 i.MinX() = (std::max)(a.MinX(), b.MinX());
797 i.MinY() = (std::max)(a.MinY(), b.MinY());
798 i.MaxX() = (std::min)(a.MaxX(), b.MaxX());
799 i.MaxY() = (std::min)(a.MaxY(), b.MaxY());
805 u.MinX() = (std::min)(a.MinX(), b.MinX());
806 u.MinY() = (std::min)(a.MinY(), b.MinY());
807 u.MaxX() = (std::max)(a.MaxX(), b.MaxX());
808 u.MaxY() = (std::max)(a.MaxY(), b.MaxY());
820 static std::list<RectF> Subtract(
const RectF& minuend,
const RectF& subtrahend) {
821 if (!minuend.HasIntersection(subtrahend)) {
824 RectF intersection = RectF::Intersect(minuend, subtrahend);
825 std::list<RectF> result;
826 if (minuend.MinY() < intersection.MinY())
827 result.emplace_back(
RectF(minuend.MinX(), minuend.MinY(), minuend.MaxX(), intersection.MinY()));
828 if (minuend.MaxY() > intersection.MaxY())
829 result.emplace_back(
RectF(minuend.MinX(), intersection.MaxY(), minuend.MaxX(), minuend.MaxY()));
830 if (minuend.MinX() < intersection.MinX())
831 result.emplace_back(
RectF(minuend.MinX(), intersection.MinY(), intersection.MinX(), intersection.MaxY()));
832 if (minuend.MaxX() > intersection.MaxX())
833 result.emplace_back(
RectF(intersection.MaxX(), intersection.MinY(), minuend.MaxX(), intersection.MaxY()));
847 topleft = that.topleft;
848 topright = that.topright;
849 botleft = that.botleft;
850 botright = that.botright;
855 topright = rightTop_;
856 botleft = leftBottom_;
857 botright = rightBottom_;
861 topleft =
PointF(rect.left, rect.top);
862 topright =
PointF(rect.right, rect.top);
863 botleft =
PointF(rect.left, rect.bottom);
864 botright =
PointF(rect.right, rect.bottom);
867 PointF LeftTop()
const {
return topleft; }
868 PointF RightTop()
const {
return topright; }
869 PointF LeftBottom()
const {
return botleft; }
870 PointF RightBottom()
const {
return botright; }
872 PointF CenterTop()
const {
return PointF::AtCenter(topleft, topright); }
873 PointF LeftCenter()
const {
return PointF::AtCenter(topleft, botleft); }
874 PointF CenterBottom()
const {
return PointF::AtCenter(botleft, botright); }
875 PointF RightCenter()
const {
return PointF::AtCenter(botright, topright); }
877 PointF GetCenter()
const {
return PointF::AtCenter(PointF::AtCenter(topleft, botright), PointF::AtCenter(topright, botleft)); }
878 RectF GetBound()
const {
879 auto [minx, maxx] = std::minmax({topleft.x, topright.x, botleft.x, botright.x});
880 auto [miny, maxy] = std::minmax({topleft.y, topright.y, botleft.y, botright.y});
881 return RectF(minx, maxy, maxx, miny);
884 float GetRotationAngle()
const {
return std::atan2(topright.y - topleft.y, topright.x - topleft.x); }
886 bool IsRectangle()
const {
887 return (std::min)(topleft.x, botright.x) == (std::min)(botleft.x, topright.x) &&
888 (std::max)(topleft.x, botright.x) == (std::max)(botleft.x, topright.x) &&
889 (std::min)(topleft.y, botright.y) == (std::min)(botleft.y, topright.y) &&
890 (std::max)(topleft.y, botright.y) == (std::max)(botleft.y, topright.y);
893 bool Equals(
const Quad& that)
const {
896 return (LeftTop() == that.LeftTop()) &&
897 (RightTop() == that.RightTop()) &&
898 (LeftBottom() == that.LeftBottom()) &&
899 (RightBottom() == that.RightBottom());
902 bool operator==(
const Quad& that)
const {
return Equals(that); }
903 bool operator!=(
const Quad& that)
const {
return !Equals(that); }
905 void Offset(
float dx,
float dy) {
916 void Offset(
const PointF& point) { Offset(point.x, point.y); }
918 static Quad Offset(
const Quad& quad,
float dx,
float dy) {
920 result.Offset(dx, dy);
924 static Quad Offset(
const Quad& quad,
const PointF& delta) {
return Offset(quad, delta.x, delta.y); }
936 Quad operator+(
const PointF& point)
const {
return (
Quad(*
this) += point); }
937 Quad operator-(
const PointF& point)
const {
return (
Quad(*
this) -= point); }
939 bool Contains(
const PointF& point)
const {
940 RectF bound = GetBound();
941 if (!bound.Contains(point))
944 if (point == topleft || point == topright || point == botleft || point == botright)
947 if (Math::FloatEq(0.f, point.DistanceToEdge(topleft, topright)) ||
948 Math::FloatEq(0.f, point.DistanceToEdge(topright, botright)) ||
949 Math::FloatEq(0.f, point.DistanceToEdge(botright, botleft)) ||
950 Math::FloatEq(0.f, point.DistanceToEdge(botleft, topleft)))
954 winding += (point.
TestSide(topleft, topright) > 0) ? 1 : -1;
955 winding += (point.
TestSide(topright, botright) > 0) ? 1 : -1;
956 winding += (point.
TestSide(botright, botleft) > 0) ? 1 : -1;
957 winding += (point.
TestSide(botleft, topleft) > 0) ? 1 : -1;
961 bool Contains(
const Quad& that)
const {
962 return Contains(that.LeftTop()) &&
963 Contains(that.RightTop()) &&
964 Contains(that.LeftBottom()) &&
965 Contains(that.RightBottom());
968 bool HasIntersectionWithEdge(
const PointF& a,
const PointF& b)
const {
969 return (Contains(a) ||
971 Math::EdgeIntersectsEdge(LeftTop(), RightTop(), a, b) ||
972 Math::EdgeIntersectsEdge(RightTop(), RightBottom(), a, b) ||
973 Math::EdgeIntersectsEdge(RightBottom(), LeftBottom(), a, b) ||
974 Math::EdgeIntersectsEdge(LeftBottom(), LeftTop(), a, b));
977 bool HasIntersection(
const RectF& rect)
const {
978 return HasIntersection(
Quad{rect});
981 bool HasIntersection(
const Quad& quad)
const {
982 return (Contains(quad.LeftTop()) ||
983 Contains(quad.RightTop()) ||
984 Contains(quad.RightBottom()) ||
985 Contains(quad.LeftBottom()) ||
986 quad.Contains(LeftTop()) ||
987 quad.Contains(RightTop()) ||
988 quad.Contains(RightBottom()) ||
989 quad.Contains(LeftBottom()) ||
990 HasIntersectionWithEdge(quad.LeftTop(), quad.RightTop()) ||
991 HasIntersectionWithEdge(quad.RightTop(), quad.RightBottom()) ||
992 HasIntersectionWithEdge(quad.RightBottom(), quad.LeftBottom()) ||
993 HasIntersectionWithEdge(quad.LeftBottom(), quad.LeftTop()));
999 : quads(std::move(Quads_)) {
1002 RectF GetBound()
const {
1004 if (!quads.empty()) {
1005 rect = quads[0].GetBound();
1006 for (
const auto& quad : quads)
1007 rect = RectF::Union(rect, quad.GetBound());
1013 std::vector<Quad> merged;
1015 static constexpr auto areLinesCollinear = [](
const PointF& p0,
const PointF& p1,
const PointF& p2) {
1016 return Math::FloatEq(PointF::VectorCrossProduct(p1 - p0, p2 - p0), 0.f);
1020 bool initialized =
false;
1021 for (
size_t i = 0; i <= quads.size(); ++i) {
1022 if (i == quads.size()) {
1023 merged.emplace_back(quad);
1027 auto& newQuad = quads[i];
1032 if (areLinesCollinear(quad.LeftBottom(), quad.RightBottom(), newQuad.LeftBottom()) &&
1033 areLinesCollinear(quad.LeftTop(), quad.RightTop(), newQuad.LeftTop()) &&
1034 quad.HasIntersection(newQuad)) {
1035 quad.topright = newQuad.RightTop();
1036 quad.botright = newQuad.RightBottom();
1038 merged.emplace_back(quad);
1047 std::vector<Quad> quads;
1063 Matrix(
float a_,
float b_,
float c_,
float d_,
float e_,
float f_) {
1081 bool Equals(
const Matrix& that)
const {
1085 return (Math::FloatEq(a, that.a) && Math::FloatEq(b, that.b) && Math::FloatEq(c, that.c) &&
1086 Math::FloatEq(d, that.d) && Math::FloatEq(e, that.e) && Math::FloatEq(f, that.f));
1089 bool operator==(
const Matrix& that)
const {
return Equals(that); }
1090 bool operator!=(
const Matrix& that)
const {
return !Equals(that); }
1092 bool IsIdentity()
const {
return *
this ==
Matrix{}; }
1095 return Matrix(lhs.a * rhs.a + lhs.b * rhs.c,
1096 lhs.a * rhs.b + lhs.b * rhs.d,
1097 lhs.c * rhs.a + lhs.d * rhs.c,
1098 lhs.c * rhs.b + lhs.d * rhs.d,
1099 lhs.e * rhs.a + lhs.f * rhs.c + rhs.e,
1100 lhs.e * rhs.b + lhs.f * rhs.d + rhs.f);
1103 Matrix operator*(
const Matrix& that)
const {
return Concat(*
this, that); }
1104 Matrix& operator*=(
const Matrix& that) {
return (*
this = *
this * that); }
1107 return PointF(a * point.x + c * point.y + e, b * point.x + d * point.y + f);
1111 return SizeF(a * size.width + c * size.height, b * size.width + d * size.height);
1115 return MapQuad(
Quad{rect}).GetBound();
1118 Quad MapQuad(
const Quad& quad)
const {
1119 return Quad(MapPoint(quad.LeftTop()),
1120 MapPoint(quad.RightTop()),
1121 MapPoint(quad.LeftBottom()),
1122 MapPoint(quad.RightBottom()));
1125 float GetScalingX()
const {
1126 return std::sqrt(a * a + c * c);
1129 float GetScalingY()
const {
1130 return std::sqrt(b * b + d * d);
1133 bool HasRotation()
const {
1134 return !Math::FloatEq(b, 0.f) || !Math::FloatEq(c, 0.f);
1137 float GetRotation()
const {
1138 return std::atan2(b, a);
1141 float GetRotationDegrees()
const {
1142 return Math::RadianToDegree(GetRotation());
1145 double Determinant()
const {
return static_cast<double>(a) * d -
static_cast<double>(b) * c; }
1147 static Matrix Scaling(
float value) {
return Scaling(value, value); }
1148 static Matrix Scaling(
float x,
float y) {
return Matrix(x, 0.f, 0.f, y, 0.f, 0.f); }
1149 static Matrix Scaling(
const SizeF& scaling) {
return Scaling(scaling.width, scaling.height); }
1150 static Matrix Scaling(
float value,
const PointF& origin) {
return Scaling(value, value, origin); }
1151 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); }
1152 static Matrix Scaling(
const SizeF& scaling,
const PointF& origin) {
return Scaling(scaling.width, scaling.height, origin); }
1154 void Scale(
float value) { *
this = Scaling(value) * *
this; }
1155 void Scale(
float x,
float y) { *
this = Scaling(x, y) * *
this; }
1156 void Scale(
const SizeF& scaling) { *
this = Scaling(scaling) * *
this; }
1157 void Scale(
float value,
const PointF& origin) { *
this = Scaling(value, origin) * *
this; }
1158 void Scale(
float x,
float y,
const PointF& origin) { *
this = Scaling(x, y, origin) * *
this; }
1159 void Scale(
const SizeF& scaling,
const PointF& origin) { *
this = Scaling(scaling, origin) * *
this; }
1161 static Matrix Rotation(
double radians) {
1162 float sina =
static_cast<float>(std::sin(radians));
1163 float cosa =
static_cast<float>(std::cos(radians));
1164 return Matrix(cosa, sina, -sina, cosa, 0.f, 0.f);
1167 static Matrix Rotation(
double radians,
const PointF& origin) {
1168 float sina =
static_cast<float>(std::sin(radians));
1169 float cosa =
static_cast<float>(std::cos(radians));
1170 float dx = origin.x * (1.f - cosa) + origin.y * sina;
1171 float dy = origin.y * (1.f - cosa) - origin.x * sina;
1172 return Matrix(cosa, sina, -sina, cosa, dx, dy);
1175 void Rotate(
double radians) { *
this = Rotation(radians) * *
this; }
1176 void Rotate(
double radians,
const PointF& origin) { *
this = Rotation(radians, origin) * *
this; }
1178 static Matrix RotationDegree(
double degree) {
1180 return RotationDegree(degree, zero);
1183 static Matrix RotationDegree(
double degree,
const PointF& origin) {
1186 if (degree == 0.0) {
1189 }
else if (degree == 90.0) {
1192 }
else if (degree == 180.0) {
1195 }
else if (degree == 270.0) {
1199 double radians = Math::DegreeToRadian(degree);
1200 sina =
static_cast<float>(std::sin(radians));
1201 cosa =
static_cast<float>(std::cos(radians));
1204 float dx = origin.x * (1.f - cosa) + origin.y * sina;
1205 float dy = origin.y * (1.f - cosa) - origin.x * sina;
1206 return Matrix(cosa, sina, -sina, cosa, dx, dy);
1209 void RotateDegree(
double degree) { *
this = RotationDegree(degree) * *
this; }
1210 void RotateDegree(
double degree,
const PointF& origin) { *
this = RotationDegree(degree, origin) * *
this; }
1212 static Matrix ReflectionX(
float width = 0.f) {
return Matrix(-1.f, 0.f, 0.f, 1.f, 0.f, width); }
1213 static Matrix ReflectionY(
float height = 0.f) {
return Matrix(1.f, 0.f, 0.f, -1.f, 0.f, height); }
1215 void ReflectX(
float width = 0.f) { *
this = ReflectionX(width) * *
this; }
1216 void ReflectY(
float height = 0.f) { *
this = ReflectionY(height) * *
this; }
1218 bool IsReflected()
const {
1219 return a * d < b * c;
1222 static Matrix Translation(
float x,
float y) {
return Matrix(1.f, 0.f, 0.f, 1.f, x, y); }
1223 static Matrix Translation(
const PointF& v) {
return Translation(v.x, v.y); }
1225 void Translate(
float x,
float y) { *
this = Translation(x, y) * *
this; }
1226 void Translate(
const PointF& v) { *
this = Translation(v) * *
this; }
1228 bool IsInvertible()
const {
1229 double det = Determinant();
1230 static constexpr double EPS = std::numeric_limits<double>::epsilon();
1231 return (det < -EPS || det > EPS);
1235 std::optional<Matrix> Inverse()
const {
1236 double det = Determinant();
1238 static constexpr double EPS = std::numeric_limits<double>::epsilon();
1239 if (det >= -EPS && det <= EPS)
1240 return std::nullopt;
1242 double invdet = 1.0 / det;
1243 float A =
static_cast<float>(d * invdet);
1244 float B =
static_cast<float>(-b * invdet);
1245 float C =
static_cast<float>(-c * invdet);
1246 float D =
static_cast<float>(a * invdet);
1247 float E =
static_cast<float>((f * c - e * d) * invdet);
1248 float F =
static_cast<float>((e * b - f * a) * invdet);
1249 return Matrix(A, B, C, D, E, F);
1253 Matrix InverseOrIdentity()
const {
1254 return Inverse().value_or(
Matrix{});
1258 float sx = dest.GetWidth() / source.GetWidth();
1259 float sy = dest.GetHeight() / source.GetHeight();
1260 float dx = dest.MinX() - sx * source.MinX();
1261 float dy = dest.MinY() - sy * source.MinY();
1262 return Matrix(sx, 0.f, 0.f, sy, dx, dy);
1265 static Matrix RectToRectProportional(
const RectF& source,
const RectF& dest) {
1266 float sx = dest.GetWidth() / source.GetWidth();
1267 float sy = dest.GetHeight() / source.GetHeight();
1268 float scale = (std::min)(sx, sy);
1269 float dx = dest.MinX() - scale * source.MinX() + (dest.GetWidth() - scale * source.GetWidth()) / 2.0f;
1270 float dy = dest.MinY() - scale * source.MinY() + (dest.GetHeight() - scale * source.GetHeight()) / 2.0f;
1271 return Matrix(scale, 0.f, 0.f, scale, dx, dy);
1274 bool DoesPreserveRects()
const {
1275 bool A = !Math::FloatEq(a, 0.0f);
1276 bool B = !Math::FloatEq(b, 0.0f);
1277 bool C = !Math::FloatEq(c, 0.0f);
1278 bool D = !Math::FloatEq(d, 0.0f);
1279 return (A == D && B == C && A != B && C != D);