1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2006 The Android Open Source Project 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/core/SkMatrix.h" 9cb93a386Sopenharmony_ci#include "include/core/SkPoint3.h" 10cb93a386Sopenharmony_ci#include "include/private/SkNx.h" 11cb93a386Sopenharmony_ci#include "include/private/SkTPin.h" 12cb93a386Sopenharmony_ci#include "include/private/SkVx.h" 13cb93a386Sopenharmony_ci#include "src/core/SkGeometry.h" 14cb93a386Sopenharmony_ci#include "src/core/SkPointPriv.h" 15cb93a386Sopenharmony_ci 16cb93a386Sopenharmony_ci#include <algorithm> 17cb93a386Sopenharmony_ci#include <tuple> 18cb93a386Sopenharmony_ci#include <utility> 19cb93a386Sopenharmony_ci 20cb93a386Sopenharmony_cistatic SkVector to_vector(const Sk2s& x) { 21cb93a386Sopenharmony_ci SkVector vector; 22cb93a386Sopenharmony_ci x.store(&vector); 23cb93a386Sopenharmony_ci return vector; 24cb93a386Sopenharmony_ci} 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_ci//////////////////////////////////////////////////////////////////////// 27cb93a386Sopenharmony_ci 28cb93a386Sopenharmony_cistatic int is_not_monotonic(SkScalar a, SkScalar b, SkScalar c) { 29cb93a386Sopenharmony_ci SkScalar ab = a - b; 30cb93a386Sopenharmony_ci SkScalar bc = b - c; 31cb93a386Sopenharmony_ci if (ab < 0) { 32cb93a386Sopenharmony_ci bc = -bc; 33cb93a386Sopenharmony_ci } 34cb93a386Sopenharmony_ci return ab == 0 || bc < 0; 35cb93a386Sopenharmony_ci} 36cb93a386Sopenharmony_ci 37cb93a386Sopenharmony_ci//////////////////////////////////////////////////////////////////////// 38cb93a386Sopenharmony_ci 39cb93a386Sopenharmony_cistatic int valid_unit_divide(SkScalar numer, SkScalar denom, SkScalar* ratio) { 40cb93a386Sopenharmony_ci SkASSERT(ratio); 41cb93a386Sopenharmony_ci 42cb93a386Sopenharmony_ci if (numer < 0) { 43cb93a386Sopenharmony_ci numer = -numer; 44cb93a386Sopenharmony_ci denom = -denom; 45cb93a386Sopenharmony_ci } 46cb93a386Sopenharmony_ci 47cb93a386Sopenharmony_ci if (denom == 0 || numer == 0 || numer >= denom) { 48cb93a386Sopenharmony_ci return 0; 49cb93a386Sopenharmony_ci } 50cb93a386Sopenharmony_ci 51cb93a386Sopenharmony_ci SkScalar r = numer / denom; 52cb93a386Sopenharmony_ci if (SkScalarIsNaN(r)) { 53cb93a386Sopenharmony_ci return 0; 54cb93a386Sopenharmony_ci } 55cb93a386Sopenharmony_ci SkASSERTF(r >= 0 && r < SK_Scalar1, "numer %f, denom %f, r %f", numer, denom, r); 56cb93a386Sopenharmony_ci if (r == 0) { // catch underflow if numer <<<< denom 57cb93a386Sopenharmony_ci return 0; 58cb93a386Sopenharmony_ci } 59cb93a386Sopenharmony_ci *ratio = r; 60cb93a386Sopenharmony_ci return 1; 61cb93a386Sopenharmony_ci} 62cb93a386Sopenharmony_ci 63cb93a386Sopenharmony_ci// Just returns its argument, but makes it easy to set a break-point to know when 64cb93a386Sopenharmony_ci// SkFindUnitQuadRoots is going to return 0 (an error). 65cb93a386Sopenharmony_cistatic int return_check_zero(int value) { 66cb93a386Sopenharmony_ci if (value == 0) { 67cb93a386Sopenharmony_ci return 0; 68cb93a386Sopenharmony_ci } 69cb93a386Sopenharmony_ci return value; 70cb93a386Sopenharmony_ci} 71cb93a386Sopenharmony_ci 72cb93a386Sopenharmony_ci/** From Numerical Recipes in C. 73cb93a386Sopenharmony_ci 74cb93a386Sopenharmony_ci Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C]) 75cb93a386Sopenharmony_ci x1 = Q / A 76cb93a386Sopenharmony_ci x2 = C / Q 77cb93a386Sopenharmony_ci*/ 78cb93a386Sopenharmony_ciint SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]) { 79cb93a386Sopenharmony_ci SkASSERT(roots); 80cb93a386Sopenharmony_ci 81cb93a386Sopenharmony_ci if (A == 0) { 82cb93a386Sopenharmony_ci return return_check_zero(valid_unit_divide(-C, B, roots)); 83cb93a386Sopenharmony_ci } 84cb93a386Sopenharmony_ci 85cb93a386Sopenharmony_ci SkScalar* r = roots; 86cb93a386Sopenharmony_ci 87cb93a386Sopenharmony_ci // use doubles so we don't overflow temporarily trying to compute R 88cb93a386Sopenharmony_ci double dr = (double)B * B - 4 * (double)A * C; 89cb93a386Sopenharmony_ci if (dr < 0) { 90cb93a386Sopenharmony_ci return return_check_zero(0); 91cb93a386Sopenharmony_ci } 92cb93a386Sopenharmony_ci dr = sqrt(dr); 93cb93a386Sopenharmony_ci SkScalar R = SkDoubleToScalar(dr); 94cb93a386Sopenharmony_ci if (!SkScalarIsFinite(R)) { 95cb93a386Sopenharmony_ci return return_check_zero(0); 96cb93a386Sopenharmony_ci } 97cb93a386Sopenharmony_ci 98cb93a386Sopenharmony_ci SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2; 99cb93a386Sopenharmony_ci r += valid_unit_divide(Q, A, r); 100cb93a386Sopenharmony_ci r += valid_unit_divide(C, Q, r); 101cb93a386Sopenharmony_ci if (r - roots == 2) { 102cb93a386Sopenharmony_ci if (roots[0] > roots[1]) { 103cb93a386Sopenharmony_ci using std::swap; 104cb93a386Sopenharmony_ci swap(roots[0], roots[1]); 105cb93a386Sopenharmony_ci } else if (roots[0] == roots[1]) { // nearly-equal? 106cb93a386Sopenharmony_ci r -= 1; // skip the double root 107cb93a386Sopenharmony_ci } 108cb93a386Sopenharmony_ci } 109cb93a386Sopenharmony_ci return return_check_zero((int)(r - roots)); 110cb93a386Sopenharmony_ci} 111cb93a386Sopenharmony_ci 112cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 113cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 114cb93a386Sopenharmony_ci 115cb93a386Sopenharmony_civoid SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent) { 116cb93a386Sopenharmony_ci SkASSERT(src); 117cb93a386Sopenharmony_ci SkASSERT(t >= 0 && t <= SK_Scalar1); 118cb93a386Sopenharmony_ci 119cb93a386Sopenharmony_ci if (pt) { 120cb93a386Sopenharmony_ci *pt = SkEvalQuadAt(src, t); 121cb93a386Sopenharmony_ci } 122cb93a386Sopenharmony_ci if (tangent) { 123cb93a386Sopenharmony_ci *tangent = SkEvalQuadTangentAt(src, t); 124cb93a386Sopenharmony_ci } 125cb93a386Sopenharmony_ci} 126cb93a386Sopenharmony_ci 127cb93a386Sopenharmony_ciSkPoint SkEvalQuadAt(const SkPoint src[3], SkScalar t) { 128cb93a386Sopenharmony_ci return to_point(SkQuadCoeff(src).eval(t)); 129cb93a386Sopenharmony_ci} 130cb93a386Sopenharmony_ci 131cb93a386Sopenharmony_ciSkVector SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t) { 132cb93a386Sopenharmony_ci // The derivative equation is 2(b - a +(a - 2b +c)t). This returns a 133cb93a386Sopenharmony_ci // zero tangent vector when t is 0 or 1, and the control point is equal 134cb93a386Sopenharmony_ci // to the end point. In this case, use the quad end points to compute the tangent. 135cb93a386Sopenharmony_ci if ((t == 0 && src[0] == src[1]) || (t == 1 && src[1] == src[2])) { 136cb93a386Sopenharmony_ci return src[2] - src[0]; 137cb93a386Sopenharmony_ci } 138cb93a386Sopenharmony_ci SkASSERT(src); 139cb93a386Sopenharmony_ci SkASSERT(t >= 0 && t <= SK_Scalar1); 140cb93a386Sopenharmony_ci 141cb93a386Sopenharmony_ci Sk2s P0 = from_point(src[0]); 142cb93a386Sopenharmony_ci Sk2s P1 = from_point(src[1]); 143cb93a386Sopenharmony_ci Sk2s P2 = from_point(src[2]); 144cb93a386Sopenharmony_ci 145cb93a386Sopenharmony_ci Sk2s B = P1 - P0; 146cb93a386Sopenharmony_ci Sk2s A = P2 - P1 - B; 147cb93a386Sopenharmony_ci Sk2s T = A * Sk2s(t) + B; 148cb93a386Sopenharmony_ci 149cb93a386Sopenharmony_ci return to_vector(T + T); 150cb93a386Sopenharmony_ci} 151cb93a386Sopenharmony_ci 152cb93a386Sopenharmony_cistatic inline Sk2s interp(const Sk2s& v0, const Sk2s& v1, const Sk2s& t) { 153cb93a386Sopenharmony_ci return v0 + (v1 - v0) * t; 154cb93a386Sopenharmony_ci} 155cb93a386Sopenharmony_ci 156cb93a386Sopenharmony_civoid SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t) { 157cb93a386Sopenharmony_ci SkASSERT(t > 0 && t < SK_Scalar1); 158cb93a386Sopenharmony_ci 159cb93a386Sopenharmony_ci Sk2s p0 = from_point(src[0]); 160cb93a386Sopenharmony_ci Sk2s p1 = from_point(src[1]); 161cb93a386Sopenharmony_ci Sk2s p2 = from_point(src[2]); 162cb93a386Sopenharmony_ci Sk2s tt(t); 163cb93a386Sopenharmony_ci 164cb93a386Sopenharmony_ci Sk2s p01 = interp(p0, p1, tt); 165cb93a386Sopenharmony_ci Sk2s p12 = interp(p1, p2, tt); 166cb93a386Sopenharmony_ci 167cb93a386Sopenharmony_ci dst[0] = to_point(p0); 168cb93a386Sopenharmony_ci dst[1] = to_point(p01); 169cb93a386Sopenharmony_ci dst[2] = to_point(interp(p01, p12, tt)); 170cb93a386Sopenharmony_ci dst[3] = to_point(p12); 171cb93a386Sopenharmony_ci dst[4] = to_point(p2); 172cb93a386Sopenharmony_ci} 173cb93a386Sopenharmony_ci 174cb93a386Sopenharmony_civoid SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]) { 175cb93a386Sopenharmony_ci SkChopQuadAt(src, dst, 0.5f); 176cb93a386Sopenharmony_ci} 177cb93a386Sopenharmony_ci 178cb93a386Sopenharmony_cifloat SkMeasureAngleBetweenVectors(SkVector a, SkVector b) { 179cb93a386Sopenharmony_ci float cosTheta = sk_ieee_float_divide(a.dot(b), sqrtf(a.dot(a) * b.dot(b))); 180cb93a386Sopenharmony_ci // Pin cosTheta such that if it is NaN (e.g., if a or b was 0), then we return acos(1) = 0. 181cb93a386Sopenharmony_ci cosTheta = std::max(std::min(1.f, cosTheta), -1.f); 182cb93a386Sopenharmony_ci return acosf(cosTheta); 183cb93a386Sopenharmony_ci} 184cb93a386Sopenharmony_ci 185cb93a386Sopenharmony_ciSkVector SkFindBisector(SkVector a, SkVector b) { 186cb93a386Sopenharmony_ci std::array<SkVector, 2> v; 187cb93a386Sopenharmony_ci if (a.dot(b) >= 0) { 188cb93a386Sopenharmony_ci // a,b are within +/-90 degrees apart. 189cb93a386Sopenharmony_ci v = {a, b}; 190cb93a386Sopenharmony_ci } else if (a.cross(b) >= 0) { 191cb93a386Sopenharmony_ci // a,b are >90 degrees apart. Find the bisector of their interior normals instead. (Above 90 192cb93a386Sopenharmony_ci // degrees, the original vectors start cancelling each other out which eventually becomes 193cb93a386Sopenharmony_ci // unstable.) 194cb93a386Sopenharmony_ci v[0].set(-a.fY, +a.fX); 195cb93a386Sopenharmony_ci v[1].set(+b.fY, -b.fX); 196cb93a386Sopenharmony_ci } else { 197cb93a386Sopenharmony_ci // a,b are <-90 degrees apart. Find the bisector of their interior normals instead. (Below 198cb93a386Sopenharmony_ci // -90 degrees, the original vectors start cancelling each other out which eventually 199cb93a386Sopenharmony_ci // becomes unstable.) 200cb93a386Sopenharmony_ci v[0].set(+a.fY, -a.fX); 201cb93a386Sopenharmony_ci v[1].set(-b.fY, +b.fX); 202cb93a386Sopenharmony_ci } 203cb93a386Sopenharmony_ci // Return "normalize(v[0]) + normalize(v[1])". 204cb93a386Sopenharmony_ci Sk2f x0_x1, y0_y1; 205cb93a386Sopenharmony_ci Sk2f::Load2(v.data(), &x0_x1, &y0_y1); 206cb93a386Sopenharmony_ci Sk2f invLengths = 1.0f / (x0_x1 * x0_x1 + y0_y1 * y0_y1).sqrt(); 207cb93a386Sopenharmony_ci x0_x1 *= invLengths; 208cb93a386Sopenharmony_ci y0_y1 *= invLengths; 209cb93a386Sopenharmony_ci return SkPoint{x0_x1[0] + x0_x1[1], y0_y1[0] + y0_y1[1]}; 210cb93a386Sopenharmony_ci} 211cb93a386Sopenharmony_ci 212cb93a386Sopenharmony_cifloat SkFindQuadMidTangent(const SkPoint src[3]) { 213cb93a386Sopenharmony_ci // Tangents point in the direction of increasing T, so tan0 and -tan1 both point toward the 214cb93a386Sopenharmony_ci // midtangent. The bisector of tan0 and -tan1 is orthogonal to the midtangent: 215cb93a386Sopenharmony_ci // 216cb93a386Sopenharmony_ci // n dot midtangent = 0 217cb93a386Sopenharmony_ci // 218cb93a386Sopenharmony_ci SkVector tan0 = src[1] - src[0]; 219cb93a386Sopenharmony_ci SkVector tan1 = src[2] - src[1]; 220cb93a386Sopenharmony_ci SkVector bisector = SkFindBisector(tan0, -tan1); 221cb93a386Sopenharmony_ci 222cb93a386Sopenharmony_ci // The midtangent can be found where (F' dot bisector) = 0: 223cb93a386Sopenharmony_ci // 224cb93a386Sopenharmony_ci // 0 = (F'(T) dot bisector) = |2*T 1| * |p0 - 2*p1 + p2| * |bisector.x| 225cb93a386Sopenharmony_ci // |-2*p0 + 2*p1 | |bisector.y| 226cb93a386Sopenharmony_ci // 227cb93a386Sopenharmony_ci // = |2*T 1| * |tan1 - tan0| * |nx| 228cb93a386Sopenharmony_ci // |2*tan0 | |ny| 229cb93a386Sopenharmony_ci // 230cb93a386Sopenharmony_ci // = 2*T * ((tan1 - tan0) dot bisector) + (2*tan0 dot bisector) 231cb93a386Sopenharmony_ci // 232cb93a386Sopenharmony_ci // T = (tan0 dot bisector) / ((tan0 - tan1) dot bisector) 233cb93a386Sopenharmony_ci float T = sk_ieee_float_divide(tan0.dot(bisector), (tan0 - tan1).dot(bisector)); 234cb93a386Sopenharmony_ci if (!(T > 0 && T < 1)) { // Use "!(positive_logic)" so T=nan will take this branch. 235cb93a386Sopenharmony_ci T = .5; // The quadratic was a line or near-line. Just chop at .5. 236cb93a386Sopenharmony_ci } 237cb93a386Sopenharmony_ci 238cb93a386Sopenharmony_ci return T; 239cb93a386Sopenharmony_ci} 240cb93a386Sopenharmony_ci 241cb93a386Sopenharmony_ci/** Quad'(t) = At + B, where 242cb93a386Sopenharmony_ci A = 2(a - 2b + c) 243cb93a386Sopenharmony_ci B = 2(b - a) 244cb93a386Sopenharmony_ci Solve for t, only if it fits between 0 < t < 1 245cb93a386Sopenharmony_ci*/ 246cb93a386Sopenharmony_ciint SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValue[1]) { 247cb93a386Sopenharmony_ci /* At + B == 0 248cb93a386Sopenharmony_ci t = -B / A 249cb93a386Sopenharmony_ci */ 250cb93a386Sopenharmony_ci return valid_unit_divide(a - b, a - b - b + c, tValue); 251cb93a386Sopenharmony_ci} 252cb93a386Sopenharmony_ci 253cb93a386Sopenharmony_cistatic inline void flatten_double_quad_extrema(SkScalar coords[14]) { 254cb93a386Sopenharmony_ci coords[2] = coords[6] = coords[4]; 255cb93a386Sopenharmony_ci} 256cb93a386Sopenharmony_ci 257cb93a386Sopenharmony_ci/* Returns 0 for 1 quad, and 1 for two quads, either way the answer is 258cb93a386Sopenharmony_ci stored in dst[]. Guarantees that the 1/2 quads will be monotonic. 259cb93a386Sopenharmony_ci */ 260cb93a386Sopenharmony_ciint SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5]) { 261cb93a386Sopenharmony_ci SkASSERT(src); 262cb93a386Sopenharmony_ci SkASSERT(dst); 263cb93a386Sopenharmony_ci 264cb93a386Sopenharmony_ci SkScalar a = src[0].fY; 265cb93a386Sopenharmony_ci SkScalar b = src[1].fY; 266cb93a386Sopenharmony_ci SkScalar c = src[2].fY; 267cb93a386Sopenharmony_ci 268cb93a386Sopenharmony_ci if (is_not_monotonic(a, b, c)) { 269cb93a386Sopenharmony_ci SkScalar tValue; 270cb93a386Sopenharmony_ci if (valid_unit_divide(a - b, a - b - b + c, &tValue)) { 271cb93a386Sopenharmony_ci SkChopQuadAt(src, dst, tValue); 272cb93a386Sopenharmony_ci flatten_double_quad_extrema(&dst[0].fY); 273cb93a386Sopenharmony_ci return 1; 274cb93a386Sopenharmony_ci } 275cb93a386Sopenharmony_ci // if we get here, we need to force dst to be monotonic, even though 276cb93a386Sopenharmony_ci // we couldn't compute a unit_divide value (probably underflow). 277cb93a386Sopenharmony_ci b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c; 278cb93a386Sopenharmony_ci } 279cb93a386Sopenharmony_ci dst[0].set(src[0].fX, a); 280cb93a386Sopenharmony_ci dst[1].set(src[1].fX, b); 281cb93a386Sopenharmony_ci dst[2].set(src[2].fX, c); 282cb93a386Sopenharmony_ci return 0; 283cb93a386Sopenharmony_ci} 284cb93a386Sopenharmony_ci 285cb93a386Sopenharmony_ci/* Returns 0 for 1 quad, and 1 for two quads, either way the answer is 286cb93a386Sopenharmony_ci stored in dst[]. Guarantees that the 1/2 quads will be monotonic. 287cb93a386Sopenharmony_ci */ 288cb93a386Sopenharmony_ciint SkChopQuadAtXExtrema(const SkPoint src[3], SkPoint dst[5]) { 289cb93a386Sopenharmony_ci SkASSERT(src); 290cb93a386Sopenharmony_ci SkASSERT(dst); 291cb93a386Sopenharmony_ci 292cb93a386Sopenharmony_ci SkScalar a = src[0].fX; 293cb93a386Sopenharmony_ci SkScalar b = src[1].fX; 294cb93a386Sopenharmony_ci SkScalar c = src[2].fX; 295cb93a386Sopenharmony_ci 296cb93a386Sopenharmony_ci if (is_not_monotonic(a, b, c)) { 297cb93a386Sopenharmony_ci SkScalar tValue; 298cb93a386Sopenharmony_ci if (valid_unit_divide(a - b, a - b - b + c, &tValue)) { 299cb93a386Sopenharmony_ci SkChopQuadAt(src, dst, tValue); 300cb93a386Sopenharmony_ci flatten_double_quad_extrema(&dst[0].fX); 301cb93a386Sopenharmony_ci return 1; 302cb93a386Sopenharmony_ci } 303cb93a386Sopenharmony_ci // if we get here, we need to force dst to be monotonic, even though 304cb93a386Sopenharmony_ci // we couldn't compute a unit_divide value (probably underflow). 305cb93a386Sopenharmony_ci b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c; 306cb93a386Sopenharmony_ci } 307cb93a386Sopenharmony_ci dst[0].set(a, src[0].fY); 308cb93a386Sopenharmony_ci dst[1].set(b, src[1].fY); 309cb93a386Sopenharmony_ci dst[2].set(c, src[2].fY); 310cb93a386Sopenharmony_ci return 0; 311cb93a386Sopenharmony_ci} 312cb93a386Sopenharmony_ci 313cb93a386Sopenharmony_ci// F(t) = a (1 - t) ^ 2 + 2 b t (1 - t) + c t ^ 2 314cb93a386Sopenharmony_ci// F'(t) = 2 (b - a) + 2 (a - 2b + c) t 315cb93a386Sopenharmony_ci// F''(t) = 2 (a - 2b + c) 316cb93a386Sopenharmony_ci// 317cb93a386Sopenharmony_ci// A = 2 (b - a) 318cb93a386Sopenharmony_ci// B = 2 (a - 2b + c) 319cb93a386Sopenharmony_ci// 320cb93a386Sopenharmony_ci// Maximum curvature for a quadratic means solving 321cb93a386Sopenharmony_ci// Fx' Fx'' + Fy' Fy'' = 0 322cb93a386Sopenharmony_ci// 323cb93a386Sopenharmony_ci// t = - (Ax Bx + Ay By) / (Bx ^ 2 + By ^ 2) 324cb93a386Sopenharmony_ci// 325cb93a386Sopenharmony_ciSkScalar SkFindQuadMaxCurvature(const SkPoint src[3]) { 326cb93a386Sopenharmony_ci SkScalar Ax = src[1].fX - src[0].fX; 327cb93a386Sopenharmony_ci SkScalar Ay = src[1].fY - src[0].fY; 328cb93a386Sopenharmony_ci SkScalar Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX; 329cb93a386Sopenharmony_ci SkScalar By = src[0].fY - src[1].fY - src[1].fY + src[2].fY; 330cb93a386Sopenharmony_ci 331cb93a386Sopenharmony_ci SkScalar numer = -(Ax * Bx + Ay * By); 332cb93a386Sopenharmony_ci SkScalar denom = Bx * Bx + By * By; 333cb93a386Sopenharmony_ci if (denom < 0) { 334cb93a386Sopenharmony_ci numer = -numer; 335cb93a386Sopenharmony_ci denom = -denom; 336cb93a386Sopenharmony_ci } 337cb93a386Sopenharmony_ci if (numer <= 0) { 338cb93a386Sopenharmony_ci return 0; 339cb93a386Sopenharmony_ci } 340cb93a386Sopenharmony_ci if (numer >= denom) { // Also catches denom=0. 341cb93a386Sopenharmony_ci return 1; 342cb93a386Sopenharmony_ci } 343cb93a386Sopenharmony_ci SkScalar t = numer / denom; 344cb93a386Sopenharmony_ci SkASSERT((0 <= t && t < 1) || SkScalarIsNaN(t)); 345cb93a386Sopenharmony_ci return t; 346cb93a386Sopenharmony_ci} 347cb93a386Sopenharmony_ci 348cb93a386Sopenharmony_ciint SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5]) { 349cb93a386Sopenharmony_ci SkScalar t = SkFindQuadMaxCurvature(src); 350cb93a386Sopenharmony_ci if (t > 0 && t < 1) { 351cb93a386Sopenharmony_ci SkChopQuadAt(src, dst, t); 352cb93a386Sopenharmony_ci return 2; 353cb93a386Sopenharmony_ci } else { 354cb93a386Sopenharmony_ci memcpy(dst, src, 3 * sizeof(SkPoint)); 355cb93a386Sopenharmony_ci return 1; 356cb93a386Sopenharmony_ci } 357cb93a386Sopenharmony_ci} 358cb93a386Sopenharmony_ci 359cb93a386Sopenharmony_civoid SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4]) { 360cb93a386Sopenharmony_ci Sk2s scale(SkDoubleToScalar(2.0 / 3.0)); 361cb93a386Sopenharmony_ci Sk2s s0 = from_point(src[0]); 362cb93a386Sopenharmony_ci Sk2s s1 = from_point(src[1]); 363cb93a386Sopenharmony_ci Sk2s s2 = from_point(src[2]); 364cb93a386Sopenharmony_ci 365cb93a386Sopenharmony_ci dst[0] = to_point(s0); 366cb93a386Sopenharmony_ci dst[1] = to_point(s0 + (s1 - s0) * scale); 367cb93a386Sopenharmony_ci dst[2] = to_point(s2 + (s1 - s2) * scale); 368cb93a386Sopenharmony_ci dst[3] = to_point(s2); 369cb93a386Sopenharmony_ci} 370cb93a386Sopenharmony_ci 371cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////// 372cb93a386Sopenharmony_ci///// CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS ///// 373cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////// 374cb93a386Sopenharmony_ci 375cb93a386Sopenharmony_cistatic SkVector eval_cubic_derivative(const SkPoint src[4], SkScalar t) { 376cb93a386Sopenharmony_ci SkQuadCoeff coeff; 377cb93a386Sopenharmony_ci Sk2s P0 = from_point(src[0]); 378cb93a386Sopenharmony_ci Sk2s P1 = from_point(src[1]); 379cb93a386Sopenharmony_ci Sk2s P2 = from_point(src[2]); 380cb93a386Sopenharmony_ci Sk2s P3 = from_point(src[3]); 381cb93a386Sopenharmony_ci 382cb93a386Sopenharmony_ci coeff.fA = P3 + Sk2s(3) * (P1 - P2) - P0; 383cb93a386Sopenharmony_ci coeff.fB = times_2(P2 - times_2(P1) + P0); 384cb93a386Sopenharmony_ci coeff.fC = P1 - P0; 385cb93a386Sopenharmony_ci return to_vector(coeff.eval(t)); 386cb93a386Sopenharmony_ci} 387cb93a386Sopenharmony_ci 388cb93a386Sopenharmony_cistatic SkVector eval_cubic_2ndDerivative(const SkPoint src[4], SkScalar t) { 389cb93a386Sopenharmony_ci Sk2s P0 = from_point(src[0]); 390cb93a386Sopenharmony_ci Sk2s P1 = from_point(src[1]); 391cb93a386Sopenharmony_ci Sk2s P2 = from_point(src[2]); 392cb93a386Sopenharmony_ci Sk2s P3 = from_point(src[3]); 393cb93a386Sopenharmony_ci Sk2s A = P3 + Sk2s(3) * (P1 - P2) - P0; 394cb93a386Sopenharmony_ci Sk2s B = P2 - times_2(P1) + P0; 395cb93a386Sopenharmony_ci 396cb93a386Sopenharmony_ci return to_vector(A * Sk2s(t) + B); 397cb93a386Sopenharmony_ci} 398cb93a386Sopenharmony_ci 399cb93a386Sopenharmony_civoid SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* loc, 400cb93a386Sopenharmony_ci SkVector* tangent, SkVector* curvature) { 401cb93a386Sopenharmony_ci SkASSERT(src); 402cb93a386Sopenharmony_ci SkASSERT(t >= 0 && t <= SK_Scalar1); 403cb93a386Sopenharmony_ci 404cb93a386Sopenharmony_ci if (loc) { 405cb93a386Sopenharmony_ci *loc = to_point(SkCubicCoeff(src).eval(t)); 406cb93a386Sopenharmony_ci } 407cb93a386Sopenharmony_ci if (tangent) { 408cb93a386Sopenharmony_ci // The derivative equation returns a zero tangent vector when t is 0 or 1, and the 409cb93a386Sopenharmony_ci // adjacent control point is equal to the end point. In this case, use the 410cb93a386Sopenharmony_ci // next control point or the end points to compute the tangent. 411cb93a386Sopenharmony_ci if ((t == 0 && src[0] == src[1]) || (t == 1 && src[2] == src[3])) { 412cb93a386Sopenharmony_ci if (t == 0) { 413cb93a386Sopenharmony_ci *tangent = src[2] - src[0]; 414cb93a386Sopenharmony_ci } else { 415cb93a386Sopenharmony_ci *tangent = src[3] - src[1]; 416cb93a386Sopenharmony_ci } 417cb93a386Sopenharmony_ci if (!tangent->fX && !tangent->fY) { 418cb93a386Sopenharmony_ci *tangent = src[3] - src[0]; 419cb93a386Sopenharmony_ci } 420cb93a386Sopenharmony_ci } else { 421cb93a386Sopenharmony_ci *tangent = eval_cubic_derivative(src, t); 422cb93a386Sopenharmony_ci } 423cb93a386Sopenharmony_ci } 424cb93a386Sopenharmony_ci if (curvature) { 425cb93a386Sopenharmony_ci *curvature = eval_cubic_2ndDerivative(src, t); 426cb93a386Sopenharmony_ci } 427cb93a386Sopenharmony_ci} 428cb93a386Sopenharmony_ci 429cb93a386Sopenharmony_ci/** Cubic'(t) = At^2 + Bt + C, where 430cb93a386Sopenharmony_ci A = 3(-a + 3(b - c) + d) 431cb93a386Sopenharmony_ci B = 6(a - 2b + c) 432cb93a386Sopenharmony_ci C = 3(b - a) 433cb93a386Sopenharmony_ci Solve for t, keeping only those that fit betwee 0 < t < 1 434cb93a386Sopenharmony_ci*/ 435cb93a386Sopenharmony_ciint SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d, 436cb93a386Sopenharmony_ci SkScalar tValues[2]) { 437cb93a386Sopenharmony_ci // we divide A,B,C by 3 to simplify 438cb93a386Sopenharmony_ci SkScalar A = d - a + 3*(b - c); 439cb93a386Sopenharmony_ci SkScalar B = 2*(a - b - b + c); 440cb93a386Sopenharmony_ci SkScalar C = b - a; 441cb93a386Sopenharmony_ci 442cb93a386Sopenharmony_ci return SkFindUnitQuadRoots(A, B, C, tValues); 443cb93a386Sopenharmony_ci} 444cb93a386Sopenharmony_ci 445cb93a386Sopenharmony_ci// This does not return b when t==1, but it otherwise seems to get better precision than 446cb93a386Sopenharmony_ci// "a*(1 - t) + b*t" for things like chopping cubics on exact cusp points. 447cb93a386Sopenharmony_ci// The responsibility falls on the caller to check that t != 1 before calling. 448cb93a386Sopenharmony_citemplate<int N, typename T> 449cb93a386Sopenharmony_ciinline static skvx::Vec<N,T> unchecked_mix(const skvx::Vec<N,T>& a, const skvx::Vec<N,T>& b, 450cb93a386Sopenharmony_ci const skvx::Vec<N,T>& t) { 451cb93a386Sopenharmony_ci return (b - a)*t + a; 452cb93a386Sopenharmony_ci} 453cb93a386Sopenharmony_ci 454cb93a386Sopenharmony_civoid SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t) { 455cb93a386Sopenharmony_ci using float2 = skvx::Vec<2,float>; 456cb93a386Sopenharmony_ci SkASSERT(0 <= t && t <= 1); 457cb93a386Sopenharmony_ci 458cb93a386Sopenharmony_ci if (t == 1) { 459cb93a386Sopenharmony_ci memcpy(dst, src, sizeof(SkPoint) * 4); 460cb93a386Sopenharmony_ci dst[4] = dst[5] = dst[6] = src[3]; 461cb93a386Sopenharmony_ci return; 462cb93a386Sopenharmony_ci } 463cb93a386Sopenharmony_ci 464cb93a386Sopenharmony_ci float2 p0 = skvx::bit_pun<float2>(src[0]); 465cb93a386Sopenharmony_ci float2 p1 = skvx::bit_pun<float2>(src[1]); 466cb93a386Sopenharmony_ci float2 p2 = skvx::bit_pun<float2>(src[2]); 467cb93a386Sopenharmony_ci float2 p3 = skvx::bit_pun<float2>(src[3]); 468cb93a386Sopenharmony_ci float2 T = t; 469cb93a386Sopenharmony_ci 470cb93a386Sopenharmony_ci float2 ab = unchecked_mix(p0, p1, T); 471cb93a386Sopenharmony_ci float2 bc = unchecked_mix(p1, p2, T); 472cb93a386Sopenharmony_ci float2 cd = unchecked_mix(p2, p3, T); 473cb93a386Sopenharmony_ci float2 abc = unchecked_mix(ab, bc, T); 474cb93a386Sopenharmony_ci float2 bcd = unchecked_mix(bc, cd, T); 475cb93a386Sopenharmony_ci float2 abcd = unchecked_mix(abc, bcd, T); 476cb93a386Sopenharmony_ci 477cb93a386Sopenharmony_ci dst[0] = skvx::bit_pun<SkPoint>(p0); 478cb93a386Sopenharmony_ci dst[1] = skvx::bit_pun<SkPoint>(ab); 479cb93a386Sopenharmony_ci dst[2] = skvx::bit_pun<SkPoint>(abc); 480cb93a386Sopenharmony_ci dst[3] = skvx::bit_pun<SkPoint>(abcd); 481cb93a386Sopenharmony_ci dst[4] = skvx::bit_pun<SkPoint>(bcd); 482cb93a386Sopenharmony_ci dst[5] = skvx::bit_pun<SkPoint>(cd); 483cb93a386Sopenharmony_ci dst[6] = skvx::bit_pun<SkPoint>(p3); 484cb93a386Sopenharmony_ci} 485cb93a386Sopenharmony_ci 486cb93a386Sopenharmony_civoid SkChopCubicAt(const SkPoint src[4], SkPoint dst[10], float t0, float t1) { 487cb93a386Sopenharmony_ci using float4 = skvx::Vec<4,float>; 488cb93a386Sopenharmony_ci using float2 = skvx::Vec<2,float>; 489cb93a386Sopenharmony_ci SkASSERT(0 <= t0 && t0 <= t1 && t1 <= 1); 490cb93a386Sopenharmony_ci 491cb93a386Sopenharmony_ci if (t1 == 1) { 492cb93a386Sopenharmony_ci SkChopCubicAt(src, dst, t0); 493cb93a386Sopenharmony_ci dst[7] = dst[8] = dst[9] = src[3]; 494cb93a386Sopenharmony_ci return; 495cb93a386Sopenharmony_ci } 496cb93a386Sopenharmony_ci 497cb93a386Sopenharmony_ci // Perform both chops in parallel using 4-lane SIMD. 498cb93a386Sopenharmony_ci float4 p00, p11, p22, p33, T; 499cb93a386Sopenharmony_ci p00.lo = p00.hi = skvx::bit_pun<float2>(src[0]); 500cb93a386Sopenharmony_ci p11.lo = p11.hi = skvx::bit_pun<float2>(src[1]); 501cb93a386Sopenharmony_ci p22.lo = p22.hi = skvx::bit_pun<float2>(src[2]); 502cb93a386Sopenharmony_ci p33.lo = p33.hi = skvx::bit_pun<float2>(src[3]); 503cb93a386Sopenharmony_ci T.lo = t0; 504cb93a386Sopenharmony_ci T.hi = t1; 505cb93a386Sopenharmony_ci 506cb93a386Sopenharmony_ci float4 ab = unchecked_mix(p00, p11, T); 507cb93a386Sopenharmony_ci float4 bc = unchecked_mix(p11, p22, T); 508cb93a386Sopenharmony_ci float4 cd = unchecked_mix(p22, p33, T); 509cb93a386Sopenharmony_ci float4 abc = unchecked_mix(ab, bc, T); 510cb93a386Sopenharmony_ci float4 bcd = unchecked_mix(bc, cd, T); 511cb93a386Sopenharmony_ci float4 abcd = unchecked_mix(abc, bcd, T); 512cb93a386Sopenharmony_ci float4 middle = unchecked_mix(abc, bcd, skvx::shuffle<2,3,0,1>(T)); 513cb93a386Sopenharmony_ci 514cb93a386Sopenharmony_ci dst[0] = skvx::bit_pun<SkPoint>(p00.lo); 515cb93a386Sopenharmony_ci dst[1] = skvx::bit_pun<SkPoint>(ab.lo); 516cb93a386Sopenharmony_ci dst[2] = skvx::bit_pun<SkPoint>(abc.lo); 517cb93a386Sopenharmony_ci dst[3] = skvx::bit_pun<SkPoint>(abcd.lo); 518cb93a386Sopenharmony_ci middle.store(dst + 4); 519cb93a386Sopenharmony_ci dst[6] = skvx::bit_pun<SkPoint>(abcd.hi); 520cb93a386Sopenharmony_ci dst[7] = skvx::bit_pun<SkPoint>(bcd.hi); 521cb93a386Sopenharmony_ci dst[8] = skvx::bit_pun<SkPoint>(cd.hi); 522cb93a386Sopenharmony_ci dst[9] = skvx::bit_pun<SkPoint>(p33.hi); 523cb93a386Sopenharmony_ci} 524cb93a386Sopenharmony_ci 525cb93a386Sopenharmony_civoid SkChopCubicAt(const SkPoint src[4], SkPoint dst[], 526cb93a386Sopenharmony_ci const SkScalar tValues[], int tCount) { 527cb93a386Sopenharmony_ci using float2 = skvx::Vec<2,float>; 528cb93a386Sopenharmony_ci 529cb93a386Sopenharmony_ci SkASSERT(std::all_of(tValues, tValues + tCount, [](SkScalar t) { return t >= 0 && t <= 1; })); 530cb93a386Sopenharmony_ci SkASSERT(std::is_sorted(tValues, tValues + tCount)); 531cb93a386Sopenharmony_ci 532cb93a386Sopenharmony_ci if (dst) { 533cb93a386Sopenharmony_ci if (tCount == 0) { // nothing to chop 534cb93a386Sopenharmony_ci memcpy(dst, src, 4*sizeof(SkPoint)); 535cb93a386Sopenharmony_ci } else { 536cb93a386Sopenharmony_ci int i = 0; 537cb93a386Sopenharmony_ci for (; i < tCount - 1; i += 2) { 538cb93a386Sopenharmony_ci // Do two chops at once. 539cb93a386Sopenharmony_ci float2 tt = float2::Load(tValues + i); 540cb93a386Sopenharmony_ci if (i != 0) { 541cb93a386Sopenharmony_ci float lastT = tValues[i - 1]; 542cb93a386Sopenharmony_ci tt = skvx::pin((tt - lastT) / (1 - lastT), float2(0), float2(1)); 543cb93a386Sopenharmony_ci } 544cb93a386Sopenharmony_ci SkChopCubicAt(src, dst, tt[0], tt[1]); 545cb93a386Sopenharmony_ci src = dst = dst + 6; 546cb93a386Sopenharmony_ci } 547cb93a386Sopenharmony_ci if (i < tCount) { 548cb93a386Sopenharmony_ci // Chop the final cubic if there was an odd number of chops. 549cb93a386Sopenharmony_ci SkASSERT(i + 1 == tCount); 550cb93a386Sopenharmony_ci float t = tValues[i]; 551cb93a386Sopenharmony_ci if (i != 0) { 552cb93a386Sopenharmony_ci float lastT = tValues[i - 1]; 553cb93a386Sopenharmony_ci t = SkTPin(sk_ieee_float_divide(t - lastT, 1 - lastT), 0.f, 1.f); 554cb93a386Sopenharmony_ci } 555cb93a386Sopenharmony_ci SkChopCubicAt(src, dst, t); 556cb93a386Sopenharmony_ci } 557cb93a386Sopenharmony_ci } 558cb93a386Sopenharmony_ci } 559cb93a386Sopenharmony_ci} 560cb93a386Sopenharmony_ci 561cb93a386Sopenharmony_civoid SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7]) { 562cb93a386Sopenharmony_ci SkChopCubicAt(src, dst, 0.5f); 563cb93a386Sopenharmony_ci} 564cb93a386Sopenharmony_ci 565cb93a386Sopenharmony_cifloat SkMeasureNonInflectCubicRotation(const SkPoint pts[4]) { 566cb93a386Sopenharmony_ci SkVector a = pts[1] - pts[0]; 567cb93a386Sopenharmony_ci SkVector b = pts[2] - pts[1]; 568cb93a386Sopenharmony_ci SkVector c = pts[3] - pts[2]; 569cb93a386Sopenharmony_ci if (a.isZero()) { 570cb93a386Sopenharmony_ci return SkMeasureAngleBetweenVectors(b, c); 571cb93a386Sopenharmony_ci } 572cb93a386Sopenharmony_ci if (b.isZero()) { 573cb93a386Sopenharmony_ci return SkMeasureAngleBetweenVectors(a, c); 574cb93a386Sopenharmony_ci } 575cb93a386Sopenharmony_ci if (c.isZero()) { 576cb93a386Sopenharmony_ci return SkMeasureAngleBetweenVectors(a, b); 577cb93a386Sopenharmony_ci } 578cb93a386Sopenharmony_ci // Postulate: When no points are colocated and there are no inflection points in T=0..1, the 579cb93a386Sopenharmony_ci // rotation is: 360 degrees, minus the angle [p0,p1,p2], minus the angle [p1,p2,p3]. 580cb93a386Sopenharmony_ci return 2*SK_ScalarPI - SkMeasureAngleBetweenVectors(a,-b) - SkMeasureAngleBetweenVectors(b,-c); 581cb93a386Sopenharmony_ci} 582cb93a386Sopenharmony_ci 583cb93a386Sopenharmony_cistatic Sk4f fma(const Sk4f& f, float m, const Sk4f& a) { 584cb93a386Sopenharmony_ci return SkNx_fma(f, Sk4f(m), a); 585cb93a386Sopenharmony_ci} 586cb93a386Sopenharmony_ci 587cb93a386Sopenharmony_ci// Finds the root nearest 0.5. Returns 0.5 if the roots are undefined or outside 0..1. 588cb93a386Sopenharmony_cistatic float solve_quadratic_equation_for_midtangent(float a, float b, float c, float discr) { 589cb93a386Sopenharmony_ci // Quadratic formula from Numerical Recipes in C: 590cb93a386Sopenharmony_ci float q = -.5f * (b + copysignf(sqrtf(discr), b)); 591cb93a386Sopenharmony_ci // The roots are q/a and c/q. Pick the midtangent closer to T=.5. 592cb93a386Sopenharmony_ci float _5qa = -.5f*q*a; 593cb93a386Sopenharmony_ci float T = fabsf(q*q + _5qa) < fabsf(a*c + _5qa) ? sk_ieee_float_divide(q,a) 594cb93a386Sopenharmony_ci : sk_ieee_float_divide(c,q); 595cb93a386Sopenharmony_ci if (!(T > 0 && T < 1)) { // Use "!(positive_logic)" so T=NaN will take this branch. 596cb93a386Sopenharmony_ci // Either the curve is a flat line with no rotation or FP precision failed us. Chop at .5. 597cb93a386Sopenharmony_ci T = .5; 598cb93a386Sopenharmony_ci } 599cb93a386Sopenharmony_ci return T; 600cb93a386Sopenharmony_ci} 601cb93a386Sopenharmony_ci 602cb93a386Sopenharmony_cistatic float solve_quadratic_equation_for_midtangent(float a, float b, float c) { 603cb93a386Sopenharmony_ci return solve_quadratic_equation_for_midtangent(a, b, c, b*b - 4*a*c); 604cb93a386Sopenharmony_ci} 605cb93a386Sopenharmony_ci 606cb93a386Sopenharmony_cifloat SkFindCubicMidTangent(const SkPoint src[4]) { 607cb93a386Sopenharmony_ci // Tangents point in the direction of increasing T, so tan0 and -tan1 both point toward the 608cb93a386Sopenharmony_ci // midtangent. The bisector of tan0 and -tan1 is orthogonal to the midtangent: 609cb93a386Sopenharmony_ci // 610cb93a386Sopenharmony_ci // bisector dot midtangent == 0 611cb93a386Sopenharmony_ci // 612cb93a386Sopenharmony_ci SkVector tan0 = (src[0] == src[1]) ? src[2] - src[0] : src[1] - src[0]; 613cb93a386Sopenharmony_ci SkVector tan1 = (src[2] == src[3]) ? src[3] - src[1] : src[3] - src[2]; 614cb93a386Sopenharmony_ci SkVector bisector = SkFindBisector(tan0, -tan1); 615cb93a386Sopenharmony_ci 616cb93a386Sopenharmony_ci // Find the T value at the midtangent. This is a simple quadratic equation: 617cb93a386Sopenharmony_ci // 618cb93a386Sopenharmony_ci // midtangent dot bisector == 0, or using a tangent matrix C' in power basis form: 619cb93a386Sopenharmony_ci // 620cb93a386Sopenharmony_ci // |C'x C'y| 621cb93a386Sopenharmony_ci // |T^2 T 1| * |. . | * |bisector.x| == 0 622cb93a386Sopenharmony_ci // |. . | |bisector.y| 623cb93a386Sopenharmony_ci // 624cb93a386Sopenharmony_ci // The coeffs for the quadratic equation we need to solve are therefore: C' * bisector 625cb93a386Sopenharmony_ci static const Sk4f kM[4] = {Sk4f(-1, 2, -1, 0), 626cb93a386Sopenharmony_ci Sk4f( 3, -4, 1, 0), 627cb93a386Sopenharmony_ci Sk4f(-3, 2, 0, 0)}; 628cb93a386Sopenharmony_ci Sk4f C_x = fma(kM[0], src[0].fX, 629cb93a386Sopenharmony_ci fma(kM[1], src[1].fX, 630cb93a386Sopenharmony_ci fma(kM[2], src[2].fX, Sk4f(src[3].fX, 0,0,0)))); 631cb93a386Sopenharmony_ci Sk4f C_y = fma(kM[0], src[0].fY, 632cb93a386Sopenharmony_ci fma(kM[1], src[1].fY, 633cb93a386Sopenharmony_ci fma(kM[2], src[2].fY, Sk4f(src[3].fY, 0,0,0)))); 634cb93a386Sopenharmony_ci Sk4f coeffs = C_x * bisector.x() + C_y * bisector.y(); 635cb93a386Sopenharmony_ci 636cb93a386Sopenharmony_ci // Now solve the quadratic for T. 637cb93a386Sopenharmony_ci float T = 0; 638cb93a386Sopenharmony_ci float a=coeffs[0], b=coeffs[1], c=coeffs[2]; 639cb93a386Sopenharmony_ci float discr = b*b - 4*a*c; 640cb93a386Sopenharmony_ci if (discr > 0) { // This will only be false if the curve is a line. 641cb93a386Sopenharmony_ci return solve_quadratic_equation_for_midtangent(a, b, c, discr); 642cb93a386Sopenharmony_ci } else { 643cb93a386Sopenharmony_ci // This is a 0- or 360-degree flat line. It doesn't have single points of midtangent. 644cb93a386Sopenharmony_ci // (tangent == midtangent at every point on the curve except the cusp points.) 645cb93a386Sopenharmony_ci // Chop in between both cusps instead, if any. There can be up to two cusps on a flat line, 646cb93a386Sopenharmony_ci // both where the tangent is perpendicular to the starting tangent: 647cb93a386Sopenharmony_ci // 648cb93a386Sopenharmony_ci // tangent dot tan0 == 0 649cb93a386Sopenharmony_ci // 650cb93a386Sopenharmony_ci coeffs = C_x * tan0.x() + C_y * tan0.y(); 651cb93a386Sopenharmony_ci a = coeffs[0]; 652cb93a386Sopenharmony_ci b = coeffs[1]; 653cb93a386Sopenharmony_ci if (a != 0) { 654cb93a386Sopenharmony_ci // We want the point in between both cusps. The midpoint of: 655cb93a386Sopenharmony_ci // 656cb93a386Sopenharmony_ci // (-b +/- sqrt(b^2 - 4*a*c)) / (2*a) 657cb93a386Sopenharmony_ci // 658cb93a386Sopenharmony_ci // Is equal to: 659cb93a386Sopenharmony_ci // 660cb93a386Sopenharmony_ci // -b / (2*a) 661cb93a386Sopenharmony_ci T = -b / (2*a); 662cb93a386Sopenharmony_ci } 663cb93a386Sopenharmony_ci if (!(T > 0 && T < 1)) { // Use "!(positive_logic)" so T=NaN will take this branch. 664cb93a386Sopenharmony_ci // Either the curve is a flat line with no rotation or FP precision failed us. Chop at 665cb93a386Sopenharmony_ci // .5. 666cb93a386Sopenharmony_ci T = .5; 667cb93a386Sopenharmony_ci } 668cb93a386Sopenharmony_ci return T; 669cb93a386Sopenharmony_ci } 670cb93a386Sopenharmony_ci} 671cb93a386Sopenharmony_ci 672cb93a386Sopenharmony_cistatic void flatten_double_cubic_extrema(SkScalar coords[14]) { 673cb93a386Sopenharmony_ci coords[4] = coords[8] = coords[6]; 674cb93a386Sopenharmony_ci} 675cb93a386Sopenharmony_ci 676cb93a386Sopenharmony_ci/** Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that 677cb93a386Sopenharmony_ci the resulting beziers are monotonic in Y. This is called by the scan 678cb93a386Sopenharmony_ci converter. Depending on what is returned, dst[] is treated as follows: 679cb93a386Sopenharmony_ci 0 dst[0..3] is the original cubic 680cb93a386Sopenharmony_ci 1 dst[0..3] and dst[3..6] are the two new cubics 681cb93a386Sopenharmony_ci 2 dst[0..3], dst[3..6], dst[6..9] are the three new cubics 682cb93a386Sopenharmony_ci If dst == null, it is ignored and only the count is returned. 683cb93a386Sopenharmony_ci*/ 684cb93a386Sopenharmony_ciint SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10]) { 685cb93a386Sopenharmony_ci SkScalar tValues[2]; 686cb93a386Sopenharmony_ci int roots = SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY, 687cb93a386Sopenharmony_ci src[3].fY, tValues); 688cb93a386Sopenharmony_ci 689cb93a386Sopenharmony_ci SkChopCubicAt(src, dst, tValues, roots); 690cb93a386Sopenharmony_ci if (dst && roots > 0) { 691cb93a386Sopenharmony_ci // we do some cleanup to ensure our Y extrema are flat 692cb93a386Sopenharmony_ci flatten_double_cubic_extrema(&dst[0].fY); 693cb93a386Sopenharmony_ci if (roots == 2) { 694cb93a386Sopenharmony_ci flatten_double_cubic_extrema(&dst[3].fY); 695cb93a386Sopenharmony_ci } 696cb93a386Sopenharmony_ci } 697cb93a386Sopenharmony_ci return roots; 698cb93a386Sopenharmony_ci} 699cb93a386Sopenharmony_ci 700cb93a386Sopenharmony_ciint SkChopCubicAtXExtrema(const SkPoint src[4], SkPoint dst[10]) { 701cb93a386Sopenharmony_ci SkScalar tValues[2]; 702cb93a386Sopenharmony_ci int roots = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX, 703cb93a386Sopenharmony_ci src[3].fX, tValues); 704cb93a386Sopenharmony_ci 705cb93a386Sopenharmony_ci SkChopCubicAt(src, dst, tValues, roots); 706cb93a386Sopenharmony_ci if (dst && roots > 0) { 707cb93a386Sopenharmony_ci // we do some cleanup to ensure our Y extrema are flat 708cb93a386Sopenharmony_ci flatten_double_cubic_extrema(&dst[0].fX); 709cb93a386Sopenharmony_ci if (roots == 2) { 710cb93a386Sopenharmony_ci flatten_double_cubic_extrema(&dst[3].fX); 711cb93a386Sopenharmony_ci } 712cb93a386Sopenharmony_ci } 713cb93a386Sopenharmony_ci return roots; 714cb93a386Sopenharmony_ci} 715cb93a386Sopenharmony_ci 716cb93a386Sopenharmony_ci/** http://www.faculty.idc.ac.il/arik/quality/appendixA.html 717cb93a386Sopenharmony_ci 718cb93a386Sopenharmony_ci Inflection means that curvature is zero. 719cb93a386Sopenharmony_ci Curvature is [F' x F''] / [F'^3] 720cb93a386Sopenharmony_ci So we solve F'x X F''y - F'y X F''y == 0 721cb93a386Sopenharmony_ci After some canceling of the cubic term, we get 722cb93a386Sopenharmony_ci A = b - a 723cb93a386Sopenharmony_ci B = c - 2b + a 724cb93a386Sopenharmony_ci C = d - 3c + 3b - a 725cb93a386Sopenharmony_ci (BxCy - ByCx)t^2 + (AxCy - AyCx)t + AxBy - AyBx == 0 726cb93a386Sopenharmony_ci*/ 727cb93a386Sopenharmony_ciint SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[]) { 728cb93a386Sopenharmony_ci SkScalar Ax = src[1].fX - src[0].fX; 729cb93a386Sopenharmony_ci SkScalar Ay = src[1].fY - src[0].fY; 730cb93a386Sopenharmony_ci SkScalar Bx = src[2].fX - 2 * src[1].fX + src[0].fX; 731cb93a386Sopenharmony_ci SkScalar By = src[2].fY - 2 * src[1].fY + src[0].fY; 732cb93a386Sopenharmony_ci SkScalar Cx = src[3].fX + 3 * (src[1].fX - src[2].fX) - src[0].fX; 733cb93a386Sopenharmony_ci SkScalar Cy = src[3].fY + 3 * (src[1].fY - src[2].fY) - src[0].fY; 734cb93a386Sopenharmony_ci 735cb93a386Sopenharmony_ci return SkFindUnitQuadRoots(Bx*Cy - By*Cx, 736cb93a386Sopenharmony_ci Ax*Cy - Ay*Cx, 737cb93a386Sopenharmony_ci Ax*By - Ay*Bx, 738cb93a386Sopenharmony_ci tValues); 739cb93a386Sopenharmony_ci} 740cb93a386Sopenharmony_ci 741cb93a386Sopenharmony_ciint SkChopCubicAtInflections(const SkPoint src[], SkPoint dst[10]) { 742cb93a386Sopenharmony_ci SkScalar tValues[2]; 743cb93a386Sopenharmony_ci int count = SkFindCubicInflections(src, tValues); 744cb93a386Sopenharmony_ci 745cb93a386Sopenharmony_ci if (dst) { 746cb93a386Sopenharmony_ci if (count == 0) { 747cb93a386Sopenharmony_ci memcpy(dst, src, 4 * sizeof(SkPoint)); 748cb93a386Sopenharmony_ci } else { 749cb93a386Sopenharmony_ci SkChopCubicAt(src, dst, tValues, count); 750cb93a386Sopenharmony_ci } 751cb93a386Sopenharmony_ci } 752cb93a386Sopenharmony_ci return count + 1; 753cb93a386Sopenharmony_ci} 754cb93a386Sopenharmony_ci 755cb93a386Sopenharmony_ci// Assumes the third component of points is 1. 756cb93a386Sopenharmony_ci// Calcs p0 . (p1 x p2) 757cb93a386Sopenharmony_cistatic double calc_dot_cross_cubic(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) { 758cb93a386Sopenharmony_ci const double xComp = (double) p0.fX * ((double) p1.fY - (double) p2.fY); 759cb93a386Sopenharmony_ci const double yComp = (double) p0.fY * ((double) p2.fX - (double) p1.fX); 760cb93a386Sopenharmony_ci const double wComp = (double) p1.fX * (double) p2.fY - (double) p1.fY * (double) p2.fX; 761cb93a386Sopenharmony_ci return (xComp + yComp + wComp); 762cb93a386Sopenharmony_ci} 763cb93a386Sopenharmony_ci 764cb93a386Sopenharmony_ci// Returns a positive power of 2 that, when multiplied by n, and excepting the two edge cases listed 765cb93a386Sopenharmony_ci// below, shifts the exponent of n to yield a magnitude somewhere inside [1..2). 766cb93a386Sopenharmony_ci// Returns 2^1023 if abs(n) < 2^-1022 (including 0). 767cb93a386Sopenharmony_ci// Returns NaN if n is Inf or NaN. 768cb93a386Sopenharmony_ciinline static double previous_inverse_pow2(double n) { 769cb93a386Sopenharmony_ci uint64_t bits; 770cb93a386Sopenharmony_ci memcpy(&bits, &n, sizeof(double)); 771cb93a386Sopenharmony_ci bits = ((1023llu*2 << 52) + ((1llu << 52) - 1)) - bits; // exp=-exp 772cb93a386Sopenharmony_ci bits &= (0x7ffllu) << 52; // mantissa=1.0, sign=0 773cb93a386Sopenharmony_ci memcpy(&n, &bits, sizeof(double)); 774cb93a386Sopenharmony_ci return n; 775cb93a386Sopenharmony_ci} 776cb93a386Sopenharmony_ci 777cb93a386Sopenharmony_ciinline static void write_cubic_inflection_roots(double t0, double s0, double t1, double s1, 778cb93a386Sopenharmony_ci double* t, double* s) { 779cb93a386Sopenharmony_ci t[0] = t0; 780cb93a386Sopenharmony_ci s[0] = s0; 781cb93a386Sopenharmony_ci 782cb93a386Sopenharmony_ci // This copysign/abs business orients the implicit function so positive values are always on the 783cb93a386Sopenharmony_ci // "left" side of the curve. 784cb93a386Sopenharmony_ci t[1] = -copysign(t1, t1 * s1); 785cb93a386Sopenharmony_ci s[1] = -fabs(s1); 786cb93a386Sopenharmony_ci 787cb93a386Sopenharmony_ci // Ensure t[0]/s[0] <= t[1]/s[1] (s[1] is negative from above). 788cb93a386Sopenharmony_ci if (copysign(s[1], s[0]) * t[0] > -fabs(s[0]) * t[1]) { 789cb93a386Sopenharmony_ci using std::swap; 790cb93a386Sopenharmony_ci swap(t[0], t[1]); 791cb93a386Sopenharmony_ci swap(s[0], s[1]); 792cb93a386Sopenharmony_ci } 793cb93a386Sopenharmony_ci} 794cb93a386Sopenharmony_ci 795cb93a386Sopenharmony_ciSkCubicType SkClassifyCubic(const SkPoint P[4], double t[2], double s[2], double d[4]) { 796cb93a386Sopenharmony_ci // Find the cubic's inflection function, I = [T^3 -3T^2 3T -1] dot D. (D0 will always be 0 797cb93a386Sopenharmony_ci // for integral cubics.) 798cb93a386Sopenharmony_ci // 799cb93a386Sopenharmony_ci // See "Resolution Independent Curve Rendering using Programmable Graphics Hardware", 800cb93a386Sopenharmony_ci // 4.2 Curve Categorization: 801cb93a386Sopenharmony_ci // 802cb93a386Sopenharmony_ci // https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf 803cb93a386Sopenharmony_ci double A1 = calc_dot_cross_cubic(P[0], P[3], P[2]); 804cb93a386Sopenharmony_ci double A2 = calc_dot_cross_cubic(P[1], P[0], P[3]); 805cb93a386Sopenharmony_ci double A3 = calc_dot_cross_cubic(P[2], P[1], P[0]); 806cb93a386Sopenharmony_ci 807cb93a386Sopenharmony_ci double D3 = 3 * A3; 808cb93a386Sopenharmony_ci double D2 = D3 - A2; 809cb93a386Sopenharmony_ci double D1 = D2 - A2 + A1; 810cb93a386Sopenharmony_ci 811cb93a386Sopenharmony_ci // Shift the exponents in D so the largest magnitude falls somewhere in 1..2. This protects us 812cb93a386Sopenharmony_ci // from overflow down the road while solving for roots and KLM functionals. 813cb93a386Sopenharmony_ci double Dmax = std::max(std::max(fabs(D1), fabs(D2)), fabs(D3)); 814cb93a386Sopenharmony_ci double norm = previous_inverse_pow2(Dmax); 815cb93a386Sopenharmony_ci D1 *= norm; 816cb93a386Sopenharmony_ci D2 *= norm; 817cb93a386Sopenharmony_ci D3 *= norm; 818cb93a386Sopenharmony_ci 819cb93a386Sopenharmony_ci if (d) { 820cb93a386Sopenharmony_ci d[3] = D3; 821cb93a386Sopenharmony_ci d[2] = D2; 822cb93a386Sopenharmony_ci d[1] = D1; 823cb93a386Sopenharmony_ci d[0] = 0; 824cb93a386Sopenharmony_ci } 825cb93a386Sopenharmony_ci 826cb93a386Sopenharmony_ci // Now use the inflection function to classify the cubic. 827cb93a386Sopenharmony_ci // 828cb93a386Sopenharmony_ci // See "Resolution Independent Curve Rendering using Programmable Graphics Hardware", 829cb93a386Sopenharmony_ci // 4.4 Integral Cubics: 830cb93a386Sopenharmony_ci // 831cb93a386Sopenharmony_ci // https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf 832cb93a386Sopenharmony_ci if (0 != D1) { 833cb93a386Sopenharmony_ci double discr = 3*D2*D2 - 4*D1*D3; 834cb93a386Sopenharmony_ci if (discr > 0) { // Serpentine. 835cb93a386Sopenharmony_ci if (t && s) { 836cb93a386Sopenharmony_ci double q = 3*D2 + copysign(sqrt(3*discr), D2); 837cb93a386Sopenharmony_ci write_cubic_inflection_roots(q, 6*D1, 2*D3, q, t, s); 838cb93a386Sopenharmony_ci } 839cb93a386Sopenharmony_ci return SkCubicType::kSerpentine; 840cb93a386Sopenharmony_ci } else if (discr < 0) { // Loop. 841cb93a386Sopenharmony_ci if (t && s) { 842cb93a386Sopenharmony_ci double q = D2 + copysign(sqrt(-discr), D2); 843cb93a386Sopenharmony_ci write_cubic_inflection_roots(q, 2*D1, 2*(D2*D2 - D3*D1), D1*q, t, s); 844cb93a386Sopenharmony_ci } 845cb93a386Sopenharmony_ci return SkCubicType::kLoop; 846cb93a386Sopenharmony_ci } else { // Cusp. 847cb93a386Sopenharmony_ci if (t && s) { 848cb93a386Sopenharmony_ci write_cubic_inflection_roots(D2, 2*D1, D2, 2*D1, t, s); 849cb93a386Sopenharmony_ci } 850cb93a386Sopenharmony_ci return SkCubicType::kLocalCusp; 851cb93a386Sopenharmony_ci } 852cb93a386Sopenharmony_ci } else { 853cb93a386Sopenharmony_ci if (0 != D2) { // Cusp at T=infinity. 854cb93a386Sopenharmony_ci if (t && s) { 855cb93a386Sopenharmony_ci write_cubic_inflection_roots(D3, 3*D2, 1, 0, t, s); // T1=infinity. 856cb93a386Sopenharmony_ci } 857cb93a386Sopenharmony_ci return SkCubicType::kCuspAtInfinity; 858cb93a386Sopenharmony_ci } else { // Degenerate. 859cb93a386Sopenharmony_ci if (t && s) { 860cb93a386Sopenharmony_ci write_cubic_inflection_roots(1, 0, 1, 0, t, s); // T0=T1=infinity. 861cb93a386Sopenharmony_ci } 862cb93a386Sopenharmony_ci return 0 != D3 ? SkCubicType::kQuadratic : SkCubicType::kLineOrPoint; 863cb93a386Sopenharmony_ci } 864cb93a386Sopenharmony_ci } 865cb93a386Sopenharmony_ci} 866cb93a386Sopenharmony_ci 867cb93a386Sopenharmony_citemplate <typename T> void bubble_sort(T array[], int count) { 868cb93a386Sopenharmony_ci for (int i = count - 1; i > 0; --i) 869cb93a386Sopenharmony_ci for (int j = i; j > 0; --j) 870cb93a386Sopenharmony_ci if (array[j] < array[j-1]) 871cb93a386Sopenharmony_ci { 872cb93a386Sopenharmony_ci T tmp(array[j]); 873cb93a386Sopenharmony_ci array[j] = array[j-1]; 874cb93a386Sopenharmony_ci array[j-1] = tmp; 875cb93a386Sopenharmony_ci } 876cb93a386Sopenharmony_ci} 877cb93a386Sopenharmony_ci 878cb93a386Sopenharmony_ci/** 879cb93a386Sopenharmony_ci * Given an array and count, remove all pair-wise duplicates from the array, 880cb93a386Sopenharmony_ci * keeping the existing sorting, and return the new count 881cb93a386Sopenharmony_ci */ 882cb93a386Sopenharmony_cistatic int collaps_duplicates(SkScalar array[], int count) { 883cb93a386Sopenharmony_ci for (int n = count; n > 1; --n) { 884cb93a386Sopenharmony_ci if (array[0] == array[1]) { 885cb93a386Sopenharmony_ci for (int i = 1; i < n; ++i) { 886cb93a386Sopenharmony_ci array[i - 1] = array[i]; 887cb93a386Sopenharmony_ci } 888cb93a386Sopenharmony_ci count -= 1; 889cb93a386Sopenharmony_ci } else { 890cb93a386Sopenharmony_ci array += 1; 891cb93a386Sopenharmony_ci } 892cb93a386Sopenharmony_ci } 893cb93a386Sopenharmony_ci return count; 894cb93a386Sopenharmony_ci} 895cb93a386Sopenharmony_ci 896cb93a386Sopenharmony_ci#ifdef SK_DEBUG 897cb93a386Sopenharmony_ci 898cb93a386Sopenharmony_ci#define TEST_COLLAPS_ENTRY(array) array, SK_ARRAY_COUNT(array) 899cb93a386Sopenharmony_ci 900cb93a386Sopenharmony_cistatic void test_collaps_duplicates() { 901cb93a386Sopenharmony_ci static bool gOnce; 902cb93a386Sopenharmony_ci if (gOnce) { return; } 903cb93a386Sopenharmony_ci gOnce = true; 904cb93a386Sopenharmony_ci const SkScalar src0[] = { 0 }; 905cb93a386Sopenharmony_ci const SkScalar src1[] = { 0, 0 }; 906cb93a386Sopenharmony_ci const SkScalar src2[] = { 0, 1 }; 907cb93a386Sopenharmony_ci const SkScalar src3[] = { 0, 0, 0 }; 908cb93a386Sopenharmony_ci const SkScalar src4[] = { 0, 0, 1 }; 909cb93a386Sopenharmony_ci const SkScalar src5[] = { 0, 1, 1 }; 910cb93a386Sopenharmony_ci const SkScalar src6[] = { 0, 1, 2 }; 911cb93a386Sopenharmony_ci const struct { 912cb93a386Sopenharmony_ci const SkScalar* fData; 913cb93a386Sopenharmony_ci int fCount; 914cb93a386Sopenharmony_ci int fCollapsedCount; 915cb93a386Sopenharmony_ci } data[] = { 916cb93a386Sopenharmony_ci { TEST_COLLAPS_ENTRY(src0), 1 }, 917cb93a386Sopenharmony_ci { TEST_COLLAPS_ENTRY(src1), 1 }, 918cb93a386Sopenharmony_ci { TEST_COLLAPS_ENTRY(src2), 2 }, 919cb93a386Sopenharmony_ci { TEST_COLLAPS_ENTRY(src3), 1 }, 920cb93a386Sopenharmony_ci { TEST_COLLAPS_ENTRY(src4), 2 }, 921cb93a386Sopenharmony_ci { TEST_COLLAPS_ENTRY(src5), 2 }, 922cb93a386Sopenharmony_ci { TEST_COLLAPS_ENTRY(src6), 3 }, 923cb93a386Sopenharmony_ci }; 924cb93a386Sopenharmony_ci for (size_t i = 0; i < SK_ARRAY_COUNT(data); ++i) { 925cb93a386Sopenharmony_ci SkScalar dst[3]; 926cb93a386Sopenharmony_ci memcpy(dst, data[i].fData, data[i].fCount * sizeof(dst[0])); 927cb93a386Sopenharmony_ci int count = collaps_duplicates(dst, data[i].fCount); 928cb93a386Sopenharmony_ci SkASSERT(data[i].fCollapsedCount == count); 929cb93a386Sopenharmony_ci for (int j = 1; j < count; ++j) { 930cb93a386Sopenharmony_ci SkASSERT(dst[j-1] < dst[j]); 931cb93a386Sopenharmony_ci } 932cb93a386Sopenharmony_ci } 933cb93a386Sopenharmony_ci} 934cb93a386Sopenharmony_ci#endif 935cb93a386Sopenharmony_ci 936cb93a386Sopenharmony_cistatic SkScalar SkScalarCubeRoot(SkScalar x) { 937cb93a386Sopenharmony_ci return SkScalarPow(x, 0.3333333f); 938cb93a386Sopenharmony_ci} 939cb93a386Sopenharmony_ci 940cb93a386Sopenharmony_ci/* Solve coeff(t) == 0, returning the number of roots that 941cb93a386Sopenharmony_ci lie withing 0 < t < 1. 942cb93a386Sopenharmony_ci coeff[0]t^3 + coeff[1]t^2 + coeff[2]t + coeff[3] 943cb93a386Sopenharmony_ci 944cb93a386Sopenharmony_ci Eliminates repeated roots (so that all tValues are distinct, and are always 945cb93a386Sopenharmony_ci in increasing order. 946cb93a386Sopenharmony_ci*/ 947cb93a386Sopenharmony_cistatic int solve_cubic_poly(const SkScalar coeff[4], SkScalar tValues[3]) { 948cb93a386Sopenharmony_ci if (SkScalarNearlyZero(coeff[0])) { // we're just a quadratic 949cb93a386Sopenharmony_ci return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues); 950cb93a386Sopenharmony_ci } 951cb93a386Sopenharmony_ci 952cb93a386Sopenharmony_ci SkScalar a, b, c, Q, R; 953cb93a386Sopenharmony_ci 954cb93a386Sopenharmony_ci { 955cb93a386Sopenharmony_ci SkASSERT(coeff[0] != 0); 956cb93a386Sopenharmony_ci 957cb93a386Sopenharmony_ci SkScalar inva = SkScalarInvert(coeff[0]); 958cb93a386Sopenharmony_ci a = coeff[1] * inva; 959cb93a386Sopenharmony_ci b = coeff[2] * inva; 960cb93a386Sopenharmony_ci c = coeff[3] * inva; 961cb93a386Sopenharmony_ci } 962cb93a386Sopenharmony_ci Q = (a*a - b*3) / 9; 963cb93a386Sopenharmony_ci R = (2*a*a*a - 9*a*b + 27*c) / 54; 964cb93a386Sopenharmony_ci 965cb93a386Sopenharmony_ci SkScalar Q3 = Q * Q * Q; 966cb93a386Sopenharmony_ci SkScalar R2MinusQ3 = R * R - Q3; 967cb93a386Sopenharmony_ci SkScalar adiv3 = a / 3; 968cb93a386Sopenharmony_ci 969cb93a386Sopenharmony_ci if (R2MinusQ3 < 0) { // we have 3 real roots 970cb93a386Sopenharmony_ci // the divide/root can, due to finite precisions, be slightly outside of -1...1 971cb93a386Sopenharmony_ci SkScalar theta = SkScalarACos(SkTPin(R / SkScalarSqrt(Q3), -1.0f, 1.0f)); 972cb93a386Sopenharmony_ci SkScalar neg2RootQ = -2 * SkScalarSqrt(Q); 973cb93a386Sopenharmony_ci 974cb93a386Sopenharmony_ci tValues[0] = SkTPin(neg2RootQ * SkScalarCos(theta/3) - adiv3, 0.0f, 1.0f); 975cb93a386Sopenharmony_ci tValues[1] = SkTPin(neg2RootQ * SkScalarCos((theta + 2*SK_ScalarPI)/3) - adiv3, 0.0f, 1.0f); 976cb93a386Sopenharmony_ci tValues[2] = SkTPin(neg2RootQ * SkScalarCos((theta - 2*SK_ScalarPI)/3) - adiv3, 0.0f, 1.0f); 977cb93a386Sopenharmony_ci SkDEBUGCODE(test_collaps_duplicates();) 978cb93a386Sopenharmony_ci 979cb93a386Sopenharmony_ci // now sort the roots 980cb93a386Sopenharmony_ci bubble_sort(tValues, 3); 981cb93a386Sopenharmony_ci return collaps_duplicates(tValues, 3); 982cb93a386Sopenharmony_ci } else { // we have 1 real root 983cb93a386Sopenharmony_ci SkScalar A = SkScalarAbs(R) + SkScalarSqrt(R2MinusQ3); 984cb93a386Sopenharmony_ci A = SkScalarCubeRoot(A); 985cb93a386Sopenharmony_ci if (R > 0) { 986cb93a386Sopenharmony_ci A = -A; 987cb93a386Sopenharmony_ci } 988cb93a386Sopenharmony_ci if (A != 0) { 989cb93a386Sopenharmony_ci A += Q / A; 990cb93a386Sopenharmony_ci } 991cb93a386Sopenharmony_ci tValues[0] = SkTPin(A - adiv3, 0.0f, 1.0f); 992cb93a386Sopenharmony_ci return 1; 993cb93a386Sopenharmony_ci } 994cb93a386Sopenharmony_ci} 995cb93a386Sopenharmony_ci 996cb93a386Sopenharmony_ci/* Looking for F' dot F'' == 0 997cb93a386Sopenharmony_ci 998cb93a386Sopenharmony_ci A = b - a 999cb93a386Sopenharmony_ci B = c - 2b + a 1000cb93a386Sopenharmony_ci C = d - 3c + 3b - a 1001cb93a386Sopenharmony_ci 1002cb93a386Sopenharmony_ci F' = 3Ct^2 + 6Bt + 3A 1003cb93a386Sopenharmony_ci F'' = 6Ct + 6B 1004cb93a386Sopenharmony_ci 1005cb93a386Sopenharmony_ci F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB 1006cb93a386Sopenharmony_ci*/ 1007cb93a386Sopenharmony_cistatic void formulate_F1DotF2(const SkScalar src[], SkScalar coeff[4]) { 1008cb93a386Sopenharmony_ci SkScalar a = src[2] - src[0]; 1009cb93a386Sopenharmony_ci SkScalar b = src[4] - 2 * src[2] + src[0]; 1010cb93a386Sopenharmony_ci SkScalar c = src[6] + 3 * (src[2] - src[4]) - src[0]; 1011cb93a386Sopenharmony_ci 1012cb93a386Sopenharmony_ci coeff[0] = c * c; 1013cb93a386Sopenharmony_ci coeff[1] = 3 * b * c; 1014cb93a386Sopenharmony_ci coeff[2] = 2 * b * b + c * a; 1015cb93a386Sopenharmony_ci coeff[3] = a * b; 1016cb93a386Sopenharmony_ci} 1017cb93a386Sopenharmony_ci 1018cb93a386Sopenharmony_ci/* Looking for F' dot F'' == 0 1019cb93a386Sopenharmony_ci 1020cb93a386Sopenharmony_ci A = b - a 1021cb93a386Sopenharmony_ci B = c - 2b + a 1022cb93a386Sopenharmony_ci C = d - 3c + 3b - a 1023cb93a386Sopenharmony_ci 1024cb93a386Sopenharmony_ci F' = 3Ct^2 + 6Bt + 3A 1025cb93a386Sopenharmony_ci F'' = 6Ct + 6B 1026cb93a386Sopenharmony_ci 1027cb93a386Sopenharmony_ci F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB 1028cb93a386Sopenharmony_ci*/ 1029cb93a386Sopenharmony_ciint SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3]) { 1030cb93a386Sopenharmony_ci SkScalar coeffX[4], coeffY[4]; 1031cb93a386Sopenharmony_ci int i; 1032cb93a386Sopenharmony_ci 1033cb93a386Sopenharmony_ci formulate_F1DotF2(&src[0].fX, coeffX); 1034cb93a386Sopenharmony_ci formulate_F1DotF2(&src[0].fY, coeffY); 1035cb93a386Sopenharmony_ci 1036cb93a386Sopenharmony_ci for (i = 0; i < 4; i++) { 1037cb93a386Sopenharmony_ci coeffX[i] += coeffY[i]; 1038cb93a386Sopenharmony_ci } 1039cb93a386Sopenharmony_ci 1040cb93a386Sopenharmony_ci int numRoots = solve_cubic_poly(coeffX, tValues); 1041cb93a386Sopenharmony_ci // now remove extrema where the curvature is zero (mins) 1042cb93a386Sopenharmony_ci // !!!! need a test for this !!!! 1043cb93a386Sopenharmony_ci return numRoots; 1044cb93a386Sopenharmony_ci} 1045cb93a386Sopenharmony_ci 1046cb93a386Sopenharmony_ciint SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], 1047cb93a386Sopenharmony_ci SkScalar tValues[3]) { 1048cb93a386Sopenharmony_ci SkScalar t_storage[3]; 1049cb93a386Sopenharmony_ci 1050cb93a386Sopenharmony_ci if (tValues == nullptr) { 1051cb93a386Sopenharmony_ci tValues = t_storage; 1052cb93a386Sopenharmony_ci } 1053cb93a386Sopenharmony_ci 1054cb93a386Sopenharmony_ci SkScalar roots[3]; 1055cb93a386Sopenharmony_ci int rootCount = SkFindCubicMaxCurvature(src, roots); 1056cb93a386Sopenharmony_ci 1057cb93a386Sopenharmony_ci // Throw out values not inside 0..1. 1058cb93a386Sopenharmony_ci int count = 0; 1059cb93a386Sopenharmony_ci for (int i = 0; i < rootCount; ++i) { 1060cb93a386Sopenharmony_ci if (0 < roots[i] && roots[i] < 1) { 1061cb93a386Sopenharmony_ci tValues[count++] = roots[i]; 1062cb93a386Sopenharmony_ci } 1063cb93a386Sopenharmony_ci } 1064cb93a386Sopenharmony_ci 1065cb93a386Sopenharmony_ci if (dst) { 1066cb93a386Sopenharmony_ci if (count == 0) { 1067cb93a386Sopenharmony_ci memcpy(dst, src, 4 * sizeof(SkPoint)); 1068cb93a386Sopenharmony_ci } else { 1069cb93a386Sopenharmony_ci SkChopCubicAt(src, dst, tValues, count); 1070cb93a386Sopenharmony_ci } 1071cb93a386Sopenharmony_ci } 1072cb93a386Sopenharmony_ci return count + 1; 1073cb93a386Sopenharmony_ci} 1074cb93a386Sopenharmony_ci 1075cb93a386Sopenharmony_ci// Returns a constant proportional to the dimensions of the cubic. 1076cb93a386Sopenharmony_ci// Constant found through experimentation -- maybe there's a better way.... 1077cb93a386Sopenharmony_cistatic SkScalar calc_cubic_precision(const SkPoint src[4]) { 1078cb93a386Sopenharmony_ci return (SkPointPriv::DistanceToSqd(src[1], src[0]) + SkPointPriv::DistanceToSqd(src[2], src[1]) 1079cb93a386Sopenharmony_ci + SkPointPriv::DistanceToSqd(src[3], src[2])) * 1e-8f; 1080cb93a386Sopenharmony_ci} 1081cb93a386Sopenharmony_ci 1082cb93a386Sopenharmony_ci// Returns true if both points src[testIndex], src[testIndex+1] are in the same half plane defined 1083cb93a386Sopenharmony_ci// by the line segment src[lineIndex], src[lineIndex+1]. 1084cb93a386Sopenharmony_cistatic bool on_same_side(const SkPoint src[4], int testIndex, int lineIndex) { 1085cb93a386Sopenharmony_ci SkPoint origin = src[lineIndex]; 1086cb93a386Sopenharmony_ci SkVector line = src[lineIndex + 1] - origin; 1087cb93a386Sopenharmony_ci SkScalar crosses[2]; 1088cb93a386Sopenharmony_ci for (int index = 0; index < 2; ++index) { 1089cb93a386Sopenharmony_ci SkVector testLine = src[testIndex + index] - origin; 1090cb93a386Sopenharmony_ci crosses[index] = line.cross(testLine); 1091cb93a386Sopenharmony_ci } 1092cb93a386Sopenharmony_ci return crosses[0] * crosses[1] >= 0; 1093cb93a386Sopenharmony_ci} 1094cb93a386Sopenharmony_ci 1095cb93a386Sopenharmony_ci// Return location (in t) of cubic cusp, if there is one. 1096cb93a386Sopenharmony_ci// Note that classify cubic code does not reliably return all cusp'd cubics, so 1097cb93a386Sopenharmony_ci// it is not called here. 1098cb93a386Sopenharmony_ciSkScalar SkFindCubicCusp(const SkPoint src[4]) { 1099cb93a386Sopenharmony_ci // When the adjacent control point matches the end point, it behaves as if 1100cb93a386Sopenharmony_ci // the cubic has a cusp: there's a point of max curvature where the derivative 1101cb93a386Sopenharmony_ci // goes to zero. Ideally, this would be where t is zero or one, but math 1102cb93a386Sopenharmony_ci // error makes not so. It is not uncommon to create cubics this way; skip them. 1103cb93a386Sopenharmony_ci if (src[0] == src[1]) { 1104cb93a386Sopenharmony_ci return -1; 1105cb93a386Sopenharmony_ci } 1106cb93a386Sopenharmony_ci if (src[2] == src[3]) { 1107cb93a386Sopenharmony_ci return -1; 1108cb93a386Sopenharmony_ci } 1109cb93a386Sopenharmony_ci // Cubics only have a cusp if the line segments formed by the control and end points cross. 1110cb93a386Sopenharmony_ci // Detect crossing if line ends are on opposite sides of plane formed by the other line. 1111cb93a386Sopenharmony_ci if (on_same_side(src, 0, 2) || on_same_side(src, 2, 0)) { 1112cb93a386Sopenharmony_ci return -1; 1113cb93a386Sopenharmony_ci } 1114cb93a386Sopenharmony_ci // Cubics may have multiple points of maximum curvature, although at most only 1115cb93a386Sopenharmony_ci // one is a cusp. 1116cb93a386Sopenharmony_ci SkScalar maxCurvature[3]; 1117cb93a386Sopenharmony_ci int roots = SkFindCubicMaxCurvature(src, maxCurvature); 1118cb93a386Sopenharmony_ci for (int index = 0; index < roots; ++index) { 1119cb93a386Sopenharmony_ci SkScalar testT = maxCurvature[index]; 1120cb93a386Sopenharmony_ci if (0 >= testT || testT >= 1) { // no need to consider max curvature on the end 1121cb93a386Sopenharmony_ci continue; 1122cb93a386Sopenharmony_ci } 1123cb93a386Sopenharmony_ci // A cusp is at the max curvature, and also has a derivative close to zero. 1124cb93a386Sopenharmony_ci // Choose the 'close to zero' meaning by comparing the derivative length 1125cb93a386Sopenharmony_ci // with the overall cubic size. 1126cb93a386Sopenharmony_ci SkVector dPt = eval_cubic_derivative(src, testT); 1127cb93a386Sopenharmony_ci SkScalar dPtMagnitude = SkPointPriv::LengthSqd(dPt); 1128cb93a386Sopenharmony_ci SkScalar precision = calc_cubic_precision(src); 1129cb93a386Sopenharmony_ci if (dPtMagnitude < precision) { 1130cb93a386Sopenharmony_ci // All three max curvature t values may be close to the cusp; 1131cb93a386Sopenharmony_ci // return the first one. 1132cb93a386Sopenharmony_ci return testT; 1133cb93a386Sopenharmony_ci } 1134cb93a386Sopenharmony_ci } 1135cb93a386Sopenharmony_ci return -1; 1136cb93a386Sopenharmony_ci} 1137cb93a386Sopenharmony_ci 1138cb93a386Sopenharmony_ci#include "src/pathops/SkPathOpsCubic.h" 1139cb93a386Sopenharmony_ci 1140cb93a386Sopenharmony_citypedef int (SkDCubic::*InterceptProc)(double intercept, double roots[3]) const; 1141cb93a386Sopenharmony_ci 1142cb93a386Sopenharmony_cistatic bool cubic_dchop_at_intercept(const SkPoint src[4], SkScalar intercept, SkPoint dst[7], 1143cb93a386Sopenharmony_ci InterceptProc method) { 1144cb93a386Sopenharmony_ci SkDCubic cubic; 1145cb93a386Sopenharmony_ci double roots[3]; 1146cb93a386Sopenharmony_ci int count = (cubic.set(src).*method)(intercept, roots); 1147cb93a386Sopenharmony_ci if (count > 0) { 1148cb93a386Sopenharmony_ci SkDCubicPair pair = cubic.chopAt(roots[0]); 1149cb93a386Sopenharmony_ci for (int i = 0; i < 7; ++i) { 1150cb93a386Sopenharmony_ci dst[i] = pair.pts[i].asSkPoint(); 1151cb93a386Sopenharmony_ci } 1152cb93a386Sopenharmony_ci return true; 1153cb93a386Sopenharmony_ci } 1154cb93a386Sopenharmony_ci return false; 1155cb93a386Sopenharmony_ci} 1156cb93a386Sopenharmony_ci 1157cb93a386Sopenharmony_cibool SkChopMonoCubicAtY(SkPoint src[4], SkScalar y, SkPoint dst[7]) { 1158cb93a386Sopenharmony_ci return cubic_dchop_at_intercept(src, y, dst, &SkDCubic::horizontalIntersect); 1159cb93a386Sopenharmony_ci} 1160cb93a386Sopenharmony_ci 1161cb93a386Sopenharmony_cibool SkChopMonoCubicAtX(SkPoint src[4], SkScalar x, SkPoint dst[7]) { 1162cb93a386Sopenharmony_ci return cubic_dchop_at_intercept(src, x, dst, &SkDCubic::verticalIntersect); 1163cb93a386Sopenharmony_ci} 1164cb93a386Sopenharmony_ci 1165cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 1166cb93a386Sopenharmony_ci// 1167cb93a386Sopenharmony_ci// NURB representation for conics. Helpful explanations at: 1168cb93a386Sopenharmony_ci// 1169cb93a386Sopenharmony_ci// http://citeseerx.ist.psu.edu/viewdoc/ 1170cb93a386Sopenharmony_ci// download?doi=10.1.1.44.5740&rep=rep1&type=ps 1171cb93a386Sopenharmony_ci// and 1172cb93a386Sopenharmony_ci// http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/NURBS/RB-conics.html 1173cb93a386Sopenharmony_ci// 1174cb93a386Sopenharmony_ci// F = (A (1 - t)^2 + C t^2 + 2 B (1 - t) t w) 1175cb93a386Sopenharmony_ci// ------------------------------------------ 1176cb93a386Sopenharmony_ci// ((1 - t)^2 + t^2 + 2 (1 - t) t w) 1177cb93a386Sopenharmony_ci// 1178cb93a386Sopenharmony_ci// = {t^2 (P0 + P2 - 2 P1 w), t (-2 P0 + 2 P1 w), P0} 1179cb93a386Sopenharmony_ci// ------------------------------------------------ 1180cb93a386Sopenharmony_ci// {t^2 (2 - 2 w), t (-2 + 2 w), 1} 1181cb93a386Sopenharmony_ci// 1182cb93a386Sopenharmony_ci 1183cb93a386Sopenharmony_ci// F' = 2 (C t (1 + t (-1 + w)) - A (-1 + t) (t (-1 + w) - w) + B (1 - 2 t) w) 1184cb93a386Sopenharmony_ci// 1185cb93a386Sopenharmony_ci// t^2 : (2 P0 - 2 P2 - 2 P0 w + 2 P2 w) 1186cb93a386Sopenharmony_ci// t^1 : (-2 P0 + 2 P2 + 4 P0 w - 4 P1 w) 1187cb93a386Sopenharmony_ci// t^0 : -2 P0 w + 2 P1 w 1188cb93a386Sopenharmony_ci// 1189cb93a386Sopenharmony_ci// We disregard magnitude, so we can freely ignore the denominator of F', and 1190cb93a386Sopenharmony_ci// divide the numerator by 2 1191cb93a386Sopenharmony_ci// 1192cb93a386Sopenharmony_ci// coeff[0] for t^2 1193cb93a386Sopenharmony_ci// coeff[1] for t^1 1194cb93a386Sopenharmony_ci// coeff[2] for t^0 1195cb93a386Sopenharmony_ci// 1196cb93a386Sopenharmony_cistatic void conic_deriv_coeff(const SkScalar src[], 1197cb93a386Sopenharmony_ci SkScalar w, 1198cb93a386Sopenharmony_ci SkScalar coeff[3]) { 1199cb93a386Sopenharmony_ci const SkScalar P20 = src[4] - src[0]; 1200cb93a386Sopenharmony_ci const SkScalar P10 = src[2] - src[0]; 1201cb93a386Sopenharmony_ci const SkScalar wP10 = w * P10; 1202cb93a386Sopenharmony_ci coeff[0] = w * P20 - P20; 1203cb93a386Sopenharmony_ci coeff[1] = P20 - 2 * wP10; 1204cb93a386Sopenharmony_ci coeff[2] = wP10; 1205cb93a386Sopenharmony_ci} 1206cb93a386Sopenharmony_ci 1207cb93a386Sopenharmony_cistatic bool conic_find_extrema(const SkScalar src[], SkScalar w, SkScalar* t) { 1208cb93a386Sopenharmony_ci SkScalar coeff[3]; 1209cb93a386Sopenharmony_ci conic_deriv_coeff(src, w, coeff); 1210cb93a386Sopenharmony_ci 1211cb93a386Sopenharmony_ci SkScalar tValues[2]; 1212cb93a386Sopenharmony_ci int roots = SkFindUnitQuadRoots(coeff[0], coeff[1], coeff[2], tValues); 1213cb93a386Sopenharmony_ci SkASSERT(0 == roots || 1 == roots); 1214cb93a386Sopenharmony_ci 1215cb93a386Sopenharmony_ci if (1 == roots) { 1216cb93a386Sopenharmony_ci *t = tValues[0]; 1217cb93a386Sopenharmony_ci return true; 1218cb93a386Sopenharmony_ci } 1219cb93a386Sopenharmony_ci return false; 1220cb93a386Sopenharmony_ci} 1221cb93a386Sopenharmony_ci 1222cb93a386Sopenharmony_ci// We only interpolate one dimension at a time (the first, at +0, +3, +6). 1223cb93a386Sopenharmony_cistatic void p3d_interp(const SkScalar src[7], SkScalar dst[7], SkScalar t) { 1224cb93a386Sopenharmony_ci SkScalar ab = SkScalarInterp(src[0], src[3], t); 1225cb93a386Sopenharmony_ci SkScalar bc = SkScalarInterp(src[3], src[6], t); 1226cb93a386Sopenharmony_ci dst[0] = ab; 1227cb93a386Sopenharmony_ci dst[3] = SkScalarInterp(ab, bc, t); 1228cb93a386Sopenharmony_ci dst[6] = bc; 1229cb93a386Sopenharmony_ci} 1230cb93a386Sopenharmony_ci 1231cb93a386Sopenharmony_cistatic void ratquad_mapTo3D(const SkPoint src[3], SkScalar w, SkPoint3 dst[3]) { 1232cb93a386Sopenharmony_ci dst[0].set(src[0].fX * 1, src[0].fY * 1, 1); 1233cb93a386Sopenharmony_ci dst[1].set(src[1].fX * w, src[1].fY * w, w); 1234cb93a386Sopenharmony_ci dst[2].set(src[2].fX * 1, src[2].fY * 1, 1); 1235cb93a386Sopenharmony_ci} 1236cb93a386Sopenharmony_ci 1237cb93a386Sopenharmony_cistatic SkPoint project_down(const SkPoint3& src) { 1238cb93a386Sopenharmony_ci return {src.fX / src.fZ, src.fY / src.fZ}; 1239cb93a386Sopenharmony_ci} 1240cb93a386Sopenharmony_ci 1241cb93a386Sopenharmony_ci// return false if infinity or NaN is generated; caller must check 1242cb93a386Sopenharmony_cibool SkConic::chopAt(SkScalar t, SkConic dst[2]) const { 1243cb93a386Sopenharmony_ci SkPoint3 tmp[3], tmp2[3]; 1244cb93a386Sopenharmony_ci 1245cb93a386Sopenharmony_ci ratquad_mapTo3D(fPts, fW, tmp); 1246cb93a386Sopenharmony_ci 1247cb93a386Sopenharmony_ci p3d_interp(&tmp[0].fX, &tmp2[0].fX, t); 1248cb93a386Sopenharmony_ci p3d_interp(&tmp[0].fY, &tmp2[0].fY, t); 1249cb93a386Sopenharmony_ci p3d_interp(&tmp[0].fZ, &tmp2[0].fZ, t); 1250cb93a386Sopenharmony_ci 1251cb93a386Sopenharmony_ci dst[0].fPts[0] = fPts[0]; 1252cb93a386Sopenharmony_ci dst[0].fPts[1] = project_down(tmp2[0]); 1253cb93a386Sopenharmony_ci dst[0].fPts[2] = project_down(tmp2[1]); dst[1].fPts[0] = dst[0].fPts[2]; 1254cb93a386Sopenharmony_ci dst[1].fPts[1] = project_down(tmp2[2]); 1255cb93a386Sopenharmony_ci dst[1].fPts[2] = fPts[2]; 1256cb93a386Sopenharmony_ci 1257cb93a386Sopenharmony_ci // to put in "standard form", where w0 and w2 are both 1, we compute the 1258cb93a386Sopenharmony_ci // new w1 as sqrt(w1*w1/w0*w2) 1259cb93a386Sopenharmony_ci // or 1260cb93a386Sopenharmony_ci // w1 /= sqrt(w0*w2) 1261cb93a386Sopenharmony_ci // 1262cb93a386Sopenharmony_ci // However, in our case, we know that for dst[0]: 1263cb93a386Sopenharmony_ci // w0 == 1, and for dst[1], w2 == 1 1264cb93a386Sopenharmony_ci // 1265cb93a386Sopenharmony_ci SkScalar root = SkScalarSqrt(tmp2[1].fZ); 1266cb93a386Sopenharmony_ci dst[0].fW = tmp2[0].fZ / root; 1267cb93a386Sopenharmony_ci dst[1].fW = tmp2[2].fZ / root; 1268cb93a386Sopenharmony_ci SkASSERT(sizeof(dst[0]) == sizeof(SkScalar) * 7); 1269cb93a386Sopenharmony_ci SkASSERT(0 == offsetof(SkConic, fPts[0].fX)); 1270cb93a386Sopenharmony_ci return SkScalarsAreFinite(&dst[0].fPts[0].fX, 7 * 2); 1271cb93a386Sopenharmony_ci} 1272cb93a386Sopenharmony_ci 1273cb93a386Sopenharmony_civoid SkConic::chopAt(SkScalar t1, SkScalar t2, SkConic* dst) const { 1274cb93a386Sopenharmony_ci if (0 == t1 || 1 == t2) { 1275cb93a386Sopenharmony_ci if (0 == t1 && 1 == t2) { 1276cb93a386Sopenharmony_ci *dst = *this; 1277cb93a386Sopenharmony_ci return; 1278cb93a386Sopenharmony_ci } else { 1279cb93a386Sopenharmony_ci SkConic pair[2]; 1280cb93a386Sopenharmony_ci if (this->chopAt(t1 ? t1 : t2, pair)) { 1281cb93a386Sopenharmony_ci *dst = pair[SkToBool(t1)]; 1282cb93a386Sopenharmony_ci return; 1283cb93a386Sopenharmony_ci } 1284cb93a386Sopenharmony_ci } 1285cb93a386Sopenharmony_ci } 1286cb93a386Sopenharmony_ci SkConicCoeff coeff(*this); 1287cb93a386Sopenharmony_ci Sk2s tt1(t1); 1288cb93a386Sopenharmony_ci Sk2s aXY = coeff.fNumer.eval(tt1); 1289cb93a386Sopenharmony_ci Sk2s aZZ = coeff.fDenom.eval(tt1); 1290cb93a386Sopenharmony_ci Sk2s midTT((t1 + t2) / 2); 1291cb93a386Sopenharmony_ci Sk2s dXY = coeff.fNumer.eval(midTT); 1292cb93a386Sopenharmony_ci Sk2s dZZ = coeff.fDenom.eval(midTT); 1293cb93a386Sopenharmony_ci Sk2s tt2(t2); 1294cb93a386Sopenharmony_ci Sk2s cXY = coeff.fNumer.eval(tt2); 1295cb93a386Sopenharmony_ci Sk2s cZZ = coeff.fDenom.eval(tt2); 1296cb93a386Sopenharmony_ci Sk2s bXY = times_2(dXY) - (aXY + cXY) * Sk2s(0.5f); 1297cb93a386Sopenharmony_ci Sk2s bZZ = times_2(dZZ) - (aZZ + cZZ) * Sk2s(0.5f); 1298cb93a386Sopenharmony_ci dst->fPts[0] = to_point(aXY / aZZ); 1299cb93a386Sopenharmony_ci dst->fPts[1] = to_point(bXY / bZZ); 1300cb93a386Sopenharmony_ci dst->fPts[2] = to_point(cXY / cZZ); 1301cb93a386Sopenharmony_ci Sk2s ww = bZZ / (aZZ * cZZ).sqrt(); 1302cb93a386Sopenharmony_ci dst->fW = ww[0]; 1303cb93a386Sopenharmony_ci} 1304cb93a386Sopenharmony_ci 1305cb93a386Sopenharmony_ciSkPoint SkConic::evalAt(SkScalar t) const { 1306cb93a386Sopenharmony_ci return to_point(SkConicCoeff(*this).eval(t)); 1307cb93a386Sopenharmony_ci} 1308cb93a386Sopenharmony_ci 1309cb93a386Sopenharmony_ciSkVector SkConic::evalTangentAt(SkScalar t) const { 1310cb93a386Sopenharmony_ci // The derivative equation returns a zero tangent vector when t is 0 or 1, 1311cb93a386Sopenharmony_ci // and the control point is equal to the end point. 1312cb93a386Sopenharmony_ci // In this case, use the conic endpoints to compute the tangent. 1313cb93a386Sopenharmony_ci if ((t == 0 && fPts[0] == fPts[1]) || (t == 1 && fPts[1] == fPts[2])) { 1314cb93a386Sopenharmony_ci return fPts[2] - fPts[0]; 1315cb93a386Sopenharmony_ci } 1316cb93a386Sopenharmony_ci Sk2s p0 = from_point(fPts[0]); 1317cb93a386Sopenharmony_ci Sk2s p1 = from_point(fPts[1]); 1318cb93a386Sopenharmony_ci Sk2s p2 = from_point(fPts[2]); 1319cb93a386Sopenharmony_ci Sk2s ww(fW); 1320cb93a386Sopenharmony_ci 1321cb93a386Sopenharmony_ci Sk2s p20 = p2 - p0; 1322cb93a386Sopenharmony_ci Sk2s p10 = p1 - p0; 1323cb93a386Sopenharmony_ci 1324cb93a386Sopenharmony_ci Sk2s C = ww * p10; 1325cb93a386Sopenharmony_ci Sk2s A = ww * p20 - p20; 1326cb93a386Sopenharmony_ci Sk2s B = p20 - C - C; 1327cb93a386Sopenharmony_ci 1328cb93a386Sopenharmony_ci return to_vector(SkQuadCoeff(A, B, C).eval(t)); 1329cb93a386Sopenharmony_ci} 1330cb93a386Sopenharmony_ci 1331cb93a386Sopenharmony_civoid SkConic::evalAt(SkScalar t, SkPoint* pt, SkVector* tangent) const { 1332cb93a386Sopenharmony_ci SkASSERT(t >= 0 && t <= SK_Scalar1); 1333cb93a386Sopenharmony_ci 1334cb93a386Sopenharmony_ci if (pt) { 1335cb93a386Sopenharmony_ci *pt = this->evalAt(t); 1336cb93a386Sopenharmony_ci } 1337cb93a386Sopenharmony_ci if (tangent) { 1338cb93a386Sopenharmony_ci *tangent = this->evalTangentAt(t); 1339cb93a386Sopenharmony_ci } 1340cb93a386Sopenharmony_ci} 1341cb93a386Sopenharmony_ci 1342cb93a386Sopenharmony_cistatic SkScalar subdivide_w_value(SkScalar w) { 1343cb93a386Sopenharmony_ci return SkScalarSqrt(SK_ScalarHalf + w * SK_ScalarHalf); 1344cb93a386Sopenharmony_ci} 1345cb93a386Sopenharmony_ci 1346cb93a386Sopenharmony_civoid SkConic::chop(SkConic * SK_RESTRICT dst) const { 1347cb93a386Sopenharmony_ci Sk2s scale = Sk2s(SkScalarInvert(SK_Scalar1 + fW)); 1348cb93a386Sopenharmony_ci SkScalar newW = subdivide_w_value(fW); 1349cb93a386Sopenharmony_ci 1350cb93a386Sopenharmony_ci Sk2s p0 = from_point(fPts[0]); 1351cb93a386Sopenharmony_ci Sk2s p1 = from_point(fPts[1]); 1352cb93a386Sopenharmony_ci Sk2s p2 = from_point(fPts[2]); 1353cb93a386Sopenharmony_ci Sk2s ww(fW); 1354cb93a386Sopenharmony_ci 1355cb93a386Sopenharmony_ci Sk2s wp1 = ww * p1; 1356cb93a386Sopenharmony_ci Sk2s m = (p0 + times_2(wp1) + p2) * scale * Sk2s(0.5f); 1357cb93a386Sopenharmony_ci SkPoint mPt = to_point(m); 1358cb93a386Sopenharmony_ci if (!mPt.isFinite()) { 1359cb93a386Sopenharmony_ci double w_d = fW; 1360cb93a386Sopenharmony_ci double w_2 = w_d * 2; 1361cb93a386Sopenharmony_ci double scale_half = 1 / (1 + w_d) * 0.5; 1362cb93a386Sopenharmony_ci mPt.fX = SkDoubleToScalar((fPts[0].fX + w_2 * fPts[1].fX + fPts[2].fX) * scale_half); 1363cb93a386Sopenharmony_ci mPt.fY = SkDoubleToScalar((fPts[0].fY + w_2 * fPts[1].fY + fPts[2].fY) * scale_half); 1364cb93a386Sopenharmony_ci } 1365cb93a386Sopenharmony_ci dst[0].fPts[0] = fPts[0]; 1366cb93a386Sopenharmony_ci dst[0].fPts[1] = to_point((p0 + wp1) * scale); 1367cb93a386Sopenharmony_ci dst[0].fPts[2] = dst[1].fPts[0] = mPt; 1368cb93a386Sopenharmony_ci dst[1].fPts[1] = to_point((wp1 + p2) * scale); 1369cb93a386Sopenharmony_ci dst[1].fPts[2] = fPts[2]; 1370cb93a386Sopenharmony_ci 1371cb93a386Sopenharmony_ci dst[0].fW = dst[1].fW = newW; 1372cb93a386Sopenharmony_ci} 1373cb93a386Sopenharmony_ci 1374cb93a386Sopenharmony_ci/* 1375cb93a386Sopenharmony_ci * "High order approximation of conic sections by quadratic splines" 1376cb93a386Sopenharmony_ci * by Michael Floater, 1993 1377cb93a386Sopenharmony_ci */ 1378cb93a386Sopenharmony_ci#define AS_QUAD_ERROR_SETUP \ 1379cb93a386Sopenharmony_ci SkScalar a = fW - 1; \ 1380cb93a386Sopenharmony_ci SkScalar k = a / (4 * (2 + a)); \ 1381cb93a386Sopenharmony_ci SkScalar x = k * (fPts[0].fX - 2 * fPts[1].fX + fPts[2].fX); \ 1382cb93a386Sopenharmony_ci SkScalar y = k * (fPts[0].fY - 2 * fPts[1].fY + fPts[2].fY); 1383cb93a386Sopenharmony_ci 1384cb93a386Sopenharmony_civoid SkConic::computeAsQuadError(SkVector* err) const { 1385cb93a386Sopenharmony_ci AS_QUAD_ERROR_SETUP 1386cb93a386Sopenharmony_ci err->set(x, y); 1387cb93a386Sopenharmony_ci} 1388cb93a386Sopenharmony_ci 1389cb93a386Sopenharmony_cibool SkConic::asQuadTol(SkScalar tol) const { 1390cb93a386Sopenharmony_ci AS_QUAD_ERROR_SETUP 1391cb93a386Sopenharmony_ci return (x * x + y * y) <= tol * tol; 1392cb93a386Sopenharmony_ci} 1393cb93a386Sopenharmony_ci 1394cb93a386Sopenharmony_ci// Limit the number of suggested quads to approximate a conic 1395cb93a386Sopenharmony_ci#define kMaxConicToQuadPOW2 5 1396cb93a386Sopenharmony_ci 1397cb93a386Sopenharmony_ciint SkConic::computeQuadPOW2(SkScalar tol) const { 1398cb93a386Sopenharmony_ci if (tol < 0 || !SkScalarIsFinite(tol) || !SkPointPriv::AreFinite(fPts, 3)) { 1399cb93a386Sopenharmony_ci return 0; 1400cb93a386Sopenharmony_ci } 1401cb93a386Sopenharmony_ci 1402cb93a386Sopenharmony_ci AS_QUAD_ERROR_SETUP 1403cb93a386Sopenharmony_ci 1404cb93a386Sopenharmony_ci SkScalar error = SkScalarSqrt(x * x + y * y); 1405cb93a386Sopenharmony_ci int pow2; 1406cb93a386Sopenharmony_ci for (pow2 = 0; pow2 < kMaxConicToQuadPOW2; ++pow2) { 1407cb93a386Sopenharmony_ci if (error <= tol) { 1408cb93a386Sopenharmony_ci break; 1409cb93a386Sopenharmony_ci } 1410cb93a386Sopenharmony_ci error *= 0.25f; 1411cb93a386Sopenharmony_ci } 1412cb93a386Sopenharmony_ci // float version -- using ceil gives the same results as the above. 1413cb93a386Sopenharmony_ci if (false) { 1414cb93a386Sopenharmony_ci SkScalar err = SkScalarSqrt(x * x + y * y); 1415cb93a386Sopenharmony_ci if (err <= tol) { 1416cb93a386Sopenharmony_ci return 0; 1417cb93a386Sopenharmony_ci } 1418cb93a386Sopenharmony_ci SkScalar tol2 = tol * tol; 1419cb93a386Sopenharmony_ci if (tol2 == 0) { 1420cb93a386Sopenharmony_ci return kMaxConicToQuadPOW2; 1421cb93a386Sopenharmony_ci } 1422cb93a386Sopenharmony_ci SkScalar fpow2 = SkScalarLog2((x * x + y * y) / tol2) * 0.25f; 1423cb93a386Sopenharmony_ci int altPow2 = SkScalarCeilToInt(fpow2); 1424cb93a386Sopenharmony_ci if (altPow2 != pow2) { 1425cb93a386Sopenharmony_ci SkDebugf("pow2 %d altPow2 %d fbits %g err %g tol %g\n", pow2, altPow2, fpow2, err, tol); 1426cb93a386Sopenharmony_ci } 1427cb93a386Sopenharmony_ci pow2 = altPow2; 1428cb93a386Sopenharmony_ci } 1429cb93a386Sopenharmony_ci return pow2; 1430cb93a386Sopenharmony_ci} 1431cb93a386Sopenharmony_ci 1432cb93a386Sopenharmony_ci// This was originally developed and tested for pathops: see SkOpTypes.h 1433cb93a386Sopenharmony_ci// returns true if (a <= b <= c) || (a >= b >= c) 1434cb93a386Sopenharmony_cistatic bool between(SkScalar a, SkScalar b, SkScalar c) { 1435cb93a386Sopenharmony_ci return (a - b) * (c - b) <= 0; 1436cb93a386Sopenharmony_ci} 1437cb93a386Sopenharmony_ci 1438cb93a386Sopenharmony_cistatic SkPoint* subdivide(const SkConic& src, SkPoint pts[], int level) { 1439cb93a386Sopenharmony_ci SkASSERT(level >= 0); 1440cb93a386Sopenharmony_ci 1441cb93a386Sopenharmony_ci if (0 == level) { 1442cb93a386Sopenharmony_ci memcpy(pts, &src.fPts[1], 2 * sizeof(SkPoint)); 1443cb93a386Sopenharmony_ci return pts + 2; 1444cb93a386Sopenharmony_ci } else { 1445cb93a386Sopenharmony_ci SkConic dst[2]; 1446cb93a386Sopenharmony_ci src.chop(dst); 1447cb93a386Sopenharmony_ci const SkScalar startY = src.fPts[0].fY; 1448cb93a386Sopenharmony_ci SkScalar endY = src.fPts[2].fY; 1449cb93a386Sopenharmony_ci if (between(startY, src.fPts[1].fY, endY)) { 1450cb93a386Sopenharmony_ci // If the input is monotonic and the output is not, the scan converter hangs. 1451cb93a386Sopenharmony_ci // Ensure that the chopped conics maintain their y-order. 1452cb93a386Sopenharmony_ci SkScalar midY = dst[0].fPts[2].fY; 1453cb93a386Sopenharmony_ci if (!between(startY, midY, endY)) { 1454cb93a386Sopenharmony_ci // If the computed midpoint is outside the ends, move it to the closer one. 1455cb93a386Sopenharmony_ci SkScalar closerY = SkTAbs(midY - startY) < SkTAbs(midY - endY) ? startY : endY; 1456cb93a386Sopenharmony_ci dst[0].fPts[2].fY = dst[1].fPts[0].fY = closerY; 1457cb93a386Sopenharmony_ci } 1458cb93a386Sopenharmony_ci if (!between(startY, dst[0].fPts[1].fY, dst[0].fPts[2].fY)) { 1459cb93a386Sopenharmony_ci // If the 1st control is not between the start and end, put it at the start. 1460cb93a386Sopenharmony_ci // This also reduces the quad to a line. 1461cb93a386Sopenharmony_ci dst[0].fPts[1].fY = startY; 1462cb93a386Sopenharmony_ci } 1463cb93a386Sopenharmony_ci if (!between(dst[1].fPts[0].fY, dst[1].fPts[1].fY, endY)) { 1464cb93a386Sopenharmony_ci // If the 2nd control is not between the start and end, put it at the end. 1465cb93a386Sopenharmony_ci // This also reduces the quad to a line. 1466cb93a386Sopenharmony_ci dst[1].fPts[1].fY = endY; 1467cb93a386Sopenharmony_ci } 1468cb93a386Sopenharmony_ci // Verify that all five points are in order. 1469cb93a386Sopenharmony_ci SkASSERT(between(startY, dst[0].fPts[1].fY, dst[0].fPts[2].fY)); 1470cb93a386Sopenharmony_ci SkASSERT(between(dst[0].fPts[1].fY, dst[0].fPts[2].fY, dst[1].fPts[1].fY)); 1471cb93a386Sopenharmony_ci SkASSERT(between(dst[0].fPts[2].fY, dst[1].fPts[1].fY, endY)); 1472cb93a386Sopenharmony_ci } 1473cb93a386Sopenharmony_ci --level; 1474cb93a386Sopenharmony_ci pts = subdivide(dst[0], pts, level); 1475cb93a386Sopenharmony_ci return subdivide(dst[1], pts, level); 1476cb93a386Sopenharmony_ci } 1477cb93a386Sopenharmony_ci} 1478cb93a386Sopenharmony_ci 1479cb93a386Sopenharmony_ciint SkConic::chopIntoQuadsPOW2(SkPoint pts[], int pow2) const { 1480cb93a386Sopenharmony_ci SkASSERT(pow2 >= 0); 1481cb93a386Sopenharmony_ci *pts = fPts[0]; 1482cb93a386Sopenharmony_ci SkDEBUGCODE(SkPoint* endPts); 1483cb93a386Sopenharmony_ci if (pow2 == kMaxConicToQuadPOW2) { // If an extreme weight generates many quads ... 1484cb93a386Sopenharmony_ci SkConic dst[2]; 1485cb93a386Sopenharmony_ci this->chop(dst); 1486cb93a386Sopenharmony_ci // check to see if the first chop generates a pair of lines 1487cb93a386Sopenharmony_ci if (SkPointPriv::EqualsWithinTolerance(dst[0].fPts[1], dst[0].fPts[2]) && 1488cb93a386Sopenharmony_ci SkPointPriv::EqualsWithinTolerance(dst[1].fPts[0], dst[1].fPts[1])) { 1489cb93a386Sopenharmony_ci pts[1] = pts[2] = pts[3] = dst[0].fPts[1]; // set ctrl == end to make lines 1490cb93a386Sopenharmony_ci pts[4] = dst[1].fPts[2]; 1491cb93a386Sopenharmony_ci pow2 = 1; 1492cb93a386Sopenharmony_ci SkDEBUGCODE(endPts = &pts[5]); 1493cb93a386Sopenharmony_ci goto commonFinitePtCheck; 1494cb93a386Sopenharmony_ci } 1495cb93a386Sopenharmony_ci } 1496cb93a386Sopenharmony_ci SkDEBUGCODE(endPts = ) subdivide(*this, pts + 1, pow2); 1497cb93a386Sopenharmony_cicommonFinitePtCheck: 1498cb93a386Sopenharmony_ci const int quadCount = 1 << pow2; 1499cb93a386Sopenharmony_ci const int ptCount = 2 * quadCount + 1; 1500cb93a386Sopenharmony_ci SkASSERT(endPts - pts == ptCount); 1501cb93a386Sopenharmony_ci if (!SkPointPriv::AreFinite(pts, ptCount)) { 1502cb93a386Sopenharmony_ci // if we generated a non-finite, pin ourselves to the middle of the hull, 1503cb93a386Sopenharmony_ci // as our first and last are already on the first/last pts of the hull. 1504cb93a386Sopenharmony_ci for (int i = 1; i < ptCount - 1; ++i) { 1505cb93a386Sopenharmony_ci pts[i] = fPts[1]; 1506cb93a386Sopenharmony_ci } 1507cb93a386Sopenharmony_ci } 1508cb93a386Sopenharmony_ci return 1 << pow2; 1509cb93a386Sopenharmony_ci} 1510cb93a386Sopenharmony_ci 1511cb93a386Sopenharmony_cifloat SkConic::findMidTangent() const { 1512cb93a386Sopenharmony_ci // Tangents point in the direction of increasing T, so tan0 and -tan1 both point toward the 1513cb93a386Sopenharmony_ci // midtangent. The bisector of tan0 and -tan1 is orthogonal to the midtangent: 1514cb93a386Sopenharmony_ci // 1515cb93a386Sopenharmony_ci // bisector dot midtangent = 0 1516cb93a386Sopenharmony_ci // 1517cb93a386Sopenharmony_ci SkVector tan0 = fPts[1] - fPts[0]; 1518cb93a386Sopenharmony_ci SkVector tan1 = fPts[2] - fPts[1]; 1519cb93a386Sopenharmony_ci SkVector bisector = SkFindBisector(tan0, -tan1); 1520cb93a386Sopenharmony_ci 1521cb93a386Sopenharmony_ci // Start by finding the tangent function's power basis coefficients. These define a tangent 1522cb93a386Sopenharmony_ci // direction (scaled by some uniform value) as: 1523cb93a386Sopenharmony_ci // |T^2| 1524cb93a386Sopenharmony_ci // Tangent_Direction(T) = dx,dy = |A B C| * |T | 1525cb93a386Sopenharmony_ci // |. . .| |1 | 1526cb93a386Sopenharmony_ci // 1527cb93a386Sopenharmony_ci // The derivative of a conic has a cumbersome order-4 denominator. However, this isn't necessary 1528cb93a386Sopenharmony_ci // if we are only interested in a vector in the same *direction* as a given tangent line. Since 1529cb93a386Sopenharmony_ci // the denominator scales dx and dy uniformly, we can throw it out completely after evaluating 1530cb93a386Sopenharmony_ci // the derivative with the standard quotient rule. This leaves us with a simpler quadratic 1531cb93a386Sopenharmony_ci // function that we use to find a tangent. 1532cb93a386Sopenharmony_ci SkVector A = (fPts[2] - fPts[0]) * (fW - 1); 1533cb93a386Sopenharmony_ci SkVector B = (fPts[2] - fPts[0]) - (fPts[1] - fPts[0]) * (fW*2); 1534cb93a386Sopenharmony_ci SkVector C = (fPts[1] - fPts[0]) * fW; 1535cb93a386Sopenharmony_ci 1536cb93a386Sopenharmony_ci // Now solve for "bisector dot midtangent = 0": 1537cb93a386Sopenharmony_ci // 1538cb93a386Sopenharmony_ci // |T^2| 1539cb93a386Sopenharmony_ci // bisector * |A B C| * |T | = 0 1540cb93a386Sopenharmony_ci // |. . .| |1 | 1541cb93a386Sopenharmony_ci // 1542cb93a386Sopenharmony_ci float a = bisector.dot(A); 1543cb93a386Sopenharmony_ci float b = bisector.dot(B); 1544cb93a386Sopenharmony_ci float c = bisector.dot(C); 1545cb93a386Sopenharmony_ci return solve_quadratic_equation_for_midtangent(a, b, c); 1546cb93a386Sopenharmony_ci} 1547cb93a386Sopenharmony_ci 1548cb93a386Sopenharmony_cibool SkConic::findXExtrema(SkScalar* t) const { 1549cb93a386Sopenharmony_ci return conic_find_extrema(&fPts[0].fX, fW, t); 1550cb93a386Sopenharmony_ci} 1551cb93a386Sopenharmony_ci 1552cb93a386Sopenharmony_cibool SkConic::findYExtrema(SkScalar* t) const { 1553cb93a386Sopenharmony_ci return conic_find_extrema(&fPts[0].fY, fW, t); 1554cb93a386Sopenharmony_ci} 1555cb93a386Sopenharmony_ci 1556cb93a386Sopenharmony_cibool SkConic::chopAtXExtrema(SkConic dst[2]) const { 1557cb93a386Sopenharmony_ci SkScalar t; 1558cb93a386Sopenharmony_ci if (this->findXExtrema(&t)) { 1559cb93a386Sopenharmony_ci if (!this->chopAt(t, dst)) { 1560cb93a386Sopenharmony_ci // if chop can't return finite values, don't chop 1561cb93a386Sopenharmony_ci return false; 1562cb93a386Sopenharmony_ci } 1563cb93a386Sopenharmony_ci // now clean-up the middle, since we know t was meant to be at 1564cb93a386Sopenharmony_ci // an X-extrema 1565cb93a386Sopenharmony_ci SkScalar value = dst[0].fPts[2].fX; 1566cb93a386Sopenharmony_ci dst[0].fPts[1].fX = value; 1567cb93a386Sopenharmony_ci dst[1].fPts[0].fX = value; 1568cb93a386Sopenharmony_ci dst[1].fPts[1].fX = value; 1569cb93a386Sopenharmony_ci return true; 1570cb93a386Sopenharmony_ci } 1571cb93a386Sopenharmony_ci return false; 1572cb93a386Sopenharmony_ci} 1573cb93a386Sopenharmony_ci 1574cb93a386Sopenharmony_cibool SkConic::chopAtYExtrema(SkConic dst[2]) const { 1575cb93a386Sopenharmony_ci SkScalar t; 1576cb93a386Sopenharmony_ci if (this->findYExtrema(&t)) { 1577cb93a386Sopenharmony_ci if (!this->chopAt(t, dst)) { 1578cb93a386Sopenharmony_ci // if chop can't return finite values, don't chop 1579cb93a386Sopenharmony_ci return false; 1580cb93a386Sopenharmony_ci } 1581cb93a386Sopenharmony_ci // now clean-up the middle, since we know t was meant to be at 1582cb93a386Sopenharmony_ci // an Y-extrema 1583cb93a386Sopenharmony_ci SkScalar value = dst[0].fPts[2].fY; 1584cb93a386Sopenharmony_ci dst[0].fPts[1].fY = value; 1585cb93a386Sopenharmony_ci dst[1].fPts[0].fY = value; 1586cb93a386Sopenharmony_ci dst[1].fPts[1].fY = value; 1587cb93a386Sopenharmony_ci return true; 1588cb93a386Sopenharmony_ci } 1589cb93a386Sopenharmony_ci return false; 1590cb93a386Sopenharmony_ci} 1591cb93a386Sopenharmony_ci 1592cb93a386Sopenharmony_civoid SkConic::computeTightBounds(SkRect* bounds) const { 1593cb93a386Sopenharmony_ci SkPoint pts[4]; 1594cb93a386Sopenharmony_ci pts[0] = fPts[0]; 1595cb93a386Sopenharmony_ci pts[1] = fPts[2]; 1596cb93a386Sopenharmony_ci int count = 2; 1597cb93a386Sopenharmony_ci 1598cb93a386Sopenharmony_ci SkScalar t; 1599cb93a386Sopenharmony_ci if (this->findXExtrema(&t)) { 1600cb93a386Sopenharmony_ci this->evalAt(t, &pts[count++]); 1601cb93a386Sopenharmony_ci } 1602cb93a386Sopenharmony_ci if (this->findYExtrema(&t)) { 1603cb93a386Sopenharmony_ci this->evalAt(t, &pts[count++]); 1604cb93a386Sopenharmony_ci } 1605cb93a386Sopenharmony_ci bounds->setBounds(pts, count); 1606cb93a386Sopenharmony_ci} 1607cb93a386Sopenharmony_ci 1608cb93a386Sopenharmony_civoid SkConic::computeFastBounds(SkRect* bounds) const { 1609cb93a386Sopenharmony_ci bounds->setBounds(fPts, 3); 1610cb93a386Sopenharmony_ci} 1611cb93a386Sopenharmony_ci 1612cb93a386Sopenharmony_ci#if 0 // unimplemented 1613cb93a386Sopenharmony_cibool SkConic::findMaxCurvature(SkScalar* t) const { 1614cb93a386Sopenharmony_ci // TODO: Implement me 1615cb93a386Sopenharmony_ci return false; 1616cb93a386Sopenharmony_ci} 1617cb93a386Sopenharmony_ci#endif 1618cb93a386Sopenharmony_ci 1619cb93a386Sopenharmony_ciSkScalar SkConic::TransformW(const SkPoint pts[], SkScalar w, const SkMatrix& matrix) { 1620cb93a386Sopenharmony_ci if (!matrix.hasPerspective()) { 1621cb93a386Sopenharmony_ci return w; 1622cb93a386Sopenharmony_ci } 1623cb93a386Sopenharmony_ci 1624cb93a386Sopenharmony_ci SkPoint3 src[3], dst[3]; 1625cb93a386Sopenharmony_ci 1626cb93a386Sopenharmony_ci ratquad_mapTo3D(pts, w, src); 1627cb93a386Sopenharmony_ci 1628cb93a386Sopenharmony_ci matrix.mapHomogeneousPoints(dst, src, 3); 1629cb93a386Sopenharmony_ci 1630cb93a386Sopenharmony_ci // w' = sqrt(w1*w1/w0*w2) 1631cb93a386Sopenharmony_ci // use doubles temporarily, to handle small numer/denom 1632cb93a386Sopenharmony_ci double w0 = dst[0].fZ; 1633cb93a386Sopenharmony_ci double w1 = dst[1].fZ; 1634cb93a386Sopenharmony_ci double w2 = dst[2].fZ; 1635cb93a386Sopenharmony_ci return sk_double_to_float(sqrt(sk_ieee_double_divide(w1 * w1, w0 * w2))); 1636cb93a386Sopenharmony_ci} 1637cb93a386Sopenharmony_ci 1638cb93a386Sopenharmony_ciint SkConic::BuildUnitArc(const SkVector& uStart, const SkVector& uStop, SkRotationDirection dir, 1639cb93a386Sopenharmony_ci const SkMatrix* userMatrix, SkConic dst[kMaxConicsForArc]) { 1640cb93a386Sopenharmony_ci // rotate by x,y so that uStart is (1.0) 1641cb93a386Sopenharmony_ci SkScalar x = SkPoint::DotProduct(uStart, uStop); 1642cb93a386Sopenharmony_ci SkScalar y = SkPoint::CrossProduct(uStart, uStop); 1643cb93a386Sopenharmony_ci 1644cb93a386Sopenharmony_ci SkScalar absY = SkScalarAbs(y); 1645cb93a386Sopenharmony_ci 1646cb93a386Sopenharmony_ci // check for (effectively) coincident vectors 1647cb93a386Sopenharmony_ci // this can happen if our angle is nearly 0 or nearly 180 (y == 0) 1648cb93a386Sopenharmony_ci // ... we use the dot-prod to distinguish between 0 and 180 (x > 0) 1649cb93a386Sopenharmony_ci if (absY <= SK_ScalarNearlyZero && x > 0 && ((y >= 0 && kCW_SkRotationDirection == dir) || 1650cb93a386Sopenharmony_ci (y <= 0 && kCCW_SkRotationDirection == dir))) { 1651cb93a386Sopenharmony_ci return 0; 1652cb93a386Sopenharmony_ci } 1653cb93a386Sopenharmony_ci 1654cb93a386Sopenharmony_ci if (dir == kCCW_SkRotationDirection) { 1655cb93a386Sopenharmony_ci y = -y; 1656cb93a386Sopenharmony_ci } 1657cb93a386Sopenharmony_ci 1658cb93a386Sopenharmony_ci // We decide to use 1-conic per quadrant of a circle. What quadrant does [xy] lie in? 1659cb93a386Sopenharmony_ci // 0 == [0 .. 90) 1660cb93a386Sopenharmony_ci // 1 == [90 ..180) 1661cb93a386Sopenharmony_ci // 2 == [180..270) 1662cb93a386Sopenharmony_ci // 3 == [270..360) 1663cb93a386Sopenharmony_ci // 1664cb93a386Sopenharmony_ci int quadrant = 0; 1665cb93a386Sopenharmony_ci if (0 == y) { 1666cb93a386Sopenharmony_ci quadrant = 2; // 180 1667cb93a386Sopenharmony_ci SkASSERT(SkScalarAbs(x + SK_Scalar1) <= SK_ScalarNearlyZero); 1668cb93a386Sopenharmony_ci } else if (0 == x) { 1669cb93a386Sopenharmony_ci SkASSERT(absY - SK_Scalar1 <= SK_ScalarNearlyZero); 1670cb93a386Sopenharmony_ci quadrant = y > 0 ? 1 : 3; // 90 : 270 1671cb93a386Sopenharmony_ci } else { 1672cb93a386Sopenharmony_ci if (y < 0) { 1673cb93a386Sopenharmony_ci quadrant += 2; 1674cb93a386Sopenharmony_ci } 1675cb93a386Sopenharmony_ci if ((x < 0) != (y < 0)) { 1676cb93a386Sopenharmony_ci quadrant += 1; 1677cb93a386Sopenharmony_ci } 1678cb93a386Sopenharmony_ci } 1679cb93a386Sopenharmony_ci 1680cb93a386Sopenharmony_ci const SkPoint quadrantPts[] = { 1681cb93a386Sopenharmony_ci { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 }, { 0, -1 }, { 1, -1 } 1682cb93a386Sopenharmony_ci }; 1683cb93a386Sopenharmony_ci const SkScalar quadrantWeight = SK_ScalarRoot2Over2; 1684cb93a386Sopenharmony_ci 1685cb93a386Sopenharmony_ci int conicCount = quadrant; 1686cb93a386Sopenharmony_ci for (int i = 0; i < conicCount; ++i) { 1687cb93a386Sopenharmony_ci dst[i].set(&quadrantPts[i * 2], quadrantWeight); 1688cb93a386Sopenharmony_ci } 1689cb93a386Sopenharmony_ci 1690cb93a386Sopenharmony_ci // Now compute any remaing (sub-90-degree) arc for the last conic 1691cb93a386Sopenharmony_ci const SkPoint finalP = { x, y }; 1692cb93a386Sopenharmony_ci const SkPoint& lastQ = quadrantPts[quadrant * 2]; // will already be a unit-vector 1693cb93a386Sopenharmony_ci const SkScalar dot = SkVector::DotProduct(lastQ, finalP); 1694cb93a386Sopenharmony_ci SkASSERT(0 <= dot && dot <= SK_Scalar1 + SK_ScalarNearlyZero); 1695cb93a386Sopenharmony_ci 1696cb93a386Sopenharmony_ci if (dot < 1) { 1697cb93a386Sopenharmony_ci SkVector offCurve = { lastQ.x() + x, lastQ.y() + y }; 1698cb93a386Sopenharmony_ci // compute the bisector vector, and then rescale to be the off-curve point. 1699cb93a386Sopenharmony_ci // we compute its length from cos(theta/2) = length / 1, using half-angle identity we get 1700cb93a386Sopenharmony_ci // length = sqrt(2 / (1 + cos(theta)). We already have cos() when to computed the dot. 1701cb93a386Sopenharmony_ci // This is nice, since our computed weight is cos(theta/2) as well! 1702cb93a386Sopenharmony_ci // 1703cb93a386Sopenharmony_ci const SkScalar cosThetaOver2 = SkScalarSqrt((1 + dot) / 2); 1704cb93a386Sopenharmony_ci offCurve.setLength(SkScalarInvert(cosThetaOver2)); 1705cb93a386Sopenharmony_ci if (!SkPointPriv::EqualsWithinTolerance(lastQ, offCurve)) { 1706cb93a386Sopenharmony_ci dst[conicCount].set(lastQ, offCurve, finalP, cosThetaOver2); 1707cb93a386Sopenharmony_ci conicCount += 1; 1708cb93a386Sopenharmony_ci } 1709cb93a386Sopenharmony_ci } 1710cb93a386Sopenharmony_ci 1711cb93a386Sopenharmony_ci // now handle counter-clockwise and the initial unitStart rotation 1712cb93a386Sopenharmony_ci SkMatrix matrix; 1713cb93a386Sopenharmony_ci matrix.setSinCos(uStart.fY, uStart.fX); 1714cb93a386Sopenharmony_ci if (dir == kCCW_SkRotationDirection) { 1715cb93a386Sopenharmony_ci matrix.preScale(SK_Scalar1, -SK_Scalar1); 1716cb93a386Sopenharmony_ci } 1717cb93a386Sopenharmony_ci if (userMatrix) { 1718cb93a386Sopenharmony_ci matrix.postConcat(*userMatrix); 1719cb93a386Sopenharmony_ci } 1720cb93a386Sopenharmony_ci for (int i = 0; i < conicCount; ++i) { 1721cb93a386Sopenharmony_ci matrix.mapPoints(dst[i].fPts, 3); 1722cb93a386Sopenharmony_ci } 1723cb93a386Sopenharmony_ci return conicCount; 1724cb93a386Sopenharmony_ci} 1725