Flutter Engine
The Flutter Engine
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
Static Public Member Functions | List of all members
SkBezierCubic Class Reference

#include <SkBezierCurves.h>

Static Public Member Functions

static std::array< double, 2 > EvalAt (const double curve[8], double t)
 
static void Subdivide (const double curve[8], double t, double twoCurves[14])
 
static std::array< double, 4 > ConvertToPolynomial (const double curve[8], bool yValues)
 
static SkSpan< const float > IntersectWithHorizontalLine (SkSpan< const SkPoint > controlPoints, float yIntercept, float intersectionStorage[3])
 
static SkSpan< const float > Intersect (double AX, double BX, double CX, double DX, double AY, double BY, double CY, double DY, float toIntersect, float intersectionsStorage[3])
 

Detailed Description

Utilities for dealing with cubic Bézier curves. These have a start XY point, an end XY point, and two control XY points in between. They take a parameter t which is between 0 and 1 (inclusive) which is used to interpolate between the start and end points, via a route dictated by the control points, and return a new XY point.

We store a Bézier curve as an array of 8 floats or doubles, where the even indices are the X coordinates, and the odd indices are the Y coordinates.

Definition at line 27 of file SkBezierCurves.h.

Member Function Documentation

◆ ConvertToPolynomial()

std::array< double, 4 > SkBezierCubic::ConvertToPolynomial ( const double  curve[8],
bool  yValues 
)
static

Converts the provided Bézier curve into the the equivalent cubic f(t) = A*t^3 + B*t^2 + C*t + D where f(t) will represent Y coordinates over time if yValues is true and the X coordinates if yValues is false.

In effect, this turns the control points into an actual line, representing the x or y values.

Definition at line 98 of file SkBezierCurves.cpp.

98 {
99 const double* offset_curve = yValues ? curve + 1 : curve;
100 const auto P = [&offset_curve](size_t n) { return offset_curve[2*n]; };
101 // A cubic Bézier curve is interpolated as follows:
102 // c(t) = (1 - t)^3 P_0 + 3t(1 - t)^2 P_1 + 3t^2 (1 - t) P_2 + t^3 P_3
103 // = (-P_0 + 3P_1 + -3P_2 + P_3) t^3 + (3P_0 - 6P_1 + 3P_2) t^2 +
104 // (-3P_0 + 3P_1) t + P_0
105 // Where P_N is the Nth point. The second step expands the polynomial and groups
106 // by powers of t. The desired output is a cubic formula, so we just need to
107 // combine the appropriate points to make the coefficients.
108 std::array<double, 4> results;
109 results[0] = -P(0) + 3*P(1) - 3*P(2) + P(3);
110 results[1] = 3*P(0) - 6*P(1) + 3*P(2);
111 results[2] = -3*P(0) + 3*P(1);
112 results[3] = P(0);
113 return results;
114}

◆ EvalAt()

std::array< double, 2 > SkBezierCubic::EvalAt ( const double  curve[8],
double  t 
)
static

Evaluates the cubic Bézier curve for a given t. It returns an X and Y coordinate following the formula, which does the interpolation mentioned above. X(t) = X_0*(1-t)^3 + 3*X_1*t(1-t)^2 + 3*X_2*t^2(1-t) + X_3*t^3 Y(t) = Y_0*(1-t)^3 + 3*Y_1*t(1-t)^2 + 3*Y_2*t^2(1-t) + Y_3*t^3

t is typically in the range [0, 1], but this function will not assert that, as Bézier curves are well-defined for any real number input.

Definition at line 22 of file SkBezierCurves.cpp.

22 {
23 const auto in_X = [&curve](size_t n) { return curve[2*n]; };
24 const auto in_Y = [&curve](size_t n) { return curve[2*n + 1]; };
25
26 // Two semi-common fast paths
27 if (t == 0) {
28 return {in_X(0), in_Y(0)};
29 }
30 if (t == 1) {
31 return {in_X(3), in_Y(3)};
32 }
33 // X(t) = X_0*(1-t)^3 + 3*X_1*t(1-t)^2 + 3*X_2*t^2(1-t) + X_3*t^3
34 // Y(t) = Y_0*(1-t)^3 + 3*Y_1*t(1-t)^2 + 3*Y_2*t^2(1-t) + Y_3*t^3
35 // Some compilers are smart enough and have sufficient registers/intrinsics to write optimal
36 // code from
37 // double one_minus_t = 1 - t;
38 // double a = one_minus_t * one_minus_t * one_minus_t;
39 // double b = 3 * one_minus_t * one_minus_t * t;
40 // double c = 3 * one_minus_t * t * t;
41 // double d = t * t * t;
42 // However, some (e.g. when compiling for ARM) fail to do so, so we use this form
43 // to help more compilers generate smaller/faster ASM. https://godbolt.org/z/M6jG9x45c
44 double one_minus_t = 1 - t;
45 double one_minus_t_squared = one_minus_t * one_minus_t;
46 double a = (one_minus_t_squared * one_minus_t);
47 double b = 3 * one_minus_t_squared * t;
48 double t_squared = t * t;
49 double c = 3 * one_minus_t * t_squared;
50 double d = t_squared * t;
51
52 return {a * in_X(0) + b * in_X(1) + c * in_X(2) + d * in_X(3),
53 a * in_Y(0) + b * in_Y(1) + c * in_Y(2) + d * in_Y(3)};
54}
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
static bool b
struct MyStruct a[10]

