|
@@ -0,0 +1,171 @@
|
|
|
+const sqrt = Math.sqrt,
|
|
|
+ min = Math.min,
|
|
|
+ max = Math.max,
|
|
|
+ sign = Math.sign,
|
|
|
+ abs = Math.abs,
|
|
|
+ pow = Math.pow,
|
|
|
+ acos = Math.acos,
|
|
|
+ cos = Math.cos,
|
|
|
+ sin = Math.sin;
|
|
|
+
|
|
|
+export function transformCoord(rotation, dx, dy) {
|
|
|
+ var cos = Math.cos(rotation);
|
|
|
+ var sin = Math.sin(rotation);
|
|
|
+ return function transformPoint(x, y) {
|
|
|
+ return {
|
|
|
+ x: cos * (x - dx) + sin * (y - dy),
|
|
|
+ y: -sin * (x - dx) + cos * (y - dy),
|
|
|
+ };
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+export function createIsRoughHit(fromX, fromY, cpX, cpY, toX, toY) {
|
|
|
+ var _a, _b, _c;
|
|
|
+ var transform = transformCoord(Math.atan2(toX - fromX, toY - fromY), (fromX + toX) / 2, (fromY + toY) / 2);
|
|
|
+ var t_from = transform(fromX, fromY);
|
|
|
+ var t_cp = transform(cpX, cpY);
|
|
|
+ var t_to = transform(toX, toY);
|
|
|
+ var ratio = Math.abs(t_from.x);
|
|
|
+ var _d = [t_cp.y / 2, 0].sort((a, b) => a - b),
|
|
|
+ bottom = _d[0],
|
|
|
+ top = _d[1];
|
|
|
+ var left, right;
|
|
|
+ if (t_cp.x >= t_from.x && t_cp.x <= t_to.y) {
|
|
|
+ (_a = [t_from.x, t_to.x]), (left = _a[0]), (right = _a[1]);
|
|
|
+ } else if (t_cp.x > 0) {
|
|
|
+ (_b = [t_from.x, 0.5 * (t_cp.x + (ratio * ratio) / t_cp.x)]), (left = _b[0]), (right = _b[1]);
|
|
|
+ } else {
|
|
|
+ (_c = [0.5 * (t_cp.x + (ratio * ratio) / t_cp.x), t_to.x]), (left = _c[0]), (right = _c[1]);
|
|
|
+ }
|
|
|
+ return function isRoughtHit(x, y, distance) {
|
|
|
+ var t_point = transform(x, y);
|
|
|
+ if (t_point.x < left - distance || t_point.x > right + distance) return false;
|
|
|
+ if (t_point.y < bottom - distance || t_point.y > top + distance) return false;
|
|
|
+ return true;
|
|
|
+ };
|
|
|
+}
|
|
|
+export function baseMeasureBezier(fromX, fromY, cpX, cpY, toX, toY) {
|
|
|
+ var isRoughHit = createIsRoughHit(fromX, fromY, cpX, cpY, toX, toY);
|
|
|
+ var _a = (0, createCalDistanceToBezier)(fromX, fromY, cpX, cpY, toX, toY),
|
|
|
+ getDistance = _a.getDistance,
|
|
|
+ getInfo = _a.getInfo;
|
|
|
+ function baseIsHit(x, y, hitDistance) {
|
|
|
+ if (typeof hitDistance !== 'number') throw new Error('isHit params "hitDistance" must be a number');
|
|
|
+ // rough hit
|
|
|
+ if (!isRoughHit(x, y, hitDistance)) return false;
|
|
|
+ // analyical
|
|
|
+ return getDistance(x, y) <= hitDistance;
|
|
|
+ }
|
|
|
+ function isHit(a, b, c) {
|
|
|
+ if (typeof a === 'number') return baseIsHit(a, b, c);
|
|
|
+ if (Array.isArray(a)) return baseIsHit(a[0], a[1], b);
|
|
|
+ return baseIsHit(a.x, a.y, b);
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ isHit: isHit,
|
|
|
+ getInfo: function (a, b) {
|
|
|
+ if (typeof a === 'number') return getInfo(a, b);
|
|
|
+ if (Array.isArray(a)) return getInfo(a[0], a[1]);
|
|
|
+ return getInfo(a.x, a.y);
|
|
|
+ },
|
|
|
+ };
|
|
|
+}
|
|
|
+export function measureBezier(a, b, c, d, e, f) {
|
|
|
+ if (typeof a === 'number') {
|
|
|
+ // @ts-ignore
|
|
|
+ return baseMeasureBezier(a, b, c, d, e, f);
|
|
|
+ }
|
|
|
+ if (Array.isArray(a)) {
|
|
|
+ return baseMeasureBezier(a[0], a[1], b[0], b[1], c[0], c[1]);
|
|
|
+ }
|
|
|
+ // @ts-ignore
|
|
|
+ return baseMeasureBezier(a.x, a.y, b.x, b.y, c.x, c.y);
|
|
|
+}
|
|
|
+
|
|
|
+function clamp(x, min_n, max_n) {
|
|
|
+ return min(max(x, min_n), max_n);
|
|
|
+}
|
|
|
+function dot(x, y) {
|
|
|
+ return x[0] * y[0] + x[1] * y[1];
|
|
|
+}
|
|
|
+function dot2(a, b) {
|
|
|
+ return a * a + b * b;
|
|
|
+}
|
|
|
+export function createCalDistanceToBezier(sX, sY, cpX, cpY, eX, eY) {
|
|
|
+ var a = [cpX - sX, cpY - sY];
|
|
|
+ var b = [sX - 2 * cpX + eX, sY - 2 * cpY + eY];
|
|
|
+ var c = [2 * a[0], 2 * a[1]];
|
|
|
+ var kk = 1 / dot(b, b);
|
|
|
+ var kx = kk * dot(a, b);
|
|
|
+ var kx_2 = kx * kx;
|
|
|
+ var dot_a = dot(a, a);
|
|
|
+ var SQRT_3 = pow(3, 1 / 2);
|
|
|
+ var calBezierTValues =
|
|
|
+ dot(b, b) === 0
|
|
|
+ ? // bezier fallback to line or point
|
|
|
+ function calBezierTValues(d) {
|
|
|
+ return dot_a === 0 ? 0 : clamp(-dot(a, d) / 2 / dot_a, 0, 1);
|
|
|
+ }
|
|
|
+ : function calBezierTValues(d) {
|
|
|
+ var ky = (kk * (2.0 * dot_a + dot(d, b))) / 3.0;
|
|
|
+ var kz = kk * dot(d, a);
|
|
|
+ var p = ky - kx_2;
|
|
|
+ var p3 = p * p * p;
|
|
|
+ var q = kx * (2.0 * kx_2 - 3.0 * ky) + kz;
|
|
|
+ var h = q * q + 4.0 * p3;
|
|
|
+ if (h >= 0.0) {
|
|
|
+ h = sqrt(h);
|
|
|
+ var x_0 = (h - q) / 2.0;
|
|
|
+ var x_1 = (-h - q) / 2.0;
|
|
|
+ var t = clamp(sign(x_0) * pow(abs(x_0), 1 / 3) + sign(x_1) * pow(abs(x_1), 1 / 3) - kx, 0.0, 1.0);
|
|
|
+ return t;
|
|
|
+ } else {
|
|
|
+ var z = sqrt(-p);
|
|
|
+ var v = acos(q / (p * z * 2.0)) / 3.0;
|
|
|
+ var m = cos(v);
|
|
|
+ var n = sin(v) * SQRT_3;
|
|
|
+ return [clamp((m + m) * z - kx, 0, 1), clamp((-n - m) * z - kx, 0, 1)];
|
|
|
+ }
|
|
|
+ };
|
|
|
+ return {
|
|
|
+ getDistance: function (pX, pY) {
|
|
|
+ var d = [sX - pX, sY - pY];
|
|
|
+ var t = calBezierTValues(d);
|
|
|
+ if (typeof t === 'number') return sqrt(dot2((c[0] + b[0] * t) * t + d[0], (c[1] + b[1] * t) * t + d[1]));
|
|
|
+ var _a = t,
|
|
|
+ t_0 = _a[0],
|
|
|
+ t_1 = _a[1];
|
|
|
+ return sqrt(min(dot2(d[0] + (c[0] + b[0] * t_0) * t_0, d[1] + (c[1] + b[1] * t_0) * t_0), dot2(d[0] + (c[0] + b[0] * t_1) * t_1, d[1] + (c[1] + b[1] * t_1) * t_1)));
|
|
|
+ },
|
|
|
+ getInfo: function (pX, pY) {
|
|
|
+ var d = [sX - pX, sY - pY];
|
|
|
+ var t = calBezierTValues(d);
|
|
|
+ if (typeof t === 'number') {
|
|
|
+ var p_x = (c[0] + b[0] * t) * t + d[0];
|
|
|
+ var p_y = (c[1] + b[1] * t) * t + d[1];
|
|
|
+ return {
|
|
|
+ distance: sqrt(dot2(p_x, p_y)),
|
|
|
+ point: [p_x + pX, p_y + pY],
|
|
|
+ };
|
|
|
+ }
|
|
|
+ var _a = t,
|
|
|
+ t_0 = _a[0],
|
|
|
+ t_1 = _a[1];
|
|
|
+ var p_0_x = (c[0] + b[0] * t_0) * t_0 + d[0];
|
|
|
+ var p_0_y = (c[1] + b[1] * t_0) * t_0 + d[1];
|
|
|
+ var p_1_x = (c[0] + b[0] * t_1) * t_1 + d[0];
|
|
|
+ var p_1_y = (c[1] + b[1] * t_1) * t_1 + d[1];
|
|
|
+ var d_0 = dot2(p_0_x, p_0_y);
|
|
|
+ var d_1 = dot2(p_1_x, p_1_y);
|
|
|
+ return d_0 >= d_1
|
|
|
+ ? {
|
|
|
+ distance: sqrt(d_1),
|
|
|
+ point: [p_1_x + pX, p_1_y + pY],
|
|
|
+ }
|
|
|
+ : {
|
|
|
+ distance: sqrt(d_0),
|
|
|
+ point: [p_0_x + pX, p_0_y + pY],
|
|
|
+ };
|
|
|
+ },
|
|
|
+ };
|
|
|
+}
|