From b8ee28fd6a6dc568f2742923e8247e3586007330 Mon Sep 17 00:00:00 2001 From: york Date: Tue, 29 Oct 2024 08:20:28 +0800 Subject: [PATCH] feat: lines tangent to ellipse arc and bezier curve --- src/utils/tangent-line.ts | 98 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/src/utils/tangent-line.ts b/src/utils/tangent-line.ts index 6ea3496..68bf77f 100644 --- a/src/utils/tangent-line.ts +++ b/src/utils/tangent-line.ts @@ -30,7 +30,13 @@ export function getLinesTangentTo2GeometryLines(line1: GeometryLine, line2: Geom if (line2.type === 'quadratic curve') { return getLinesTangentToEllipseArcAndQuadraticCurve(line1.curve, line2.curve) } - return [] + if (line2.type === 'bezier curve') { + return getLinesTangentToEllipseArcAndBezierCurve(line1.curve, line2.curve) + } + if (line2.type === 'hyperbola curve') { + return [] + } + return getLinesTangentToEllipseArcAndNurbsCurve(line1.curve, line2.curve) } if (line2.type === 'ellipse arc') return getLinesTangentTo2GeometryLines(line2, line1) if (line1.type === 'quadratic curve') { @@ -158,6 +164,96 @@ export function getLinesTangentToEllipseArcAndQuadraticCurve(curve1: EllipseArc, }) } +export function getLinesTangentToEllipseArcAndBezierCurve(curve1: EllipseArc, curve2: BezierCurve): Tuple2[] { + const [p1, d1, d2] = getEllipseDerivatives(curve1) + const [p2, e1, e2] = getBezierCurveDerivatives(curve2) + const f1 = (t: Vec2): Vec2 => { + // (y1 - y2)/(x1 - x2) = y1'/x1' = y2'/x2' + // z1 = (y1 - y2)x1' - (x1 - x2)y1' + // z2 = y1'x2' - x1'y2' + const { x: x1, y: y1 } = p1(t[0]) + const { x: x11, y: y11 } = d1(t[0]) + const { x: x2, y: y2 } = p2(t[1]) + const { x: x21, y: y21 } = e1(t[1]) + return [(y1 - y2) * x11 - (x1 - x2) * y11, y11 * x21 - x11 * y21] + } + const f2 = (t: Vec2): Matrix2 => { + const { x: x1, y: y1 } = p1(t[0]) + const { x: x11, y: y11 } = d1(t[0]) + const { x: x12, y: y12 } = d2(t[0]) + const { x: x2, y: y2 } = p2(t[1]) + const { x: x21, y: y21 } = e1(t[1]) + const { x: x22, y: y22 } = e2(t[1]) + // dz1/dt1 = y1'x1' + (y1 - y2)x1'' - (x1'y1' + (x1 - x2)y1'') + // dz1/dt2 = -y2'x1' + x2'y1' + // dz2/dt1 = y1''x2' - x1''y2' + // dz2/dt2 = y1'x2'' - x1'y2'' + return [ + y11 * x11 + (y1 - y2) * x12 - (x11 * y11 + (x1 - x2) * y12), + -y21 * x11 + x21 * y11, + y12 * x21 - x12 * y21, + y11 * x22 - x11 * y22, + ] + } + let ts: Vec2[] = [] + for (const t1 of [-Math.PI / 2, Math.PI / 2]) { + for (const t2 of [0, 0.5, 1]) { + const t = newtonIterate2([t1, t2], f1, f2, delta2) + if (t !== undefined) { + ts.push(t) + } + } + } + ts = deduplicate(ts, deepEquals) + return ts.filter(v => angleInRange(v[0], curve1) && isValidPercent(v[1])).map(t => { + return [p1(t[0]), p2(t[1])] + }) +} + +export function getLinesTangentToEllipseArcAndNurbsCurve(curve1: EllipseArc, curve2: NurbsCurve): Tuple2[] { + const [p1, d1, d2] = getEllipseDerivatives(curve1) + const nurbs2 = toVerbNurbsCurve(curve2) + const f1 = (t: Vec2): Vec2 => { + // (y1 - y2)/(x1 - x2) = y1'/x1' = y2'/x2' + // z1 = (y1 - y2)x1' - (x1 - x2)y1' + // z2 = y1'x2' - x1'y2' + const { x: x1, y: y1 } = p1(t[0]) + const { x: x11, y: y11 } = d1(t[0]) + const [[x2, y2], [x21, y21]] = nurbs2.derivatives(t[1]) + return [(y1 - y2) * x11 - (x1 - x2) * y11, y11 * x21 - x11 * y21] + } + const f2 = (t: Vec2): Matrix2 => { + const { x: x1, y: y1 } = p1(t[0]) + const { x: x11, y: y11 } = d1(t[0]) + const { x: x12, y: y12 } = d2(t[0]) + const [[x2, y2], [x21, y21], [x22, y22]] = nurbs2.derivatives(t[1], 2) + // dz1/dt1 = y1'x1' + (y1 - y2)x1'' - (x1'y1' + (x1 - x2)y1'') + // dz1/dt2 = -y2'x1' + x2'y1' + // dz2/dt1 = y1''x2' - x1''y2' + // dz2/dt2 = y1'x2'' - x1'y2'' + return [ + y11 * x11 + (y1 - y2) * x12 - (x11 * y11 + (x1 - x2) * y12), + -y21 * x11 + x21 * y11, + y12 * x21 - x12 * y21, + y11 * x22 - x11 * y22, + ] + } + let ts: Vec2[] = [] + const maxParam2 = getNurbsMaxParam(curve2) + for (const t1 of [-Math.PI / 2, Math.PI / 2]) { + for (let t2 = 0.5; t2 < maxParam2; t2++) { + const t = newtonIterate2([t1, t2], f1, f2, delta2) + if (t !== undefined) { + ts.push(t) + } + } + } + ts = deduplicate(ts, deepEquals) + return ts.filter(v => angleInRange(v[0], curve1) && isBetween(v[1], 0, maxParam2)).map(t => { + return [p1(t[0]), fromVerbPoint(nurbs2.point(t[1]))] + }) +} + export function getLinesTangentTo2QuadraticCurves(curve1: QuadraticCurve, curve2: QuadraticCurve): Tuple2[] { const { from: { x: a1, y: b1 }, cp: { x: a2, y: b2 }, to: { x: a3, y: b3 } } = curve1 const c1 = a2 - a1, c2 = a3 - a2 - c1, c3 = b2 - b1, c4 = b3 - b2 - c3