1*c8dee2aaSAndroid Build Coastguard Worker /* 2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2023 Google LLC 3*c8dee2aaSAndroid Build Coastguard Worker * 4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be 5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file. 6*c8dee2aaSAndroid Build Coastguard Worker */ 7*c8dee2aaSAndroid Build Coastguard Worker #ifndef SkBezierCurves_DEFINED 8*c8dee2aaSAndroid Build Coastguard Worker #define SkBezierCurves_DEFINED 9*c8dee2aaSAndroid Build Coastguard Worker 10*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkSpan_impl.h" 11*c8dee2aaSAndroid Build Coastguard Worker 12*c8dee2aaSAndroid Build Coastguard Worker #include <array> 13*c8dee2aaSAndroid Build Coastguard Worker 14*c8dee2aaSAndroid Build Coastguard Worker struct SkPoint; 15*c8dee2aaSAndroid Build Coastguard Worker 16*c8dee2aaSAndroid Build Coastguard Worker /** 17*c8dee2aaSAndroid Build Coastguard Worker * Utilities for dealing with cubic Bézier curves. These have a start XY 18*c8dee2aaSAndroid Build Coastguard Worker * point, an end XY point, and two control XY points in between. They take 19*c8dee2aaSAndroid Build Coastguard Worker * a parameter t which is between 0 and 1 (inclusive) which is used to 20*c8dee2aaSAndroid Build Coastguard Worker * interpolate between the start and end points, via a route dictated by 21*c8dee2aaSAndroid Build Coastguard Worker * the control points, and return a new XY point. 22*c8dee2aaSAndroid Build Coastguard Worker * 23*c8dee2aaSAndroid Build Coastguard Worker * We store a Bézier curve as an array of 8 floats or doubles, where 24*c8dee2aaSAndroid Build Coastguard Worker * the even indices are the X coordinates, and the odd indices are the Y 25*c8dee2aaSAndroid Build Coastguard Worker * coordinates. 26*c8dee2aaSAndroid Build Coastguard Worker */ 27*c8dee2aaSAndroid Build Coastguard Worker class SkBezierCubic { 28*c8dee2aaSAndroid Build Coastguard Worker public: 29*c8dee2aaSAndroid Build Coastguard Worker 30*c8dee2aaSAndroid Build Coastguard Worker /** 31*c8dee2aaSAndroid Build Coastguard Worker * Evaluates the cubic Bézier curve for a given t. It returns an X and Y coordinate 32*c8dee2aaSAndroid Build Coastguard Worker * following the formula, which does the interpolation mentioned above. 33*c8dee2aaSAndroid Build Coastguard Worker * 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*c8dee2aaSAndroid Build Coastguard Worker * 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*c8dee2aaSAndroid Build Coastguard Worker * 36*c8dee2aaSAndroid Build Coastguard Worker * t is typically in the range [0, 1], but this function will not assert that, 37*c8dee2aaSAndroid Build Coastguard Worker * as Bézier curves are well-defined for any real number input. 38*c8dee2aaSAndroid Build Coastguard Worker */ 39*c8dee2aaSAndroid Build Coastguard Worker static std::array<double, 2> EvalAt(const double curve[8], double t); 40*c8dee2aaSAndroid Build Coastguard Worker 41*c8dee2aaSAndroid Build Coastguard Worker /** 42*c8dee2aaSAndroid Build Coastguard Worker * Splits the provided Bézier curve at the location t, resulting in two 43*c8dee2aaSAndroid Build Coastguard Worker * Bézier curves that share a point (the end point from curve 1 44*c8dee2aaSAndroid Build Coastguard Worker * and the start point from curve 2 are the same). 45*c8dee2aaSAndroid Build Coastguard Worker * 46*c8dee2aaSAndroid Build Coastguard Worker * t must be in the interval [0, 1]. 47*c8dee2aaSAndroid Build Coastguard Worker * 48*c8dee2aaSAndroid Build Coastguard Worker * The provided twoCurves array will be filled such that indices 49*c8dee2aaSAndroid Build Coastguard Worker * 0-7 are the first curve (representing the interval [0, t]), and 50*c8dee2aaSAndroid Build Coastguard Worker * indices 6-13 are the second curve (representing [t, 1]). 51*c8dee2aaSAndroid Build Coastguard Worker */ 52*c8dee2aaSAndroid Build Coastguard Worker static void Subdivide(const double curve[8], double t, 53*c8dee2aaSAndroid Build Coastguard Worker double twoCurves[14]); 54*c8dee2aaSAndroid Build Coastguard Worker 55*c8dee2aaSAndroid Build Coastguard Worker /** 56*c8dee2aaSAndroid Build Coastguard Worker * Converts the provided Bézier curve into the the equivalent cubic 57*c8dee2aaSAndroid Build Coastguard Worker * f(t) = A*t^3 + B*t^2 + C*t + D 58*c8dee2aaSAndroid Build Coastguard Worker * where f(t) will represent Y coordinates over time if yValues is 59*c8dee2aaSAndroid Build Coastguard Worker * true and the X coordinates if yValues is false. 60*c8dee2aaSAndroid Build Coastguard Worker * 61*c8dee2aaSAndroid Build Coastguard Worker * In effect, this turns the control points into an actual line, representing 62*c8dee2aaSAndroid Build Coastguard Worker * the x or y values. 63*c8dee2aaSAndroid Build Coastguard Worker */ 64*c8dee2aaSAndroid Build Coastguard Worker static std::array<double, 4> ConvertToPolynomial(const double curve[8], bool yValues); 65*c8dee2aaSAndroid Build Coastguard Worker 66*c8dee2aaSAndroid Build Coastguard Worker static SkSpan<const float> IntersectWithHorizontalLine( 67*c8dee2aaSAndroid Build Coastguard Worker SkSpan<const SkPoint> controlPoints, float yIntercept, 68*c8dee2aaSAndroid Build Coastguard Worker float intersectionStorage[3]); 69*c8dee2aaSAndroid Build Coastguard Worker 70*c8dee2aaSAndroid Build Coastguard Worker static SkSpan<const float> Intersect( 71*c8dee2aaSAndroid Build Coastguard Worker double AX, double BX, double CX, double DX, 72*c8dee2aaSAndroid Build Coastguard Worker double AY, double BY, double CY, double DY, 73*c8dee2aaSAndroid Build Coastguard Worker float toIntersect, float intersectionsStorage[3]); 74*c8dee2aaSAndroid Build Coastguard Worker }; 75*c8dee2aaSAndroid Build Coastguard Worker 76*c8dee2aaSAndroid Build Coastguard Worker class SkBezierQuad { 77*c8dee2aaSAndroid Build Coastguard Worker public: 78*c8dee2aaSAndroid Build Coastguard Worker static SkSpan<const float> IntersectWithHorizontalLine( 79*c8dee2aaSAndroid Build Coastguard Worker SkSpan<const SkPoint> controlPoints, float yIntercept, 80*c8dee2aaSAndroid Build Coastguard Worker float intersectionStorage[2]); 81*c8dee2aaSAndroid Build Coastguard Worker 82*c8dee2aaSAndroid Build Coastguard Worker /** 83*c8dee2aaSAndroid Build Coastguard Worker * Given 84*c8dee2aaSAndroid Build Coastguard Worker * AY*t^2 -2*BY*t + CY = 0 and AX*t^2 - 2*BX*t + CX = 0, 85*c8dee2aaSAndroid Build Coastguard Worker * 86*c8dee2aaSAndroid Build Coastguard Worker * Find the t where AY*t^2 - 2*BY*t + CY - y = 0, then return AX*t^2 + - 2*BX*t + CX 87*c8dee2aaSAndroid Build Coastguard Worker * where t is on [0, 1]. 88*c8dee2aaSAndroid Build Coastguard Worker * 89*c8dee2aaSAndroid Build Coastguard Worker * - y - is the height of the line which intersects the quadratic. 90*c8dee2aaSAndroid Build Coastguard Worker * - intersectionStorage - is the array to hold the return data pointed to in the span. 91*c8dee2aaSAndroid Build Coastguard Worker * 92*c8dee2aaSAndroid Build Coastguard Worker * Returns a span with the intersections of yIntercept, and the quadratic formed by A, B, 93*c8dee2aaSAndroid Build Coastguard Worker * and C. 94*c8dee2aaSAndroid Build Coastguard Worker */ 95*c8dee2aaSAndroid Build Coastguard Worker static SkSpan<const float> Intersect( 96*c8dee2aaSAndroid Build Coastguard Worker double AX, double BX, double CX, 97*c8dee2aaSAndroid Build Coastguard Worker double AY, double BY, double CY, 98*c8dee2aaSAndroid Build Coastguard Worker double yIntercept, 99*c8dee2aaSAndroid Build Coastguard Worker float intersectionStorage[2]); 100*c8dee2aaSAndroid Build Coastguard Worker }; 101*c8dee2aaSAndroid Build Coastguard Worker 102*c8dee2aaSAndroid Build Coastguard Worker #endif // SkBezierCurves_DEFINED 103