part of dart._engine; // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /// Computes tangent at point x,y on a line. void tangentLine(Float32List pts, double x, double y, List tangents) { final double y0 = pts[1]; final double y1 = pts[3]; if (!SPath.between(y0, y, y1)) { return; } final double x0 = pts[0]; final double x1 = pts[2]; if (!SPath.between(x0, x, x1)) { return; } final double dx = x1 - x0; final double dy = y1 - y0; if (!SPath.nearlyEqual((x - x0) * dy, dx * (y - y0))) { return; } tangents.add(ui.Offset(dx, dy)); } /// Computes tangent at point x,y on a quadratic curve. void tangentQuad(Float32List pts, double x, double y, List tangents) { final double y0 = pts[1]; final double y1 = pts[3]; final double y2 = pts[5]; if (!SPath.between(y0, y, y1) && !SPath.between(y1, y, y2)) { return; } final double x0 = pts[0]; final double x1 = pts[2]; final double x2 = pts[4]; if (!SPath.between(x0, x, x1) && !SPath.between(x1, x, x2)) { return; } final QuadRoots roots = QuadRoots(); final int n = roots.findRoots(y0 - 2 * y1 + y2, 2 * (y1 - y0), y0 - y); for (int index = 0; index < n; ++index) { final double t = index == 0 ? roots.root0! : roots.root1!; final double C = x0; final double A = x2 - 2 * x1 + C; final double B = 2 * (x1 - C); final double xt = polyEval(A, B, C, t); if (!SPath.nearlyEqual(x, xt)) { continue; } tangents.add(_evalQuadTangentAt(x0, y0, x1, y1, x2, y2, t)); } } ui.Offset _evalQuadTangentAt( double x0, double y0, double x1, double y1, double x2, double y2, double t, ) { // The derivative of a quad equation is 2(b - a +(a - 2b +c)t). // This returns a zero tangent vector when t is 0 or 1, and the control // point is equal to the end point. In this case, use the quad end points to // compute the tangent. if ((t == 0 && x0 == x1 && y0 == y1) || (t == 1 && x1 == x2 && y1 == y2)) { return ui.Offset(x2 - x0, y2 - y0); } assert(t >= 0 && t <= 1.0); final double bx = x1 - x0; final double by = y1 - y0; final double ax = x2 - x1 - bx; final double ay = y2 - y1 - by; final double tx = ax * t + bx; final double ty = ay * t + by; return ui.Offset(tx * 2, ty * 2); } /// Computes tangent at point x,y on a conic curve. void tangentConic(Float32List pts, double x, double y, double weight, List tangents) { final double y0 = pts[1]; final double y1 = pts[3]; final double y2 = pts[5]; if (!SPath.between(y0, y, y1) && !SPath.between(y1, y, y2)) { return; } final double x0 = pts[0]; final double x1 = pts[2]; final double x2 = pts[4]; if (!SPath.between(x0, x, x1) && !SPath.between(x1, x, x2)) { return; } // Check extrema. double A = y2; double B = y1 * weight - y * weight + y; double C = y0; // A = a + c - 2*(b*w - yCept*w + yCept) A += C - 2 * B; // B = b*w - w * yCept + yCept - a B -= C; C -= y; final QuadRoots quadRoots = QuadRoots(); final int n = quadRoots.findRoots(A, 2 * B, C); for (int index = 0; index < n; ++index) { final double t = index == 0 ? quadRoots.root0! : quadRoots.root1!; final double xt = Conic.evalNumerator(x0, x1, x2, weight, t) / Conic.evalDenominator(weight, t); if (!SPath.nearlyEqual(x, xt)) { continue; } final Conic conic = Conic(x0, y0, x1, y1, x2, y2, weight); tangents.add(conic.evalTangentAt(t)); } } /// Computes tangent at point x,y on a cubic curve. void tangentCubic(Float32List pts, double x, double y, List tangents) { final double y3 = pts[7]; final double y0 = pts[1]; final double y1 = pts[3]; final double y2 = pts[5]; if (!SPath.between(y0, y, y1) && !SPath.between(y1, y, y2) && !SPath.between(y2, y, y3)) { return; } final double x0 = pts[0]; final double x1 = pts[2]; final double x2 = pts[4]; final double x3 = pts[6]; if (!SPath.between(x0, x, x1) && !SPath.between(x1, x, x2) && !SPath.between(x2, x, x3)) { return; } final Float32List dst = Float32List(20); final int n = chopCubicAtYExtrema(pts, dst); for (int i = 0; i <= n; ++i) { final int bufferPos = i * 6; final double? t = chopMonoAtY(dst, i * 6, y); if (t == null) { continue; } final double xt = evalCubicPts( dst[bufferPos], dst[bufferPos + 2], dst[bufferPos + 4], dst[bufferPos + 6], t, ); if (!SPath.nearlyEqual(x, xt)) { continue; } tangents.add(_evalCubicTangentAt(dst, bufferPos, t)); } } ui.Offset _evalCubicTangentAt(Float32List points, int bufferPos, double t) { assert(t >= 0 && t <= 1.0); final double y3 = points[7 + bufferPos]; final double y0 = points[1 + bufferPos]; final double y1 = points[3 + bufferPos]; final double y2 = points[5 + bufferPos]; final double x0 = points[0 + bufferPos]; final double x1 = points[2 + bufferPos]; final double x2 = points[4 + bufferPos]; final double x3 = points[6 + bufferPos]; // The derivative equation returns a zero tangent vector when t is 0 or 1, // and the adjacent control point is equal to the end point. In this case, // use the next control point or the end points to compute the tangent. if ((t == 0 && x0 == x1 && y0 == y1) || (t == 1 && x2 == x3 && y2 == y3)) { double dx, dy; if (t == 0) { dx = x2 - x0; dy = y2 - y0; } else { dx = x3 - x1; dy = y3 - y1; } if (dx == 0 && dy == 0) { dx = x3 - x0; dy = y3 - y0; } return ui.Offset(dx, dy); } else { return _evalCubicDerivative(x0, y0, x1, y1, x2, y2, x3, y3, t); } } ui.Offset _evalCubicDerivative( double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, double t, ) { final SkQuadCoefficients coeff = SkQuadCoefficients( x3 + 3 * (x1 - x2) - x0, y3 + 3 * (y1 - y2) - y0, 2 * (x2 - (2 * x1) + x0), 2 * (y2 - (2 * y1) + y0), x1 - x0, y1 - y0, ); return ui.Offset(coeff.evalX(t), coeff.evalY(t)); }