◆ Intersect()

SkSpan< const float > SkBezierCubic::Intersect ( double  AX,
double  BX,
double  CX,
double  DX,
double  AY,
double  BY,
double  CY,
double  DY,
float  toIntersect,
float  intersectionsStorage[3] 
)
static

Definition at line 170 of file SkBezierCurves.cpp.

172 {
173 double roots[3];
175 SkCubics::RootsReal(AY, BY, CY, DY - toIntersect, roots));
176
177 int intersectionCount = 0;
178 for (double t : ts) {
179 const double pinnedT = pinTRange(t);
180 if (0 <= pinnedT && pinnedT <= 1) {
181 intersectionsStorage[intersectionCount++] = SkCubics::EvalAt(AX, BX, CX, DX, pinnedT);
182 }
183 }
184
185 return {intersectionsStorage, intersectionCount};
186}
#define CX
#define CY
SkSpan(Container &&) -> SkSpan< std::remove_pointer_t< decltype(std::data(std::declval< Container >()))> >
#define AX(width, name,...)
static double EvalAt(double A, double B, double C, double D, double t)
Definition: SkCubics.h:51
static int RootsReal(double A, double B, double C, double D, double solution[3])
Definition: SkCubics.cpp:38

◆ IntersectWithHorizontalLine()

SkSpan< const float > SkBezierCubic::IntersectWithHorizontalLine ( SkSpan< const SkPoint controlPoints,
float  yIntercept,
float  intersectionStorage[3] 
)
static

Definition at line 153 of file SkBezierCurves.cpp.

154 {
155 SkASSERT(controlPoints.size() >= 4);
156 const DPoint P0 = controlPoints[0],
157 P1 = controlPoints[1],
158 P2 = controlPoints[2],
159 P3 = controlPoints[3];
160
161 const DPoint A = -P0 + 3*P1 - 3*P2 + P3,
162 B = 3*P0 - 6*P1 + 3*P2,
163 C = -3*P0 + 3*P1,
164 D = P0;
165
166 return Intersect(A.x, B.x, C.x, D.x, A.y, B.y, C.y, D.y, yIntercept, intersectionStorage);
167}
#define SkASSERT(cond)
Definition: SkAssert.h:116
static SkSpan< const float > Intersect(double AX, double BX, double CX, double DX, double AY, double BY, double CY, double DY, float toIntersect, float intersectionsStorage[3])
constexpr size_t size() const
Definition: SkSpan_impl.h:95

◆ Subdivide()

void SkBezierCubic::Subdivide ( const double  curve[8],
double  t,
double  twoCurves[14] 
)
static

Splits the provided Bézier curve at the location t, resulting in two Bézier curves that share a point (the end point from curve 1 and the start point from curve 2 are the same).

t must be in the interval [0, 1].

The provided twoCurves array will be filled such that indices 0-7 are the first curve (representing the interval [0, t]), and indices 6-13 are the second curve (representing [t, 1]).

Definition at line 58 of file SkBezierCurves.cpp.

59 {
60 SkASSERT(0.0 <= t && t <= 1.0);
61 // We split the curve "in" into two curves "alpha" and "beta"
62 const auto in_X = [&curve](size_t n) { return curve[2*n]; };
63 const auto in_Y = [&curve](size_t n) { return curve[2*n + 1]; };
64 const auto alpha_X = [&twoCurves](size_t n) -> double& { return twoCurves[2*n]; };
65 const auto alpha_Y = [&twoCurves](size_t n) -> double& { return twoCurves[2*n + 1]; };
66 const auto beta_X = [&twoCurves](size_t n) -> double& { return twoCurves[2*n + 6]; };
67 const auto beta_Y = [&twoCurves](size_t n) -> double& { return twoCurves[2*n + 7]; };
68
69 alpha_X(0) = in_X(0);
70 alpha_Y(0) = in_Y(0);
71
72 beta_X(3) = in_X(3);
73 beta_Y(3) = in_Y(3);
74
75 double x01 = interpolate(in_X(0), in_X(1), t);
76 double y01 = interpolate(in_Y(0), in_Y(1), t);
77 double x12 = interpolate(in_X(1), in_X(2), t);
78 double y12 = interpolate(in_Y(1), in_Y(2), t);
79 double x23 = interpolate(in_X(2), in_X(3), t);
80 double y23 = interpolate(in_Y(2), in_Y(3), t);
81
82 alpha_X(1) = x01;
83 alpha_Y(1) = y01;
84
85 beta_X(2) = x23;
86 beta_Y(2) = y23;
87
88 alpha_X(2) = interpolate(x01, x12, t);
89 alpha_Y(2) = interpolate(y01, y12, t);
90
91 beta_X(1) = interpolate(x12, x23, t);
92 beta_Y(1) = interpolate(y12, y23, t);
93
94 alpha_X(3) /*= beta_X(0) */ = interpolate(alpha_X(2), beta_X(1), t);
95 alpha_Y(3) /*= beta_Y(0) */ = interpolate(alpha_Y(2), beta_Y(1), t);
96}
static double interpolate(double A, double B, double t)

The documentation for this class was generated from the following files: