fluidcad 0.0.32 → 0.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/bin/commands/init.js +55 -0
- package/bin/commands/mcp.js +33 -0
- package/bin/commands/serve.js +77 -0
- package/bin/fluidcad.js +15 -107
- package/lib/dist/common/scene-object.d.ts +4 -1
- package/lib/dist/common/scene-object.js +9 -2
- package/lib/dist/common/solid.d.ts +4 -1
- package/lib/dist/common/solid.js +13 -0
- package/lib/dist/core/2d/tarc.d.ts +20 -2
- package/lib/dist/core/2d/tarc.js +24 -0
- package/lib/dist/core/index.d.ts +2 -1
- package/lib/dist/core/index.js +1 -0
- package/lib/dist/core/interfaces.d.ts +107 -2
- package/lib/dist/core/load.d.ts +2 -2
- package/lib/dist/core/repeat.js +62 -46
- package/lib/dist/core/rib.d.ts +18 -0
- package/lib/dist/core/rib.js +37 -0
- package/lib/dist/features/2d/arc.d.ts +8 -2
- package/lib/dist/features/2d/arc.js +94 -17
- package/lib/dist/features/2d/back.js +3 -2
- package/lib/dist/features/2d/sketch.d.ts +4 -0
- package/lib/dist/features/2d/sketch.js +21 -0
- package/lib/dist/features/2d/tarc-constrained.d.ts +2 -0
- package/lib/dist/features/2d/tarc-constrained.js +8 -0
- package/lib/dist/features/2d/tarc-radius-to-object.d.ts +16 -0
- package/lib/dist/features/2d/tarc-radius-to-object.js +58 -0
- package/lib/dist/features/2d/tarc-to-object.d.ts +18 -0
- package/lib/dist/features/2d/tarc-to-object.js +66 -0
- package/lib/dist/features/2d/tarc-to-point-tangent.d.ts +2 -0
- package/lib/dist/features/2d/tarc-to-point-tangent.js +3 -0
- package/lib/dist/features/2d/tarc-to-point.d.ts +2 -0
- package/lib/dist/features/2d/tarc-to-point.js +3 -0
- package/lib/dist/features/2d/tarc-with-tangent.d.ts +2 -0
- package/lib/dist/features/2d/tarc-with-tangent.js +3 -0
- package/lib/dist/features/2d/tarc.d.ts +2 -0
- package/lib/dist/features/2d/tarc.js +3 -0
- package/lib/dist/features/extrude-base.d.ts +9 -0
- package/lib/dist/features/extrude-base.js +22 -0
- package/lib/dist/features/extrude-to-face.js +1 -5
- package/lib/dist/features/extrude-two-distances.js +1 -2
- package/lib/dist/features/extrude.js +1 -2
- package/lib/dist/features/load.d.ts +6 -0
- package/lib/dist/features/load.js +53 -1
- package/lib/dist/features/mirror-feature.d.ts +3 -2
- package/lib/dist/features/mirror-feature.js +1 -1
- package/lib/dist/features/repeat-circular.d.ts +3 -3
- package/lib/dist/features/repeat-circular.js +8 -1
- package/lib/dist/features/repeat-linear.d.ts +4 -2
- package/lib/dist/features/repeat-linear.js +10 -1
- package/lib/dist/features/repeat-matrix.d.ts +3 -1
- package/lib/dist/features/repeat-matrix.js +7 -2
- package/lib/dist/features/rib.d.ts +31 -0
- package/lib/dist/features/rib.js +321 -0
- package/lib/dist/features/select.d.ts +1 -0
- package/lib/dist/features/select.js +81 -10
- package/lib/dist/features/shell.d.ts +4 -1
- package/lib/dist/features/shell.js +14 -3
- package/lib/dist/filters/edge/belongs-to-face.d.ts +12 -9
- package/lib/dist/filters/edge/belongs-to-face.js +64 -15
- package/lib/dist/filters/filter-builder-base.d.ts +25 -0
- package/lib/dist/filters/filter-builder-base.js +47 -0
- package/lib/dist/filters/filter.js +39 -14
- package/lib/dist/filters/from-object.d.ts +4 -0
- package/lib/dist/filters/from-object.js +10 -0
- package/lib/dist/helpers/clone-transform.d.ts +2 -1
- package/lib/dist/helpers/scene-helpers.d.ts +1 -1
- package/lib/dist/helpers/scene-helpers.js +146 -12
- package/lib/dist/index.d.ts +7 -1
- package/lib/dist/index.js +3 -3
- package/lib/dist/io/file-import.d.ts +5 -1
- package/lib/dist/io/file-import.js +29 -18
- package/lib/dist/math/lazy-matrix.d.ts +31 -0
- package/lib/dist/math/lazy-matrix.js +66 -0
- package/lib/dist/oc/color-transfer.d.ts +19 -8
- package/lib/dist/oc/color-transfer.js +70 -12
- package/lib/dist/oc/constraints/constraint-solver-adaptor.d.ts +5 -0
- package/lib/dist/oc/constraints/constraint-solver-adaptor.js +16 -0
- package/lib/dist/oc/constraints/constraint-solver.d.ts +4 -0
- package/lib/dist/oc/constraints/curve/curve-constraint-solver.d.ts +4 -0
- package/lib/dist/oc/constraints/curve/curve-constraint-solver.js +3 -0
- package/lib/dist/oc/constraints/geometric/geometric-constraint-solver.d.ts +6 -1
- package/lib/dist/oc/constraints/geometric/geometric-constraint-solver.js +4 -0
- package/lib/dist/oc/constraints/geometric/tangent-arc-from-point-tangent.d.ts +8 -0
- package/lib/dist/oc/constraints/geometric/tangent-arc-from-point-tangent.js +111 -0
- package/lib/dist/oc/constraints/geometric/tangent-arc-radius-to-object.d.ts +8 -0
- package/lib/dist/oc/constraints/geometric/tangent-arc-radius-to-object.js +161 -0
- package/lib/dist/oc/extrude-ops.d.ts +2 -1
- package/lib/dist/oc/extrude-ops.js +51 -2
- package/lib/dist/oc/mesh.d.ts +9 -4
- package/lib/dist/oc/mesh.js +14 -13
- package/lib/dist/oc/rib-ops.d.ts +35 -0
- package/lib/dist/oc/rib-ops.js +619 -0
- package/lib/dist/oc/shell-ops.d.ts +2 -1
- package/lib/dist/oc/shell-ops.js +5 -2
- package/lib/dist/oc/topology-index.d.ts +6 -0
- package/lib/dist/oc/topology-index.js +36 -0
- package/lib/dist/rendering/mesh-builder.d.ts +3 -0
- package/lib/dist/rendering/mesh-builder.js +8 -4
- package/lib/dist/rendering/render-edge.d.ts +2 -1
- package/lib/dist/rendering/render-edge.js +2 -2
- package/lib/dist/rendering/render-face.d.ts +2 -1
- package/lib/dist/rendering/render-face.js +2 -2
- package/lib/dist/rendering/render-solid.d.ts +2 -1
- package/lib/dist/rendering/render-solid.js +3 -5
- package/lib/dist/rendering/render-wire.d.ts +2 -1
- package/lib/dist/rendering/render-wire.js +2 -2
- package/lib/dist/rendering/render.d.ts +4 -0
- package/lib/dist/rendering/render.js +50 -2
- package/lib/dist/rendering/scene-compare.js +3 -0
- package/lib/dist/rendering/scene.d.ts +1 -0
- package/lib/dist/rendering/scene.js +4 -0
- package/lib/dist/scene-manager.d.ts +4 -2
- package/lib/dist/scene-manager.js +12 -4
- package/lib/dist/tests/features/2d/arc.test.js +64 -0
- package/lib/dist/tests/features/2d/back.test.js +17 -1
- package/lib/dist/tests/features/2d/tarc.test.js +157 -0
- package/lib/dist/tests/features/color-lineage.test.js +18 -0
- package/lib/dist/tests/features/filter-positional.test.d.ts +1 -0
- package/lib/dist/tests/features/filter-positional.test.js +129 -0
- package/lib/dist/tests/features/repeat-user-repro.test.d.ts +1 -0
- package/lib/dist/tests/features/repeat-user-repro.test.js +60 -0
- package/lib/dist/tests/features/rib.test.d.ts +1 -0
- package/lib/dist/tests/features/rib.test.js +598 -0
- package/lib/dist/tests/features/shell.test.js +36 -0
- package/lib/dist/tests/global-setup.js +2 -1
- package/lib/dist/tests/helpers/extract-blocks.d.ts +9 -0
- package/lib/dist/tests/helpers/extract-blocks.js +56 -0
- package/lib/dist/tests/llm-docs-examples.test.d.ts +1 -0
- package/lib/dist/tests/llm-docs-examples.test.js +62 -0
- package/lib/dist/tests/scene-compare.test.d.ts +1 -0
- package/lib/dist/tests/scene-compare.test.js +77 -0
- package/lib/dist/tests/setup.js +2 -1
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/llm-docs/.coverage-allowlist.txt +9 -0
- package/llm-docs/api/arc.md +48 -0
- package/llm-docs/api/axis.md +42 -0
- package/llm-docs/api/bezier.md +42 -0
- package/llm-docs/api/booleans.md +44 -0
- package/llm-docs/api/chamfer.md +40 -0
- package/llm-docs/api/circle.md +36 -0
- package/llm-docs/api/color.md +34 -0
- package/llm-docs/api/connect.md +41 -0
- package/llm-docs/api/constraint-qualifiers.md +48 -0
- package/llm-docs/api/copy.md +63 -0
- package/llm-docs/api/cursor-lines.md +50 -0
- package/llm-docs/api/cursor-move.md +61 -0
- package/llm-docs/api/cut.md +55 -0
- package/llm-docs/api/draft.md +36 -0
- package/llm-docs/api/edge-filter.md +57 -0
- package/llm-docs/api/ellipse.md +34 -0
- package/llm-docs/api/extrude.md +74 -0
- package/llm-docs/api/face-filter.md +61 -0
- package/llm-docs/api/fillet.md +51 -0
- package/llm-docs/api/index.json +139 -0
- package/llm-docs/api/line.md +42 -0
- package/llm-docs/api/load.md +37 -0
- package/llm-docs/api/local.md +38 -0
- package/llm-docs/api/loft.md +37 -0
- package/llm-docs/api/mirror.md +44 -0
- package/llm-docs/api/offset.md +36 -0
- package/llm-docs/api/part.md +40 -0
- package/llm-docs/api/plane.md +44 -0
- package/llm-docs/api/polygon.md +37 -0
- package/llm-docs/api/primitive-solids.md +39 -0
- package/llm-docs/api/project-intersect.md +48 -0
- package/llm-docs/api/rect.md +48 -0
- package/llm-docs/api/remove.md +32 -0
- package/llm-docs/api/repeat.md +79 -0
- package/llm-docs/api/revolve.md +38 -0
- package/llm-docs/api/rib.md +40 -0
- package/llm-docs/api/rotate.md +37 -0
- package/llm-docs/api/select.md +41 -0
- package/llm-docs/api/shell.md +41 -0
- package/llm-docs/api/sketch.md +76 -0
- package/llm-docs/api/slot.md +36 -0
- package/llm-docs/api/split-trim.md +42 -0
- package/llm-docs/api/sweep.md +43 -0
- package/llm-docs/api/tarc.md +45 -0
- package/llm-docs/api/tcircle.md +38 -0
- package/llm-docs/api/tline.md +42 -0
- package/llm-docs/api/translate.md +40 -0
- package/llm-docs/api/types/aline.md +35 -0
- package/llm-docs/api/types/arc-angles.md +29 -0
- package/llm-docs/api/types/arc-points.md +48 -0
- package/llm-docs/api/types/axis-like.md +38 -0
- package/llm-docs/api/types/axis.md +21 -0
- package/llm-docs/api/types/boolean-operation.md +50 -0
- package/llm-docs/api/types/circular-repeat-options.md +31 -0
- package/llm-docs/api/types/common.md +32 -0
- package/llm-docs/api/types/cut.md +125 -0
- package/llm-docs/api/types/draft.md +21 -0
- package/llm-docs/api/types/extrudable-geometry.md +23 -0
- package/llm-docs/api/types/extrude.md +194 -0
- package/llm-docs/api/types/geometry.md +51 -0
- package/llm-docs/api/types/hline.md +35 -0
- package/llm-docs/api/types/linear-repeat-options.md +31 -0
- package/llm-docs/api/types/loft.md +154 -0
- package/llm-docs/api/types/mirror.md +35 -0
- package/llm-docs/api/types/offset.md +31 -0
- package/llm-docs/api/types/plane-like.md +35 -0
- package/llm-docs/api/types/plane-transform-options.md +29 -0
- package/llm-docs/api/types/plane.md +21 -0
- package/llm-docs/api/types/point-like.md +22 -0
- package/llm-docs/api/types/point2dlike.md +26 -0
- package/llm-docs/api/types/polygon.md +46 -0
- package/llm-docs/api/types/rect.md +128 -0
- package/llm-docs/api/types/revolve.md +102 -0
- package/llm-docs/api/types/rib.md +133 -0
- package/llm-docs/api/types/scene-object.md +33 -0
- package/llm-docs/api/types/select.md +21 -0
- package/llm-docs/api/types/shell.md +54 -0
- package/llm-docs/api/types/slot.md +43 -0
- package/llm-docs/api/types/sweep.md +189 -0
- package/llm-docs/api/types/tangent-arc-two-objects.md +46 -0
- package/llm-docs/api/types/transformable.md +93 -0
- package/llm-docs/api/types/trim.md +27 -0
- package/llm-docs/api/types/two-objects-tangent-line.md +46 -0
- package/llm-docs/api/types/vertex.md +17 -0
- package/llm-docs/api/types/vline.md +35 -0
- package/llm-docs/concepts/coordinate-system.md +45 -0
- package/llm-docs/concepts/history-and-rollback.md +40 -0
- package/llm-docs/concepts/last-selection.md +49 -0
- package/llm-docs/concepts/scene-graph.md +37 -0
- package/llm-docs/index.json +1750 -0
- package/mcp/dist/client.d.ts +64 -0
- package/mcp/dist/client.js +248 -0
- package/mcp/dist/discovery.d.ts +11 -0
- package/mcp/dist/discovery.js +78 -0
- package/mcp/dist/docs-index.d.ts +81 -0
- package/mcp/dist/docs-index.js +261 -0
- package/mcp/dist/resources.d.ts +4 -0
- package/mcp/dist/resources.js +115 -0
- package/mcp/dist/server.d.ts +12 -0
- package/mcp/dist/server.js +489 -0
- package/mcp/dist/tools/coordination.d.ts +9 -0
- package/mcp/dist/tools/coordination.js +46 -0
- package/mcp/dist/tools/docs.d.ts +66 -0
- package/mcp/dist/tools/docs.js +122 -0
- package/mcp/dist/tools/engine.d.ts +56 -0
- package/mcp/dist/tools/engine.js +145 -0
- package/mcp/dist/tools/inspection.d.ts +75 -0
- package/mcp/dist/tools/inspection.js +121 -0
- package/mcp/dist/tools/screenshot.d.ts +63 -0
- package/mcp/dist/tools/screenshot.js +263 -0
- package/mcp/dist/tools/source.d.ts +84 -0
- package/mcp/dist/tools/source.js +434 -0
- package/mcp/dist/tools/workspaces.d.ts +13 -0
- package/mcp/dist/tools/workspaces.js +33 -0
- package/mcp/dist/types.d.ts +18 -0
- package/mcp/dist/types.js +11 -0
- package/package.json +19 -5
- package/server/dist/code-editor.d.ts +36 -0
- package/server/dist/code-editor.js +8 -0
- package/server/dist/fluidcad-server.d.ts +50 -0
- package/server/dist/fluidcad-server.js +153 -1
- package/server/dist/global-registry.d.ts +30 -0
- package/server/dist/global-registry.js +126 -0
- package/server/dist/index.js +171 -26
- package/server/dist/instance-file.d.ts +31 -0
- package/server/dist/instance-file.js +73 -0
- package/server/dist/lint-fluid-js.d.ts +15 -0
- package/server/dist/lint-fluid-js.js +271 -0
- package/server/dist/routes/editor.d.ts +24 -0
- package/server/dist/routes/editor.js +44 -0
- package/server/dist/routes/export.d.ts +1 -1
- package/server/dist/routes/export.js +45 -8
- package/server/dist/routes/health.d.ts +7 -0
- package/server/dist/routes/health.js +14 -0
- package/server/dist/routes/lint.d.ts +10 -0
- package/server/dist/routes/lint.js +28 -0
- package/server/dist/routes/render.d.ts +33 -0
- package/server/dist/routes/render.js +34 -0
- package/server/dist/routes/scene.d.ts +5 -0
- package/server/dist/routes/scene.js +48 -0
- package/server/dist/routes/screenshot.js +68 -1
- package/server/dist/ws-protocol.d.ts +56 -2
- package/ui/dist/assets/{index-DMw0OYCF.js → index-BdqrMDRu.js} +30 -30
- package/ui/dist/index.html +1 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Matrix4 } from "./matrix4.js";
|
|
2
|
+
import { Axis } from "./axis.js";
|
|
3
|
+
import { Plane } from "./plane.js";
|
|
4
|
+
/** Anything that can be resolved to an `Axis` once built — e.g. `AxisObjectBase`. */
|
|
5
|
+
export interface AxisLazySource {
|
|
6
|
+
getAxis(): Axis;
|
|
7
|
+
}
|
|
8
|
+
/** Anything that can be resolved to a `Plane` once built — e.g. `PlaneObjectBase`. */
|
|
9
|
+
export interface PlaneLazySource {
|
|
10
|
+
getPlane(): Plane;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Memoized thunk over a Matrix4. Lets parse-time code thread a transform
|
|
14
|
+
* value that depends on scene state (e.g. an AxisObjectBase or
|
|
15
|
+
* PlaneObjectBase) before that state exists. Resolution is deferred to the
|
|
16
|
+
* first call to `resolve()`, by which point the renderer has built the source
|
|
17
|
+
* object. Concrete `Axis` / `Plane` sources resolve eagerly with no extra
|
|
18
|
+
* indirection.
|
|
19
|
+
*/
|
|
20
|
+
export declare class LazyMatrix {
|
|
21
|
+
private _resolver;
|
|
22
|
+
private _cached;
|
|
23
|
+
private constructor();
|
|
24
|
+
resolve(): Matrix4;
|
|
25
|
+
equals(other: LazyMatrix, tolerance?: number): boolean;
|
|
26
|
+
static of(matrix: Matrix4): LazyMatrix;
|
|
27
|
+
static from(resolver: () => Matrix4): LazyMatrix;
|
|
28
|
+
static mirror(plane: Plane | PlaneLazySource): LazyMatrix;
|
|
29
|
+
static rotation(axis: Axis | AxisLazySource, angle: number): LazyMatrix;
|
|
30
|
+
static translation(axis: Axis | AxisLazySource, distance: number): LazyMatrix;
|
|
31
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Matrix4 } from "./matrix4.js";
|
|
2
|
+
function isAxisLazySource(a) {
|
|
3
|
+
return typeof a.getAxis === "function";
|
|
4
|
+
}
|
|
5
|
+
function isPlaneLazySource(p) {
|
|
6
|
+
return typeof p.getPlane === "function";
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Memoized thunk over a Matrix4. Lets parse-time code thread a transform
|
|
10
|
+
* value that depends on scene state (e.g. an AxisObjectBase or
|
|
11
|
+
* PlaneObjectBase) before that state exists. Resolution is deferred to the
|
|
12
|
+
* first call to `resolve()`, by which point the renderer has built the source
|
|
13
|
+
* object. Concrete `Axis` / `Plane` sources resolve eagerly with no extra
|
|
14
|
+
* indirection.
|
|
15
|
+
*/
|
|
16
|
+
export class LazyMatrix {
|
|
17
|
+
_resolver;
|
|
18
|
+
_cached;
|
|
19
|
+
constructor(resolver, cached = null) {
|
|
20
|
+
this._resolver = resolver;
|
|
21
|
+
this._cached = cached;
|
|
22
|
+
}
|
|
23
|
+
resolve() {
|
|
24
|
+
if (this._cached === null) {
|
|
25
|
+
this._cached = this._resolver();
|
|
26
|
+
}
|
|
27
|
+
return this._cached;
|
|
28
|
+
}
|
|
29
|
+
equals(other, tolerance = 0) {
|
|
30
|
+
return this.resolve().equals(other.resolve(), tolerance);
|
|
31
|
+
}
|
|
32
|
+
static of(matrix) {
|
|
33
|
+
return new LazyMatrix(() => matrix, matrix);
|
|
34
|
+
}
|
|
35
|
+
static from(resolver) {
|
|
36
|
+
return new LazyMatrix(resolver);
|
|
37
|
+
}
|
|
38
|
+
static mirror(plane) {
|
|
39
|
+
if (isPlaneLazySource(plane)) {
|
|
40
|
+
return new LazyMatrix(() => {
|
|
41
|
+
const p = plane.getPlane();
|
|
42
|
+
return Matrix4.mirrorPlane(p.normal, p.origin);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return LazyMatrix.of(Matrix4.mirrorPlane(plane.normal, plane.origin));
|
|
46
|
+
}
|
|
47
|
+
static rotation(axis, angle) {
|
|
48
|
+
if (isAxisLazySource(axis)) {
|
|
49
|
+
return new LazyMatrix(() => {
|
|
50
|
+
const a = axis.getAxis();
|
|
51
|
+
return Matrix4.fromRotationAroundAxis(a.origin, a.direction, angle);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return LazyMatrix.of(Matrix4.fromRotationAroundAxis(axis.origin, axis.direction, angle));
|
|
55
|
+
}
|
|
56
|
+
static translation(axis, distance) {
|
|
57
|
+
if (isAxisLazySource(axis)) {
|
|
58
|
+
return new LazyMatrix(() => {
|
|
59
|
+
const dir = axis.getAxis().direction;
|
|
60
|
+
return Matrix4.fromTranslation(dir.x * distance, dir.y * distance, dir.z * distance);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
const dir = axis.direction;
|
|
64
|
+
return LazyMatrix.of(Matrix4.fromTranslation(dir.x * distance, dir.y * distance, dir.z * distance));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -13,16 +13,27 @@ export declare class ColorTransfer {
|
|
|
13
13
|
static applyThroughMaker(sources: Shape[], results: Shape[], maker: BRepBuilderAPI_MakeShape): void;
|
|
14
14
|
/**
|
|
15
15
|
* Color bleed pass: spreads colors to result faces that came from new
|
|
16
|
-
* geometry (tool inputs, generated faces, or just brand-new)
|
|
17
|
-
* face-edge adjacency in each result solid.
|
|
16
|
+
* geometry (tool inputs, generated faces, or just brand-new).
|
|
18
17
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
18
|
+
* Two passes, in order:
|
|
19
|
+
*
|
|
20
|
+
* 1. Lineage-based propagation. For each scene-source EDGE / VERTEX,
|
|
21
|
+
* ask `maker.Generated(input)` for the new face(s) it produced and
|
|
22
|
+
* color each generated face from the source face(s) that *contained*
|
|
23
|
+
* that input. This is what makes a fillet on a cylinder's top edge
|
|
24
|
+
* inherit the top face's color (the edge belongs to the top face) but
|
|
25
|
+
* a fillet on a box's vertical edge stay uncolored even when the top
|
|
26
|
+
* face is colored (the vertical edge belongs to two side faces only).
|
|
27
|
+
* Faces touched by this pass — colored OR not — are marked as
|
|
28
|
+
* lineage-resolved and excluded from pass 2.
|
|
29
|
+
*
|
|
30
|
+
* 2. Adjacency-based fallback for result faces that have no Generated
|
|
31
|
+
* lineage from any scene source (e.g. a fuse's tool-side faces, or
|
|
32
|
+
* brand-new section faces). These spread color via face-edge
|
|
33
|
+
* adjacency, iterating until stable. Faces that came from
|
|
34
|
+
* `sceneSources` (whether modified or unchanged) stay protected from
|
|
35
|
+
* both passes — those are explicit user choices.
|
|
24
36
|
*
|
|
25
|
-
* Iterates until stable so newly-bled faces can spread color further.
|
|
26
37
|
* Call AFTER `applyThroughMaker` so the colored seeds are in place.
|
|
27
38
|
*/
|
|
28
39
|
static applyBleeding(sceneSources: Shape[], results: Shape[], maker: BRepBuilderAPI_MakeShape): void;
|
|
@@ -45,28 +45,37 @@ export class ColorTransfer {
|
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* Color bleed pass: spreads colors to result faces that came from new
|
|
48
|
-
* geometry (tool inputs, generated faces, or just brand-new)
|
|
49
|
-
* face-edge adjacency in each result solid.
|
|
48
|
+
* geometry (tool inputs, generated faces, or just brand-new).
|
|
50
49
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
50
|
+
* Two passes, in order:
|
|
51
|
+
*
|
|
52
|
+
* 1. Lineage-based propagation. For each scene-source EDGE / VERTEX,
|
|
53
|
+
* ask `maker.Generated(input)` for the new face(s) it produced and
|
|
54
|
+
* color each generated face from the source face(s) that *contained*
|
|
55
|
+
* that input. This is what makes a fillet on a cylinder's top edge
|
|
56
|
+
* inherit the top face's color (the edge belongs to the top face) but
|
|
57
|
+
* a fillet on a box's vertical edge stay uncolored even when the top
|
|
58
|
+
* face is colored (the vertical edge belongs to two side faces only).
|
|
59
|
+
* Faces touched by this pass — colored OR not — are marked as
|
|
60
|
+
* lineage-resolved and excluded from pass 2.
|
|
61
|
+
*
|
|
62
|
+
* 2. Adjacency-based fallback for result faces that have no Generated
|
|
63
|
+
* lineage from any scene source (e.g. a fuse's tool-side faces, or
|
|
64
|
+
* brand-new section faces). These spread color via face-edge
|
|
65
|
+
* adjacency, iterating until stable. Faces that came from
|
|
66
|
+
* `sceneSources` (whether modified or unchanged) stay protected from
|
|
67
|
+
* both passes — those are explicit user choices.
|
|
56
68
|
*
|
|
57
|
-
* Iterates until stable so newly-bled faces can spread color further.
|
|
58
69
|
* Call AFTER `applyThroughMaker` so the colored seeds are in place.
|
|
59
70
|
*/
|
|
60
71
|
static applyBleeding(sceneSources, results, maker) {
|
|
61
|
-
// No colors anywhere on the source side means the bleed loop will iterate
|
|
62
|
-
// every result face only to find nothing to spread. Short-circuit before
|
|
63
|
-
// building maps or running the O(N²·E²) adjacency scan.
|
|
64
72
|
if (!sceneSources.some(s => s.hasColors())) {
|
|
65
73
|
return;
|
|
66
74
|
}
|
|
67
75
|
const oc = getOC();
|
|
68
76
|
const FACE = oc.TopAbs_ShapeEnum.TopAbs_FACE;
|
|
69
77
|
const EDGE = oc.TopAbs_ShapeEnum.TopAbs_EDGE;
|
|
78
|
+
const VERTEX = oc.TopAbs_ShapeEnum.TopAbs_VERTEX;
|
|
70
79
|
const protectedFaces = new oc.TopTools_MapOfShape();
|
|
71
80
|
for (const scene of sceneSources) {
|
|
72
81
|
for (const inputFace of Explorer.findShapes(scene.getShape(), FACE)) {
|
|
@@ -84,7 +93,52 @@ export class ColorTransfer {
|
|
|
84
93
|
}
|
|
85
94
|
for (const result of results) {
|
|
86
95
|
const allFaces = Explorer.findShapes(result.getShape(), FACE);
|
|
87
|
-
//
|
|
96
|
+
// ── Pass 1: lineage via Generated(edge|vertex) ──
|
|
97
|
+
const lineageResolved = new oc.TopTools_MapOfShape();
|
|
98
|
+
for (const scene of sceneSources) {
|
|
99
|
+
const sceneFaces = Explorer.findShapes(scene.getShape(), FACE);
|
|
100
|
+
for (const subType of [EDGE, VERTEX]) {
|
|
101
|
+
// Cache scene-face → contained subshapes so we can find which faces
|
|
102
|
+
// own a given edge/vertex without re-walking each time.
|
|
103
|
+
const sceneFaceSubs = sceneFaces.map(f => Explorer.findShapes(f, subType));
|
|
104
|
+
for (const sub of Explorer.findShapes(scene.getShape(), subType)) {
|
|
105
|
+
const generatedFaces = ShapeOps.shapeListToArray(maker.Generated(sub))
|
|
106
|
+
.filter(s => s.ShapeType() === FACE);
|
|
107
|
+
if (generatedFaces.length === 0) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
// Color the generated face from the first colored owner-face of
|
|
111
|
+
// this edge/vertex. Owners with no color don't contribute, so a
|
|
112
|
+
// fillet on a vertical box edge (owned only by uncolored sides)
|
|
113
|
+
// stays uncolored even though the new fillet face touches a
|
|
114
|
+
// colored top via adjacency.
|
|
115
|
+
let pickedColor;
|
|
116
|
+
for (let i = 0; i < sceneFaces.length; i++) {
|
|
117
|
+
if (!sceneFaceSubs[i].some(s => s.IsSame(sub))) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const c = scene.getColor(sceneFaces[i]);
|
|
121
|
+
if (c) {
|
|
122
|
+
pickedColor = c;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const g of generatedFaces) {
|
|
127
|
+
if (!allFaces.some(rf => rf.IsSame(g))) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (protectedFaces.Contains(g)) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
lineageResolved.Add(g);
|
|
134
|
+
if (pickedColor && !result.getColor(g)) {
|
|
135
|
+
result.setColor(g, pickedColor);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// ── Pass 2: adjacency fallback for faces with no Generated lineage ──
|
|
88
142
|
const faceEdges = allFaces.map(f => Explorer.findShapes(f, EDGE));
|
|
89
143
|
let changed = true;
|
|
90
144
|
while (changed) {
|
|
@@ -94,6 +148,9 @@ export class ColorTransfer {
|
|
|
94
148
|
if (protectedFaces.Contains(face)) {
|
|
95
149
|
continue;
|
|
96
150
|
}
|
|
151
|
+
if (lineageResolved.Contains(face)) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
97
154
|
if (result.getColor(face)) {
|
|
98
155
|
continue;
|
|
99
156
|
}
|
|
@@ -116,6 +173,7 @@ export class ColorTransfer {
|
|
|
116
173
|
}
|
|
117
174
|
}
|
|
118
175
|
}
|
|
176
|
+
lineageResolved.delete();
|
|
119
177
|
}
|
|
120
178
|
protectedFaces.delete();
|
|
121
179
|
}
|
|
@@ -17,6 +17,11 @@ export declare class ConstraintSolverAdaptor extends ConstraintSolver {
|
|
|
17
17
|
edges: Edge[];
|
|
18
18
|
endTangent: Point2D | null;
|
|
19
19
|
};
|
|
20
|
+
getTangentArcFromPointTangent(plane: Plane, startPoint: Point2D, startTangent: Point2D, target: QualifiedShape, flip: boolean): {
|
|
21
|
+
edges: Edge[];
|
|
22
|
+
endTangent: Point2D | null;
|
|
23
|
+
};
|
|
20
24
|
isCurve(shape: Shape): boolean;
|
|
25
|
+
private isLine;
|
|
21
26
|
private getShapeGeometry;
|
|
22
27
|
}
|
|
@@ -36,9 +36,25 @@ export class ConstraintSolverAdaptor extends ConstraintSolver {
|
|
|
36
36
|
console.log('Using geometric solver for tangent circles');
|
|
37
37
|
return this.geometricSolver.getTangentArcs(plane, shape1, shape2, radius, this.mustTouch);
|
|
38
38
|
}
|
|
39
|
+
getTangentArcFromPointTangent(plane, startPoint, startTangent, target, flip) {
|
|
40
|
+
if (this.isLine(target.shape)) {
|
|
41
|
+
return this.geometricSolver.getTangentArcFromPointTangent(plane, startPoint, startTangent, target, flip);
|
|
42
|
+
}
|
|
43
|
+
return this.curveSolver.getTangentArcFromPointTangent(plane, startPoint, startTangent, target, flip);
|
|
44
|
+
}
|
|
39
45
|
isCurve(shape) {
|
|
40
46
|
return !(shape instanceof Vertex) && this.getShapeGeometry(shape) === 'curve';
|
|
41
47
|
}
|
|
48
|
+
isLine(shape) {
|
|
49
|
+
if (shape instanceof Vertex) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const oc = getOC();
|
|
53
|
+
const adaptor = new oc.BRepAdaptor_Curve(shape.getShape());
|
|
54
|
+
const type = adaptor.GetType();
|
|
55
|
+
adaptor.delete();
|
|
56
|
+
return type === oc.GeomAbs_CurveType.GeomAbs_Line;
|
|
57
|
+
}
|
|
42
58
|
getShapeGeometry(shape) {
|
|
43
59
|
const oc = getOC();
|
|
44
60
|
const adaptor = new oc.BRepAdaptor_Curve(shape.getShape());
|
|
@@ -15,4 +15,8 @@ export declare abstract class ConstraintSolver implements TangentLineSolver, Tan
|
|
|
15
15
|
edges: Edge[];
|
|
16
16
|
endTangent: Point2D;
|
|
17
17
|
};
|
|
18
|
+
abstract getTangentArcFromPointTangent(plane: Plane, startPoint: Point2D, startTangent: Point2D, target: QualifiedShape, flip: boolean): {
|
|
19
|
+
edges: Edge[];
|
|
20
|
+
endTangent: Point2D | null;
|
|
21
|
+
};
|
|
18
22
|
}
|
|
@@ -10,4 +10,8 @@ export declare class CurveConstraintSolver extends ConstraintSolver {
|
|
|
10
10
|
edges: Edge[];
|
|
11
11
|
endTangent: Point2D | null;
|
|
12
12
|
};
|
|
13
|
+
getTangentArcFromPointTangent(_plane: Plane, _startPoint: Point2D, _startTangent: Point2D, _target: QualifiedShape, _flip: boolean): {
|
|
14
|
+
edges: Edge[];
|
|
15
|
+
endTangent: Point2D | null;
|
|
16
|
+
};
|
|
13
17
|
}
|
|
@@ -15,4 +15,7 @@ export class CurveConstraintSolver extends ConstraintSolver {
|
|
|
15
15
|
const solver = new CurveTangentCircleSolver();
|
|
16
16
|
return solver.getTangentArcs(plane, shape1, shape2, radius, mustTouch);
|
|
17
17
|
}
|
|
18
|
+
getTangentArcFromPointTangent(_plane, _startPoint, _startTangent, _target, _flip) {
|
|
19
|
+
throw new Error('tArc(target): only line targets are supported');
|
|
20
|
+
}
|
|
18
21
|
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { Edge } from "../../../common/edge.js";
|
|
2
2
|
import { QualifiedShape } from "../../../features/2d/constraints/qualified-geometry.js";
|
|
3
3
|
import { Plane } from "../../../math/plane.js";
|
|
4
|
+
import { Point2D } from "../../../math/point.js";
|
|
4
5
|
import { ConstraintSolver } from "../constraint-solver.js";
|
|
5
6
|
export declare class GeometricConstraintSolver extends ConstraintSolver {
|
|
6
7
|
getTangentLines(plane: Plane, shape1: QualifiedShape, shape2: QualifiedShape, mustTouch: boolean): Edge[];
|
|
7
8
|
getTangentCircles(plane: Plane, shape1: QualifiedShape, shape2: QualifiedShape, radius: number, mustTouch: boolean): Edge[];
|
|
8
9
|
getTangentArcs(plane: Plane, shape1: QualifiedShape, shape2: QualifiedShape, radius: number, mustTouch: boolean): {
|
|
9
10
|
edges: Edge[];
|
|
10
|
-
endTangent:
|
|
11
|
+
endTangent: Point2D | null;
|
|
12
|
+
};
|
|
13
|
+
getTangentArcFromPointTangent(plane: Plane, startPoint: Point2D, startTangent: Point2D, target: QualifiedShape, flip: boolean): {
|
|
14
|
+
edges: Edge[];
|
|
15
|
+
endTangent: Point2D | null;
|
|
11
16
|
};
|
|
12
17
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ConstraintSolver } from "../constraint-solver.js";
|
|
2
2
|
import { GeometricTangentCircleSolver } from "./tangent-circle-solver.js";
|
|
3
3
|
import { GeometricTangentLineSolver } from "./tangent-line-solver.js";
|
|
4
|
+
import { solveTangentArcFromPointTangent } from "./tangent-arc-from-point-tangent.js";
|
|
4
5
|
export class GeometricConstraintSolver extends ConstraintSolver {
|
|
5
6
|
getTangentLines(plane, shape1, shape2, mustTouch) {
|
|
6
7
|
const tangentLineSolver = new GeometricTangentLineSolver();
|
|
@@ -14,4 +15,7 @@ export class GeometricConstraintSolver extends ConstraintSolver {
|
|
|
14
15
|
const solver = new GeometricTangentCircleSolver();
|
|
15
16
|
return solver.getTangentArcs(plane, shape1, shape2, radius, mustTouch);
|
|
16
17
|
}
|
|
18
|
+
getTangentArcFromPointTangent(plane, startPoint, startTangent, target, flip) {
|
|
19
|
+
return solveTangentArcFromPointTangent(plane, startPoint, startTangent, target, flip);
|
|
20
|
+
}
|
|
17
21
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Edge } from "../../../common/edge.js";
|
|
2
|
+
import { QualifiedShape } from "../../../features/2d/constraints/qualified-geometry.js";
|
|
3
|
+
import { Plane } from "../../../math/plane.js";
|
|
4
|
+
import { Point2D } from "../../../math/point.js";
|
|
5
|
+
export declare function solveTangentArcFromPointTangent(plane: Plane, startPoint: Point2D, startTangent: Point2D, target: QualifiedShape, flip: boolean): {
|
|
6
|
+
edges: Edge[];
|
|
7
|
+
endTangent: Point2D | null;
|
|
8
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Edge } from "../../../common/edge.js";
|
|
2
|
+
import { Point2D } from "../../../math/point.js";
|
|
3
|
+
import { Geometry } from "../../geometry.js";
|
|
4
|
+
import { getOC } from "../../init.js";
|
|
5
|
+
const EPS = 1e-10;
|
|
6
|
+
export function solveTangentArcFromPointTangent(plane, startPoint, startTangent, target, flip) {
|
|
7
|
+
const lineTarget = extractLineTarget(plane, target);
|
|
8
|
+
const tlen = startTangent.length();
|
|
9
|
+
if (tlen < EPS) {
|
|
10
|
+
throw new Error('tArc(target): start tangent has zero magnitude');
|
|
11
|
+
}
|
|
12
|
+
const Tx = startTangent.x / tlen;
|
|
13
|
+
const Ty = startTangent.y / tlen;
|
|
14
|
+
const Nx = -Ty;
|
|
15
|
+
const Ny = Tx;
|
|
16
|
+
const Qx = lineTarget.pointOnLine.x;
|
|
17
|
+
const Qy = lineTarget.pointOnLine.y;
|
|
18
|
+
const dx = lineTarget.direction.x;
|
|
19
|
+
const dy = lineTarget.direction.y;
|
|
20
|
+
const nx = -dy;
|
|
21
|
+
const ny = dx;
|
|
22
|
+
const dPL = (startPoint.x - Qx) * nx + (startPoint.y - Qy) * ny;
|
|
23
|
+
const k = Nx * nx + Ny * ny;
|
|
24
|
+
// Two candidate signed radii from the line tangency equation:
|
|
25
|
+
// d_PL + r·k = s·r ⇒ r = d_PL / (s − k) for s ∈ {+1, −1}
|
|
26
|
+
// Generically one yields r > 0 (center on the +N̂ side = "primary/left")
|
|
27
|
+
// and the other yields r < 0 ("flipped/right"). Pick by sign so the
|
|
28
|
+
// user-facing default is deterministic regardless of input geometry.
|
|
29
|
+
const candidates = [];
|
|
30
|
+
for (const s of [+1, -1]) {
|
|
31
|
+
const denom = s - k;
|
|
32
|
+
if (Math.abs(denom) < EPS) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const r = dPL / denom;
|
|
36
|
+
if (Math.abs(r) < EPS) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
candidates.push(buildCandidate(startPoint, Nx, Ny, r, lineTarget));
|
|
40
|
+
}
|
|
41
|
+
if (candidates.length === 0) {
|
|
42
|
+
return { edges: [], endTangent: null };
|
|
43
|
+
}
|
|
44
|
+
const primary = candidates.find(c => c.ccw) ?? null;
|
|
45
|
+
const flipped = candidates.find(c => !c.ccw) ?? null;
|
|
46
|
+
let chosen;
|
|
47
|
+
if (flip) {
|
|
48
|
+
chosen = flipped ?? primary;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
chosen = primary ?? flipped;
|
|
52
|
+
}
|
|
53
|
+
const edge = buildArcEdge(plane, chosen);
|
|
54
|
+
return { edges: [edge], endTangent: chosen.endTangent };
|
|
55
|
+
}
|
|
56
|
+
function extractLineTarget(plane, target) {
|
|
57
|
+
const oc = getOC();
|
|
58
|
+
const shape = target.shape;
|
|
59
|
+
if (!(shape instanceof Edge)) {
|
|
60
|
+
throw new Error('tArc(target): target must be a line edge');
|
|
61
|
+
}
|
|
62
|
+
const adaptor = new oc.BRepAdaptor_Curve(shape.getShape());
|
|
63
|
+
const type = adaptor.GetType();
|
|
64
|
+
if (type !== oc.GeomAbs_CurveType.GeomAbs_Line) {
|
|
65
|
+
adaptor.delete();
|
|
66
|
+
throw new Error('tArc(target): only line targets are supported');
|
|
67
|
+
}
|
|
68
|
+
adaptor.delete();
|
|
69
|
+
const start = plane.worldToLocal(shape.getFirstVertex().toPoint());
|
|
70
|
+
const end = plane.worldToLocal(shape.getLastVertex().toPoint());
|
|
71
|
+
const diff = end.subtract(start);
|
|
72
|
+
const len = diff.length();
|
|
73
|
+
if (len < EPS) {
|
|
74
|
+
throw new Error('tArc(target): target line has zero length');
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
pointOnLine: start,
|
|
78
|
+
direction: diff.multiplyScalar(1 / len)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function buildCandidate(P, Nx, Ny, r, target) {
|
|
82
|
+
const Ox = P.x + r * Nx;
|
|
83
|
+
const Oy = P.y + r * Ny;
|
|
84
|
+
const dx = target.direction.x;
|
|
85
|
+
const dy = target.direction.y;
|
|
86
|
+
const t = (Ox - target.pointOnLine.x) * dx + (Oy - target.pointOnLine.y) * dy;
|
|
87
|
+
const Qx = target.pointOnLine.x + t * dx;
|
|
88
|
+
const Qy = target.pointOnLine.y + t * dy;
|
|
89
|
+
const radius = Math.abs(r);
|
|
90
|
+
const ccw = r > 0;
|
|
91
|
+
const endAngle = Math.atan2(Qy - Oy, Qx - Ox);
|
|
92
|
+
const sign = ccw ? 1 : -1;
|
|
93
|
+
const endTangent = new Point2D(sign * (-Math.sin(endAngle)), sign * Math.cos(endAngle));
|
|
94
|
+
return {
|
|
95
|
+
center: new Point2D(Ox, Oy),
|
|
96
|
+
radius,
|
|
97
|
+
ccw,
|
|
98
|
+
startPoint: P,
|
|
99
|
+
endPoint: new Point2D(Qx, Qy),
|
|
100
|
+
endAngle,
|
|
101
|
+
endTangent
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function buildArcEdge(plane, c) {
|
|
105
|
+
const normal = c.ccw ? plane.normal : plane.normal.negate();
|
|
106
|
+
const center = plane.localToWorld(c.center);
|
|
107
|
+
const start = plane.localToWorld(c.startPoint);
|
|
108
|
+
const end = plane.localToWorld(c.endPoint);
|
|
109
|
+
const arc = Geometry.makeArc(center, c.radius, normal, start, end);
|
|
110
|
+
return Geometry.makeEdgeFromCurve(arc);
|
|
111
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Edge } from "../../../common/edge.js";
|
|
2
|
+
import { QualifiedShape } from "../../../features/2d/constraints/qualified-geometry.js";
|
|
3
|
+
import { Plane } from "../../../math/plane.js";
|
|
4
|
+
import { Point2D } from "../../../math/point.js";
|
|
5
|
+
export declare function solveTangentArcRadiusToObject(plane: Plane, startPoint: Point2D, startTangent: Point2D, signedRadius: number, target: QualifiedShape): {
|
|
6
|
+
edges: Edge[];
|
|
7
|
+
endTangent: Point2D | null;
|
|
8
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { Edge } from "../../../common/edge.js";
|
|
2
|
+
import { Point2D } from "../../../math/point.js";
|
|
3
|
+
import { Convert } from "../../convert.js";
|
|
4
|
+
import { Geometry } from "../../geometry.js";
|
|
5
|
+
import { getOC } from "../../init.js";
|
|
6
|
+
const EPS = 1e-10;
|
|
7
|
+
export function solveTangentArcRadiusToObject(plane, startPoint, startTangent, signedRadius, target) {
|
|
8
|
+
if (Math.abs(signedRadius) < EPS) {
|
|
9
|
+
throw new Error('tArc(radius, target): radius must be non-zero');
|
|
10
|
+
}
|
|
11
|
+
const tlen = startTangent.length();
|
|
12
|
+
if (tlen < EPS) {
|
|
13
|
+
throw new Error('tArc(radius, target): start tangent has zero magnitude');
|
|
14
|
+
}
|
|
15
|
+
const Tx = startTangent.x / tlen;
|
|
16
|
+
const Ty = startTangent.y / tlen;
|
|
17
|
+
// N̂: 90° CCW from T̂. Signed radius > 0 puts center on +N̂ (left) → CCW arc.
|
|
18
|
+
const Nx = -Ty;
|
|
19
|
+
const Ny = Tx;
|
|
20
|
+
const Ox = startPoint.x + signedRadius * Nx;
|
|
21
|
+
const Oy = startPoint.y + signedRadius * Ny;
|
|
22
|
+
const center = new Point2D(Ox, Oy);
|
|
23
|
+
const radius = Math.abs(signedRadius);
|
|
24
|
+
const ccw = signedRadius > 0;
|
|
25
|
+
const targetGeom = extractTargetGeometry(plane, target);
|
|
26
|
+
const intersections = findIntersections(center, radius, targetGeom);
|
|
27
|
+
if (intersections.length === 0) {
|
|
28
|
+
throw new Error('tArc(radius, target): arc circle does not intersect target');
|
|
29
|
+
}
|
|
30
|
+
const startAngle = Math.atan2(startPoint.y - Oy, startPoint.x - Ox);
|
|
31
|
+
let best = null;
|
|
32
|
+
for (const Q of intersections) {
|
|
33
|
+
const endAngle = Math.atan2(Q.y - Oy, Q.x - Ox);
|
|
34
|
+
let sweep = endAngle - startAngle;
|
|
35
|
+
if (ccw) {
|
|
36
|
+
if (sweep < 0) {
|
|
37
|
+
sweep += 2 * Math.PI;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
if (sweep > 0) {
|
|
42
|
+
sweep -= 2 * Math.PI;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Skip degenerate "no-sweep" intersections (start point itself on target).
|
|
46
|
+
if (Math.abs(sweep) < EPS) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (!best || Math.abs(sweep) < Math.abs(best.sweep)) {
|
|
50
|
+
best = { endPoint: Q, sweep, endAngle };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!best) {
|
|
54
|
+
throw new Error('tArc(radius, target): no intersection reachable in the arc direction');
|
|
55
|
+
}
|
|
56
|
+
const normal = ccw ? plane.normal : plane.normal.negate();
|
|
57
|
+
const worldCenter = plane.localToWorld(center);
|
|
58
|
+
const worldStart = plane.localToWorld(startPoint);
|
|
59
|
+
const worldEnd = plane.localToWorld(best.endPoint);
|
|
60
|
+
const arc = Geometry.makeArc(worldCenter, radius, normal, worldStart, worldEnd);
|
|
61
|
+
const edge = Geometry.makeEdgeFromCurve(arc);
|
|
62
|
+
const sign = ccw ? 1 : -1;
|
|
63
|
+
const endTangent = new Point2D(sign * (-Math.sin(best.endAngle)), sign * Math.cos(best.endAngle));
|
|
64
|
+
return { edges: [edge], endTangent };
|
|
65
|
+
}
|
|
66
|
+
function extractTargetGeometry(plane, target) {
|
|
67
|
+
const oc = getOC();
|
|
68
|
+
const shape = target.shape;
|
|
69
|
+
if (!(shape instanceof Edge)) {
|
|
70
|
+
throw new Error('tArc(radius, target): target must be a line, circle, or arc edge');
|
|
71
|
+
}
|
|
72
|
+
const adaptor = new oc.BRepAdaptor_Curve(shape.getShape());
|
|
73
|
+
const type = adaptor.GetType();
|
|
74
|
+
if (type === oc.GeomAbs_CurveType.GeomAbs_Line) {
|
|
75
|
+
adaptor.delete();
|
|
76
|
+
const start = plane.worldToLocal(shape.getFirstVertex().toPoint());
|
|
77
|
+
const end = plane.worldToLocal(shape.getLastVertex().toPoint());
|
|
78
|
+
const diff = end.subtract(start);
|
|
79
|
+
const len = diff.length();
|
|
80
|
+
if (len < EPS) {
|
|
81
|
+
throw new Error('tArc(radius, target): target line has zero length');
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
type: 'line',
|
|
85
|
+
pointOnLine: start,
|
|
86
|
+
direction: diff.multiplyScalar(1 / len)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (type === oc.GeomAbs_CurveType.GeomAbs_Circle) {
|
|
90
|
+
const circle = adaptor.Circle();
|
|
91
|
+
const r = circle.Radius();
|
|
92
|
+
const cWorld = Convert.toPoint(circle.Location());
|
|
93
|
+
adaptor.delete();
|
|
94
|
+
return {
|
|
95
|
+
type: 'circle',
|
|
96
|
+
center: plane.worldToLocal(cWorld),
|
|
97
|
+
radius: r
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
adaptor.delete();
|
|
101
|
+
throw new Error('tArc(radius, target): target must be a line, circle, or arc');
|
|
102
|
+
}
|
|
103
|
+
function findIntersections(center, radius, target) {
|
|
104
|
+
if (target.type === 'line') {
|
|
105
|
+
return lineCircleIntersections(center, radius, target.pointOnLine, target.direction);
|
|
106
|
+
}
|
|
107
|
+
return circleCircleIntersections(center, radius, target.center, target.radius);
|
|
108
|
+
}
|
|
109
|
+
function lineCircleIntersections(O, radius, Q_L, d_L) {
|
|
110
|
+
// Line unit normal (rotate direction +90° CCW).
|
|
111
|
+
const nx = -d_L.y;
|
|
112
|
+
const ny = d_L.x;
|
|
113
|
+
const d_OL = (O.x - Q_L.x) * nx + (O.y - Q_L.y) * ny;
|
|
114
|
+
const absD = Math.abs(d_OL);
|
|
115
|
+
if (absD > radius + EPS) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
// Foot of perpendicular from O onto the line.
|
|
119
|
+
const Fx = O.x - d_OL * nx;
|
|
120
|
+
const Fy = O.y - d_OL * ny;
|
|
121
|
+
const inside = Math.max(radius * radius - d_OL * d_OL, 0);
|
|
122
|
+
const h = Math.sqrt(inside);
|
|
123
|
+
if (h < EPS) {
|
|
124
|
+
return [new Point2D(Fx, Fy)];
|
|
125
|
+
}
|
|
126
|
+
return [
|
|
127
|
+
new Point2D(Fx + h * d_L.x, Fy + h * d_L.y),
|
|
128
|
+
new Point2D(Fx - h * d_L.x, Fy - h * d_L.y)
|
|
129
|
+
];
|
|
130
|
+
}
|
|
131
|
+
function circleCircleIntersections(O, r1, Cc, r2) {
|
|
132
|
+
const dx = Cc.x - O.x;
|
|
133
|
+
const dy = Cc.y - O.y;
|
|
134
|
+
const d2 = dx * dx + dy * dy;
|
|
135
|
+
const d = Math.sqrt(d2);
|
|
136
|
+
if (d < EPS) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
if (d > r1 + r2 + EPS) {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
if (d < Math.abs(r1 - r2) - EPS) {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
const a = (d2 + r1 * r1 - r2 * r2) / (2 * d);
|
|
146
|
+
const inside = Math.max(r1 * r1 - a * a, 0);
|
|
147
|
+
const h = Math.sqrt(inside);
|
|
148
|
+
// Midpoint M = O + (a/d) * (Cc - O).
|
|
149
|
+
const Mx = O.x + (a / d) * dx;
|
|
150
|
+
const My = O.y + (a / d) * dy;
|
|
151
|
+
if (h < EPS) {
|
|
152
|
+
return [new Point2D(Mx, My)];
|
|
153
|
+
}
|
|
154
|
+
// Perpendicular (unit) to (Cc - O).
|
|
155
|
+
const px = -dy / d;
|
|
156
|
+
const py = dx / d;
|
|
157
|
+
return [
|
|
158
|
+
new Point2D(Mx + h * px, My + h * py),
|
|
159
|
+
new Point2D(Mx - h * px, My - h * py)
|
|
160
|
+
];
|
|
161
|
+
}
|
|
@@ -30,10 +30,11 @@ export declare class ExtrudeOps {
|
|
|
30
30
|
static makePrismInfinite(shape: Shape, direction: Vector3d): Shape;
|
|
31
31
|
static makePrismSymmetric(shape: Shape, direction: Vector3d): Shape;
|
|
32
32
|
static makeRevol(shape: Shape, axis: Axis, angle: number): MakeRevolResult;
|
|
33
|
-
static applyDraftOnSideFaces(solid: Shape, firstFace: Shape, lastFace: Shape, plane: Plane, angle: number): {
|
|
33
|
+
static applyDraftOnSideFaces(solid: Shape, firstFace: Shape, lastFace: Shape, plane: Plane, angle: number, excludeFaces?: Shape[]): {
|
|
34
34
|
solid: Shape;
|
|
35
35
|
firstFace: Shape;
|
|
36
36
|
lastFace: Shape;
|
|
37
|
+
remapFace: (face: Shape) => Shape[];
|
|
37
38
|
};
|
|
38
39
|
static applyDraft(shape: TopoDS_Shape, direction: Vector3d, angle: number): TopoDS_Shape;
|
|
39
40
|
}
|