532 lines
11 KiB
JavaScript
532 lines
11 KiB
JavaScript
var _vector = require("./vector");
|
|
|
|
var v2Create = _vector.create;
|
|
var v2DistSquare = _vector.distSquare;
|
|
|
|
/**
|
|
* 曲线辅助模块
|
|
* @module zrender/core/curve
|
|
* @author pissang(https://www.github.com/pissang)
|
|
*/
|
|
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}
|
|
*/
|
|
|
|
|
|
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}
|
|
*/
|
|
|
|
|
|
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} 有效根数目
|
|
*/
|
|
|
|
|
|
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} 有效数目
|
|
*/
|
|
|
|
|
|
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
|
|
*/
|
|
|
|
|
|
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}
|
|
*/
|
|
|
|
|
|
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}
|
|
*/
|
|
|
|
|
|
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}
|
|
*/
|
|
|
|
|
|
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} 有效根数目
|
|
*/
|
|
|
|
|
|
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}
|
|
*/
|
|
|
|
|
|
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
|
|
*/
|
|
|
|
|
|
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}
|
|
*/
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
exports.cubicAt = cubicAt;
|
|
exports.cubicDerivativeAt = cubicDerivativeAt;
|
|
exports.cubicRootAt = cubicRootAt;
|
|
exports.cubicExtrema = cubicExtrema;
|
|
exports.cubicSubdivide = cubicSubdivide;
|
|
exports.cubicProjectPoint = cubicProjectPoint;
|
|
exports.quadraticAt = quadraticAt;
|
|
exports.quadraticDerivativeAt = quadraticDerivativeAt;
|
|
exports.quadraticRootAt = quadraticRootAt;
|
|
exports.quadraticExtremum = quadraticExtremum;
|
|
exports.quadraticSubdivide = quadraticSubdivide;
|
|
exports.quadraticProjectPoint = quadraticProjectPoint; |