| // 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. |
| |
| import 'dart:typed_data'; |
| |
| import 'package:ui/ui.dart' as ui; |
| |
| import 'conic.dart'; |
| import 'cubic.dart'; |
| import 'path_utils.dart'; |
| |
| /// Computes tangent at point x,y on a line. |
| void tangentLine( |
| Float32List pts, double x, double y, List<ui.Offset> 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<ui.Offset> 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<ui.Offset> 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<ui.Offset> 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)); |
| } |