| /** |
| * 曲线辅助模块 |
| * @module zrender/core/curve |
| * @author pissang(https://www.github.com/pissang) |
| */ |
| import { create as v2Create, distSquare as v2DistSquare } from './vector'; |
| var mathPow = Math.pow; |
| var mathSqrt = Math.sqrt; |
| var EPSILON = 1e-8; |
| var EPSILON_NUMERIC = 1e-4; |
| var THREE_SQRT = mathSqrt(3); |
| var ONE_THIRD = 1 / 3; // 临时变量 |
| |
| var _v0 = v2Create(); |
| |
| var _v1 = v2Create(); |
| |
| var _v2 = v2Create(); |
| |
| function isAroundZero(val) { |
| return val > -EPSILON && val < EPSILON; |
| } |
| |
| function isNotAroundZero(val) { |
| return val > EPSILON || val < -EPSILON; |
| } |
| /** |
| * 计算三次贝塞尔值 |
| * @memberOf module:zrender/core/curve |
| * @param {number} p0 |
| * @param {number} p1 |
| * @param {number} p2 |
| * @param {number} p3 |
| * @param {number} t |
| * @return {number} |
| */ |
| |
| |
| export function cubicAt(p0, p1, p2, p3, t) { |
| var onet = 1 - t; |
| return onet * onet * (onet * p0 + 3 * t * p1) + t * t * (t * p3 + 3 * onet * p2); |
| } |
| /** |
| * 计算三次贝塞尔导数值 |
| * @memberOf module:zrender/core/curve |
| * @param {number} p0 |
| * @param {number} p1 |
| * @param {number} p2 |
| * @param {number} p3 |
| * @param {number} t |
| * @return {number} |
| */ |
| |
| export function cubicDerivativeAt(p0, p1, p2, p3, t) { |
| var onet = 1 - t; |
| return 3 * (((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet + (p3 - p2) * t * t); |
| } |
| /** |
| * 计算三次贝塞尔方程根,使用盛金公式 |
| * @memberOf module:zrender/core/curve |
| * @param {number} p0 |
| * @param {number} p1 |
| * @param {number} p2 |
| * @param {number} p3 |
| * @param {number} val |
| * @param {Array.<number>} roots |
| * @return {number} 有效根数目 |
| */ |
| |
| export function cubicRootAt(p0, p1, p2, p3, val, roots) { |
| // Evaluate roots of cubic functions |
| var a = p3 + 3 * (p1 - p2) - p0; |
| var b = 3 * (p2 - p1 * 2 + p0); |
| var c = 3 * (p1 - p0); |
| var d = p0 - val; |
| var A = b * b - 3 * a * c; |
| var B = b * c - 9 * a * d; |
| var C = c * c - 3 * b * d; |
| var n = 0; |
| |
| if (isAroundZero(A) && isAroundZero(B)) { |
| if (isAroundZero(b)) { |
| roots[0] = 0; |
| } else { |
| var t1 = -c / b; //t1, t2, t3, b is not zero |
| |
| if (t1 >= 0 && t1 <= 1) { |
| roots[n++] = t1; |
| } |
| } |
| } else { |
| var disc = B * B - 4 * A * C; |
| |
| if (isAroundZero(disc)) { |
| var K = B / A; |
| var t1 = -b / a + K; // t1, a is not zero |
| |
| var t2 = -K / 2; // t2, t3 |
| |
| if (t1 >= 0 && t1 <= 1) { |
| roots[n++] = t1; |
| } |
| |
| if (t2 >= 0 && t2 <= 1) { |
| roots[n++] = t2; |
| } |
| } else if (disc > 0) { |
| var discSqrt = mathSqrt(disc); |
| var Y1 = A * b + 1.5 * a * (-B + discSqrt); |
| var Y2 = A * b + 1.5 * a * (-B - discSqrt); |
| |
| if (Y1 < 0) { |
| Y1 = -mathPow(-Y1, ONE_THIRD); |
| } else { |
| Y1 = mathPow(Y1, ONE_THIRD); |
| } |
| |
| if (Y2 < 0) { |
| Y2 = -mathPow(-Y2, ONE_THIRD); |
| } else { |
| Y2 = mathPow(Y2, ONE_THIRD); |
| } |
| |
| var t1 = (-b - (Y1 + Y2)) / (3 * a); |
| |
| if (t1 >= 0 && t1 <= 1) { |
| roots[n++] = t1; |
| } |
| } else { |
| var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt(A * A * A)); |
| var theta = Math.acos(T) / 3; |
| var ASqrt = mathSqrt(A); |
| var tmp = Math.cos(theta); |
| var t1 = (-b - 2 * ASqrt * tmp) / (3 * a); |
| var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a); |
| var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a); |
| |
| if (t1 >= 0 && t1 <= 1) { |
| roots[n++] = t1; |
| } |
| |
| if (t2 >= 0 && t2 <= 1) { |
| roots[n++] = t2; |
| } |
| |
| if (t3 >= 0 && t3 <= 1) { |
| roots[n++] = t3; |
| } |
| } |
| } |
| |
| return n; |
| } |
| /** |
| * 计算三次贝塞尔方程极限值的位置 |
| * @memberOf module:zrender/core/curve |
| * @param {number} p0 |
| * @param {number} p1 |
| * @param {number} p2 |
| * @param {number} p3 |
| * @param {Array.<number>} extrema |
| * @return {number} 有效数目 |
| */ |
| |
| export function cubicExtrema(p0, p1, p2, p3, extrema) { |
| var b = 6 * p2 - 12 * p1 + 6 * p0; |
| var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2; |
| var c = 3 * p1 - 3 * p0; |
| var n = 0; |
| |
| if (isAroundZero(a)) { |
| if (isNotAroundZero(b)) { |
| var t1 = -c / b; |
| |
| if (t1 >= 0 && t1 <= 1) { |
| extrema[n++] = t1; |
| } |
| } |
| } else { |
| var disc = b * b - 4 * a * c; |
| |
| if (isAroundZero(disc)) { |
| extrema[0] = -b / (2 * a); |
| } else if (disc > 0) { |
| var discSqrt = mathSqrt(disc); |
| var t1 = (-b + discSqrt) / (2 * a); |
| var t2 = (-b - discSqrt) / (2 * a); |
| |
| if (t1 >= 0 && t1 <= 1) { |
| extrema[n++] = t1; |
| } |
| |
| if (t2 >= 0 && t2 <= 1) { |
| extrema[n++] = t2; |
| } |
| } |
| } |
| |
| return n; |
| } |
| /** |
| * 细分三次贝塞尔曲线 |
| * @memberOf module:zrender/core/curve |
| * @param {number} p0 |
| * @param {number} p1 |
| * @param {number} p2 |
| * @param {number} p3 |
| * @param {number} t |
| * @param {Array.<number>} out |
| */ |
| |
| export function cubicSubdivide(p0, p1, p2, p3, t, out) { |
| var p01 = (p1 - p0) * t + p0; |
| var p12 = (p2 - p1) * t + p1; |
| var p23 = (p3 - p2) * t + p2; |
| var p012 = (p12 - p01) * t + p01; |
| var p123 = (p23 - p12) * t + p12; |
| var p0123 = (p123 - p012) * t + p012; // Seg0 |
| |
| out[0] = p0; |
| out[1] = p01; |
| out[2] = p012; |
| out[3] = p0123; // Seg1 |
| |
| out[4] = p0123; |
| out[5] = p123; |
| out[6] = p23; |
| out[7] = p3; |
| } |
| /** |
| * 投射点到三次贝塞尔曲线上,返回投射距离。 |
| * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。 |
| * @param {number} x0 |
| * @param {number} y0 |
| * @param {number} x1 |
| * @param {number} y1 |
| * @param {number} x2 |
| * @param {number} y2 |
| * @param {number} x3 |
| * @param {number} y3 |
| * @param {number} x |
| * @param {number} y |
| * @param {Array.<number>} [out] 投射点 |
| * @return {number} |
| */ |
| |
| export function cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, out) { |
| // http://pomax.github.io/bezierinfo/#projections |
| var t; |
| var interval = 0.005; |
| var d = Infinity; |
| var prev; |
| var next; |
| var d1; |
| var d2; |
| _v0[0] = x; |
| _v0[1] = y; // 先粗略估计一下可能的最小距离的 t 值 |
| // PENDING |
| |
| for (var _t = 0; _t < 1; _t += 0.05) { |
| _v1[0] = cubicAt(x0, x1, x2, x3, _t); |
| _v1[1] = cubicAt(y0, y1, y2, y3, _t); |
| d1 = v2DistSquare(_v0, _v1); |
| |
| if (d1 < d) { |
| t = _t; |
| d = d1; |
| } |
| } |
| |
| d = Infinity; // At most 32 iteration |
| |
| for (var i = 0; i < 32; i++) { |
| if (interval < EPSILON_NUMERIC) { |
| break; |
| } |
| |
| prev = t - interval; |
| next = t + interval; // t - interval |
| |
| _v1[0] = cubicAt(x0, x1, x2, x3, prev); |
| _v1[1] = cubicAt(y0, y1, y2, y3, prev); |
| d1 = v2DistSquare(_v1, _v0); |
| |
| if (prev >= 0 && d1 < d) { |
| t = prev; |
| d = d1; |
| } else { |
| // t + interval |
| _v2[0] = cubicAt(x0, x1, x2, x3, next); |
| _v2[1] = cubicAt(y0, y1, y2, y3, next); |
| d2 = v2DistSquare(_v2, _v0); |
| |
| if (next <= 1 && d2 < d) { |
| t = next; |
| d = d2; |
| } else { |
| interval *= 0.5; |
| } |
| } |
| } // t |
| |
| |
| if (out) { |
| out[0] = cubicAt(x0, x1, x2, x3, t); |
| out[1] = cubicAt(y0, y1, y2, y3, t); |
| } // console.log(interval, i); |
| |
| |
| return mathSqrt(d); |
| } |
| /** |
| * 计算二次方贝塞尔值 |
| * @param {number} p0 |
| * @param {number} p1 |
| * @param {number} p2 |
| * @param {number} t |
| * @return {number} |
| */ |
| |
| export function quadraticAt(p0, p1, p2, t) { |
| var onet = 1 - t; |
| return onet * (onet * p0 + 2 * t * p1) + t * t * p2; |
| } |
| /** |
| * 计算二次方贝塞尔导数值 |
| * @param {number} p0 |
| * @param {number} p1 |
| * @param {number} p2 |
| * @param {number} t |
| * @return {number} |
| */ |
| |
| export function quadraticDerivativeAt(p0, p1, p2, t) { |
| return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1)); |
| } |
| /** |
| * 计算二次方贝塞尔方程根 |
| * @param {number} p0 |
| * @param {number} p1 |
| * @param {number} p2 |
| * @param {number} t |
| * @param {Array.<number>} roots |
| * @return {number} 有效根数目 |
| */ |
| |
| export function quadraticRootAt(p0, p1, p2, val, roots) { |
| var a = p0 - 2 * p1 + p2; |
| var b = 2 * (p1 - p0); |
| var c = p0 - val; |
| var n = 0; |
| |
| if (isAroundZero(a)) { |
| if (isNotAroundZero(b)) { |
| var t1 = -c / b; |
| |
| if (t1 >= 0 && t1 <= 1) { |
| roots[n++] = t1; |
| } |
| } |
| } else { |
| var disc = b * b - 4 * a * c; |
| |
| if (isAroundZero(disc)) { |
| var t1 = -b / (2 * a); |
| |
| if (t1 >= 0 && t1 <= 1) { |
| roots[n++] = t1; |
| } |
| } else if (disc > 0) { |
| var discSqrt = mathSqrt(disc); |
| var t1 = (-b + discSqrt) / (2 * a); |
| var t2 = (-b - discSqrt) / (2 * a); |
| |
| if (t1 >= 0 && t1 <= 1) { |
| roots[n++] = t1; |
| } |
| |
| if (t2 >= 0 && t2 <= 1) { |
| roots[n++] = t2; |
| } |
| } |
| } |
| |
| return n; |
| } |
| /** |
| * 计算二次贝塞尔方程极限值 |
| * @memberOf module:zrender/core/curve |
| * @param {number} p0 |
| * @param {number} p1 |
| * @param {number} p2 |
| * @return {number} |
| */ |
| |
| export function quadraticExtremum(p0, p1, p2) { |
| var divider = p0 + p2 - 2 * p1; |
| |
| if (divider === 0) { |
| // p1 is center of p0 and p2 |
| return 0.5; |
| } else { |
| return (p0 - p1) / divider; |
| } |
| } |
| /** |
| * 细分二次贝塞尔曲线 |
| * @memberOf module:zrender/core/curve |
| * @param {number} p0 |
| * @param {number} p1 |
| * @param {number} p2 |
| * @param {number} t |
| * @param {Array.<number>} out |
| */ |
| |
| export function quadraticSubdivide(p0, p1, p2, t, out) { |
| var p01 = (p1 - p0) * t + p0; |
| var p12 = (p2 - p1) * t + p1; |
| var p012 = (p12 - p01) * t + p01; // Seg0 |
| |
| out[0] = p0; |
| out[1] = p01; |
| out[2] = p012; // Seg1 |
| |
| out[3] = p012; |
| out[4] = p12; |
| out[5] = p2; |
| } |
| /** |
| * 投射点到二次贝塞尔曲线上,返回投射距离。 |
| * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。 |
| * @param {number} x0 |
| * @param {number} y0 |
| * @param {number} x1 |
| * @param {number} y1 |
| * @param {number} x2 |
| * @param {number} y2 |
| * @param {number} x |
| * @param {number} y |
| * @param {Array.<number>} out 投射点 |
| * @return {number} |
| */ |
| |
| export function quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, out) { |
| // http://pomax.github.io/bezierinfo/#projections |
| var t; |
| var interval = 0.005; |
| var d = Infinity; |
| _v0[0] = x; |
| _v0[1] = y; // 先粗略估计一下可能的最小距离的 t 值 |
| // PENDING |
| |
| for (var _t = 0; _t < 1; _t += 0.05) { |
| _v1[0] = quadraticAt(x0, x1, x2, _t); |
| _v1[1] = quadraticAt(y0, y1, y2, _t); |
| var d1 = v2DistSquare(_v0, _v1); |
| |
| if (d1 < d) { |
| t = _t; |
| d = d1; |
| } |
| } |
| |
| d = Infinity; // At most 32 iteration |
| |
| for (var i = 0; i < 32; i++) { |
| if (interval < EPSILON_NUMERIC) { |
| break; |
| } |
| |
| var prev = t - interval; |
| var next = t + interval; // t - interval |
| |
| _v1[0] = quadraticAt(x0, x1, x2, prev); |
| _v1[1] = quadraticAt(y0, y1, y2, prev); |
| var d1 = v2DistSquare(_v1, _v0); |
| |
| if (prev >= 0 && d1 < d) { |
| t = prev; |
| d = d1; |
| } else { |
| // t + interval |
| _v2[0] = quadraticAt(x0, x1, x2, next); |
| _v2[1] = quadraticAt(y0, y1, y2, next); |
| var d2 = v2DistSquare(_v2, _v0); |
| |
| if (next <= 1 && d2 < d) { |
| t = next; |
| d = d2; |
| } else { |
| interval *= 0.5; |
| } |
| } |
| } // t |
| |
| |
| if (out) { |
| out[0] = quadraticAt(x0, x1, x2, t); |
| out[1] = quadraticAt(y0, y1, y2, t); |
| } // console.log(interval, i); |
| |
| |
| return mathSqrt(d); |
| } |