5 #include "gtest/gtest.h"
7 #include "flutter/testing/testing.h"
17 TEST(PathTest, CubicPathComponentPolylineDoesNotIncludePointOne) {
20 component.AppendPolylinePoints(1.0f,
polyline);
27 TEST(PathTest, EmptyPathWithContour) {
31 EXPECT_TRUE(path.IsEmpty());
34 TEST(PathTest, PathCreatePolyLineDoesNotDuplicatePoints) {
44 ASSERT_EQ(
polyline.contours.size(), 2u);
45 ASSERT_EQ(
polyline.points->size(), 5u);
46 ASSERT_EQ(
polyline.GetPoint(0).x, 10);
47 ASSERT_EQ(
polyline.GetPoint(1).x, 20);
48 ASSERT_EQ(
polyline.GetPoint(2).x, 30);
49 ASSERT_EQ(
polyline.GetPoint(3).x, 40);
50 ASSERT_EQ(
polyline.GetPoint(4).x, 50);
53 TEST(PathTest, PathSingleContour) {
103 .
AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
126 TEST(PathTest, PathSingleContourDoubleShapes) {
131 .AddCircle({100, 100}, 50)
210 .
AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
211 .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
221 .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
231 .AddConicCurve({100, 100}, {100, 50}, {200, 100}, 0.75f)
238 TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) {
242 EXPECT_NE(path.
begin(), path.
end());
245 ASSERT_NE(contour,
nullptr);
247 EXPECT_TRUE(contour->IsClosed());
253 EXPECT_NE(path.
begin(), path.
end());
256 ASSERT_NE(contour,
nullptr);
258 EXPECT_TRUE(contour->IsClosed());
264 EXPECT_NE(path.
begin(), path.
end());
267 ASSERT_NE(contour,
nullptr);
269 EXPECT_TRUE(contour->IsClosed());
277 EXPECT_NE(path.
begin(), path.
end());
280 ASSERT_NE(contour,
nullptr);
282 EXPECT_TRUE(contour->IsClosed());
290 EXPECT_NE(path.
begin(), path.
end());
293 ASSERT_NE(contour,
nullptr);
295 EXPECT_TRUE(contour->IsClosed());
303 EXPECT_NE(path.
begin(), path.
end());
306 ASSERT_NE(contour,
nullptr);
308 EXPECT_TRUE(contour->IsClosed());
316 EXPECT_NE(path.
begin(), path.
end());
319 ASSERT_NE(contour,
nullptr);
321 EXPECT_TRUE(contour->IsClosed());
328 EXPECT_NE(path.
begin(), path.
end());
331 ASSERT_NE(contour,
nullptr);
333 EXPECT_FALSE(contour->IsClosed());
339 .
AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
341 EXPECT_NE(path.
begin(), path.
end());
344 ASSERT_NE(contour,
nullptr);
346 EXPECT_FALSE(contour->IsClosed());
353 EXPECT_NE(path.
begin(), path.
end());
356 ASSERT_NE(contour,
nullptr);
358 EXPECT_FALSE(contour->IsClosed());
365 EXPECT_NE(path.
begin(), path.
end());
368 ASSERT_NE(contour,
nullptr);
370 EXPECT_FALSE(contour->IsClosed());
374 TEST(PathTest, PathCreatePolylineGeneratesCorrectContourData) {
376 .
AddLine({100, 100}, {200, 100})
382 .CreatePolyline(1.0f);
383 ASSERT_EQ(
polyline.points->size(), 6u);
384 ASSERT_EQ(
polyline.contours.size(), 2u);
385 ASSERT_EQ(
polyline.contours[0].is_closed,
false);
386 ASSERT_EQ(
polyline.contours[0].start_index, 0u);
387 ASSERT_EQ(
polyline.contours[1].is_closed,
true);
388 ASSERT_EQ(
polyline.contours[1].start_index, 2u);
391 TEST(PathTest, PolylineGetContourPointBoundsReturnsCorrectRanges) {
393 .
AddLine({100, 100}, {200, 100})
399 .CreatePolyline(1.0f);
400 size_t a1, a2, b1, b2;
401 std::tie(a1, a2) =
polyline.GetContourPointBounds(0);
402 std::tie(b1, b2) =
polyline.GetContourPointBounds(1);
409 TEST(PathTest, PathAddRectPolylineHasCorrectContourData) {
414 ASSERT_EQ(
polyline.contours.size(), 1u);
415 ASSERT_TRUE(
polyline.contours[0].is_closed);
416 ASSERT_EQ(
polyline.contours[0].start_index, 0u);
417 ASSERT_EQ(
polyline.points->size(), 5u);
425 TEST(PathTest, PathPolylineDuplicatesAreRemovedForSameContour) {
438 .CreatePolyline(1.0f);
439 ASSERT_EQ(
polyline.contours.size(), 2u);
440 ASSERT_EQ(
polyline.contours[0].start_index, 0u);
441 ASSERT_TRUE(
polyline.contours[0].is_closed);
442 ASSERT_EQ(
polyline.contours[1].start_index, 4u);
443 ASSERT_FALSE(
polyline.contours[1].is_closed);
444 ASSERT_EQ(
polyline.points->size(), 7u);
454 TEST(PathTest, PolylineBufferReuse) {
455 auto point_buffer = std::make_unique<std::vector<Point>>();
456 auto point_buffer_address =
reinterpret_cast<uintptr_t
>(point_buffer.get());
463 1.0f, std::move(point_buffer),
464 [point_buffer_address](
466 ASSERT_EQ(point_buffer->size(), 0u);
467 ASSERT_EQ(point_buffer_address,
468 reinterpret_cast<uintptr_t
>(point_buffer.get()));
472 TEST(PathTest, PolylineFailsWithNullptrBuffer) {
477 .CreatePolyline(1.0f,
nullptr),
493 auto it = path.
begin();
497 ASSERT_NE(contour,
nullptr);
502 ASSERT_NE(linear,
nullptr);
510 ASSERT_NE(quad,
nullptr);
518 ASSERT_NE(conic,
nullptr);
526 ASSERT_NE(cubic,
nullptr);
533 EXPECT_EQ(it, path.end());
537 EXPECT_EQ(linear->
p1,
Point(1, 1));
538 EXPECT_EQ(linear->
p2,
Point(11, 11));
540 EXPECT_EQ(quad->
cp,
Point(16, 16));
541 EXPECT_EQ(quad->
p1,
Point(11, 11));
542 EXPECT_EQ(quad->
p2,
Point(21, 21));
544 EXPECT_EQ(cubic->
cp1,
Point(26, 26));
545 EXPECT_EQ(cubic->
cp2,
Point(-4, -4));
546 EXPECT_EQ(cubic->
p1,
Point(21, 21));
547 EXPECT_EQ(cubic->
p2,
Point(31, 31));
550 TEST(PathTest, PathBuilderWillComputeBounds) {
552 auto path_1 = builder.
AddLine({0, 0}, {1, 1}).TakePath();
557 auto path_2 = builder.
AddLine({-1, -1}, {1, 1}).TakePath();
564 auto path_3 = builder.
AddLine({0, 0}, {1, 1})
572 TEST(PathTest, PathHorizontalLine) {
576 auto it = path.
begin();
582 ASSERT_NE(linear,
nullptr);
584 EXPECT_EQ(linear->
p1,
Point(0, 0));
585 EXPECT_EQ(linear->
p2,
Point(10, 0));
588 TEST(PathTest, PathVerticalLine) {
592 auto it = path.
begin();
598 ASSERT_NE(linear,
nullptr);
600 EXPECT_EQ(linear->
p1,
Point(0, 0));
601 EXPECT_EQ(linear->
p2,
Point(0, 10));
604 TEST(PathTest, QuadradicPath) {
608 auto it = path.
begin();
614 ASSERT_NE(quad,
nullptr);
616 EXPECT_EQ(quad->
p1,
Point(0, 0));
617 EXPECT_EQ(quad->
cp,
Point(10, 10));
618 EXPECT_EQ(quad->
p2,
Point(20, 20));
626 auto it = path.
begin();
632 ASSERT_NE(conic,
nullptr);
634 EXPECT_EQ(conic->
p1,
Point(0, 0));
635 EXPECT_EQ(conic->
cp,
Point(10, 10));
636 EXPECT_EQ(conic->
p2,
Point(20, 20));
646 auto it = path.
begin();
652 ASSERT_NE(cubic,
nullptr);
654 EXPECT_EQ(cubic->
p1,
Point(0, 0));
655 EXPECT_EQ(cubic->
cp1,
Point(10, 10));
656 EXPECT_EQ(cubic->
cp2,
Point(-10, -10));
657 EXPECT_EQ(cubic->
p2,
Point(20, 20));
660 TEST(PathTest, BoundingBoxCubic) {
663 builder.
AddCubicCurve({120, 160}, {25, 200}, {220, 260}, {220, 40})
665 auto box = path.GetBoundingBox();
667 ASSERT_TRUE(box.has_value());
671 TEST(PathTest, BoundingBoxOfCompositePathIsCorrect) {
679 ASSERT_TRUE(actual.has_value());
683 TEST(PathTest, ExtremaOfCubicPathComponentIsCorrect) {
685 {-6.2857933, 204.356461},
686 {-4.53997231, 156.552902},
687 {17.0067291, 109.472488}};
690 ASSERT_EQ(points.size(),
static_cast<size_t>(3));
694 TEST(PathTest, PathGetBoundingBoxForCubicWithNoDerivativeRootsIsCorrect) {
702 ASSERT_TRUE(actual.has_value());
708 ASSERT_EQ(path.GetComponentCount(), 1u);
714 ASSERT_TRUE(
polyline.points->empty());
715 ASSERT_TRUE(
polyline.contours.empty());
721 auto path = builder.
AddLine({0, 0}, {100, 100})
722 .AddQuadraticCurve({100, 100}, {200, 200}, {300, 300})
723 .AddConicCurve({100, 100}, {200, 200}, {300, 300}, 0.75f)
724 .AddCubicCurve({300, 300}, {400, 400}, {500, 500}, {600, 600})
727 EXPECT_EQ(path.GetComponentCount(), 8u);
734 auto it = path.begin();
739 ASSERT_NE(contour,
nullptr);
750 ASSERT_NE(linear,
nullptr);
755 EXPECT_EQ(linear->
p1, p1);
756 EXPECT_EQ(linear->
p2, p2);
762 ASSERT_NE(contour,
nullptr);
773 ASSERT_NE(quad,
nullptr);
779 EXPECT_EQ(quad->
p1, p1);
780 EXPECT_EQ(quad->
cp, cp);
781 EXPECT_EQ(quad->
p2, p2);
787 ASSERT_NE(contour,
nullptr);
798 ASSERT_NE(conic,
nullptr);
804 Point weight(0.75f, 0.75f);
805 EXPECT_EQ(conic->
p1, p1);
806 EXPECT_EQ(conic->
cp, cp);
807 EXPECT_EQ(conic->
p2, p2);
808 EXPECT_EQ(conic->
weight, weight);
814 ASSERT_NE(contour,
nullptr);
825 ASSERT_NE(cubic,
nullptr);
832 EXPECT_EQ(cubic->
p1, p1);
833 EXPECT_EQ(cubic->
cp1, cp1);
834 EXPECT_EQ(cubic->
cp2, cp2);
835 EXPECT_EQ(cubic->
p2, p2);
838 EXPECT_EQ(it, path.end());
841 TEST(PathTest, RepeatCloseDoesNotAddNewLines) {
843 auto path = builder.
LineTo({0, 10})
850 EXPECT_EQ(path.GetComponentCount(), 5u);
855 TEST(PathTest, CloseAfterMoveDoesNotAddNewLines) {
857 auto path = builder.
LineTo({0, 10})
864 EXPECT_EQ(path.GetComponentCount(), 4u);
869 TEST(PathTest, CloseAtOriginDoesNotAddNewLineSegment) {
873 auto path = builder.
LineTo({10, 0})
880 EXPECT_EQ(path.GetComponentCount(), 6u);
894 auto path_b = path_a;
896 EXPECT_EQ(path_a.GetBoundingBox(), path_b.GetBoundingBox());
897 EXPECT_EQ(path_a.GetFillType(), path_b.GetFillType());
898 EXPECT_EQ(path_a.IsConvex(), path_b.IsConvex());
900 auto poly_a = path_a.CreatePolyline(1.0);
901 auto poly_b = path_b.CreatePolyline(1.0);
903 ASSERT_EQ(poly_a.points->size(), poly_b.points->size());
904 ASSERT_EQ(poly_a.contours.size(), poly_b.contours.size());
906 for (
auto i = 0u; i < poly_a.points->size(); i++) {
907 EXPECT_EQ((*poly_a.points)[i], (*poly_b.points)[i]);
910 for (
auto i = 0u; i < poly_a.contours.size(); i++) {
911 EXPECT_EQ(poly_a.contours[i].start_index, poly_b.contours[i].start_index);
912 EXPECT_EQ(poly_a.contours[i].start_direction,
913 poly_b.contours[i].start_direction);
917 TEST(PathTest, FanTessellation) {
924 std::vector<Point> point_storage(points);
925 std::vector<uint16_t> index_storage(points + (contours - 1));
931 EXPECT_EQ(point_storage[0],
Point(10, 0));
935 TEST(PathTest, FanTessellationUnclosedPath) {
943 std::vector<Point> expected = {{0, 0}, {100, 0}, {100, 100},
944 {0, 100}, {0, 0}, {0, 0}};
945 std::vector<uint16_t> expected_indices = {0, 1, 2, 3, 0xFFFF, 0};
949 std::vector<Point> point_storage(points);
950 std::vector<uint16_t> index_storage(points + (contours - 1));
955 EXPECT_LE(index_storage, expected_indices);
956 EXPECT_EQ(point_storage, expected);
960 TEST(PathTest, StripTessellationUnclosedPath) {
968 std::vector<Point> expected = {{0, 0}, {100, 0}, {100, 100},
969 {0, 100}, {0, 0}, {0, 0}};
970 std::vector<uint16_t> expected_indices = {0, 1, 3, 2, 0xFFFF, 0};
974 std::vector<Point> point_storage(points);
975 std::vector<uint16_t> index_storage(points + (contours - 1));
980 EXPECT_LE(index_storage, expected_indices);
981 EXPECT_EQ(point_storage, expected);
984 TEST(PathTest, FanTessellationMultiContour) {
986 for (
auto i = 0; i < 10; i++) {
993 std::vector<Point> point_storage(points);
994 std::vector<uint16_t> index_storage(points + (contours - 1));
997 path.WritePolyline(1.0, writer);
1000 EXPECT_EQ(point_storage[0],
Point(10, 0));
1003 TEST(PathTest, StripTessellation) {
1010 std::vector<Point> point_storage(points);
1011 std::vector<uint16_t> index_storage(points + (contours - 1));
1017 EXPECT_EQ(point_storage[0],
Point(10, 0));
1020 TEST(PathTest, StripTessellationMultiContour) {
1022 for (
auto i = 0; i < 10; i++) {
1029 std::vector<Point> point_storage(points);
1030 std::vector<uint16_t> index_storage(points + (contours - 1));
1033 path.WritePolyline(1.0, writer);
1036 EXPECT_EQ(point_storage[0],
Point(10, 0));
1039 TEST(PathTest, PathBuilderAddPathBasher) {
1043 for (
int i = 0; i < 2000; i++) {
1045 for (
int j = 0; j < 10; j++) {
1048 path_builder.
AddPath(test_path);
1058 EXPECT_TRUE(bounds.has_value());
1059 if (bounds.has_value()) {
1065 TEST(PathTest, PathBuilderDoesNotMutateCopiedPaths) {
1066 auto test_isolation =
1067 [](
const std::function<void(
PathBuilder & builder)>& mutator,
1068 bool will_close,
Point mutation_offset,
const std::string& label) {
1070 builder.
MoveTo({10, 10});
1071 builder.
LineTo({20, 20});
1072 builder.
LineTo({20, 10});
1074 auto verify_path = [](
const Path& path,
bool is_mutated,
bool is_closed,
1084 auto it = path.
begin();
1088 ASSERT_NE(contour,
nullptr) << label;
1092 EXPECT_EQ(contour->
IsClosed(), is_closed) << label;
1097 ASSERT_NE(line,
nullptr) << label;
1106 ASSERT_NE(line,
nullptr) << label;
1113 EXPECT_EQ(it, path.
end()) << label;
1118 verify_path(path1,
false,
false, {},
1119 "Initial Path1 state before " + label);
1121 for (
int i = 0; i < 10; i++) {
1124 path,
false,
false, {},
1125 "Extra CopyPath #" + std::to_string(i + 1) +
" for " + label);
1128 verify_path(path1,
false,
false, {},
1129 "Path1 state after subsequent " + label);
1132 verify_path(path1,
false,
false, {},
1133 "Path1 state after subsequent " + label +
" and CopyPath");
1134 verify_path(path2,
true, will_close, mutation_offset,
1135 "Initial Path2 state with subsequent " + label);
1142 false, {},
"SetConvex");
1148 false, {},
"SetUnknownConvex");
1158 builder.
MoveTo({20, 30},
false);
1160 false, {},
"Absolute MoveTo");
1164 builder.
MoveTo({20, 30},
true);
1166 false, {},
"Relative MoveTo");
1170 builder.
LineTo({20, 30},
false);
1172 false, {},
"Absolute LineTo");
1176 builder.
LineTo({20, 30},
true);
1178 false, {},
"Relative LineTo");
1184 false, {},
"Absolute HorizontalLineTo");
1190 false, {},
"Relative HorizontalLineTo");
1196 false, {},
"Absolute VerticalLineTo");
1202 false, {},
"Relative VerticalLineTo");
1208 false, {},
"Absolute QuadraticCurveTo");
1214 false, {},
"Relative QuadraticCurveTo");
1218 builder.
ConicCurveTo({20, 30}, {30, 20}, 0.75f,
false);
1220 false, {},
"Absolute ConicCurveTo");
1226 false, {},
"Relative ConicCurveTo");
1230 builder.
CubicCurveTo({20, 30}, {30, 20}, {30, 30},
false);
1232 false, {},
"Absolute CubicCurveTo");
1236 builder.
CubicCurveTo({20, 30}, {30, 20}, {30, 30},
true);
1238 false, {},
"Relative CubicCurveTo");
1242 builder.
AddLine({100, 100}, {150, 100});
1244 false, {},
"AddLine");
1250 false, {},
"AddRect");
1256 false, {},
"AddOval");
1262 false, {},
"AddCircle");
1269 false, {},
"AddArc");
1275 false, {},
"AddQuadraticCurve");
1279 builder.
AddConicCurve({100, 100}, {150, 100}, {150, 150}, 0.75f);
1281 false, {},
"AddConicCurve");
1285 builder.
AddCubicCurve({100, 100}, {150, 100}, {100, 150}, {150, 150});
1287 false, {},
"AddCubicCurve");
1291 builder.
Shift({23, 42});
1293 false, {23, 42},
"Shift");
A vertex writer that generates a triangle fan and requires primitive restart.
size_t GetIndexCount() const
const ContourComponent * contour() const
ComponentType type() const
Path TakePath(FillType fill=FillType::kNonZero)
PathBuilder & AddArc(const Rect &oval_bounds, Radians start, Radians sweep, bool use_center=false)
PathBuilder & AddRoundRect(RoundRect rect)
PathBuilder & LineTo(Point point, bool relative=false)
Insert a line from the current position to point.
PathBuilder & MoveTo(Point point, bool relative=false)
PathBuilder & AddRect(const Rect &rect)
PathBuilder & SetBounds(Rect bounds)
Set the bounding box that will be used by Path.GetBoundingBox in place of performing the computation.
PathBuilder & AddCubicCurve(const Point &p1, const Point &cp1, const Point &cp2, const Point &p2)
Move to point p1, then insert a cubic curve from p1 to p2 with control points cp1 and cp2.
PathBuilder & AddOval(const Rect &rect)
PathBuilder & AddCircle(const Point ¢er, Scalar radius)
PathBuilder & AddPath(const Path &path)
PathBuilder & AddConicCurve(const Point &p1, const Point &cp, const Point &p2, Scalar weight)
Move to point p1, then insert a conic curve from p1 to p2 with the control point cp and weight weight...
Path CopyPath(FillType fill=FillType::kNonZero)
PathBuilder & VerticalLineTo(Scalar y, bool relative=false)
PathBuilder & Shift(Point offset)
Transform the existing path segments and contours by the given offset.
PathBuilder & AddLine(const Point &p1, const Point &p2)
Move to point p1, then insert a line from p1 to p2.
PathBuilder & CubicCurveTo(Point controlPoint1, Point controlPoint2, Point point, bool relative=false)
Insert a cubic curve from the curren position to point using the control points controlPoint1 and con...
PathBuilder & ConicCurveTo(Point controlPoint, Point point, Scalar weight, bool relative=false)
Insert a conic curve from the current position to point using the control point controlPoint and the ...
PathBuilder & AddQuadraticCurve(const Point &p1, const Point &cp, const Point &p2)
Move to point p1, then insert a quadradic curve from p1 to p2 with the control point cp.
PathBuilder & HorizontalLineTo(Scalar x, bool relative=false)
PathBuilder & AddRoundSuperellipse(RoundSuperellipse rse)
PathBuilder & SetConvexity(Convexity value)
PathBuilder & QuadraticCurveTo(Point controlPoint, Point point, bool relative=false)
Insert a quadradic curve from the current position to point using the control point controlPoint.
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
size_t GetComponentCount(std::optional< ComponentType > type={}) const
ComponentIterator begin() const
Polyline CreatePolyline(Scalar scale, Polyline::PointBufferPtr point_buffer=std::make_unique< std::vector< Point >>(), Polyline::ReclaimPointBufferCallback reclaim=nullptr) const
bool IsSingleContour() const
Whether the line contains a single contour.
ComponentIterator end() const
void WritePolyline(Scalar scale, VertexWriter &writer) const
std::optional< Rect > GetBoundingBox() const
std::pair< size_t, size_t > CountStorage(Scalar scale) const
Determine required storage for points and number of contours.
A vertex writer that generates a triangle strip and requires primitive restart.
size_t GetIndexCount() const
#define ASSERT_RECT_NEAR(a, b)
#define ASSERT_POINT_NEAR(a, b)
#define EXPECT_POINT_NEAR(a, b)
TEST(AllocationSizeTest, CanCreateTypedAllocations)
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
void Close(PathBuilder *builder)
const Path::Polyline & polyline
constexpr bool IsClosed() const
std::vector< Point > Extrema() const
std::unique_ptr< std::vector< Point > > PointBufferPtr
constexpr static RoundRect MakeRectRadius(const Rect &rect, Scalar radius)
constexpr static RoundRect MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
constexpr static RoundSuperellipse MakeRectRadius(const Rect &rect, Scalar radius)
constexpr static RoundSuperellipse MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
constexpr static TRect MakeMaximum()