fluidcad 0.0.34 → 0.0.35
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 +69 -0
- package/bin/commands/login.js +120 -0
- package/bin/commands/pack.js +49 -0
- package/bin/commands/publish.js +136 -0
- package/bin/fluidcad.js +6 -0
- package/bin/lib/api-client.js +40 -0
- package/bin/lib/browser.js +16 -0
- package/bin/lib/config.js +39 -0
- package/bin/lib/model-config.js +38 -0
- package/bin/lib/workspace.js +57 -0
- package/lib/dist/common/shape-factory.d.ts +2 -1
- package/lib/dist/common/shape-factory.js +4 -0
- package/lib/dist/common/transformable-primitive.d.ts +6 -5
- package/lib/dist/common/transformable-primitive.js +8 -7
- package/lib/dist/common/vertex.js +0 -1
- package/lib/dist/core/2d/aline.d.ts +4 -3
- package/lib/dist/core/2d/aline.js +3 -2
- package/lib/dist/core/2d/arc.d.ts +3 -2
- package/lib/dist/core/2d/arc.js +4 -3
- package/lib/dist/core/2d/bezier.d.ts +8 -6
- package/lib/dist/core/2d/circle.d.ts +4 -3
- package/lib/dist/core/2d/circle.js +3 -2
- package/lib/dist/core/2d/ellipse.d.ts +5 -4
- package/lib/dist/core/2d/ellipse.js +5 -4
- package/lib/dist/core/2d/hline.d.ts +4 -3
- package/lib/dist/core/2d/hline.js +5 -3
- package/lib/dist/core/2d/line.js +1 -0
- package/lib/dist/core/2d/offset.d.ts +3 -2
- package/lib/dist/core/2d/offset.js +6 -5
- package/lib/dist/core/2d/polygon.d.ts +5 -4
- package/lib/dist/core/2d/polygon.js +10 -9
- package/lib/dist/core/2d/rect.d.ts +4 -3
- package/lib/dist/core/2d/rect.js +10 -9
- package/lib/dist/core/2d/slot.d.ts +14 -6
- package/lib/dist/core/2d/slot.js +19 -8
- package/lib/dist/core/2d/vline.d.ts +4 -3
- package/lib/dist/core/2d/vline.js +5 -3
- package/lib/dist/core/chamfer.d.ts +5 -4
- package/lib/dist/core/chamfer.js +7 -6
- package/lib/dist/core/color.d.ts +3 -2
- package/lib/dist/core/color.js +2 -1
- package/lib/dist/core/cut.d.ts +4 -3
- package/lib/dist/core/cut.js +5 -4
- package/lib/dist/core/cylinder.d.ts +2 -1
- package/lib/dist/core/cylinder.js +2 -1
- package/lib/dist/core/draft.d.ts +3 -2
- package/lib/dist/core/draft.js +3 -2
- package/lib/dist/core/extrude.d.ts +4 -3
- package/lib/dist/core/extrude.js +5 -4
- package/lib/dist/core/fillet.d.ts +5 -4
- package/lib/dist/core/fillet.js +6 -5
- package/lib/dist/core/index.d.ts +1 -0
- package/lib/dist/core/index.js +1 -0
- package/lib/dist/core/interfaces.d.ts +25 -24
- package/lib/dist/core/param.d.ts +74 -0
- package/lib/dist/core/param.js +147 -0
- package/lib/dist/core/repeat.d.ts +2 -1
- package/lib/dist/core/repeat.js +10 -8
- package/lib/dist/core/revolve.d.ts +2 -1
- package/lib/dist/core/revolve.js +3 -2
- package/lib/dist/core/rib.d.ts +3 -2
- package/lib/dist/core/rib.js +6 -2
- package/lib/dist/core/rotate.d.ts +5 -4
- package/lib/dist/core/rotate.js +4 -3
- package/lib/dist/core/shell.d.ts +3 -2
- package/lib/dist/core/shell.js +3 -2
- package/lib/dist/core/sphere.d.ts +3 -2
- package/lib/dist/core/sphere.js +2 -1
- package/lib/dist/core/translate.d.ts +7 -6
- package/lib/dist/core/translate.js +6 -5
- package/lib/dist/features/2d/arc.js +5 -5
- package/lib/dist/features/2d/bezier.js +16 -16
- package/lib/dist/features/2d/circle.js +4 -0
- package/lib/dist/features/2d/ellipse.js +4 -0
- package/lib/dist/features/2d/hline.d.ts +3 -0
- package/lib/dist/features/2d/hline.js +9 -2
- package/lib/dist/features/2d/line.d.ts +3 -0
- package/lib/dist/features/2d/line.js +11 -3
- package/lib/dist/features/2d/sketch.js +5 -1
- package/lib/dist/features/2d/slot.d.ts +5 -0
- package/lib/dist/features/2d/slot.js +52 -7
- package/lib/dist/features/2d/tarc-to-point-tangent.js +3 -0
- package/lib/dist/features/2d/tarc-to-point.js +3 -0
- package/lib/dist/features/2d/tarc-with-tangent.js +3 -0
- package/lib/dist/features/2d/tarc.js +3 -0
- package/lib/dist/features/2d/vline.d.ts +3 -0
- package/lib/dist/features/2d/vline.js +9 -2
- package/lib/dist/features/copy-circular.d.ts +4 -3
- package/lib/dist/features/copy-circular.js +16 -9
- package/lib/dist/features/copy-circular2d.js +16 -9
- package/lib/dist/features/copy-linear.d.ts +4 -3
- package/lib/dist/features/copy-linear.js +18 -12
- package/lib/dist/features/copy-linear2d.js +18 -12
- package/lib/dist/features/extrude-base.d.ts +4 -3
- package/lib/dist/features/extrude-base.js +10 -3
- package/lib/dist/features/mirror-shape2d.js +2 -2
- package/lib/dist/features/repeat-base.d.ts +13 -0
- package/lib/dist/features/repeat-base.js +21 -0
- package/lib/dist/features/repeat-circular.d.ts +6 -5
- package/lib/dist/features/repeat-circular.js +3 -6
- package/lib/dist/features/repeat-linear.d.ts +7 -7
- package/lib/dist/features/repeat-linear.js +3 -6
- package/lib/dist/index.d.ts +5 -0
- package/lib/dist/index.js +8 -1
- package/lib/dist/io/file-import.d.ts +7 -0
- package/lib/dist/io/file-import.js +30 -10
- package/lib/dist/math/lazy-matrix.d.ts +5 -0
- package/lib/dist/math/lazy-matrix.js +78 -10
- package/lib/dist/oc/boolean-ops.d.ts +2 -2
- package/lib/dist/param-registry.d.ts +34 -0
- package/lib/dist/param-registry.js +60 -0
- package/lib/dist/rendering/mesh-builder.js +2 -1
- package/lib/dist/tests/features/copy-circular.test.js +1 -1
- package/lib/dist/tests/features/copy-linear.test.js +10 -10
- package/lib/dist/tests/features/repeat-user-repro-cache.test.d.ts +1 -0
- package/lib/dist/tests/features/repeat-user-repro-cache.test.js +97 -0
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/llm-docs/api/bezier.md +10 -11
- package/llm-docs/api/index.json +1 -1
- package/llm-docs/api/types/arc-points.md +2 -2
- package/llm-docs/api/types/cut.md +10 -10
- package/llm-docs/api/types/extrude.md +10 -10
- package/llm-docs/api/types/loft.md +6 -6
- package/llm-docs/api/types/revolve.md +6 -6
- package/llm-docs/api/types/rib.md +2 -2
- package/llm-docs/api/types/slot.md +2 -2
- package/llm-docs/api/types/sweep.md +10 -10
- package/llm-docs/api/types/transformable.md +14 -14
- package/llm-docs/index.json +12 -12
- package/mcp/dist/client.d.ts +1 -0
- package/mcp/dist/client.js +8 -1
- package/mcp/dist/server.js +14 -1
- package/mcp/dist/tools/engine.d.ts +16 -0
- package/mcp/dist/tools/engine.js +45 -0
- package/package.json +9 -3
- package/server/dist/api.d.ts +37 -0
- package/server/dist/api.js +44 -0
- package/server/dist/code-editor.d.ts +64 -0
- package/server/dist/code-editor.js +520 -2
- package/server/dist/fluidcad-server.d.ts +68 -1
- package/server/dist/fluidcad-server.js +224 -88
- package/server/dist/host/blocked-imports.d.ts +8 -0
- package/server/dist/host/blocked-imports.js +30 -0
- package/server/dist/{vite-manager.d.ts → host/local-scene-host.d.ts} +3 -1
- package/server/dist/{vite-manager.js → host/local-scene-host.js} +6 -26
- package/server/dist/host/scene-host.d.ts +19 -0
- package/server/dist/host/scene-host.js +1 -0
- package/server/dist/index.js +24 -117
- package/server/dist/model-package/capture-params.d.ts +19 -0
- package/server/dist/model-package/capture-params.js +42 -0
- package/server/dist/model-package/pack.d.ts +23 -0
- package/server/dist/model-package/pack.js +229 -0
- package/server/dist/model-package/types.d.ts +78 -0
- package/server/dist/model-package/types.js +17 -0
- package/server/dist/routes/hit-test.d.ts +3 -0
- package/server/dist/routes/hit-test.js +17 -0
- package/server/dist/routes/pack.d.ts +10 -0
- package/server/dist/routes/pack.js +47 -0
- package/server/dist/routes/params.d.ts +3 -0
- package/server/dist/routes/params.js +75 -0
- package/server/dist/routes/sketch-edits.d.ts +3 -0
- package/server/dist/routes/sketch-edits.js +542 -0
- package/server/dist/routes/timeline.d.ts +3 -0
- package/server/dist/routes/timeline.js +49 -0
- package/server/dist/server-core.d.ts +53 -0
- package/server/dist/server-core.js +147 -0
- package/server/dist/ws-protocol.d.ts +101 -2
- package/ui/dist/assets/index-CDJmUpFI.css +2 -0
- package/ui/dist/assets/index-MRqwG9Vh.js +5417 -0
- package/ui/dist/index.html +2 -2
- package/server/dist/routes/actions.d.ts +0 -3
- package/server/dist/routes/actions.js +0 -309
- package/ui/dist/assets/index-BdqrMDRu.js +0 -4946
- package/ui/dist/assets/index-DR7c2Qk9.css +0 -2
|
@@ -16,9 +16,11 @@ function isPlaneLazySource(p) {
|
|
|
16
16
|
export class LazyMatrix {
|
|
17
17
|
_resolver;
|
|
18
18
|
_cached;
|
|
19
|
-
|
|
19
|
+
_identity;
|
|
20
|
+
constructor(resolver, identity, cached = null) {
|
|
20
21
|
this._resolver = resolver;
|
|
21
22
|
this._cached = cached;
|
|
23
|
+
this._identity = identity;
|
|
22
24
|
}
|
|
23
25
|
resolve() {
|
|
24
26
|
if (this._cached === null) {
|
|
@@ -27,40 +29,106 @@ export class LazyMatrix {
|
|
|
27
29
|
return this._cached;
|
|
28
30
|
}
|
|
29
31
|
equals(other, tolerance = 0) {
|
|
30
|
-
|
|
32
|
+
const a = this._identity;
|
|
33
|
+
const b = other._identity;
|
|
34
|
+
if (a.kind !== b.kind) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
switch (a.kind) {
|
|
38
|
+
case "eager":
|
|
39
|
+
return a.matrix.equals(b.matrix, tolerance);
|
|
40
|
+
case "rotation": {
|
|
41
|
+
const bo = b;
|
|
42
|
+
return Math.abs(a.angle - bo.angle) <= tolerance
|
|
43
|
+
&& LazyMatrix.axisSourceEquals(a.axis, bo.axis, tolerance);
|
|
44
|
+
}
|
|
45
|
+
case "translation": {
|
|
46
|
+
const bo = b;
|
|
47
|
+
return Math.abs(a.distance - bo.distance) <= tolerance
|
|
48
|
+
&& LazyMatrix.axisSourceEquals(a.axis, bo.axis, tolerance);
|
|
49
|
+
}
|
|
50
|
+
case "mirror": {
|
|
51
|
+
const bo = b;
|
|
52
|
+
return LazyMatrix.planeSourceEquals(a.plane, bo.plane, tolerance);
|
|
53
|
+
}
|
|
54
|
+
case "opaque":
|
|
55
|
+
// No structural information — can't compare safely without resolving,
|
|
56
|
+
// and resolving may dereference unbuilt sources. Treat as unequal so
|
|
57
|
+
// the caller takes the rebuild path instead of crashing.
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
static axisSourceEquals(a, b, tolerance) {
|
|
62
|
+
if (isAxisLazySource(a)) {
|
|
63
|
+
if (!isAxisLazySource(b)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (a === b) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
if (typeof a.compareTo === "function" && typeof b.compareTo === "function") {
|
|
70
|
+
return a.compareTo(b);
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
if (isAxisLazySource(b)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
return a.equals(b, tolerance);
|
|
78
|
+
}
|
|
79
|
+
static planeSourceEquals(a, b, tolerance) {
|
|
80
|
+
if (isPlaneLazySource(a)) {
|
|
81
|
+
if (!isPlaneLazySource(b)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
if (a === b) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
if (typeof a.compareTo === "function" && typeof b.compareTo === "function") {
|
|
88
|
+
return a.compareTo(b);
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
if (isPlaneLazySource(b)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
return a.compareTo(b, tolerance);
|
|
31
96
|
}
|
|
32
97
|
static of(matrix) {
|
|
33
|
-
return new LazyMatrix(() => matrix, matrix);
|
|
98
|
+
return new LazyMatrix(() => matrix, { kind: "eager", matrix }, matrix);
|
|
34
99
|
}
|
|
35
100
|
static from(resolver) {
|
|
36
|
-
return new LazyMatrix(resolver);
|
|
101
|
+
return new LazyMatrix(resolver, { kind: "opaque" });
|
|
37
102
|
}
|
|
38
103
|
static mirror(plane) {
|
|
39
104
|
if (isPlaneLazySource(plane)) {
|
|
40
105
|
return new LazyMatrix(() => {
|
|
41
106
|
const p = plane.getPlane();
|
|
42
107
|
return Matrix4.mirrorPlane(p.normal, p.origin);
|
|
43
|
-
});
|
|
108
|
+
}, { kind: "mirror", plane });
|
|
44
109
|
}
|
|
45
|
-
|
|
110
|
+
const matrix = Matrix4.mirrorPlane(plane.normal, plane.origin);
|
|
111
|
+
return new LazyMatrix(() => matrix, { kind: "mirror", plane }, matrix);
|
|
46
112
|
}
|
|
47
113
|
static rotation(axis, angle) {
|
|
48
114
|
if (isAxisLazySource(axis)) {
|
|
49
115
|
return new LazyMatrix(() => {
|
|
50
116
|
const a = axis.getAxis();
|
|
51
117
|
return Matrix4.fromRotationAroundAxis(a.origin, a.direction, angle);
|
|
52
|
-
});
|
|
118
|
+
}, { kind: "rotation", axis, angle });
|
|
53
119
|
}
|
|
54
|
-
|
|
120
|
+
const matrix = Matrix4.fromRotationAroundAxis(axis.origin, axis.direction, angle);
|
|
121
|
+
return new LazyMatrix(() => matrix, { kind: "rotation", axis, angle }, matrix);
|
|
55
122
|
}
|
|
56
123
|
static translation(axis, distance) {
|
|
57
124
|
if (isAxisLazySource(axis)) {
|
|
58
125
|
return new LazyMatrix(() => {
|
|
59
126
|
const dir = axis.getAxis().direction;
|
|
60
127
|
return Matrix4.fromTranslation(dir.x * distance, dir.y * distance, dir.z * distance);
|
|
61
|
-
});
|
|
128
|
+
}, { kind: "translation", axis, distance });
|
|
62
129
|
}
|
|
63
130
|
const dir = axis.direction;
|
|
64
|
-
|
|
131
|
+
const matrix = Matrix4.fromTranslation(dir.x * distance, dir.y * distance, dir.z * distance);
|
|
132
|
+
return new LazyMatrix(() => matrix, { kind: "translation", axis, distance }, matrix);
|
|
65
133
|
}
|
|
66
134
|
}
|
|
@@ -8,8 +8,8 @@ export declare class BooleanOps {
|
|
|
8
8
|
static cutShapes(shape: Shape, tool: Shape): Shape;
|
|
9
9
|
static cutShapesRaw(shape: TopoDS_Shape, tool: TopoDS_Shape): TopoDS_Shape;
|
|
10
10
|
static cutMultiShape(stocks: Shape[], tools: Shape[], plane?: Plane, cutDistance?: number): {
|
|
11
|
-
result: Edge | import("../common/wire.js").Wire | Solid | Face;
|
|
12
|
-
modified: (shape: Shape) => (Edge | import("../common/wire.js").Wire | Solid | Face)[];
|
|
11
|
+
result: import("../common/vertex.js").Vertex | Edge | import("../common/wire.js").Wire | Solid | Face;
|
|
12
|
+
modified: (shape: Shape) => (import("../common/vertex.js").Vertex | Edge | import("../common/wire.js").Wire | Solid | Face)[];
|
|
13
13
|
sectionEdges: Edge[];
|
|
14
14
|
startEdges: Edge[];
|
|
15
15
|
endEdges: Edge[];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type ControlType = 'auto' | 'text' | 'number' | 'slider' | 'select' | 'checkbox' | 'color';
|
|
2
|
+
export type MultiControlType = 'select' | 'checkboxes' | 'chips';
|
|
3
|
+
export type SelectOption = {
|
|
4
|
+
label: string;
|
|
5
|
+
value: string | number;
|
|
6
|
+
};
|
|
7
|
+
export type ParamScalar = string | number | boolean;
|
|
8
|
+
export type ParamVal = ParamScalar | (string | number)[];
|
|
9
|
+
export type ParamDefinition = {
|
|
10
|
+
label: string;
|
|
11
|
+
defaultValue: ParamVal;
|
|
12
|
+
currentValue: ParamVal;
|
|
13
|
+
controlType: ControlType;
|
|
14
|
+
description?: string;
|
|
15
|
+
group?: string;
|
|
16
|
+
min?: number;
|
|
17
|
+
max?: number;
|
|
18
|
+
step?: number;
|
|
19
|
+
options?: SelectOption[];
|
|
20
|
+
multi?: boolean;
|
|
21
|
+
multiControlType?: MultiControlType;
|
|
22
|
+
};
|
|
23
|
+
export declare class ParamRegistry {
|
|
24
|
+
private definitions;
|
|
25
|
+
private overrides;
|
|
26
|
+
register(def: ParamDefinition): void;
|
|
27
|
+
resolve(label: string, defaultValue: (string | number)[]): (string | number)[];
|
|
28
|
+
resolve<T extends string | number | boolean>(label: string, defaultValue: T): T;
|
|
29
|
+
setOverrides(overrides: Map<string, any>): void;
|
|
30
|
+
getDefinitions(): ParamDefinition[];
|
|
31
|
+
clear(): void;
|
|
32
|
+
}
|
|
33
|
+
export declare function createParamRegistry(): ParamRegistry;
|
|
34
|
+
export declare function getParamRegistry(): ParamRegistry;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export class ParamRegistry {
|
|
2
|
+
definitions = new Map();
|
|
3
|
+
overrides = new Map();
|
|
4
|
+
register(def) {
|
|
5
|
+
this.definitions.set(def.label, def);
|
|
6
|
+
}
|
|
7
|
+
resolve(label, defaultValue) {
|
|
8
|
+
if (!this.overrides.has(label)) {
|
|
9
|
+
return defaultValue;
|
|
10
|
+
}
|
|
11
|
+
const override = this.overrides.get(label);
|
|
12
|
+
if (Array.isArray(defaultValue)) {
|
|
13
|
+
if (Array.isArray(override)) {
|
|
14
|
+
return override;
|
|
15
|
+
}
|
|
16
|
+
if (override != null) {
|
|
17
|
+
return [override];
|
|
18
|
+
}
|
|
19
|
+
return defaultValue;
|
|
20
|
+
}
|
|
21
|
+
if (typeof defaultValue === 'boolean') {
|
|
22
|
+
if (override === true || override === 'true' || override === 1) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
if (override === false || override === 'false' || override === 0) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return defaultValue;
|
|
29
|
+
}
|
|
30
|
+
if (typeof defaultValue === 'number') {
|
|
31
|
+
const num = Number(override);
|
|
32
|
+
if (Number.isFinite(num)) {
|
|
33
|
+
return num;
|
|
34
|
+
}
|
|
35
|
+
return defaultValue;
|
|
36
|
+
}
|
|
37
|
+
return String(override);
|
|
38
|
+
}
|
|
39
|
+
setOverrides(overrides) {
|
|
40
|
+
this.overrides = overrides;
|
|
41
|
+
}
|
|
42
|
+
getDefinitions() {
|
|
43
|
+
return Array.from(this.definitions.values());
|
|
44
|
+
}
|
|
45
|
+
clear() {
|
|
46
|
+
this.definitions.clear();
|
|
47
|
+
this.overrides.clear();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
let currentRegistry = null;
|
|
51
|
+
export function createParamRegistry() {
|
|
52
|
+
currentRegistry = new ParamRegistry();
|
|
53
|
+
return currentRegistry;
|
|
54
|
+
}
|
|
55
|
+
export function getParamRegistry() {
|
|
56
|
+
if (!currentRegistry) {
|
|
57
|
+
currentRegistry = new ParamRegistry();
|
|
58
|
+
}
|
|
59
|
+
return currentRegistry;
|
|
60
|
+
}
|
|
@@ -33,7 +33,8 @@ export class MeshBuilder {
|
|
|
33
33
|
console.warn("Shell shapes are not supported yet.");
|
|
34
34
|
}
|
|
35
35
|
else if (Explorer.isVertex(shape)) {
|
|
36
|
-
|
|
36
|
+
const pt = shapeObj.toPoint();
|
|
37
|
+
result = { vertices: [pt.x, pt.y, pt.z], normals: [], indices: [] };
|
|
37
38
|
}
|
|
38
39
|
else {
|
|
39
40
|
console.warn("Shape is not a valid TopoDS_Shape.");
|
|
@@ -28,7 +28,7 @@ describe("copy circular", () => {
|
|
|
28
28
|
const c = copy("circular", "z", { count: 3, angle: 180 }, e);
|
|
29
29
|
render();
|
|
30
30
|
const shapes = c.getShapes();
|
|
31
|
-
expect(shapes).toHaveLength(
|
|
31
|
+
expect(shapes).toHaveLength(3);
|
|
32
32
|
});
|
|
33
33
|
it("should skip specified indices", () => {
|
|
34
34
|
sketch("xy", () => {
|
|
@@ -25,11 +25,11 @@ describe("copy linear", () => {
|
|
|
25
25
|
const e = extrude(10).new();
|
|
26
26
|
const c = copy("linear", "x", { count: 3, offset: 50 }, e);
|
|
27
27
|
render();
|
|
28
|
-
// 2 copies (index 1 and 2), each offset by 50 along X
|
|
28
|
+
// Original + 2 copies (index 1 and 2), each offset by 50 along X
|
|
29
29
|
const shapes = c.getShapes();
|
|
30
|
-
expect(shapes).toHaveLength(
|
|
31
|
-
const bbox1 = ShapeOps.getBoundingBox(shapes[
|
|
32
|
-
const bbox2 = ShapeOps.getBoundingBox(shapes[
|
|
30
|
+
expect(shapes).toHaveLength(3);
|
|
31
|
+
const bbox1 = ShapeOps.getBoundingBox(shapes[1]);
|
|
32
|
+
const bbox2 = ShapeOps.getBoundingBox(shapes[2]);
|
|
33
33
|
expect(bbox1.minX).toBeCloseTo(50, 0);
|
|
34
34
|
expect(bbox2.minX).toBeCloseTo(100, 0);
|
|
35
35
|
});
|
|
@@ -41,8 +41,8 @@ describe("copy linear", () => {
|
|
|
41
41
|
const c = copy("linear", "y", { count: 2, offset: 60 }, e);
|
|
42
42
|
render();
|
|
43
43
|
const shapes = c.getShapes();
|
|
44
|
-
expect(shapes).toHaveLength(
|
|
45
|
-
const bbox = ShapeOps.getBoundingBox(shapes[
|
|
44
|
+
expect(shapes).toHaveLength(2);
|
|
45
|
+
const bbox = ShapeOps.getBoundingBox(shapes[1]);
|
|
46
46
|
expect(bbox.minY).toBeCloseTo(60, 0);
|
|
47
47
|
});
|
|
48
48
|
it("should copy along Z axis", () => {
|
|
@@ -53,8 +53,8 @@ describe("copy linear", () => {
|
|
|
53
53
|
const c = copy("linear", "z", { count: 2, offset: 30 }, e);
|
|
54
54
|
render();
|
|
55
55
|
const shapes = c.getShapes();
|
|
56
|
-
expect(shapes).toHaveLength(
|
|
57
|
-
const bbox = ShapeOps.getBoundingBox(shapes[
|
|
56
|
+
expect(shapes).toHaveLength(2);
|
|
57
|
+
const bbox = ShapeOps.getBoundingBox(shapes[1]);
|
|
58
58
|
expect(bbox.minZ).toBeCloseTo(30, 0);
|
|
59
59
|
});
|
|
60
60
|
it("should distribute copies by total length", () => {
|
|
@@ -66,8 +66,8 @@ describe("copy linear", () => {
|
|
|
66
66
|
const c = copy("linear", "x", { count: 4, length: 120 }, e);
|
|
67
67
|
render();
|
|
68
68
|
const shapes = c.getShapes();
|
|
69
|
-
expect(shapes).toHaveLength(
|
|
70
|
-
const bbox = ShapeOps.getBoundingBox(shapes[
|
|
69
|
+
expect(shapes).toHaveLength(4);
|
|
70
|
+
const bbox = ShapeOps.getBoundingBox(shapes[1]);
|
|
71
71
|
expect(bbox.minX).toBeCloseTo(40, 0);
|
|
72
72
|
});
|
|
73
73
|
it("should create a 2D grid with multiple axes", () => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { setupOC, render } from "../setup.js";
|
|
3
|
+
import { getSceneManager } from "../../scene-manager.js";
|
|
4
|
+
import { SceneCompare } from "../../rendering/scene-compare.js";
|
|
5
|
+
import sketch from "../../core/sketch.js";
|
|
6
|
+
import extrude from "../../core/extrude.js";
|
|
7
|
+
import axis from "../../core/axis.js";
|
|
8
|
+
import repeat from "../../core/repeat.js";
|
|
9
|
+
import sweep from "../../core/sweep.js";
|
|
10
|
+
import cut from "../../core/cut.js";
|
|
11
|
+
import remove from "../../core/remove.js";
|
|
12
|
+
import { rect, circle, vLine, tArc, tLine, slot, move, hMove } from "../../core/2d/index.js";
|
|
13
|
+
import copy from "../../core/copy.js";
|
|
14
|
+
// Regression: re-rendering a scene whose `repeat("circular", ...)` axis is an
|
|
15
|
+
// AxisFromEdge derived from a sketch line used to crash inside
|
|
16
|
+
// SceneCompare.compare with "Cannot read properties of undefined (reading
|
|
17
|
+
// 'origin')". RepeatCircular.compareTo eagerly called getAxis() on the
|
|
18
|
+
// new-scene axis source, which hadn't been built yet (compare runs pre-render).
|
|
19
|
+
describe("repeat circular cache-compare on unbuilt axis source", () => {
|
|
20
|
+
setupOC();
|
|
21
|
+
function buildScene(includeRemove) {
|
|
22
|
+
const spine = sketch("front", () => {
|
|
23
|
+
vLine(1.5);
|
|
24
|
+
tArc(-4, 45);
|
|
25
|
+
const topSegment = tLine(1.5);
|
|
26
|
+
return { topSegment };
|
|
27
|
+
}).reusable();
|
|
28
|
+
const profile = sketch("top", () => {
|
|
29
|
+
const innerPipe = circle(1.5);
|
|
30
|
+
const outerPipe = circle(2);
|
|
31
|
+
return { innerPipe, outerPipe };
|
|
32
|
+
});
|
|
33
|
+
const pipe = sweep(spine, profile.regions.outerPipe);
|
|
34
|
+
sketch("top", () => {
|
|
35
|
+
rect(3.5).centered().radius(0.5);
|
|
36
|
+
move([-2.5 / 2, -2.5 / 2]);
|
|
37
|
+
const c = circle(0.5);
|
|
38
|
+
copy("circular", [0, 0], { count: 4, angle: 360 }, c);
|
|
39
|
+
});
|
|
40
|
+
extrude(0.375);
|
|
41
|
+
sketch(pipe.endFaces(), () => {
|
|
42
|
+
circle(4);
|
|
43
|
+
});
|
|
44
|
+
const upperFlange = extrude(-0.625);
|
|
45
|
+
sweep(spine, profile.regions.innerPipe).remove();
|
|
46
|
+
const slots = sketch(upperFlange.endFaces(), () => {
|
|
47
|
+
hMove(3.25 / 2);
|
|
48
|
+
const outerSlot = slot(1, 0.75 / 2);
|
|
49
|
+
const innerSlot = slot(1, 0.45 / 2);
|
|
50
|
+
return { outerSlot, innerSlot };
|
|
51
|
+
});
|
|
52
|
+
const s1 = cut(slots.regions.innerSlot);
|
|
53
|
+
const s2 = cut(0.25, slots.regions.outerSlot);
|
|
54
|
+
const a = axis(spine.regions.topSegment);
|
|
55
|
+
repeat("circular", a, { count: 4, angle: 360 }, s1, s2);
|
|
56
|
+
if (includeRemove) {
|
|
57
|
+
remove(spine);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function currentScene() {
|
|
61
|
+
const mgr = getSceneManager();
|
|
62
|
+
if (!mgr) {
|
|
63
|
+
throw new Error("Scene manager not initialized");
|
|
64
|
+
}
|
|
65
|
+
return mgr.currentScene;
|
|
66
|
+
}
|
|
67
|
+
function startNewScene() {
|
|
68
|
+
const mgr = getSceneManager();
|
|
69
|
+
if (!mgr) {
|
|
70
|
+
throw new Error("Scene manager not initialized");
|
|
71
|
+
}
|
|
72
|
+
return mgr.startScene();
|
|
73
|
+
}
|
|
74
|
+
it("re-parsing the same script with cache compare does not throw", () => {
|
|
75
|
+
buildScene(false);
|
|
76
|
+
render();
|
|
77
|
+
const previousScene = currentScene();
|
|
78
|
+
expect(previousScene.getAllSceneObjects().some(o => o.getError())).toBe(false);
|
|
79
|
+
const newScene = startNewScene();
|
|
80
|
+
buildScene(false);
|
|
81
|
+
SceneCompare.compare(previousScene, newScene);
|
|
82
|
+
const rendered = render();
|
|
83
|
+
const errored = rendered.getAllSceneObjects().filter(o => o.getError());
|
|
84
|
+
expect(errored.map(o => `${o.getUniqueType()}: ${o.getError()}`)).toEqual([]);
|
|
85
|
+
});
|
|
86
|
+
it("appending remove(spine) after a cached render does not throw", () => {
|
|
87
|
+
buildScene(false);
|
|
88
|
+
render();
|
|
89
|
+
const previousScene = currentScene();
|
|
90
|
+
const newScene = startNewScene();
|
|
91
|
+
buildScene(true);
|
|
92
|
+
SceneCompare.compare(previousScene, newScene);
|
|
93
|
+
const rendered = render();
|
|
94
|
+
const errored = rendered.getAllSceneObjects().filter(o => o.getError());
|
|
95
|
+
expect(errored.map(o => `${o.getUniqueType()}: ${o.getError()}`)).toEqual([]);
|
|
96
|
+
});
|
|
97
|
+
});
|