Skip to content

Commit

Permalink
feat: lines tangent to 2 ellipse arcs 67c0059
Browse files Browse the repository at this point in the history
  • Loading branch information
plantain-00 committed Oct 23, 2024
1 parent 59336d0 commit 588ff79
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 2 deletions.
2 changes: 1 addition & 1 deletion main.bundle.js

Large diffs are not rendered by default.

97 changes: 96 additions & 1 deletion src/utils/tangent-line.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { angleInRange } from "./angle";
import { BezierCurve, getBezierCurveDerivatives, getBezierCurvePointAtPercent, getQuadraticCurveDerivatives, getQuadraticCurvePointAtPercent, QuadraticCurve } from "./bezier";
import { EllipseArc, getEllipseDerivatives } from "./ellipse";
import { calculateEquation4, calculateEquation5, newtonIterate2 } from "./equation-calculater";
import { GeometryLine } from "./geometry-line";
import { getHyperbolaDerivatives, HyperbolaSegment } from "./hyperbola";
Expand All @@ -22,6 +24,9 @@ export function getLinesTangentTo2GeometryLines(line1: GeometryLine, line2: Geom
}
if (line2.type === 'arc') return getLinesTangentTo2GeometryLines(line2, line1)
if (line1.type === 'ellipse arc') {
if (line2.type === 'ellipse arc') {
return getLinesTangentTo2EllipseArcs(line1.curve, line2.curve)
}
return []
}
if (line2.type === 'ellipse arc') return getLinesTangentTo2GeometryLines(line2, line1)
Expand Down Expand Up @@ -52,12 +57,58 @@ export function getLinesTangentTo2GeometryLines(line1: GeometryLine, line2: Geom
if (line2.type === 'hyperbola curve') {
return getLinesTangentTo2Hyperbolas(line1.curve, line2.curve)
}
return []
return getLinesTangentToHyperbolaAndNurbsCurve(line1.curve, line2.curve)
}
if (line2.type === 'hyperbola curve') return getLinesTangentTo2GeometryLines(line2, line1)
return []
}

export function getLinesTangentTo2EllipseArcs(curve1: EllipseArc, curve2: EllipseArc): Tuple2<Position>[] {
const [p1, d1, d2] = getEllipseDerivatives(curve1)
const [p2, e1, e2] = getEllipseDerivatives(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 [-Math.PI / 2, Math.PI / 2]) {
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) && angleInRange(v[1], curve2)).map(t => {
return [p1(t[0]), p2(t[1])]
})
}

export function getLinesTangentTo2QuadraticCurves(curve1: QuadraticCurve, curve2: QuadraticCurve): Tuple2<Position>[] {
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
Expand Down Expand Up @@ -436,3 +487,47 @@ export function getLinesTangentTo2Hyperbolas(curve1: HyperbolaSegment, curve2: H
return [p1(t[0]), p2(t[1])]
})
}

export function getLinesTangentToHyperbolaAndNurbsCurve(curve1: HyperbolaSegment, curve2: NurbsCurve): Tuple2<Position>[] {
const [p1, d1, d2] = getHyperbolaDerivatives(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 [-1, 1]) {
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 => isBetween(v[0], curve1.t1, curve1.t2) && isBetween(v[1], 0, maxParam2)).map(t => {
return [p1(t[0]), fromVerbPoint(nurbs2.point(t[1]))]
})
}

0 comments on commit 588ff79

Please sign in to comment.