551 RectI(
int left_,
int top_,
int right_,
int bottom_) {
562 bottom = size.height;
568 right = origin.x + size.width;
569 bottom = origin.y + size.height;
575 right = rightBottom.x;
576 bottom = rightBottom.y;
583 bottom = that.bottom;
586 void SortForPageSpace() {
588 std::swap(left, right);
590 std::swap(top, bottom);
592 static RectI MakeSortedForPageSpace(
const RectI& rect) {
594 result.SortForPageSpace();
598 void SortForDeviceSpace() {
600 std::swap(left, right);
602 std::swap(top, bottom);
604 static RectI MakeSortedForDeviceSpace(
const RectI& rect) {
606 result.SortForDeviceSpace();
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); }
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); }
622 bool Equals(
const RectI& that)
const {
625 return MinX() == that.MinX() &&
626 MaxX() == that.MaxX() &&
627 MinY() == that.MinY() &&
628 MaxY() == that.MaxY();
631 bool operator==(
const RectI& that)
const {
return Equals(that); }
632 bool operator!=(
const RectI& that)
const {
return !Equals(that); }
634 int GetWidth()
const {
return std::abs(right - left); }
635 int GetHeight()
const {
return std::abs(top - bottom); }
637 int GetArea()
const {
return GetWidth() * GetHeight(); }
638 bool IsAreaEmpty()
const {
return left == right || top == bottom; }
640 PointI GetOrigin()
const {
return PointI(MinX(), MinY()); }
641 SizeI GetSize()
const {
return SizeI(GetWidth(), GetHeight()); }
644 PointI GetLeftCenter()
const {
return PointI(left, MinY() + GetHeight() / 2); }
645 PointI GetLeftBottom()
const {
return PointI(left, bottom); }
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); }
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); }
655 void Offset(
int dx,
int dy) {
661 void Offset(
const PointI& delta) { Offset(delta.x, delta.y); }
662 static RectI MakeOffset(
const RectI& rect,
int dx,
int dy) {
664 result.Offset(dx, dy);
667 static RectI MakeOffset(
const RectI& rect,
const PointI& delta) {
return MakeOffset(rect, delta.x, delta.y); }
677 RectI operator+(
const PointI& point)
const {
return MakeOffset(*
this, point); }
678 RectI operator-(
const PointI& point)
const {
return MakeOffset(*
this, -point); }
680 void Inflate(
int dx,
int dy) {
690 void Inflate(
int delta) {
return Inflate(delta, delta); }
691 static RectI MakeInflated(
const RectI& rect,
int dx,
int dy) {
693 result.Inflate(dx, dy);
696 static RectI MakeInflated(
const RectI& rect,
int delta) {
return MakeInflated(rect, delta, delta); }
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);
706 result.Extend(point);
710 bool Contains(
const PointI& point)
const {
711 return point.x >= MinX() && point.x < MaxX() && point.y >= MinY() && point.y < MaxY();
713 bool Contains(
const RectI& rect)
const {
714 return MinX() <= rect.MinX() && MaxX() >= rect.MaxX() && MinY() <= rect.MinY() && MaxY() >= rect.MaxY();
717 bool HasIntersection(
const RectI& rect)
const {
718 return MinX() < rect.MaxX() && MinY() < rect.MaxY() && MaxX() > rect.MinX() && MaxY() > rect.MinY();
721 if (!a.HasIntersection(b))
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());
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());
745 return MakeUnion(a, b);
759 RectF(
float left_,
float top_,
float right_,
float bottom_) {
770 bottom = rect.bottom;
782 top = origin.y + size.height;
783 right = origin.x + size.width;
790 right = rightBottom.x;
791 bottom = rightBottom.y;
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);
802 template<
class Po
intsIter>
803 static RectF EnclosingPoints(
const PointsIter& pointsBegin,
const PointsIter& pointsEnd) {
805 auto it = pointsBegin;
806 if (it != pointsEnd) {
807 rect =
RectF(*it, *it);
808 while (++it != pointsEnd)
814 template<
class Po
intsContainer>
815 static RectF EnclosingPoints(
const PointsContainer& points) {
816 return EnclosingPoints(std::begin(points), std::end(points));
819 static RectF EnclosingPoints(
const std::initializer_list<PointF>& points) {
820 return EnclosingPoints(std::begin(points), std::end(points));
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())));
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())));
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())));
843 void SortForPageSpace() {
845 std::swap(left, right);
847 std::swap(top, bottom);
849 static RectF MakeSortedForPageSpace(
const RectF& rect) {
851 result.SortForPageSpace();
855 void SortForDeviceSpace() {
857 std::swap(left, right);
859 std::swap(top, bottom);
861 static RectF MakeSortedForDeviceSpace(
const RectF& rect) {
863 result.SortForDeviceSpace();
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); }
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); }
879 bool Equals(
const RectF& that,
const float eps = Math::EPSILON)
const {
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);
888 bool operator==(
const RectF& that)
const {
return Equals(that); }
889 bool operator!=(
const RectF& that)
const {
return !Equals(that); }
891 float GetWidth()
const {
return std::abs(right - left); }
892 float GetHeight()
const {
return std::abs(top - bottom); }
894 float GetArea()
const {
return GetWidth() * GetHeight(); }
895 bool IsAreaEmpty()
const {
return Math::FloatEq(left, right) || Math::FloatEq(top, bottom); }
897 PointF GetOrigin()
const {
return PointF(MinX(), MinY()); }
898 SizeF GetSize()
const {
return SizeF(GetWidth(), GetHeight()); }
902 PointF GetLeftBottom()
const {
return PointF(left, bottom); }
905 PointF GetCenter()
const {
return PointF(MinX() + GetWidth() / 2.f, MinY() + GetHeight() / 2.f); }
908 PointF GetRightTop()
const {
return PointF(right, top); }
910 PointF GetRightBottom()
const {
return PointF(right, bottom); }
912 void Offset(
float dx,
float dy) {
918 void Offset(
const PointF& delta) { Offset(delta.x, delta.y); }
919 static RectF MakeOffset(
const RectF& rect,
float dx,
float dy) {
921 result.Offset(dx, dy);
924 static RectF MakeOffset(
const RectF& rect,
const PointF& delta) {
return MakeOffset(rect, delta.x, delta.y); }
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) {
933 static RectF MakeMoved(
const RectF& rect,
const PointF& point) {
return MakeMoved(rect, point.x, point.y); }
943 RectF operator+(
const PointF& point)
const {
return (
RectF(*
this) += point); }
944 RectF operator-(
const PointF& point)
const {
return (
RectF(*
this) -= point); }
946 void Inflate(
float dx,
float dy) {
956 void Inflate(
float delta) { Inflate(delta, delta); }
957 static RectF MakeInflated(
const RectF& rect,
float dx,
float dy) {
959 result.Inflate(dx, dy);
962 static RectF MakeInflated(
const RectF& rect,
float delta) {
return MakeInflated(rect, delta, delta); }
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);
976 result.Extend(point);
980 bool Contains(
const PointF& point)
const {
981 return point.x >= MinX() && point.x <= MaxX() && point.y >= MinY() && point.y <= MaxY();
983 bool Contains(
const RectF& rect)
const {
984 return MinX() <= rect.MinX() && MaxX() >= rect.MaxX() && MinY() <= rect.MinY() && MaxY() >= rect.MaxY();
987 bool HasIntersectionWithEdge(
const PointF& a,
const PointF& b)
const {
988 return (Contains(a) ||
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));
995 bool HasIntersection(
const RectF& rect)
const {
996 return MinX() < rect.MaxX() && MinY() < rect.MaxY() && MaxX() > rect.MinX() && MaxY() > rect.MinY();
1000 if (!a.HasIntersection(b))
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());
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());
1021 if (a.IsAreaEmpty())
1023 if (b.IsAreaEmpty())
1025 return MakeUnion(a, b);
1040 topleft = that.topleft;
1041 topright = that.topright;
1042 botleft = that.botleft;
1043 botright = that.botright;
1048 topright = rightTop_;
1049 botleft = leftBottom_;
1050 botright = rightBottom_;
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);
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);
1067 PointF GetLeftTop()
const {
return topleft; }
1069 PointF GetLeftBottom()
const {
return botleft; }
1075 PointF GetRightTop()
const {
return topright; }
1076 PointF GetRightBottom()
const {
return botright; }
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);
1085 float GetRotationAngle()
const {
return std::atan2(topright.y - topleft.y, topright.x - topleft.x); }
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);
1094 bool Equals(
const Quad& that,
const float eps = Math::EPSILON)
const {
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);
1103 bool operator==(
const Quad& that)
const {
return Equals(that); }
1104 bool operator!=(
const Quad& that)
const {
return !Equals(that); }
1106 void Offset(
float dx,
float dy) {
1116 void Offset(
const PointF& point) { Offset(point.x, point.y); }
1118 static Quad MakeOffset(
const Quad& quad,
float dx,
float dy) {
1120 result.Offset(dx, dy);
1123 static Quad MakeOffset(
const Quad& quad,
const PointF& delta) {
return MakeOffset(quad, delta.x, delta.y); }
1134 Quad operator+(
const PointF& point)
const {
return (
Quad(*
this) += point); }
1135 Quad operator-(
const PointF& point)
const {
return (
Quad(*
this) -= point); }
1137 bool Contains(
const PointF& point)
const {
1138 RectF bound = GetBound();
1139 if (!bound.Contains(point))
1142 if (point == topleft || point == topright || point == botleft || point == botright)
1145 if (Math::FloatEq(0.f, point.
DistanceToEdge(topleft, topright)) ||
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;
1159 bool Contains(
const Quad& that)
const {
1160 return Contains(that.GetLeftTop()) &&
1161 Contains(that.GetRightTop()) &&
1162 Contains(that.GetLeftBottom()) &&
1163 Contains(that.GetRightBottom());
1166 bool HasIntersectionWithEdge(
const PointF& a,
const PointF& b)
const {
1167 return (Contains(a) ||
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));
1175 bool HasIntersection(
const RectF& rect)
const {
1176 return HasIntersection(
Quad{rect});
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()));
1198 explicit QuadPoints(std::vector<Quad> Quads_)
1199 : quads(std::move(Quads_)) {
1202 RectF GetBound()
const {
1204 if (!quads.empty()) {
1205 rect = quads[0].GetBound();
1206 for (
const auto& quad : quads)
1207 rect = RectF::MakeUnion(rect, quad.GetBound());
1212 bool Equals(
const QuadPoints& that,
const float eps = Math::EPSILON)
const {
1215 if (quads.size() != that.quads.size())
1217 for (
size_t i = 0; i < quads.size(); ++i) {
1218 if (!(quads[i].Equals(that.quads[i], eps)))
1224 bool operator==(
const QuadPoints& that)
const {
return Equals(that); }
1225 bool operator!=(
const QuadPoints& that)
const {
return !Equals(that); }
1231 std::vector<Quad> merged;
1233 static constexpr auto areLinesCollinear = [](
const PointF& p0,
const PointF& p1,
const PointF& p2) {
1238 bool initialized =
false;
1239 for (
size_t i = 0; i <= quads.size(); ++i) {
1240 if (i == quads.size()) {
1241 merged.emplace_back(quad);
1245 auto& newQuad = quads[i];
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();
1256 merged.emplace_back(quad);
1265 std::vector<Quad> quads;
1283 Matrix(
float a_,
float b_,
float c_,
float d_,
float e_,
float f_) {
1301 bool Equals(
const Matrix& that,
const float eps = Math::EPSILON)
const {
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);
1312 bool operator==(
const Matrix& that)
const {
return Equals(that); }
1313 bool operator!=(
const Matrix& that)
const {
return !Equals(that); }
1315 bool IsIdentity()
const {
return *
this ==
Matrix{}; }
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);
1326 Matrix operator*(
const Matrix& that)
const {
return Concat(*
this, that); }
1327 Matrix& operator*=(
const Matrix& that) {
return (*
this = *
this * that); }
1330 return PointF(a * point.x + c * point.y + e, b * point.x + d * point.y + f);
1334 return SizeF(a * size.width + c * size.height, b * size.width + d * size.height);
1338 return MapQuad(
Quad{rect}).GetBound();
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()));
1348 float GetScalingX()
const {
1349 return std::sqrt(a * a + c * c);
1352 float GetScalingY()
const {
1353 return std::sqrt(b * b + d * d);
1356 bool HasRotation()
const {
1357 return !Math::FloatEq(b, 0.f) || !Math::FloatEq(c, 0.f);
1360 float GetRotation()
const {
1361 return std::atan2(b, a);
1364 float GetRotationDegrees()
const {
1365 return Math::RadianToDegree(GetRotation());
1368 double Determinant()
const {
return static_cast<double>(a) * d -
static_cast<double>(b) * c; }
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); }
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; }
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);
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);
1397 void Rotate(
double radians) { *
this = Rotation(radians) * *
this; }
1398 void Rotate(
double radians,
const PointF& origin) { *
this = Rotation(radians, origin) * *
this; }
1400 static Matrix RotationDegree(
double degree) {
1402 return RotationDegree(degree, zero);
1404 static Matrix RotationDegree(
double degree,
const PointF& origin) {
1407 if (degree == 0.0) {
1410 }
else if (degree == 90.0) {
1413 }
else if (degree == 180.0) {
1416 }
else if (degree == 270.0) {
1420 double radians = Math::DegreeToRadian(degree);
1421 sina =
static_cast<float>(std::sin(radians));
1422 cosa =
static_cast<float>(std::cos(radians));
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);
1430 void RotateDegree(
double degree) { *
this = RotationDegree(degree) * *
this; }
1431 void RotateDegree(
double degree,
const PointF& origin) { *
this = RotationDegree(degree, origin) * *
this; }
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); }
1436 void ReflectX(
float width = 0.f) { *
this = ReflectionX(width) * *
this; }
1437 void ReflectY(
float height = 0.f) { *
this = ReflectionY(height) * *
this; }
1439 bool IsReflected()
const {
1440 return a * d < b * c;
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); }
1446 void Translate(
float x,
float y) { *
this = Translation(x, y) * *
this; }
1447 void Translate(
const PointF& v) { *
this = Translation(v) * *
this; }
1449 bool IsInvertible()
const {
1450 double det = Determinant();
1451 static constexpr double EPS = std::numeric_limits<double>::epsilon();
1452 return (det < -EPS || det > EPS);
1456 std::optional<Matrix> Inverse()
const {
1457 double det = Determinant();
1459 static constexpr double EPS = std::numeric_limits<double>::epsilon();
1460 if (det >= -EPS && det <= EPS)
1461 return std::nullopt;
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);
1474 Matrix InverseOrIdentity()
const {
1475 return Inverse().value_or(
Matrix{});
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);
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);
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);