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
|
@@ -1,16 +1,53 @@
|
|
|
1
|
+
import { Face } from "../../common/shapes.js";
|
|
2
|
+
import { Explorer } from "../../oc/explorer.js";
|
|
3
|
+
import { TopologyIndex } from "../../oc/topology-index.js";
|
|
1
4
|
import { FilterBase } from "../filter-base.js";
|
|
2
|
-
|
|
5
|
+
class BelongsToFaceFilterBase extends FilterBase {
|
|
3
6
|
faceFilterBuilders;
|
|
7
|
+
scopeSolids = [];
|
|
4
8
|
scopeFaces = [];
|
|
9
|
+
faceByHash = new Map();
|
|
5
10
|
constructor(faceFilterBuilders) {
|
|
6
11
|
super();
|
|
7
12
|
this.faceFilterBuilders = faceFilterBuilders;
|
|
8
13
|
}
|
|
9
|
-
|
|
10
|
-
this.
|
|
14
|
+
setScopeIndex(solids, extraFaces, faceByHash) {
|
|
15
|
+
this.scopeSolids = solids;
|
|
16
|
+
this.scopeFaces = extraFaces;
|
|
17
|
+
this.faceByHash = faceByHash;
|
|
11
18
|
}
|
|
19
|
+
findContainingFaces(edge) {
|
|
20
|
+
const edgeShape = edge.getShape();
|
|
21
|
+
const seen = new Set();
|
|
22
|
+
const result = [];
|
|
23
|
+
for (const solid of this.scopeSolids) {
|
|
24
|
+
const index = solid.getEdgeToFacesIndex();
|
|
25
|
+
const rawFaces = TopologyIndex.seekShapes(index, edgeShape);
|
|
26
|
+
for (const raw of rawFaces) {
|
|
27
|
+
const wrapper = resolveFaceWrapper(raw, this.faceByHash);
|
|
28
|
+
if (wrapper && !seen.has(wrapper)) {
|
|
29
|
+
seen.add(wrapper);
|
|
30
|
+
result.push(wrapper);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (this.scopeFaces.length > 0) {
|
|
35
|
+
for (const face of this.scopeFaces) {
|
|
36
|
+
if (seen.has(face)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (face.hasEdge(edgeShape) !== null) {
|
|
40
|
+
seen.add(face);
|
|
41
|
+
result.push(face);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export class BelongsToFaceFilter extends BelongsToFaceFilterBase {
|
|
12
49
|
match(shape) {
|
|
13
|
-
const containingFaces = this.
|
|
50
|
+
const containingFaces = this.findContainingFaces(shape);
|
|
14
51
|
return this.faceFilterBuilders.every(builder => {
|
|
15
52
|
const filters = builder.getFilters();
|
|
16
53
|
return containingFaces.some(face => filters.every(f => f.match(face)));
|
|
@@ -32,18 +69,9 @@ export class BelongsToFaceFilter extends FilterBase {
|
|
|
32
69
|
return new BelongsToFaceFilter(transformed);
|
|
33
70
|
}
|
|
34
71
|
}
|
|
35
|
-
export class NotBelongsToFaceFilter extends
|
|
36
|
-
faceFilterBuilders;
|
|
37
|
-
scopeFaces = [];
|
|
38
|
-
constructor(faceFilterBuilders) {
|
|
39
|
-
super();
|
|
40
|
-
this.faceFilterBuilders = faceFilterBuilders;
|
|
41
|
-
}
|
|
42
|
-
setScopeFaces(faces) {
|
|
43
|
-
this.scopeFaces = faces;
|
|
44
|
-
}
|
|
72
|
+
export class NotBelongsToFaceFilter extends BelongsToFaceFilterBase {
|
|
45
73
|
match(shape) {
|
|
46
|
-
const containingFaces = this.
|
|
74
|
+
const containingFaces = this.findContainingFaces(shape);
|
|
47
75
|
return !this.faceFilterBuilders.every(builder => {
|
|
48
76
|
const filters = builder.getFilters();
|
|
49
77
|
return containingFaces.some(face => filters.every(f => f.match(face)));
|
|
@@ -65,3 +93,24 @@ export class NotBelongsToFaceFilter extends FilterBase {
|
|
|
65
93
|
return new NotBelongsToFaceFilter(transformed);
|
|
66
94
|
}
|
|
67
95
|
}
|
|
96
|
+
function resolveFaceWrapper(rawFace, faceByHash) {
|
|
97
|
+
const hash = rawFace.HashCode(2147483647);
|
|
98
|
+
const bucket = faceByHash.get(hash);
|
|
99
|
+
if (bucket) {
|
|
100
|
+
for (const candidate of bucket) {
|
|
101
|
+
if (candidate.getShape().IsSame(rawFace)) {
|
|
102
|
+
return candidate;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Not in scope (e.g. neighbor face from another part / out-of-scope solid).
|
|
107
|
+
// Wrap on the fly so the face filters can still evaluate it.
|
|
108
|
+
const wrapped = Face.fromTopoDSFace(Explorer.toFace(rawFace));
|
|
109
|
+
if (!bucket) {
|
|
110
|
+
faceByHash.set(hash, [wrapped]);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
bucket.push(wrapped);
|
|
114
|
+
}
|
|
115
|
+
return wrapped;
|
|
116
|
+
}
|
|
@@ -1,12 +1,37 @@
|
|
|
1
1
|
import { Shape } from "../common/shapes.js";
|
|
2
2
|
import { FilterBase } from "./filter-base.js";
|
|
3
|
+
export type IndexSelector = {
|
|
4
|
+
type: 'first';
|
|
5
|
+
} | {
|
|
6
|
+
type: 'last';
|
|
7
|
+
} | {
|
|
8
|
+
type: 'at';
|
|
9
|
+
index: number;
|
|
10
|
+
};
|
|
3
11
|
export declare class FilterBuilderBase<TShape extends Shape = Shape> {
|
|
4
12
|
protected filters: FilterBase<TShape>[];
|
|
5
13
|
protected _withTangents: boolean;
|
|
14
|
+
protected _indexSelector?: IndexSelector;
|
|
6
15
|
filter(filter: FilterBase<TShape>): this;
|
|
7
16
|
/**
|
|
8
17
|
* Expands the selection to include all shapes transitively connected
|
|
9
18
|
* by tangency (G1 continuity) to the initially matched shapes.
|
|
10
19
|
*/
|
|
11
20
|
withTangents(): this;
|
|
21
|
+
/**
|
|
22
|
+
* Selects the first matching shape (in iteration order). If called multiple
|
|
23
|
+
* times on the same builder, the last positional call wins.
|
|
24
|
+
*/
|
|
25
|
+
first(): this;
|
|
26
|
+
/**
|
|
27
|
+
* Selects the last matching shape (in iteration order). If called multiple
|
|
28
|
+
* times on the same builder, the last positional call wins.
|
|
29
|
+
*/
|
|
30
|
+
last(): this;
|
|
31
|
+
/**
|
|
32
|
+
* Selects the matching shape at the given zero-based index.
|
|
33
|
+
* Out-of-range indices yield no match. Negative indices are not supported;
|
|
34
|
+
* use {@link last} for the final element.
|
|
35
|
+
*/
|
|
36
|
+
at(index: number): this;
|
|
12
37
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export class FilterBuilderBase {
|
|
2
2
|
filters = [];
|
|
3
3
|
_withTangents = false;
|
|
4
|
+
_indexSelector;
|
|
4
5
|
filter(filter) {
|
|
5
6
|
this.filters.push(filter);
|
|
6
7
|
return this;
|
|
@@ -13,6 +14,34 @@ export class FilterBuilderBase {
|
|
|
13
14
|
this._withTangents = true;
|
|
14
15
|
return this;
|
|
15
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Selects the first matching shape (in iteration order). If called multiple
|
|
19
|
+
* times on the same builder, the last positional call wins.
|
|
20
|
+
*/
|
|
21
|
+
first() {
|
|
22
|
+
this._indexSelector = { type: 'first' };
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Selects the last matching shape (in iteration order). If called multiple
|
|
27
|
+
* times on the same builder, the last positional call wins.
|
|
28
|
+
*/
|
|
29
|
+
last() {
|
|
30
|
+
this._indexSelector = { type: 'last' };
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Selects the matching shape at the given zero-based index.
|
|
35
|
+
* Out-of-range indices yield no match. Negative indices are not supported;
|
|
36
|
+
* use {@link last} for the final element.
|
|
37
|
+
*/
|
|
38
|
+
at(index) {
|
|
39
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
40
|
+
throw new Error(`at(index): index must be a non-negative integer (got ${index}). Use last() for the final element.`);
|
|
41
|
+
}
|
|
42
|
+
this._indexSelector = { type: 'at', index };
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
16
45
|
/**
|
|
17
46
|
* @internal
|
|
18
47
|
*/
|
|
@@ -22,6 +51,12 @@ export class FilterBuilderBase {
|
|
|
22
51
|
/**
|
|
23
52
|
* @internal
|
|
24
53
|
*/
|
|
54
|
+
getIndexSelector() {
|
|
55
|
+
return this._indexSelector;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
25
60
|
getFilters() {
|
|
26
61
|
return this.filters;
|
|
27
62
|
}
|
|
@@ -34,6 +69,9 @@ export class FilterBuilderBase {
|
|
|
34
69
|
transformedBuilder.filter(filter.transform(matrix));
|
|
35
70
|
}
|
|
36
71
|
transformedBuilder._withTangents = this._withTangents;
|
|
72
|
+
// Index is invariant under Matrix4: the selector is re-applied against
|
|
73
|
+
// the transformed shape's own ordered match list at evaluation time.
|
|
74
|
+
transformedBuilder._indexSelector = this._indexSelector;
|
|
37
75
|
return transformedBuilder;
|
|
38
76
|
}
|
|
39
77
|
/**
|
|
@@ -45,6 +83,7 @@ export class FilterBuilderBase {
|
|
|
45
83
|
remappedBuilder.filter(filter.remap(remap));
|
|
46
84
|
}
|
|
47
85
|
remappedBuilder._withTangents = this._withTangents;
|
|
86
|
+
remappedBuilder._indexSelector = this._indexSelector;
|
|
48
87
|
return remappedBuilder;
|
|
49
88
|
}
|
|
50
89
|
/**
|
|
@@ -54,6 +93,14 @@ export class FilterBuilderBase {
|
|
|
54
93
|
if (this._withTangents !== other._withTangents) {
|
|
55
94
|
return false;
|
|
56
95
|
}
|
|
96
|
+
const a = this._indexSelector;
|
|
97
|
+
const b = other._indexSelector;
|
|
98
|
+
if ((a?.type) !== (b?.type)) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
if (a?.type === 'at' && b?.type === 'at' && a.index !== b.index) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
57
104
|
if (this.filters.length !== other.filters.length) {
|
|
58
105
|
return false;
|
|
59
106
|
}
|
|
@@ -10,30 +10,55 @@ export class ShapeFilter {
|
|
|
10
10
|
if (!this.builders?.length) {
|
|
11
11
|
return this.shapes;
|
|
12
12
|
}
|
|
13
|
-
const result =
|
|
14
|
-
for (const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const result = new Set();
|
|
14
|
+
for (const builder of this.builders) {
|
|
15
|
+
const filters = builder.getFilters();
|
|
16
|
+
// Per-builder ordered match list — preserves input (OCC iteration) order
|
|
17
|
+
// so positional selectors (.first/.last/.at) are deterministic.
|
|
18
|
+
const matched = [];
|
|
19
|
+
for (const shape of this.shapes) {
|
|
20
|
+
let ok = true;
|
|
21
|
+
for (const f of filters) {
|
|
18
22
|
try {
|
|
19
|
-
|
|
23
|
+
if (!f.match(shape)) {
|
|
24
|
+
ok = false;
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
20
27
|
}
|
|
21
28
|
catch (e) {
|
|
22
29
|
console.error('Error applying filter:', e, f);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
})) {
|
|
26
|
-
if (!result.includes(shape)) {
|
|
27
|
-
result.push(shape);
|
|
30
|
+
ok = false;
|
|
31
|
+
break;
|
|
28
32
|
}
|
|
29
33
|
}
|
|
34
|
+
if (ok) {
|
|
35
|
+
matched.push(shape);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const sel = builder.getIndexSelector();
|
|
39
|
+
let selected;
|
|
40
|
+
if (!sel) {
|
|
41
|
+
selected = matched;
|
|
42
|
+
}
|
|
43
|
+
else if (sel.type === 'first') {
|
|
44
|
+
selected = matched.length > 0 ? [matched[0]] : [];
|
|
45
|
+
}
|
|
46
|
+
else if (sel.type === 'last') {
|
|
47
|
+
selected = matched.length > 0 ? [matched[matched.length - 1]] : [];
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
selected = sel.index < matched.length ? [matched[sel.index]] : [];
|
|
51
|
+
}
|
|
52
|
+
for (const s of selected) {
|
|
53
|
+
result.add(s);
|
|
30
54
|
}
|
|
31
55
|
}
|
|
56
|
+
const resultArr = [...result];
|
|
32
57
|
// Tangent expansion: if any builder requests it, expand result set via BFS
|
|
33
58
|
const needsExpansion = this.builders.some(b => b.hasTangentExpansion());
|
|
34
|
-
if (needsExpansion &&
|
|
35
|
-
return TangentExpander.expand(
|
|
59
|
+
if (needsExpansion && resultArr.length > 0) {
|
|
60
|
+
return TangentExpander.expand(resultArr, this.shapes);
|
|
36
61
|
}
|
|
37
|
-
return
|
|
62
|
+
return resultArr;
|
|
38
63
|
}
|
|
39
64
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TopTools_MapOfShape } from "occjs-wrapper";
|
|
1
2
|
import { Matrix4 } from "../math/matrix4.js";
|
|
2
3
|
import { Shape } from "../common/shapes.js";
|
|
3
4
|
import { ShapeType } from "../common/shape-type.js";
|
|
@@ -6,8 +7,11 @@ import { FilterBase } from "./filter-base.js";
|
|
|
6
7
|
export declare class FromSceneObjectFilter<TShape extends Shape> extends FilterBase<TShape> {
|
|
7
8
|
private sceneObjects;
|
|
8
9
|
private shapeType;
|
|
10
|
+
private membershipSet;
|
|
9
11
|
constructor(sceneObjects: SceneObject[], shapeType: ShapeType);
|
|
10
12
|
getSceneObjects(): SceneObject[];
|
|
13
|
+
getShapeType(): ShapeType;
|
|
14
|
+
setMembershipSet(set: TopTools_MapOfShape | null): void;
|
|
11
15
|
match(shape: TShape): boolean;
|
|
12
16
|
compareTo(other: FromSceneObjectFilter<TShape>): boolean;
|
|
13
17
|
transform(_matrix: Matrix4): FromSceneObjectFilter<TShape>;
|
|
@@ -2,6 +2,7 @@ import { FilterBase } from "./filter-base.js";
|
|
|
2
2
|
export class FromSceneObjectFilter extends FilterBase {
|
|
3
3
|
sceneObjects;
|
|
4
4
|
shapeType;
|
|
5
|
+
membershipSet = null;
|
|
5
6
|
constructor(sceneObjects, shapeType) {
|
|
6
7
|
super();
|
|
7
8
|
this.sceneObjects = sceneObjects;
|
|
@@ -10,7 +11,16 @@ export class FromSceneObjectFilter extends FilterBase {
|
|
|
10
11
|
getSceneObjects() {
|
|
11
12
|
return this.sceneObjects;
|
|
12
13
|
}
|
|
14
|
+
getShapeType() {
|
|
15
|
+
return this.shapeType;
|
|
16
|
+
}
|
|
17
|
+
setMembershipSet(set) {
|
|
18
|
+
this.membershipSet = set;
|
|
19
|
+
}
|
|
13
20
|
match(shape) {
|
|
21
|
+
if (this.membershipSet) {
|
|
22
|
+
return this.membershipSet.Contains(shape.getShape());
|
|
23
|
+
}
|
|
14
24
|
for (const obj of this.sceneObjects) {
|
|
15
25
|
const subShapes = obj.getShapes().flatMap(s => s.getSubShapes(this.shapeType));
|
|
16
26
|
if (subShapes.some(sub => sub.isSame(shape))) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Matrix4 } from "../math/matrix4.js";
|
|
2
|
+
import { LazyMatrix } from "../math/lazy-matrix.js";
|
|
2
3
|
import { SceneObject } from "../common/scene-object.js";
|
|
3
|
-
export declare function cloneWithTransform(objects: SceneObject[], transform: Matrix4, container: SceneObject): SceneObject[];
|
|
4
|
+
export declare function cloneWithTransform(objects: SceneObject[], transform: Matrix4 | LazyMatrix, container: SceneObject): SceneObject[];
|
|
@@ -12,7 +12,7 @@ export declare function fuseWithSceneObjects(sceneObjects: SceneObject[], extrus
|
|
|
12
12
|
modifiedShapes: any[];
|
|
13
13
|
toolHistory?: undefined;
|
|
14
14
|
} | {
|
|
15
|
-
newShapes: Shape<
|
|
15
|
+
newShapes: Shape<any>[];
|
|
16
16
|
modifiedShapes: {
|
|
17
17
|
shape: Shape<any>;
|
|
18
18
|
object: SceneObject;
|
|
@@ -42,23 +42,105 @@ export function fuseWithSceneObjects(sceneObjects, extrusions, opts) {
|
|
|
42
42
|
// stay on their original owners so we must not duplicate them.
|
|
43
43
|
const unconsumed = sceneShapes.filter(s => !modifiedShapes.includes(s));
|
|
44
44
|
const shapesToAdd = result.filter(s => !unconsumed.some(u => u.getShape().IsPartner(s.getShape())));
|
|
45
|
+
// Clean each addition with UnifySameDomain so that coplanar wall pieces
|
|
46
|
+
// split by the boolean fuse merge back into single faces (the visible
|
|
47
|
+
// "artifact seams" on the target solid). Lineage is captured per cleanup
|
|
48
|
+
// so downstream history can be remapped onto post-clean faces.
|
|
49
|
+
const cleanedShapesToAdd = [];
|
|
50
|
+
const cleanups = [];
|
|
51
|
+
const runCleanups = () => {
|
|
52
|
+
for (const shape of shapesToAdd) {
|
|
53
|
+
const cleanup = ShapeOps.cleanShapeWithLineage(shape);
|
|
54
|
+
cleanedShapesToAdd.push(cleanup.shape);
|
|
55
|
+
cleanups.push(cleanup);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
p ? p.record('Clean fuse result', runCleanups) : runCleanups();
|
|
45
59
|
let toolHistory;
|
|
46
60
|
if (opts?.recordHistoryFor) {
|
|
47
61
|
const recordHistory = () => {
|
|
48
|
-
recordFusionHistory(opts.recordHistoryFor, sceneShapes, objShapeMap,
|
|
62
|
+
recordFusionHistory(opts.recordHistoryFor, sceneShapes, objShapeMap, cleanedShapesToAdd, maker, cleanups, p);
|
|
49
63
|
// Separately track tool-side (extrusion) lineage so callers can remap
|
|
50
64
|
// pre-fusion categorizations (start/end/side/…) onto the post-fusion
|
|
51
|
-
// faces.
|
|
52
|
-
//
|
|
53
|
-
// Tool-side history is only consumed by `remapClassifiedFaces`, which
|
|
54
|
-
// touches modifiedFaces only — skip the added* output traversal.
|
|
65
|
+
// faces. Tool-side history is only consumed by `remapClassifiedFaces`,
|
|
66
|
+
// which touches modifiedFaces only — skip the added* output traversal.
|
|
55
67
|
const collectTools = () => ShapeHistoryTracker.collect(maker, extrusions, { skipAdded: true });
|
|
56
|
-
|
|
68
|
+
const rawToolHistory = p ? p.record('Collect tool history', collectTools) : collectTools();
|
|
69
|
+
toolHistory = remapHistoryThroughCleanups(rawToolHistory, cleanups);
|
|
57
70
|
};
|
|
58
71
|
p ? p.record('Record fusion history', recordHistory) : recordHistory();
|
|
59
72
|
}
|
|
73
|
+
for (const cleanup of cleanups) {
|
|
74
|
+
cleanup.dispose();
|
|
75
|
+
}
|
|
60
76
|
dispose();
|
|
61
|
-
return { newShapes:
|
|
77
|
+
return { newShapes: cleanedShapesToAdd, modifiedShapes: modified, toolHistory };
|
|
78
|
+
}
|
|
79
|
+
// Remap a pre-clean history through a set of cleanup lineages. Modified
|
|
80
|
+
// records get their result faces/edges replaced with the post-clean image;
|
|
81
|
+
// records whose results entirely vanish during cleanup are dropped (the
|
|
82
|
+
// caller will have already recorded removals for the corresponding sources).
|
|
83
|
+
function remapHistoryThroughCleanups(history, cleanups) {
|
|
84
|
+
const remapFaces = (faces) => {
|
|
85
|
+
const out = [];
|
|
86
|
+
for (const f of faces) {
|
|
87
|
+
let mapped = null;
|
|
88
|
+
for (const c of cleanups) {
|
|
89
|
+
const r = c.remapFace(f);
|
|
90
|
+
if (r !== null) {
|
|
91
|
+
mapped = r;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (mapped) {
|
|
96
|
+
out.push(...mapped);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
out.push(f);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
};
|
|
104
|
+
const remapEdges = (edges) => {
|
|
105
|
+
const out = [];
|
|
106
|
+
for (const e of edges) {
|
|
107
|
+
let mapped = null;
|
|
108
|
+
for (const c of cleanups) {
|
|
109
|
+
const r = c.remapEdge(e);
|
|
110
|
+
if (r !== null) {
|
|
111
|
+
mapped = r;
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (mapped) {
|
|
116
|
+
out.push(...mapped);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
out.push(e);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
addedFaces: remapFaces(history.addedFaces),
|
|
126
|
+
modifiedFaces: history.modifiedFaces
|
|
127
|
+
.map(r => ({ sources: r.sources, results: remapFaces(r.results) }))
|
|
128
|
+
.filter(r => r.results.length > 0),
|
|
129
|
+
generatedFaces: history.generatedFaces.map(r => ({
|
|
130
|
+
sources: r.sources,
|
|
131
|
+
results: remapFaces(r.results),
|
|
132
|
+
})),
|
|
133
|
+
removedFaces: history.removedFaces,
|
|
134
|
+
addedEdges: remapEdges(history.addedEdges),
|
|
135
|
+
modifiedEdges: history.modifiedEdges
|
|
136
|
+
.map(r => ({ sources: r.sources, results: remapEdges(r.results) }))
|
|
137
|
+
.filter(r => r.results.length > 0),
|
|
138
|
+
generatedEdges: history.generatedEdges.map(r => ({
|
|
139
|
+
sources: r.sources,
|
|
140
|
+
results: remapEdges(r.results),
|
|
141
|
+
})),
|
|
142
|
+
removedEdges: history.removedEdges,
|
|
143
|
+
};
|
|
62
144
|
}
|
|
63
145
|
/**
|
|
64
146
|
* Record faces/edges from each shape as additions on `caller`. Used when a
|
|
@@ -86,12 +168,50 @@ function recordShapesAsAdditions(caller, shapes) {
|
|
|
86
168
|
* of a scene-shape modification. This captures both extrusion-derived faces
|
|
87
169
|
* (which appear in the result via tool-side Modified()) and truly new faces.
|
|
88
170
|
*/
|
|
89
|
-
function recordFusionHistory(caller, sceneShapes, owners, newShapes, maker, p) {
|
|
171
|
+
function recordFusionHistory(caller, sceneShapes, owners, newShapes, maker, cleanups, p) {
|
|
90
172
|
const oc = getOC();
|
|
91
173
|
const FACE = oc.TopAbs_ShapeEnum.TopAbs_FACE;
|
|
92
174
|
const EDGE = oc.TopAbs_ShapeEnum.TopAbs_EDGE;
|
|
93
175
|
const claimedFaces = new oc.TopTools_MapOfShape();
|
|
94
176
|
const claimedEdges = new oc.TopTools_MapOfShape();
|
|
177
|
+
// Remap pre-clean faces/edges through whichever cleanup handled them,
|
|
178
|
+
// mirroring the cut-side helper. Faces no cleanup knew about pass through.
|
|
179
|
+
const remapFaces = (faces) => {
|
|
180
|
+
const out = [];
|
|
181
|
+
for (const face of faces) {
|
|
182
|
+
let matched = false;
|
|
183
|
+
for (const cleanup of cleanups) {
|
|
184
|
+
const remapped = cleanup.remapFace(face);
|
|
185
|
+
if (remapped !== null) {
|
|
186
|
+
out.push(...remapped);
|
|
187
|
+
matched = true;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (!matched) {
|
|
192
|
+
out.push(face);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
};
|
|
197
|
+
const remapEdges = (edges) => {
|
|
198
|
+
const out = [];
|
|
199
|
+
for (const edge of edges) {
|
|
200
|
+
let matched = false;
|
|
201
|
+
for (const cleanup of cleanups) {
|
|
202
|
+
const remapped = cleanup.remapEdge(edge);
|
|
203
|
+
if (remapped !== null) {
|
|
204
|
+
out.push(...remapped);
|
|
205
|
+
matched = true;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (!matched) {
|
|
210
|
+
out.push(edge);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return out;
|
|
214
|
+
};
|
|
95
215
|
const collectScene = () => {
|
|
96
216
|
for (const sceneShape of sceneShapes) {
|
|
97
217
|
const owner = owners.get(sceneShape);
|
|
@@ -103,14 +223,28 @@ function recordFusionHistory(caller, sceneShapes, owners, newShapes, maker, p) {
|
|
|
103
223
|
// need to compute its own added* sets.
|
|
104
224
|
const history = ShapeHistoryTracker.collect(maker, [sceneShape], { skipAdded: true });
|
|
105
225
|
for (const record of history.modifiedFaces) {
|
|
106
|
-
|
|
107
|
-
|
|
226
|
+
const postCleanResults = remapFaces(record.results);
|
|
227
|
+
if (postCleanResults.length === 0) {
|
|
228
|
+
for (const src of record.sources) {
|
|
229
|
+
owner.recordRemovedFace(src, caller);
|
|
230
|
+
}
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
owner.recordModifiedFaces(record.sources, postCleanResults, caller);
|
|
234
|
+
for (const r of postCleanResults) {
|
|
108
235
|
claimedFaces.Add(r.getShape());
|
|
109
236
|
}
|
|
110
237
|
}
|
|
111
238
|
for (const record of history.modifiedEdges) {
|
|
112
|
-
|
|
113
|
-
|
|
239
|
+
const postCleanResults = remapEdges(record.results);
|
|
240
|
+
if (postCleanResults.length === 0) {
|
|
241
|
+
for (const src of record.sources) {
|
|
242
|
+
owner.recordRemovedEdge(src, caller);
|
|
243
|
+
}
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
owner.recordModifiedEdges(record.sources, postCleanResults, caller);
|
|
247
|
+
for (const r of postCleanResults) {
|
|
114
248
|
claimedEdges.Add(r.getShape());
|
|
115
249
|
}
|
|
116
250
|
}
|
package/lib/dist/index.d.ts
CHANGED
|
@@ -17,7 +17,13 @@ export type SceneParserContext = {
|
|
|
17
17
|
getActiveSketch(): Sketch | null;
|
|
18
18
|
};
|
|
19
19
|
export declare function registerBuilder<T extends Function>(builder: (context: SceneParserContext) => T): T;
|
|
20
|
-
export
|
|
20
|
+
export interface FluidCADOptions {
|
|
21
|
+
mesh?: {
|
|
22
|
+
lineDeflection?: number;
|
|
23
|
+
angularDeflection?: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export declare function init(options?: FluidCADOptions): Promise<{
|
|
21
27
|
currentScene: Scene;
|
|
22
28
|
currentFile: string;
|
|
23
29
|
renderer: import("./rendering/render.js").SceneRenderer;
|
package/lib/dist/index.js
CHANGED
|
@@ -88,8 +88,8 @@ export function registerBuilder(builder) {
|
|
|
88
88
|
return fn;
|
|
89
89
|
;
|
|
90
90
|
}
|
|
91
|
-
export async function init(
|
|
91
|
+
export async function init(options) {
|
|
92
92
|
await loadOC();
|
|
93
|
-
const resolvedPath =
|
|
94
|
-
return createManager(resolvedPath);
|
|
93
|
+
const resolvedPath = process.env.FLUIDCAD_WORKSPACE_PATH || '';
|
|
94
|
+
return createManager(resolvedPath, options);
|
|
95
95
|
}
|
|
@@ -4,5 +4,9 @@ export declare class FileImport {
|
|
|
4
4
|
static deserializeShapes(fileName: string): Solid[];
|
|
5
5
|
static serializeShape(shape: Shape, workspacePath: string, fileName: string): void;
|
|
6
6
|
static importFile(workspacePath: string, fileName: string, data: Uint8Array): Solid[];
|
|
7
|
-
static deserializeShapesWithMetadata(fileName: string
|
|
7
|
+
static deserializeShapesWithMetadata(fileName: string, options?: {
|
|
8
|
+
noColors?: boolean;
|
|
9
|
+
include?: Set<number>;
|
|
10
|
+
exclude?: Set<number>;
|
|
11
|
+
}): Solid[];
|
|
8
12
|
}
|
|
@@ -49,32 +49,43 @@ export class FileImport {
|
|
|
49
49
|
console.log(`Imported ${solids.length} solids with color metadata`);
|
|
50
50
|
return solids;
|
|
51
51
|
}
|
|
52
|
-
static deserializeShapesWithMetadata(fileName) {
|
|
52
|
+
static deserializeShapesWithMetadata(fileName, options) {
|
|
53
53
|
// Read geometry from .brep
|
|
54
54
|
const brepFileName = fileName.replace(/\.(step|stp|brep)$/i, '');
|
|
55
55
|
const shapes = FileImport.deserializeShapes(brepFileName);
|
|
56
|
-
// Read color metadata from JSON sidecar
|
|
57
|
-
const sceneManager = getSceneManager();
|
|
58
|
-
const jsonPath = join(sceneManager.rootPath, 'imports', brepFileName + '.colors.json');
|
|
56
|
+
// Read color metadata from JSON sidecar (skipped when noColors is set)
|
|
59
57
|
let colorData = [];
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
if (!options?.noColors) {
|
|
59
|
+
const sceneManager = getSceneManager();
|
|
60
|
+
const jsonPath = join(sceneManager.rootPath, 'imports', brepFileName + '.colors.json');
|
|
61
|
+
if (fs.existsSync(jsonPath)) {
|
|
62
|
+
colorData = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
|
|
63
|
+
console.log(`Loaded color metadata from ${jsonPath}`);
|
|
64
|
+
}
|
|
63
65
|
}
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
66
|
+
const include = options?.include;
|
|
67
|
+
const exclude = options?.exclude;
|
|
68
|
+
// Build Solid objects, filter by original index, and apply colors by face index.
|
|
69
|
+
const solids = [];
|
|
70
|
+
for (let solidIndex = 0; solidIndex < shapes.length; solidIndex++) {
|
|
71
|
+
if (include && !include.has(solidIndex)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (exclude && exclude.has(solidIndex)) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const solid = shapes[solidIndex];
|
|
67
78
|
const solidColors = colorData[solidIndex];
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
if (solidColors) {
|
|
80
|
+
const faces = OcIO.findFaces(solid);
|
|
81
|
+
for (const entry of solidColors.faces) {
|
|
82
|
+
if (entry.faceIndex < faces.length) {
|
|
83
|
+
solid.setColor(faces[entry.faceIndex].getShape(), entry.color);
|
|
84
|
+
}
|
|
74
85
|
}
|
|
75
86
|
}
|
|
76
|
-
|
|
77
|
-
}
|
|
87
|
+
solids.push(solid);
|
|
88
|
+
}
|
|
78
89
|
console.log(`Deserialized ${solids.length} solids with color metadata`);
|
|
79
90
|
return solids;
|
|
80
91
|
}
|