Skip to content

Commit

Permalink
feat: create tangent tangent line at points e76bbef
Browse files Browse the repository at this point in the history
  • Loading branch information
plantain-00 committed Nov 8, 2024
1 parent 7c99d63 commit 3cb7479
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import type { PluginContext } from './types'
import type * as core from '../../../src'
import type { Command } from '../command'
import type { LineContent } from './line-polyline.plugin'

export function getCommand(ctx: PluginContext): Command {
const React = ctx.React
const icon = (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="78" cy="80" r="18" strokeWidth="5" strokeMiterlimit="10" strokeLinejoin="miter" strokeLinecap="butt" fillOpacity="1" strokeOpacity="1" fill="none" stroke="currentColor"></circle>
<circle cx="29" cy="29" r="27" strokeWidth="5" strokeMiterlimit="10" strokeLinejoin="miter" strokeLinecap="butt" fillOpacity="1" strokeOpacity="1" fill="none" stroke="currentColor"></circle>
<circle cx="92" cy="70" r="8" fillOpacity="1" strokeOpacity="1" fill="currentColor" stroke="none"></circle>
<circle cx="51" cy="13" r="8" fillOpacity="1" strokeOpacity="1" fill="currentColor" stroke="none"></circle>
<polyline points="92,70 51,13" strokeWidth="5" strokeMiterlimit="10" strokeLinejoin="miter" strokeLinecap="butt" strokeOpacity="1" fill="none" stroke="currentColor"></polyline>
</svg>
)
return {
name: 'create tangent tangent line at points',
useCommand({ onEnd, type, scale, contents }) {
const [start, setStart] = React.useState<{ point: core.Position, param: number, line: core.GeometryLine }>()
const [cursor, setCursor] = React.useState<core.Position>()
const [result, setResult] = React.useState<[core.Position, core.Position]>()
const assistentContents: LineContent[] = []
if (start && cursor && type) {
assistentContents.push({
points: [start.point, cursor],
type: 'line',
dashArray: [4 / scale],
})
}
if (result) {
assistentContents.push({
points: result,
type: 'line',
})
}
const reset = () => {
setStart(undefined)
setResult(undefined)
setCursor(undefined)
}
const getTarget = (point: core.Position, id: number, param: number) => {
const content = contents[id]
if (!content) return
const lines = ctx.getContentModel(content)?.getGeometries?.(content, contents)?.lines
if (!lines) return
const index = Math.floor(param)
return { point, line: lines[index], param: param - index }
}
return {
onStart(p, target) {
if (!type) return
if (!target) return
if (target.param === undefined) return
if (!start) {
setStart(getTarget(p, target.id, target.param))
} else if (result) {
onEnd({
updateContents: (contents) => {
contents.push({ type: 'line', points: result } as LineContent)
}
})
reset()
}
},
onMove(p, _, target) {
if (!type) return
setCursor(p)
setResult(undefined)
if (!target) return
if (target.param === undefined) return
if (!start) return
const end = getTarget(p, target.id, target.param)
if (!end) return
const params = ctx.getLineTangentToTwoGeometryLinesNearParam(start.line, end.line, start.param, end.param)
if (params) {
setResult([ctx.getGeometryLinePointAtParam(params[0], start.line), ctx.getGeometryLinePointAtParam(params[1], end.line)])
}
},
assistentContents,
reset,
}
},
selectCount: 0,
icon,
}
}
80 changes: 80 additions & 0 deletions dev/cad-editor/plugins/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2555,6 +2555,86 @@ export {
getCommand
};
`,
`// dev/cad-editor/plugins/create-tangent-tangent-line-at-points.plugin.tsx
function getCommand(ctx) {
const React = ctx.React;
const icon = /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 100 100" }, /* @__PURE__ */ React.createElement("circle", { cx: "78", cy: "80", r: "18", strokeWidth: "5", strokeMiterlimit: "10", strokeLinejoin: "miter", strokeLinecap: "butt", fillOpacity: "1", strokeOpacity: "1", fill: "none", stroke: "currentColor" }), /* @__PURE__ */ React.createElement("circle", { cx: "29", cy: "29", r: "27", strokeWidth: "5", strokeMiterlimit: "10", strokeLinejoin: "miter", strokeLinecap: "butt", fillOpacity: "1", strokeOpacity: "1", fill: "none", stroke: "currentColor" }), /* @__PURE__ */ React.createElement("circle", { cx: "92", cy: "70", r: "8", fillOpacity: "1", strokeOpacity: "1", fill: "currentColor", stroke: "none" }), /* @__PURE__ */ React.createElement("circle", { cx: "51", cy: "13", r: "8", fillOpacity: "1", strokeOpacity: "1", fill: "currentColor", stroke: "none" }), /* @__PURE__ */ React.createElement("polyline", { points: "92,70 51,13", strokeWidth: "5", strokeMiterlimit: "10", strokeLinejoin: "miter", strokeLinecap: "butt", strokeOpacity: "1", fill: "none", stroke: "currentColor" }));
return {
name: "create tangent tangent line at points",
useCommand({ onEnd, type, scale, contents }) {
const [start, setStart] = React.useState();
const [cursor, setCursor] = React.useState();
const [result, setResult] = React.useState();
const assistentContents = [];
if (start && cursor && type) {
assistentContents.push({
points: [start.point, cursor],
type: "line",
dashArray: [4 / scale]
});
}
if (result) {
assistentContents.push({
points: result,
type: "line"
});
}
const reset = () => {
setStart(void 0);
setResult(void 0);
setCursor(void 0);
};
const getTarget = (point, id, param) => {
var _a, _b, _c;
const content = contents[id];
if (!content) return;
const lines = (_c = (_b = (_a = ctx.getContentModel(content)) == null ? void 0 : _a.getGeometries) == null ? void 0 : _b.call(_a, content, contents)) == null ? void 0 : _c.lines;
if (!lines) return;
const index = Math.floor(param);
return { point, line: lines[index], param: param - index };
};
return {
onStart(p, target) {
if (!type) return;
if (!target) return;
if (target.param === void 0) return;
if (!start) {
setStart(getTarget(p, target.id, target.param));
} else if (result) {
onEnd({
updateContents: (contents2) => {
contents2.push({ type: "line", points: result });
}
});
reset();
}
},
onMove(p, _, target) {
if (!type) return;
setCursor(p);
setResult(void 0);
if (!target) return;
if (target.param === void 0) return;
if (!start) return;
const end = getTarget(p, target.id, target.param);
if (!end) return;
const params = ctx.getLineTangentToTwoGeometryLinesNearParam(start.line, end.line, start.param, end.param);
if (params) {
setResult([ctx.getGeometryLinePointAtParam(params[0], start.line), ctx.getGeometryLinePointAtParam(params[1], end.line)]);
}
},
assistentContents,
reset
};
},
selectCount: 0,
icon
};
}
export {
getCommand
};
`,
`// dev/cad-editor/plugins/create-tangent-tangent-line.plugin.tsx
function getCommand(ctx) {
function getTangentTangentLines(line1, line2) {
Expand Down
2 changes: 1 addition & 1 deletion main.bundle.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/utils/geometry-line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -726,12 +726,12 @@ export function getGeometryLineDerivatives(line: GeometryLine): [(t: number) =>
const derivatives = getHyperbolaDerivatives(line.curve)
return [
t => {
const angle = getParamAtPercent(t, line.curve.t1, line.curve.t2)
return [derivatives[0](angle), derivatives[1](angle)]
const param = getParamAtPercent(t, line.curve.t1, line.curve.t2)
return [derivatives[0](param), derivatives[1](param)]
},
t => {
const angle = getParamAtPercent(t, line.curve.t1, line.curve.t2)
return [derivatives[0](angle), derivatives[1](angle), derivatives[2](angle)]
const param = getParamAtPercent(t, line.curve.t1, line.curve.t2)
return [derivatives[0](param), derivatives[1](param), derivatives[2](param)]
},
]
}
Expand Down
6 changes: 3 additions & 3 deletions src/utils/hyperbola.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,8 +282,8 @@ export function getHyperbolaDerivatives({ angle, a, b, x: x1, y: y1 }: Hyperbola
// y = c2 + b3 t + b4(t^2 + 1)^0.5
// x' = b2 t (t^2 + 1)^-0.5 + b1
// y' = b4 t (t^2 + 1)^-0.5 + b3
// x' = b2 (t^2 + 1)^-0.5 - b2 t t (t^2 + 1)^-1.5
// y' = b4 (t^2 + 1)^-0.5 - b4 t t (t^2 + 1)^-1.5
// x'' = b2 (t^2 + 1)^-0.5 - b2 t t (t^2 + 1)^-1.5
// y'' = b4 (t^2 + 1)^-0.5 - b4 t t (t^2 + 1)^-1.5
return [
t => {
const d = Math.sqrt(t ** 2 + 1)
Expand All @@ -299,7 +299,7 @@ export function getHyperbolaDerivatives({ angle, a, b, x: x1, y: y1 }: Hyperbola
y: b4 * d + b3,
}
},
(t) => {
t => {
const d0 = t ** 2, d1 = d0 + 1, d2 = Math.sqrt(d1)
const d = 1 / d2 - d0 / d1 / d2
return {
Expand Down
30 changes: 29 additions & 1 deletion src/utils/tangent-line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { BezierCurve, getBezierCurveDerivatives, getBezierCurvePointAtPercent, g
import { Arc, getCircleDerivatives } from "./circle";
import { EllipseArc, getEllipseDerivatives } from "./ellipse";
import { calculateEquation4, calculateEquation5, newtonIterate2 } from "./equation-calculater";
import { GeometryLine } from "./geometry-line";
import { GeometryLine, getGeometryLineDerivatives } from "./geometry-line";
import { getHyperbolaDerivatives, HyperbolaSegment } from "./hyperbola";
import { deduplicate, deepEquals, delta2, isBetween, isValidPercent } from "./math";
import { Matrix2 } from "./matrix";
Expand Down Expand Up @@ -85,6 +85,34 @@ export function getLinesTangentTo2GeometryLines(line1: GeometryLine, line2: Geom
return getLinesTangentToTwoNurbsCurves(line1.curve, line2.curve)
}

export function getLineTangentToTwoGeometryLinesNearParam(curve1: GeometryLine, curve2: GeometryLine, t1: number, t2: number): Vec2 | undefined {
const derivative1 = getGeometryLineDerivatives(curve1)
const derivative2 = getGeometryLineDerivatives(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 }, { x: x11, y: y11 }] = derivative1[0](t[0])
const [{ x: x2, y: y2 }, { x: x21, y: y21 }] = derivative2[0](t[1])
return [(y1 - y2) * x11 - (x1 - x2) * y11, y11 * x21 - x11 * y21]
}
const f2 = (t: Vec2): Matrix2 => {
const [{ x: x1, y: y1 }, { x: x11, y: y11 }, { x: x12, y: y12 }] = derivative1[1](t[0])
const [{ x: x2, y: y2 }, { x: x21, y: y21 }, { x: x22, y: y22 }] = derivative2[1](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,
]
}
return newtonIterate2([t1, t2], f1, f2, delta2)
}

export function getLinesTangentToArcAndEllipseArc(curve1: Arc, curve2: EllipseArc): Tuple2<Position>[] {
const [p1, d1, d2] = getCircleDerivatives(curve1)
const [p2, e1, e2] = getEllipseDerivatives(curve2)
Expand Down

0 comments on commit 3cb7479

Please sign in to comment.