|
@@ -1,4 +1,5 @@
|
|
|
import Constant from "../Constant";
|
|
|
+import bezierUtil from "./bezierUtil.js";
|
|
|
|
|
|
export default class MathUtil {
|
|
|
constructor() {}
|
|
@@ -1043,22 +1044,22 @@ export default class MathUtil {
|
|
|
pointMinus(v1, v2) {
|
|
|
return {
|
|
|
x: v1.x - v2.x,
|
|
|
- y: v1.y - v2.y
|
|
|
- }
|
|
|
+ y: v1.y - v2.y,
|
|
|
+ };
|
|
|
}
|
|
|
// 两点相加
|
|
|
pointPlus(v1, v2) {
|
|
|
return {
|
|
|
x: v1.x + v2.x,
|
|
|
- y: v1.y + v2.y
|
|
|
- }
|
|
|
+ y: v1.y + v2.y,
|
|
|
+ };
|
|
|
}
|
|
|
// 点放大
|
|
|
pointScale(v, a) {
|
|
|
return {
|
|
|
x: v.x * a,
|
|
|
- y: v.y * a
|
|
|
- }
|
|
|
+ y: v.y * a,
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
clamp(value, min, max) {
|
|
@@ -1069,7 +1070,6 @@ export default class MathUtil {
|
|
|
return v.x * v.x + v.y * v.y;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
// 当前点 下一个点 下下个点
|
|
|
getCurvesControls(p1, pt, p2, scale = 0.3) {
|
|
|
const vec1T = mathUtil.pointMinus(p1, pt);
|
|
@@ -1082,12 +1082,18 @@ export default class MathUtil {
|
|
|
if (v > 1) {
|
|
|
delta = mathUtil.pointMinus(
|
|
|
p1,
|
|
|
- mathUtil.pointPlus(pt, mathUtil.pointScale(mathUtil.pointMinus(p2, pt), (1 / v))),
|
|
|
+ mathUtil.pointPlus(
|
|
|
+ pt,
|
|
|
+ mathUtil.pointScale(mathUtil.pointMinus(p2, pt), 1 / v)
|
|
|
+ )
|
|
|
);
|
|
|
} else {
|
|
|
delta = mathUtil.pointMinus(
|
|
|
- mathUtil.pointPlus(pt, mathUtil.pointScale(mathUtil.pointMinus(p1, pt), v)),
|
|
|
- p2,
|
|
|
+ mathUtil.pointPlus(
|
|
|
+ pt,
|
|
|
+ mathUtil.pointScale(mathUtil.pointMinus(p1, pt), v)
|
|
|
+ ),
|
|
|
+ p2
|
|
|
);
|
|
|
}
|
|
|
delta = mathUtil.pointScale(delta, scale);
|
|
@@ -1100,44 +1106,42 @@ export default class MathUtil {
|
|
|
x: mathUtil.pointMinus(pt, delta).x,
|
|
|
y: mathUtil.pointMinus(pt, delta).y,
|
|
|
};
|
|
|
- return {control1, control2}
|
|
|
+ return { control1, control2 };
|
|
|
}
|
|
|
|
|
|
getCurvesByPoints(points, scale = 0.2) {
|
|
|
const curves = [];
|
|
|
- let preControl1, preControl2
|
|
|
+ let preControl1, preControl2;
|
|
|
for (let i = 0; i < points.length - 2; i++) {
|
|
|
- const {control1, control2} = mathUtil.getCurvesControls(
|
|
|
+ const { control1, control2 } = mathUtil.getCurvesControls(
|
|
|
points[i],
|
|
|
points[i + 1],
|
|
|
points[i + 2],
|
|
|
scale
|
|
|
- )
|
|
|
+ );
|
|
|
curves.push({
|
|
|
start: points[i],
|
|
|
end: points[i + 1],
|
|
|
- controls: i === 0 ? [control1] : [preControl2, control1]
|
|
|
- })
|
|
|
+ controls: i === 0 ? [control1] : [preControl2, control1],
|
|
|
+ });
|
|
|
if (i + 2 === points.length - 1) {
|
|
|
curves.push({
|
|
|
start: points[i + 1],
|
|
|
controls: [control2],
|
|
|
end: points[i + 2],
|
|
|
- })
|
|
|
+ });
|
|
|
}
|
|
|
- preControl1 = control1
|
|
|
- preControl2 = control2
|
|
|
+ preControl1 = control1;
|
|
|
+ preControl2 = control2;
|
|
|
}
|
|
|
curves.push({
|
|
|
start: points[points.length - 2],
|
|
|
controls: [preControl2],
|
|
|
end: points[points.length - 1],
|
|
|
- })
|
|
|
- return curves
|
|
|
+ });
|
|
|
+ return curves;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
/**
|
|
|
* 已知四个控制点,及曲线中的某一个点的 x/y,反推求 t
|
|
|
* @param {number} x1 起点 x/y
|
|
@@ -1148,63 +1152,75 @@ export default class MathUtil {
|
|
|
* @returns {number[]} t[]
|
|
|
*/
|
|
|
getThreeBezierT(x1, x2, x3, x4, X) {
|
|
|
- const a = -x1 + 3 * x2 - 3 * x3 + x4
|
|
|
- const b = 3 * x1 - 6 * x2 + 3 * x3
|
|
|
- const c = -3 * x1 + 3 * x2
|
|
|
- const d = x1 - X
|
|
|
+ const a = -x1 + 3 * x2 - 3 * x3 + x4;
|
|
|
+ const b = 3 * x1 - 6 * x2 + 3 * x3;
|
|
|
+ const c = -3 * x1 + 3 * x2;
|
|
|
+ const d = x1 - X;
|
|
|
|
|
|
// 盛金公式, 预先需满足, a !== 0
|
|
|
// 判别式
|
|
|
- const A = Math.pow(b, 2) - 3 * a * c
|
|
|
- const B = b * c - 9 * a * d
|
|
|
- const C = Math.pow(c, 2) - 3 * b * d
|
|
|
- const delta = Math.pow(B, 2) - 4 * A * C
|
|
|
+ const A = Math.pow(b, 2) - 3 * a * c;
|
|
|
+ const B = b * c - 9 * a * d;
|
|
|
+ const C = Math.pow(c, 2) - 3 * b * d;
|
|
|
+ const delta = Math.pow(B, 2) - 4 * A * C;
|
|
|
|
|
|
- let t1 = -100, t2 = -100, t3 = -100
|
|
|
+ let t1 = -100,
|
|
|
+ t2 = -100,
|
|
|
+ t3 = -100;
|
|
|
|
|
|
// 3个相同实数根
|
|
|
if (A === B && A === 0) {
|
|
|
- t1 = -b / (3 * a)
|
|
|
- t2 = -c / b
|
|
|
- t3 = -3 * d / c
|
|
|
- return [t1, t2, t3]
|
|
|
+ t1 = -b / (3 * a);
|
|
|
+ t2 = -c / b;
|
|
|
+ t3 = (-3 * d) / c;
|
|
|
+ return [t1, t2, t3];
|
|
|
}
|
|
|
|
|
|
// 1个实数根和1对共轭复数根
|
|
|
if (delta > 0) {
|
|
|
- const v = Math.pow(B, 2) - 4 * A * C
|
|
|
- const xsv = v < 0 ? -1 : 1
|
|
|
+ const v = Math.pow(B, 2) - 4 * A * C;
|
|
|
+ const xsv = v < 0 ? -1 : 1;
|
|
|
|
|
|
- const m1 = A * b + 3 * a * (-B + (v * xsv) ** (1 / 2) * xsv) / 2
|
|
|
- const m2 = A * b + 3 * a * (-B - (v * xsv) ** (1 / 2) * xsv) / 2
|
|
|
+ const m1 = A * b + (3 * a * (-B + (v * xsv) ** (1 / 2) * xsv)) / 2;
|
|
|
+ const m2 = A * b + (3 * a * (-B - (v * xsv) ** (1 / 2) * xsv)) / 2;
|
|
|
|
|
|
- const xs1 = m1 < 0 ? -1 : 1
|
|
|
- const xs2 = m2 < 0 ? -1 : 1
|
|
|
+ const xs1 = m1 < 0 ? -1 : 1;
|
|
|
+ const xs2 = m2 < 0 ? -1 : 1;
|
|
|
|
|
|
- t1 = (-b - (m1 * xs1) ** (1 / 3) * xs1 - (m2 * xs2) ** (1 / 3) * xs2) / (3 * a)
|
|
|
+ t1 =
|
|
|
+ (-b - (m1 * xs1) ** (1 / 3) * xs1 - (m2 * xs2) ** (1 / 3) * xs2) /
|
|
|
+ (3 * a);
|
|
|
// 涉及虚数,可不考虑。i ** 2 = -1
|
|
|
}
|
|
|
|
|
|
// 3个实数根
|
|
|
if (delta === 0) {
|
|
|
- const K = B / A
|
|
|
- t1 = -b / a + K
|
|
|
- t2 = t3 = -K / 2
|
|
|
+ const K = B / A;
|
|
|
+ t1 = -b / a + K;
|
|
|
+ t2 = t3 = -K / 2;
|
|
|
}
|
|
|
|
|
|
// 3个不相等实数根
|
|
|
if (delta < 0) {
|
|
|
- const xsA = A < 0 ? -1 : 1
|
|
|
- const T = (2 * A * b - 3 * a * B) / (2 * (A * xsA) ** (3 / 2) * xsA)
|
|
|
- const theta = Math.acos(T)
|
|
|
+ const xsA = A < 0 ? -1 : 1;
|
|
|
+ const T = (2 * A * b - 3 * a * B) / (2 * (A * xsA) ** (3 / 2) * xsA);
|
|
|
+ const theta = Math.acos(T);
|
|
|
|
|
|
if (A > 0 && T < 1 && T > -1) {
|
|
|
- t1 = (-b - 2 * A ** (1 / 2) * Math.cos(theta / 3)) / (3 * a)
|
|
|
- t2 = (-b + A ** (1 / 2) * (Math.cos(theta / 3) + 3 ** (1 / 2) * Math.sin(theta / 3))) / (3 * a)
|
|
|
- t3 = (-b + A ** (1 / 2) * (Math.cos(theta / 3) - 3 ** (1 / 2) * Math.sin(theta / 3))) / (3 * a)
|
|
|
+ t1 = (-b - 2 * A ** (1 / 2) * Math.cos(theta / 3)) / (3 * a);
|
|
|
+ t2 =
|
|
|
+ (-b +
|
|
|
+ A ** (1 / 2) *
|
|
|
+ (Math.cos(theta / 3) + 3 ** (1 / 2) * Math.sin(theta / 3))) /
|
|
|
+ (3 * a);
|
|
|
+ t3 =
|
|
|
+ (-b +
|
|
|
+ A ** (1 / 2) *
|
|
|
+ (Math.cos(theta / 3) - 3 ** (1 / 2) * Math.sin(theta / 3))) /
|
|
|
+ (3 * a);
|
|
|
}
|
|
|
}
|
|
|
- return [t1, t2, t3]
|
|
|
+ return [t1, t2, t3];
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1217,55 +1233,113 @@ export default class MathUtil {
|
|
|
* @param {Array} cp2 控制点2
|
|
|
*/
|
|
|
getThreeBezierPoint(t, p1, cp1, cp2, p2) {
|
|
|
- const {x: x1, y: y1} = p1
|
|
|
- const {x: x2, y: y2} = p2
|
|
|
- const {x: cx1, y: cy1} = cp1
|
|
|
- const {x: cx2, y: cy2} = cp2
|
|
|
+ const { x: x1, y: y1 } = p1;
|
|
|
+ const { x: x2, y: y2 } = p2;
|
|
|
+ const { x: cx1, y: cy1 } = cp1;
|
|
|
+ const { x: cx2, y: cy2 } = cp2;
|
|
|
|
|
|
const x =
|
|
|
x1 * (1 - t) * (1 - t) * (1 - t) +
|
|
|
3 * cx1 * t * (1 - t) * (1 - t) +
|
|
|
3 * cx2 * t * t * (1 - t) +
|
|
|
- x2 * t * t * t
|
|
|
+ x2 * t * t * t;
|
|
|
const y =
|
|
|
y1 * (1 - t) * (1 - t) * (1 - t) +
|
|
|
3 * cy1 * t * (1 - t) * (1 - t) +
|
|
|
3 * cy2 * t * t * (1 - t) +
|
|
|
- y2 * t * t * t
|
|
|
- return {x, y}
|
|
|
+ y2 * t * t * t;
|
|
|
+ return { x, y };
|
|
|
}
|
|
|
|
|
|
- isAboveThreeBezier(offsetX, offsetY, curve, rang = 3) {
|
|
|
+ getHitInfoForThreeBezier(offsetX, offsetY, curve, rang = 3) {
|
|
|
// 用 x 求出对应的 t,用 t 求相应位置的 y,再比较得出的 y 与 offsetY 之间的差值
|
|
|
- const tsx = mathUtil.getThreeBezierT(curve.start.x, curve.controls[0].x, curve.controls[1].x, curve.end.x, offsetX)
|
|
|
+ const tsx = mathUtil.getThreeBezierT(
|
|
|
+ curve.start.x,
|
|
|
+ curve.controls[0].x,
|
|
|
+ curve.controls[1].x,
|
|
|
+ curve.end.x,
|
|
|
+ offsetX
|
|
|
+ );
|
|
|
for (let x = 0; x < 3; x++) {
|
|
|
if (tsx[x] <= 1 && tsx[x] >= 0) {
|
|
|
- const point = mathUtil.getThreeBezierPoint(tsx[x], curve.start, curve.controls[0], curve.controls[1], curve.end)
|
|
|
+ const point = mathUtil.getThreeBezierPoint(
|
|
|
+ tsx[x],
|
|
|
+ curve.start,
|
|
|
+ curve.controls[0],
|
|
|
+ curve.controls[1],
|
|
|
+ curve.end
|
|
|
+ );
|
|
|
if (Math.abs(point.y - offsetY) < rang) {
|
|
|
- return point
|
|
|
+ return point;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
// 如果上述没有结果,则用 y 求出对应的 t,再用 t 求出对应的 x,与 offsetX 进行匹配
|
|
|
- const tsy = mathUtil.getThreeBezierT(curve.start.y, curve.controls[0].y, curve.controls[1].y, curve.end.y, offsetY)
|
|
|
+ const tsy = mathUtil.getThreeBezierT(
|
|
|
+ curve.start.y,
|
|
|
+ curve.controls[0].y,
|
|
|
+ curve.controls[1].y,
|
|
|
+ curve.end.y,
|
|
|
+ offsetY
|
|
|
+ );
|
|
|
for (let y = 0; y < 3; y++) {
|
|
|
if (tsy[y] <= 1 && tsy[y] >= 0) {
|
|
|
- const point = mathUtil.getThreeBezierPoint(tsy[y], curve.start, curve.controls[0], curve.controls[1], curve.end)
|
|
|
+ const point = mathUtil.getThreeBezierPoint(
|
|
|
+ tsy[y],
|
|
|
+ curve.start,
|
|
|
+ curve.controls[0],
|
|
|
+ curve.controls[1],
|
|
|
+ curve.end
|
|
|
+ );
|
|
|
if (Math.abs(point.x - offsetX) < rang) {
|
|
|
- return point
|
|
|
+ return point;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- isAboveCurves(pos, curves) {
|
|
|
+ // 二次曲线
|
|
|
+ getHitInfoForTwoBezier(position, curves) {
|
|
|
+ let joinInfo = {
|
|
|
+ position: null,
|
|
|
+ distance: null,
|
|
|
+ };
|
|
|
+ for (let i = 0; i < curves.length; ++i) {
|
|
|
+ const curve = curves[i];
|
|
|
+ let bezierData = [];
|
|
|
+ bezierData.push(curve.start.x);
|
|
|
+ bezierData.push(curve.start.y);
|
|
|
+ bezierData.push(curve.control.x);
|
|
|
+ bezierData.push(curve.control.y);
|
|
|
+ bezierData.push(curve.end.x);
|
|
|
+ bezierData.push(curve.end.y);
|
|
|
+ const { isHit, getInfo } = bezierUtil.measureBezier(...bezierData);
|
|
|
+ const { point } = getInfo(position);
|
|
|
+ const distance = mathUtil.getDistance(position, {
|
|
|
+ x: point[0],
|
|
|
+ y: point[1],
|
|
|
+ });
|
|
|
+ if (joinInfo.distance == null || distance < joinInfo.distance) {
|
|
|
+ joinInfo.distance = mathUtil.getDistance(position, {
|
|
|
+ x: point[0],
|
|
|
+ y: point[1],
|
|
|
+ });
|
|
|
+ joinInfo.position = {
|
|
|
+ x: point[0],
|
|
|
+ y: point[1],
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return joinInfo;
|
|
|
+ }
|
|
|
+
|
|
|
+ getHitInfoForCurves(pos, curves) {
|
|
|
for (const curve of curves) {
|
|
|
if (curve.controls.length === 2) {
|
|
|
- if (mathUtil.isAboveThreeBezier(pos.x, pos.y, curve)) {
|
|
|
+ if (mathUtil.getHitInfoForThreeBezier(pos.x, pos.y, curve)) {
|
|
|
return true;
|
|
|
}
|
|
|
} else {
|
|
|
- // 二次曲线
|
|
|
}
|
|
|
}
|
|
|
}
|