fluidcad 0.0.33 → 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 +2 -1
- package/lib/dist/common/scene-object.js +3 -2
- package/lib/dist/core/2d/tarc.d.ts +20 -2
- package/lib/dist/core/2d/tarc.js +24 -0
- package/lib/dist/core/interfaces.d.ts +31 -2
- package/lib/dist/core/repeat.js +62 -46
- 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/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/shell.d.ts +4 -1
- package/lib/dist/features/shell.js +14 -3
- package/lib/dist/helpers/clone-transform.d.ts +2 -1
- package/lib/dist/index.d.ts +7 -1
- package/lib/dist/index.js +3 -3
- package/lib/dist/math/lazy-matrix.d.ts +31 -0
- package/lib/dist/math/lazy-matrix.js +66 -0
- 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/mesh.d.ts +9 -4
- package/lib/dist/oc/mesh.js +14 -13
- package/lib/dist/oc/shell-ops.d.ts +2 -1
- package/lib/dist/oc/shell-ops.js +5 -2
- 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 +2 -2
- 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 +3 -0
- package/lib/dist/rendering/render.js +7 -2
- 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/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/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/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-CFi9p7wR.js → index-BdqrMDRu.js} +21 -21
- package/ui/dist/index.html +1 -1
|
@@ -8,7 +8,12 @@ import { Mesh } from "../oc/mesh.js";
|
|
|
8
8
|
import { Profiler } from "../common/profiler.js";
|
|
9
9
|
import { describeError } from "../common/describe-error.js";
|
|
10
10
|
export class SceneRenderer {
|
|
11
|
-
|
|
11
|
+
meshConfig;
|
|
12
|
+
meshBuilder;
|
|
13
|
+
constructor(meshConfig) {
|
|
14
|
+
this.meshConfig = meshConfig;
|
|
15
|
+
this.meshBuilder = new MeshBuilder(meshConfig);
|
|
16
|
+
}
|
|
12
17
|
render(scene) {
|
|
13
18
|
const sceneObjects = scene.getAllSceneObjects();
|
|
14
19
|
console.log("============ Rendering ==============", sceneObjects.length);
|
|
@@ -121,7 +126,7 @@ export class SceneRenderer {
|
|
|
121
126
|
const compound = ShapeOps.makeCompoundRaw([...targets].map(s => s.getShape()));
|
|
122
127
|
try {
|
|
123
128
|
const t0 = performance.now();
|
|
124
|
-
Mesh.ensureTriangulated(compound);
|
|
129
|
+
Mesh.ensureTriangulated(compound, this.meshConfig);
|
|
125
130
|
console.log(`Batched mesh: ${targets.size} shapes in ${(performance.now() - t0).toFixed(1)}ms`);
|
|
126
131
|
}
|
|
127
132
|
finally {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Scene } from "./rendering/scene.js";
|
|
2
2
|
import { SceneRenderer } from "./rendering/render.js";
|
|
3
|
+
import type { MeshConfig } from "./oc/mesh.js";
|
|
4
|
+
import type { FluidCADOptions } from "./index.js";
|
|
3
5
|
import type { ExportOptions } from "./io/file-export.js";
|
|
4
6
|
import type { ShapeProperties } from "./oc/props.js";
|
|
5
7
|
import type { FaceProperties } from "./oc/face-props.js";
|
|
@@ -10,7 +12,7 @@ declare class SceneManager {
|
|
|
10
12
|
currentScene: Scene;
|
|
11
13
|
currentFile: string;
|
|
12
14
|
renderer: SceneRenderer;
|
|
13
|
-
constructor(rootPath: string);
|
|
15
|
+
constructor(rootPath: string, meshConfig: MeshConfig);
|
|
14
16
|
setCurrentFile(filePath: string): void;
|
|
15
17
|
startScene(): Scene;
|
|
16
18
|
renderScene(scene: Scene): Scene;
|
|
@@ -26,7 +28,7 @@ declare class SceneManager {
|
|
|
26
28
|
};
|
|
27
29
|
hitTest(scene: Scene, shapeId: string, rayOrigin: [number, number, number], rayDir: [number, number, number], edgeThreshold: number): HitTestResult;
|
|
28
30
|
}
|
|
29
|
-
export declare function createManager(rootPath: string): SceneManager;
|
|
31
|
+
export declare function createManager(rootPath: string, options?: FluidCADOptions): SceneManager;
|
|
30
32
|
export declare function getCurrentScene(): Scene;
|
|
31
33
|
export declare function getCurrentFile(): string;
|
|
32
34
|
export declare function setCurrentFile(filePath: string): void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Scene } from "./rendering/scene.js";
|
|
2
2
|
import { SceneRenderer } from "./rendering/render.js";
|
|
3
3
|
import { SceneCompare } from "./rendering/scene-compare.js";
|
|
4
|
+
import { DEFAULT_MESH_CONFIG } from "./oc/mesh.js";
|
|
4
5
|
import { FileImport } from "./io/file-import.js";
|
|
5
6
|
import { FileExport } from "./io/file-export.js";
|
|
6
7
|
import { ShapeProps } from "./oc/props.js";
|
|
@@ -12,9 +13,10 @@ class SceneManager {
|
|
|
12
13
|
rootPath;
|
|
13
14
|
currentScene = new Scene();
|
|
14
15
|
currentFile = '';
|
|
15
|
-
renderer
|
|
16
|
-
constructor(rootPath) {
|
|
16
|
+
renderer;
|
|
17
|
+
constructor(rootPath, meshConfig) {
|
|
17
18
|
this.rootPath = rootPath;
|
|
19
|
+
this.renderer = new SceneRenderer(meshConfig);
|
|
18
20
|
}
|
|
19
21
|
setCurrentFile(filePath) {
|
|
20
22
|
this.currentFile = filePath;
|
|
@@ -100,9 +102,15 @@ class SceneManager {
|
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
104
|
let currentManager = null;
|
|
103
|
-
|
|
105
|
+
function resolveMeshConfig(options) {
|
|
106
|
+
return {
|
|
107
|
+
linDefl: options?.mesh?.lineDeflection ?? DEFAULT_MESH_CONFIG.linDefl,
|
|
108
|
+
angDefl: options?.mesh?.angularDeflection ?? DEFAULT_MESH_CONFIG.angDefl,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export function createManager(rootPath, options) {
|
|
104
112
|
console.log(`Creating SceneManager with root path: ${rootPath}`);
|
|
105
|
-
currentManager = new SceneManager(rootPath);
|
|
113
|
+
currentManager = new SceneManager(rootPath, resolveMeshConfig(options));
|
|
106
114
|
return currentManager;
|
|
107
115
|
}
|
|
108
116
|
export function getCurrentScene() {
|
|
@@ -36,6 +36,70 @@ describe("arc", () => {
|
|
|
36
36
|
expect(arcEdges.length).toBeGreaterThan(0);
|
|
37
37
|
});
|
|
38
38
|
});
|
|
39
|
+
describe("clockwise arc with .cw()", () => {
|
|
40
|
+
it("should create a CW arc from start to end around a center point", () => {
|
|
41
|
+
sketch("xy", () => {
|
|
42
|
+
arc([0, 0], [20, 0]).center([10, 0]).cw();
|
|
43
|
+
line([0, 0]);
|
|
44
|
+
});
|
|
45
|
+
const e = extrude(10);
|
|
46
|
+
render();
|
|
47
|
+
expect(e.getShapes()).toHaveLength(1);
|
|
48
|
+
const solid = e.getShapes()[0];
|
|
49
|
+
const arcEdges = getEdgesByType(solid, "arc");
|
|
50
|
+
expect(arcEdges.length).toBeGreaterThan(0);
|
|
51
|
+
});
|
|
52
|
+
it("should create a valid arc when chained from current position", () => {
|
|
53
|
+
sketch("xy", () => {
|
|
54
|
+
hLine(20);
|
|
55
|
+
arc([0, 0]).center([10, 0]).cw();
|
|
56
|
+
});
|
|
57
|
+
const e = extrude(10);
|
|
58
|
+
render();
|
|
59
|
+
expect(e.getShapes()).toHaveLength(1);
|
|
60
|
+
const solid = e.getShapes()[0];
|
|
61
|
+
const arcEdges = getEdgesByType(solid, "arc");
|
|
62
|
+
expect(arcEdges.length).toBeGreaterThan(0);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe("major arc with .major()", () => {
|
|
66
|
+
it("should create a major arc (>180°) from current position", () => {
|
|
67
|
+
sketch("xy", () => {
|
|
68
|
+
arc([30, 0]).radius(20).major();
|
|
69
|
+
line([0, 0]);
|
|
70
|
+
});
|
|
71
|
+
const e = extrude(10);
|
|
72
|
+
render();
|
|
73
|
+
expect(e.getShapes()).toHaveLength(1);
|
|
74
|
+
const solid = e.getShapes()[0];
|
|
75
|
+
const arcEdges = getEdgesByType(solid, "arc");
|
|
76
|
+
expect(arcEdges.length).toBeGreaterThan(0);
|
|
77
|
+
});
|
|
78
|
+
it("should create a major arc with two explicit points", () => {
|
|
79
|
+
sketch("xy", () => {
|
|
80
|
+
arc([0, 0], [30, 0]).radius(20).major();
|
|
81
|
+
line([0, 0]);
|
|
82
|
+
});
|
|
83
|
+
const e = extrude(10);
|
|
84
|
+
render();
|
|
85
|
+
expect(e.getShapes()).toHaveLength(1);
|
|
86
|
+
const solid = e.getShapes()[0];
|
|
87
|
+
const arcEdges = getEdgesByType(solid, "arc");
|
|
88
|
+
expect(arcEdges.length).toBeGreaterThan(0);
|
|
89
|
+
});
|
|
90
|
+
it("should create a CW major arc with negative radius and major", () => {
|
|
91
|
+
sketch("xy", () => {
|
|
92
|
+
arc([30, 0]).radius(-20).major();
|
|
93
|
+
line([0, 0]);
|
|
94
|
+
});
|
|
95
|
+
const e = extrude(10);
|
|
96
|
+
render();
|
|
97
|
+
expect(e.getShapes()).toHaveLength(1);
|
|
98
|
+
const solid = e.getShapes()[0];
|
|
99
|
+
const arcEdges = getEdgesByType(solid, "arc");
|
|
100
|
+
expect(arcEdges.length).toBeGreaterThan(0);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
39
103
|
describe("combined with lines", () => {
|
|
40
104
|
it("should create a shape with straight and curved edges", () => {
|
|
41
105
|
sketch("xy", () => {
|
|
@@ -2,8 +2,9 @@ import { describe, it, expect } from "vitest";
|
|
|
2
2
|
import { setupOC, render } from "../../setup.js";
|
|
3
3
|
import sketch from "../../../core/sketch.js";
|
|
4
4
|
import extrude from "../../../core/extrude.js";
|
|
5
|
-
import { move, hMove, back, rect } from "../../../core/2d/index.js";
|
|
5
|
+
import { move, hMove, back, rect, hLine, vLine, tLine } from "../../../core/2d/index.js";
|
|
6
6
|
import { ShapeOps } from "../../../oc/shape-ops.js";
|
|
7
|
+
import { Edge } from "../../../common/edge.js";
|
|
7
8
|
describe("back", () => {
|
|
8
9
|
setupOC();
|
|
9
10
|
it("should revert cursor to the previous position", () => {
|
|
@@ -57,4 +58,19 @@ describe("back", () => {
|
|
|
57
58
|
const bbox = ShapeOps.getBoundingBox(e.getShapes()[0]);
|
|
58
59
|
expect(bbox.minX).toBeCloseTo(40, 0);
|
|
59
60
|
});
|
|
61
|
+
it("should restore the tangent to its prior value, not just the position", () => {
|
|
62
|
+
let t;
|
|
63
|
+
sketch("xy", () => {
|
|
64
|
+
hLine(10); // tangent (+1, 0), pos (10, 0)
|
|
65
|
+
vLine(10); // tangent (0, +1), pos (10, 10)
|
|
66
|
+
back(); // pos reverts to (10, 0); tangent should revert to (+1, 0)
|
|
67
|
+
t = tLine(5); // with restored tangent, this should go to (15, 0)
|
|
68
|
+
});
|
|
69
|
+
render();
|
|
70
|
+
const edges = t.getOwnShapes().filter((sh) => sh instanceof Edge);
|
|
71
|
+
expect(edges).toHaveLength(1);
|
|
72
|
+
const endPoint = edges[0].getLastVertex().toPoint();
|
|
73
|
+
expect(endPoint.x).toBeCloseTo(15, 1);
|
|
74
|
+
expect(endPoint.y).toBeCloseTo(0, 1);
|
|
75
|
+
});
|
|
60
76
|
});
|
|
@@ -234,4 +234,161 @@ describe("tArc", () => {
|
|
|
234
234
|
render();
|
|
235
235
|
});
|
|
236
236
|
});
|
|
237
|
+
describe("tangent arc from current position to target line (target)", () => {
|
|
238
|
+
it("should create a tangent arc from a horizontal line ending tangent to a vertical line", () => {
|
|
239
|
+
const s = sketch("xy", () => {
|
|
240
|
+
const v = vLine([200, 200], 100).guide();
|
|
241
|
+
move([0, 0]);
|
|
242
|
+
hLine(100);
|
|
243
|
+
tArc(v);
|
|
244
|
+
});
|
|
245
|
+
render();
|
|
246
|
+
const shapes = s.getShapes();
|
|
247
|
+
// hLine + tArc (guide excluded) = at least 2 edges
|
|
248
|
+
expect(shapes.length).toBeGreaterThanOrEqual(2);
|
|
249
|
+
});
|
|
250
|
+
it("should accept a qualified line target", () => {
|
|
251
|
+
const s = sketch("xy", () => {
|
|
252
|
+
const v = vLine([200, 200], 100).guide();
|
|
253
|
+
move([0, 0]);
|
|
254
|
+
hLine(100);
|
|
255
|
+
tArc(outside(v));
|
|
256
|
+
});
|
|
257
|
+
render();
|
|
258
|
+
const shapes = s.getShapes();
|
|
259
|
+
expect(shapes.length).toBeGreaterThanOrEqual(2);
|
|
260
|
+
});
|
|
261
|
+
it("should allow chaining geometry after the solved arc", () => {
|
|
262
|
+
const s = sketch("xy", () => {
|
|
263
|
+
const v = vLine([200, 200], 100).guide();
|
|
264
|
+
move([0, 0]);
|
|
265
|
+
hLine(100);
|
|
266
|
+
tArc(v);
|
|
267
|
+
vLine(40);
|
|
268
|
+
});
|
|
269
|
+
render();
|
|
270
|
+
const shapes = s.getShapes();
|
|
271
|
+
// chain continues — hLine + tArc + vLine
|
|
272
|
+
expect(shapes.length).toBeGreaterThanOrEqual(3);
|
|
273
|
+
});
|
|
274
|
+
it("default arc curves to the left of the start tangent", () => {
|
|
275
|
+
const s = sketch("xy", () => {
|
|
276
|
+
const h = hLine([-150, 100], 300).guide();
|
|
277
|
+
move([0, 0]);
|
|
278
|
+
vLine(80);
|
|
279
|
+
tArc(h);
|
|
280
|
+
});
|
|
281
|
+
render();
|
|
282
|
+
const arcs = getEdgesByType(s.getShapes(), "arc");
|
|
283
|
+
expect(arcs.length).toBe(1);
|
|
284
|
+
// start tangent is +Y; "left" of +Y is -X. End vertex sits at negative x.
|
|
285
|
+
const endX = arcs[0].getLastVertex().toPoint().x;
|
|
286
|
+
expect(endX).toBeLessThan(0);
|
|
287
|
+
});
|
|
288
|
+
it(".flip() reverses the curve to the right of the start tangent", () => {
|
|
289
|
+
const s = sketch("xy", () => {
|
|
290
|
+
const h = hLine([-150, 100], 300).guide();
|
|
291
|
+
move([0, 0]);
|
|
292
|
+
vLine(80);
|
|
293
|
+
tArc(h).flip();
|
|
294
|
+
});
|
|
295
|
+
render();
|
|
296
|
+
const arcs = getEdgesByType(s.getShapes(), "arc");
|
|
297
|
+
expect(arcs.length).toBe(1);
|
|
298
|
+
const endX = arcs[0].getLastVertex().toPoint().x;
|
|
299
|
+
expect(endX).toBeGreaterThan(0);
|
|
300
|
+
});
|
|
301
|
+
it("should reject circle targets", () => {
|
|
302
|
+
const s = sketch("xy", () => {
|
|
303
|
+
const c = circle([200, 80], 30).guide();
|
|
304
|
+
move([0, 0]);
|
|
305
|
+
hLine(100);
|
|
306
|
+
tArc(c);
|
|
307
|
+
});
|
|
308
|
+
render();
|
|
309
|
+
const children = s.getChildren();
|
|
310
|
+
const arc = children[children.length - 1];
|
|
311
|
+
expect(arc.getError()).toMatch(/only line targets are supported/);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
describe("tangent arc with radius ending at intersection (radius, target)", () => {
|
|
315
|
+
it("should end at the intersection with a line target", () => {
|
|
316
|
+
const s = sketch("xy", () => {
|
|
317
|
+
const h = hLine([-200, 100], 400).guide();
|
|
318
|
+
move([0, 0]);
|
|
319
|
+
vLine(50);
|
|
320
|
+
tArc(60, h);
|
|
321
|
+
});
|
|
322
|
+
render();
|
|
323
|
+
const arcs = getEdgesByType(s.getShapes(), "arc");
|
|
324
|
+
expect(arcs.length).toBe(1);
|
|
325
|
+
// End vertex must lie on the target line y = 100.
|
|
326
|
+
const endY = arcs[0].getLastVertex().toPoint().y;
|
|
327
|
+
expect(endY).toBeCloseTo(100, 6);
|
|
328
|
+
});
|
|
329
|
+
it("should end at the intersection with a circle target", () => {
|
|
330
|
+
const s = sketch("xy", () => {
|
|
331
|
+
// circle(diameter); 80 diameter = 40 radius
|
|
332
|
+
const c = circle([80, 0], 80).guide();
|
|
333
|
+
move([0, 0]);
|
|
334
|
+
hLine(40);
|
|
335
|
+
tArc(50, c);
|
|
336
|
+
});
|
|
337
|
+
render();
|
|
338
|
+
const arcs = getEdgesByType(s.getShapes(), "arc");
|
|
339
|
+
expect(arcs.length).toBe(1);
|
|
340
|
+
// End vertex must lie on the target circle (radius 40 from (80, 0)).
|
|
341
|
+
const end = arcs[0].getLastVertex().toPoint();
|
|
342
|
+
const dist = Math.hypot(end.x - 80, end.y - 0);
|
|
343
|
+
expect(dist).toBeCloseTo(40, 6);
|
|
344
|
+
});
|
|
345
|
+
it("negative radius flips the sweep direction", () => {
|
|
346
|
+
// Same line, same start, same tangent — only the sign of the radius
|
|
347
|
+
// differs. Positive radius (CCW) curves to the left of the tangent;
|
|
348
|
+
// negative radius (CW) curves to the right.
|
|
349
|
+
const sCCW = sketch("xy", () => {
|
|
350
|
+
const h = hLine([-200, 100], 400).guide();
|
|
351
|
+
move([0, 0]);
|
|
352
|
+
vLine(50);
|
|
353
|
+
tArc(60, h);
|
|
354
|
+
});
|
|
355
|
+
render();
|
|
356
|
+
const sCW = sketch("xy", () => {
|
|
357
|
+
const h = hLine([-200, 100], 400).guide();
|
|
358
|
+
move([0, 0]);
|
|
359
|
+
vLine(50);
|
|
360
|
+
tArc(-60, h);
|
|
361
|
+
});
|
|
362
|
+
render();
|
|
363
|
+
const ccwArc = getEdgesByType(sCCW.getShapes(), "arc")[0];
|
|
364
|
+
const cwArc = getEdgesByType(sCW.getShapes(), "arc")[0];
|
|
365
|
+
// T̂ = (0, +1); "left" of the tangent is −x, "right" is +x.
|
|
366
|
+
expect(ccwArc.getLastVertex().toPoint().x).toBeLessThan(0);
|
|
367
|
+
expect(cwArc.getLastVertex().toPoint().x).toBeGreaterThan(0);
|
|
368
|
+
});
|
|
369
|
+
it("should chain geometry after the solved arc", () => {
|
|
370
|
+
const s = sketch("xy", () => {
|
|
371
|
+
const h = hLine([-200, 100], 400).guide();
|
|
372
|
+
move([0, 0]);
|
|
373
|
+
vLine(50);
|
|
374
|
+
tArc(60, h);
|
|
375
|
+
hLine(40);
|
|
376
|
+
});
|
|
377
|
+
render();
|
|
378
|
+
const shapes = s.getShapes();
|
|
379
|
+
expect(shapes.length).toBeGreaterThanOrEqual(3);
|
|
380
|
+
});
|
|
381
|
+
it("should record an error when there is no intersection", () => {
|
|
382
|
+
const s = sketch("xy", () => {
|
|
383
|
+
const h = hLine([-200, 500], 400).guide();
|
|
384
|
+
move([0, 0]);
|
|
385
|
+
vLine(10);
|
|
386
|
+
tArc(20, h);
|
|
387
|
+
});
|
|
388
|
+
render();
|
|
389
|
+
const children = s.getChildren();
|
|
390
|
+
const arc = children[children.length - 1];
|
|
391
|
+
expect(arc.getError()).toMatch(/does not intersect target/);
|
|
392
|
+
});
|
|
393
|
+
});
|
|
237
394
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { setupOC, render } from "../setup.js";
|
|
3
|
+
import sketch from "../../core/sketch.js";
|
|
4
|
+
import extrude from "../../core/extrude.js";
|
|
5
|
+
import axis from "../../core/axis.js";
|
|
6
|
+
import repeat from "../../core/repeat.js";
|
|
7
|
+
import sweep from "../../core/sweep.js";
|
|
8
|
+
import cut from "../../core/cut.js";
|
|
9
|
+
import { rect, circle, vLine, tArc, tLine, slot, move, hMove } from "../../core/2d/index.js";
|
|
10
|
+
import copy from "../../core/copy.js";
|
|
11
|
+
import { countShapes } from "../utils.js";
|
|
12
|
+
// Regression: a circular repeat whose axis is an AxisFromEdge derived from
|
|
13
|
+
// a sketch line, whose targets are cuts on sketch-region slots. Exercises:
|
|
14
|
+
// - LazyMatrix.rotation with an unbuilt AxisObjectBase source
|
|
15
|
+
// - Extrude.getSourceDependencies pulling the containing Sketch into the
|
|
16
|
+
// clone set so Sketch.build's clone-mode path carries shapes for cloned
|
|
17
|
+
// geometry primitives (Slot here) that would otherwise lose their
|
|
18
|
+
// Sketch parent context after clone.
|
|
19
|
+
describe("repeat circular with axis-from-sketch-line and cut-of-slot targets", () => {
|
|
20
|
+
setupOC();
|
|
21
|
+
it("renders without errors and produces the expected cut instances", () => {
|
|
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
|
+
const scene = render();
|
|
57
|
+
expect(countShapes(scene)).toBeGreaterThan(0);
|
|
58
|
+
expect(scene.getAllSceneObjects().some(o => o.getError())).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -6,6 +6,7 @@ import shell from "../../core/shell.js";
|
|
|
6
6
|
import select from "../../core/select.js";
|
|
7
7
|
import cylinder from "../../core/cylinder.js";
|
|
8
8
|
import { rect } from "../../core/2d/index.js";
|
|
9
|
+
import { Shell } from "../../features/shell.js";
|
|
9
10
|
import { countShapes } from "../utils.js";
|
|
10
11
|
import { ShapeProps } from "../../oc/props.js";
|
|
11
12
|
import { face } from "../../filters/index.js";
|
|
@@ -198,6 +199,41 @@ describe("shell", () => {
|
|
|
198
199
|
expect(first.getShapes()[0].isSame(allShapes[0])).toBe(true);
|
|
199
200
|
});
|
|
200
201
|
});
|
|
202
|
+
describe("join type", () => {
|
|
203
|
+
const buildShelledBox = (apply) => {
|
|
204
|
+
sketch("xy", () => {
|
|
205
|
+
rect(100, 100);
|
|
206
|
+
});
|
|
207
|
+
extrude(50);
|
|
208
|
+
select(face().onPlane("xy", 50));
|
|
209
|
+
const s = shell(5);
|
|
210
|
+
apply(s);
|
|
211
|
+
const scene = render();
|
|
212
|
+
return scene.getAllSceneObjects()
|
|
213
|
+
.flatMap(o => o.getShapes())
|
|
214
|
+
.find(sh => sh.getType() === "solid");
|
|
215
|
+
};
|
|
216
|
+
it("should produce a valid solid with default join type (arc)", () => {
|
|
217
|
+
const solid = buildShelledBox(() => { });
|
|
218
|
+
expect(solid).toBeDefined();
|
|
219
|
+
expect(ShapeProps.getProperties(solid.getShape()).volumeMm3).toBeGreaterThan(0);
|
|
220
|
+
});
|
|
221
|
+
it("should produce a valid solid with join('intersection')", () => {
|
|
222
|
+
const solid = buildShelledBox(s => s.join('intersection'));
|
|
223
|
+
expect(solid).toBeDefined();
|
|
224
|
+
expect(ShapeProps.getProperties(solid.getShape()).volumeMm3).toBeGreaterThan(0);
|
|
225
|
+
});
|
|
226
|
+
it("should produce a valid solid with join('tangent')", () => {
|
|
227
|
+
const solid = buildShelledBox(s => s.join('tangent'));
|
|
228
|
+
expect(solid).toBeDefined();
|
|
229
|
+
expect(ShapeProps.getProperties(solid.getShape()).volumeMm3).toBeGreaterThan(0);
|
|
230
|
+
});
|
|
231
|
+
it("should treat shells with different join types as not equal", () => {
|
|
232
|
+
const a = new Shell(5);
|
|
233
|
+
const b = new Shell(5).join('intersection');
|
|
234
|
+
expect(a.compareTo(b)).toBe(false);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
201
237
|
describe("shell with multiple selections", () => {
|
|
202
238
|
it("should shell a box by removing two faces", () => {
|
|
203
239
|
sketch("xy", () => {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type FluidBlock = {
|
|
2
|
+
/** Path relative to the docs root, forward-slash. */
|
|
3
|
+
file: string;
|
|
4
|
+
/** 1-based line number where the block body starts (the line after the opening fence). */
|
|
5
|
+
line: number;
|
|
6
|
+
/** Block body — no fences. */
|
|
7
|
+
block: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function extractFluidJsBlocks(docsRootRel: string): FluidBlock[];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
const FENCE_RE = /^(\s*)```([A-Za-z0-9_.-]*)\s*$/;
|
|
4
|
+
const RUNNABLE_LANGS = new Set(["fluid.js", "fluid", "fluidjs"]);
|
|
5
|
+
function listMarkdown(dir) {
|
|
6
|
+
const out = [];
|
|
7
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
8
|
+
if (entry.name.startsWith(".") || entry.name.startsWith("_")) {
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
const full = path.join(dir, entry.name);
|
|
12
|
+
if (entry.isDirectory()) {
|
|
13
|
+
out.push(...listMarkdown(full));
|
|
14
|
+
}
|
|
15
|
+
else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
16
|
+
out.push(full);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
function extractFromFile(file, docsRoot) {
|
|
22
|
+
const text = fs.readFileSync(file, "utf8");
|
|
23
|
+
const lines = text.split("\n");
|
|
24
|
+
const rel = path.relative(docsRoot, file).split(path.sep).join("/");
|
|
25
|
+
const blocks = [];
|
|
26
|
+
let i = 0;
|
|
27
|
+
while (i < lines.length) {
|
|
28
|
+
const openMatch = lines[i].match(FENCE_RE);
|
|
29
|
+
if (!openMatch || !RUNNABLE_LANGS.has(openMatch[2])) {
|
|
30
|
+
i++;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const startLine = i + 2;
|
|
34
|
+
const body = [];
|
|
35
|
+
i++;
|
|
36
|
+
while (i < lines.length) {
|
|
37
|
+
const closeMatch = lines[i].match(FENCE_RE);
|
|
38
|
+
if (closeMatch && closeMatch[2] === "") {
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
body.push(lines[i]);
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
blocks.push({ file: rel, line: startLine, block: body.join("\n") });
|
|
45
|
+
i++;
|
|
46
|
+
}
|
|
47
|
+
return blocks;
|
|
48
|
+
}
|
|
49
|
+
export function extractFluidJsBlocks(docsRootRel) {
|
|
50
|
+
const docsRoot = path.resolve(docsRootRel);
|
|
51
|
+
const blocks = [];
|
|
52
|
+
for (const file of listMarkdown(docsRoot).sort()) {
|
|
53
|
+
blocks.push(...extractFromFile(file, docsRoot));
|
|
54
|
+
}
|
|
55
|
+
return blocks;
|
|
56
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Runs every fluid.js fenced block in llm-docs/**/*.md against the real
|
|
2
|
+
// FluidCAD runtime and asserts the scene compiles and produces ≥1 rendered
|
|
3
|
+
// object. Catches three classes of doc rot:
|
|
4
|
+
//
|
|
5
|
+
// 1. Syntax errors in examples (renamed args, dropped commas, etc.).
|
|
6
|
+
// 2. API drift — an example calls a method that no longer exists.
|
|
7
|
+
// 3. Geometry-empty examples — the snippet runs but yields nothing visible,
|
|
8
|
+
// which usually means the user-facing intent broke.
|
|
9
|
+
//
|
|
10
|
+
// The runner reuses the global OCC init from global-setup.ts and the
|
|
11
|
+
// startScene/render plumbing from setup.ts, so it sits on the same OCC
|
|
12
|
+
// initialisation the rest of the test suite uses.
|
|
13
|
+
import { describe, it, expect } from "vitest";
|
|
14
|
+
import { setupOC, render } from "./setup.js";
|
|
15
|
+
import { extractFluidJsBlocks } from "./helpers/extract-blocks.js";
|
|
16
|
+
import * as core from "../core/index.js";
|
|
17
|
+
import * as filters from "../filters/index.js";
|
|
18
|
+
import * as constraints from "../features/2d/constraints/geometry-qualifier.js";
|
|
19
|
+
import * as math from "../math/index.js";
|
|
20
|
+
import { countShapes } from "./utils.js";
|
|
21
|
+
function buildGlobals() {
|
|
22
|
+
return {
|
|
23
|
+
...core,
|
|
24
|
+
...filters,
|
|
25
|
+
...constraints,
|
|
26
|
+
...math,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// Doc examples often show the `import { foo } from "fluidcad/..."` line a
|
|
30
|
+
// real user would write at the top of a .fluid.js file. The runner injects
|
|
31
|
+
// every public export as a Function() parameter, so those imports are dead
|
|
32
|
+
// weight here — and `new Function` can't parse them anyway. Strip them out
|
|
33
|
+
// before executing.
|
|
34
|
+
const IMPORT_LINE_RE = /^\s*import\s[\s\S]*?from\s+['"][^'"]+['"]\s*;?\s*$/gm;
|
|
35
|
+
function stripImports(block) {
|
|
36
|
+
return block.replace(IMPORT_LINE_RE, "");
|
|
37
|
+
}
|
|
38
|
+
function runBlock(block, file, line) {
|
|
39
|
+
const globals = buildGlobals();
|
|
40
|
+
const paramNames = Object.keys(globals);
|
|
41
|
+
const paramValues = paramNames.map((n) => globals[n]);
|
|
42
|
+
const wrapped = `"use strict";\n${stripImports(block)}\n//# sourceURL=llm-docs/${file}:${line}`;
|
|
43
|
+
const fn = new Function(...paramNames, wrapped);
|
|
44
|
+
fn(...paramValues);
|
|
45
|
+
}
|
|
46
|
+
const blocks = extractFluidJsBlocks("llm-docs");
|
|
47
|
+
describe("llm-docs fluid.js examples", () => {
|
|
48
|
+
setupOC();
|
|
49
|
+
if (blocks.length === 0) {
|
|
50
|
+
it("found at least one fluid.js block", () => {
|
|
51
|
+
expect(blocks.length).toBeGreaterThan(0);
|
|
52
|
+
});
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
for (const { file, line, block } of blocks) {
|
|
56
|
+
it(`${file}:${line} compiles and produces ≥1 rendered object`, () => {
|
|
57
|
+
runBlock(block, file, line);
|
|
58
|
+
const scene = render();
|
|
59
|
+
expect(countShapes(scene)).toBeGreaterThan(0);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
});
|
package/lib/dist/tests/setup.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { beforeEach } from "vitest";
|
|
2
2
|
import { getSceneManager, getCurrentScene } from "../scene-manager.js";
|
|
3
3
|
import { SceneRenderer } from "../rendering/render.js";
|
|
4
|
-
|
|
4
|
+
import { DEFAULT_MESH_CONFIG } from "../oc/mesh.js";
|
|
5
|
+
const renderer = new SceneRenderer(DEFAULT_MESH_CONFIG);
|
|
5
6
|
export function setupOC() {
|
|
6
7
|
beforeEach(() => {
|
|
7
8
|
getSceneManager().startScene();
|