• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/private/base/SkFloatingPoint.h"
9 #include "include/private/base/SkSpan_impl.h"
10 #include "src/base/SkBezierCurves.h"
11 #include "tests/Test.h"
12 
13 #include <string>
14 
15 // Grouping the test inputs into DoublePoints makes the test cases easier to read.
16 struct DoublePoint {
17     double x;
18     double y;
19 };
20 
nearly_equal(double expected,double actual)21 static bool nearly_equal(double expected, double actual) {
22     if (sk_double_nearly_zero(expected)) {
23         return sk_double_nearly_zero(actual);
24     }
25     return sk_doubles_nearly_equal_ulps(expected, actual, 64);
26 }
27 
testCubicEvalAtT(skiatest::Reporter * reporter,std::string name,SkSpan<const DoublePoint> curveInputs,double t,const DoublePoint & expectedXY)28 static void testCubicEvalAtT(skiatest::Reporter* reporter, std::string name,
29                              SkSpan<const DoublePoint> curveInputs, double t,
30                              const DoublePoint& expectedXY) {
31     skiatest::ReporterContext subtest(reporter, name);
32     REPORTER_ASSERT(reporter, curveInputs.size() == 4,
33                     "Invalid test case. Should have 4 input points.");
34     REPORTER_ASSERT(reporter, t >= 0.0 && t <= 1.0,
35                     "Invalid test case. t %f should be in [0, 1]", t);
36 
37     auto [x, y] = SkBezierCubic::EvalAt(reinterpret_cast<const double*>(curveInputs.data()), t);
38 
39     REPORTER_ASSERT(reporter, nearly_equal(expectedXY.x, x),
40                     "X wrong %1.16f != %1.16f", expectedXY.x, x);
41     REPORTER_ASSERT(reporter, nearly_equal(expectedXY.y, y),
42                     "Y wrong %1.16f != %1.16f", expectedXY.y, y);
43 }
44 
DEF_TEST(BezierCubicEvalAt,reporter)45 DEF_TEST(BezierCubicEvalAt, reporter) {
46     testCubicEvalAtT(reporter, "linear curve @0.1234",
47                      {{ 0, 0 }, { 0, 0 }, { 10, 10 }, { 10, 10 }},
48                      0.1234,
49                      { 0.4192451819200000, 0.4192451819200000 });
50 
51     testCubicEvalAtT(reporter, "linear curve @0.2345",
52                      {{ 0, 0 }, { 5, 5 }, { 5, 5 }, { 10, 10 }},
53                      0.2345,
54                      { 2.8215983862500000, 2.8215983862500000 });
55 
56     testCubicEvalAtT(reporter, "Arbitrary Cubic, t=0.0",
57                      {{ -10, -20 }, { -7, 5 }, { 14, -2 }, { 3, 13 }},
58                      0.0,
59                      { -10, -20 });
60 
61     testCubicEvalAtT(reporter, "Arbitrary Cubic, t=0.3456",
62                      {{ -10, -20 }, { -7, 5 }, { 14, -2 }, { 3, 13 }},
63                      0.3456,
64                      { -2.503786700800000, -3.31715344793600 });
65 
66     testCubicEvalAtT(reporter, "Arbitrary Cubic, t=0.5",
67                      {{ -10, -20 }, { -7, 5 }, { 14, -2 }, { 3, 13 }},
68                      0.5,
69                      { 1.75, 0.25 });
70 
71     testCubicEvalAtT(reporter, "Arbitrary Cubic, t=0.7891",
72                      {{ -10, -20 }, { -7, 5 }, { 14, -2 }, { 3, 13 }},
73                      0.7891,
74                      { 6.158763291450000, 5.938550084434000 });
75 
76     testCubicEvalAtT(reporter, "Arbitrary Cubic, t=1.0",
77                      {{ -10, -20 }, { -7, 5 }, { 14, -2 }, { 3, 13 }},
78                      1.0,
79                      { 3, 13 });
80 }
81 
testCubicConvertToPolynomial(skiatest::Reporter * reporter,std::string name,SkSpan<const DoublePoint> curveInputs,bool yValues,double expectedA,double expectedB,double expectedC,double expectedD)82 static void testCubicConvertToPolynomial(skiatest::Reporter* reporter, std::string name,
83                                          SkSpan<const DoublePoint> curveInputs, bool yValues,
84                                          double expectedA, double expectedB,
85                                          double expectedC, double expectedD) {
86     skiatest::ReporterContext subtest(reporter, name);
87     REPORTER_ASSERT(reporter, curveInputs.size() == 4,
88                     "Invalid test case. Need 4 points (start, control, control, end)");
89 
90     skiatest::ReporterContext subsubtest(reporter, "SkBezierCurve Implementation");
91     const double* input = &curveInputs[0].x;
92     auto [A, B, C, D] = SkBezierCubic::ConvertToPolynomial(input, yValues);
93 
94     REPORTER_ASSERT(reporter, nearly_equal(expectedA, A), "%f != %f", expectedA, A);
95     REPORTER_ASSERT(reporter, nearly_equal(expectedB, B), "%f != %f", expectedB, B);
96     REPORTER_ASSERT(reporter, nearly_equal(expectedC, C), "%f != %f", expectedC, C);
97     REPORTER_ASSERT(reporter, nearly_equal(expectedD, D), "%f != %f", expectedD, D);
98 }
99 
DEF_TEST(BezierCubicToPolynomials,reporter)100 DEF_TEST(BezierCubicToPolynomials, reporter) {
101     // See also tests/PathOpsDCubicTest.cpp->SkDCubicPolynomialCoefficients
102     testCubicConvertToPolynomial(reporter, "Arbitrary control points X direction",
103         {{1, 2}, {-3, 4}, {5, -6}, {7, 8}}, false, /*=yValues*/
104         -18, 36, -12, 1
105     );
106     testCubicConvertToPolynomial(reporter, "Arbitrary control points Y direction",
107         {{1, 2}, {-3, 4}, {5, -6}, {7, 8}}, true, /*=yValues*/
108         36, -36, 6, 2
109     );
110 }
111