fluidcad 0.0.21 → 0.0.23

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.
Files changed (63) hide show
  1. package/README.md +7 -2
  2. package/lib/dist/common/shape.d.ts +7 -0
  3. package/lib/dist/common/shape.js +7 -0
  4. package/lib/dist/core/copy.js +1 -1
  5. package/lib/dist/core/draft.d.ts +16 -0
  6. package/lib/dist/core/draft.js +29 -0
  7. package/lib/dist/core/index.d.ts +2 -3
  8. package/lib/dist/core/index.js +1 -1
  9. package/lib/dist/core/interfaces.d.ts +2 -0
  10. package/lib/dist/core/part.d.ts +2 -6
  11. package/lib/dist/core/part.js +12 -22
  12. package/lib/dist/core/trim.d.ts +12 -5
  13. package/lib/dist/core/trim.js +7 -2
  14. package/lib/dist/features/2d/sketch.js +23 -17
  15. package/lib/dist/features/copy-circular.js +1 -0
  16. package/lib/dist/features/copy-circular2d.js +1 -0
  17. package/lib/dist/features/copy-linear.js +4 -5
  18. package/lib/dist/features/copy-linear2d.js +4 -5
  19. package/lib/dist/features/draft.d.ts +15 -0
  20. package/lib/dist/features/draft.js +88 -0
  21. package/lib/dist/features/extrude-base.js +1 -0
  22. package/lib/dist/features/remove.d.ts +2 -0
  23. package/lib/dist/features/remove.js +7 -0
  24. package/lib/dist/features/trim2d.d.ts +16 -4
  25. package/lib/dist/features/trim2d.js +80 -29
  26. package/lib/dist/filters/edge/above-below.d.ts +20 -0
  27. package/lib/dist/filters/edge/above-below.js +57 -0
  28. package/lib/dist/filters/edge/edge-filter.d.ts +40 -6
  29. package/lib/dist/filters/edge/edge-filter.js +76 -8
  30. package/lib/dist/filters/edge/intersects-with.d.ts +18 -0
  31. package/lib/dist/filters/edge/intersects-with.js +38 -0
  32. package/lib/dist/filters/edge/on-plane.d.ts +4 -2
  33. package/lib/dist/filters/edge/on-plane.js +37 -12
  34. package/lib/dist/filters/filter-builder-base.d.ts +0 -5
  35. package/lib/dist/filters/filter-builder-base.js +12 -0
  36. package/lib/dist/helpers/clone-transform.js +3 -0
  37. package/lib/dist/oc/draft-ops.d.ts +5 -0
  38. package/lib/dist/oc/draft-ops.js +51 -0
  39. package/lib/dist/oc/edge-query.d.ts +5 -1
  40. package/lib/dist/oc/edge-query.js +40 -0
  41. package/lib/dist/oc/mesh.d.ts +2 -0
  42. package/lib/dist/oc/mesh.js +14 -6
  43. package/lib/dist/rendering/mesh-transform.d.ts +3 -0
  44. package/lib/dist/rendering/mesh-transform.js +22 -0
  45. package/lib/dist/rendering/render-solid.js +3 -2
  46. package/lib/dist/rendering/render.js +28 -6
  47. package/lib/dist/tests/features/chamfer.test.js +1 -1
  48. package/lib/dist/tests/features/draft.test.d.ts +1 -0
  49. package/lib/dist/tests/features/draft.test.js +147 -0
  50. package/lib/dist/tests/features/fillet.test.js +1 -1
  51. package/lib/dist/tests/features/part.test.js +69 -114
  52. package/lib/dist/tests/features/select.test.js +101 -3
  53. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  54. package/package.json +2 -3
  55. package/server/dist/fluidcad-server.d.ts +2 -0
  56. package/server/dist/fluidcad-server.js +10 -0
  57. package/server/dist/routes/actions.js +20 -0
  58. package/server/dist/vite-manager.js +7 -1
  59. package/ui/dist/assets/{index-B1LkrBga.js → index-CqP_mgZk.js} +23 -12
  60. package/ui/dist/assets/{index-BfcNNxXr.css → index-gPoNOiIs.css} +1 -1
  61. package/ui/dist/index.html +2 -2
  62. package/lib/dist/core/use.d.ts +0 -5
  63. package/lib/dist/core/use.js +0 -22
@@ -1,6 +1,6 @@
1
- import { describe, it, expect, beforeEach } from "vitest";
1
+ import { describe, it, expect } from "vitest";
2
2
  import { setupOC, render } from "../setup.js";
3
- import { getCurrentScene, setCurrentFile } from "../../scene-manager.js";
3
+ import { getCurrentScene } from "../../scene-manager.js";
4
4
  import sketch from "../../core/sketch.js";
5
5
  import extrude from "../../core/extrude.js";
6
6
  import cut from "../../core/cut.js";
@@ -8,84 +8,67 @@ import repeat from "../../core/repeat.js";
8
8
  import translate from "../../core/translate.js";
9
9
  import { circle, rect } from "../../core/2d/index.js";
10
10
  import part from "../../core/part.js";
11
- import use from "../../core/use.js";
12
11
  import { Part } from "../../features/part.js";
13
12
  import { Extrude } from "../../features/extrude.js";
14
13
  import { Sketch } from "../../features/2d/sketch.js";
15
14
  import { countShapes } from "../utils.js";
16
15
  describe("part", () => {
17
16
  setupOC();
18
- beforeEach(() => {
19
- // Simulate direct editing: currentFile matches the source location
20
- // Since tests don't have .fluid.js stack frames, we set currentFile to empty
21
- // so that direct execution tests need special handling
22
- setCurrentFile('');
23
- });
24
- describe("use()", () => {
25
- it("should execute a part handle via use()", () => {
26
- const handle = part("my-part", () => {
27
- sketch("xy", () => {
28
- circle(10);
29
- });
30
- extrude(20);
17
+ it("should execute a part callback immediately", () => {
18
+ part("my-part", () => {
19
+ sketch("xy", () => {
20
+ circle(10);
31
21
  });
32
- // Since there's no .fluid.js source frame, the callback won't auto-execute.
33
- // We need use() to trigger it.
34
- use(handle);
35
- const scene = getCurrentScene();
36
- const objects = scene.getAllSceneObjects();
37
- // Should have Part, Plane, Sketch, Circle, Extrude
38
- const parts = objects.filter(o => o instanceof Part);
39
- expect(parts).toHaveLength(1);
40
- expect(parts[0].partName).toBe("my-part");
22
+ extrude(20);
41
23
  });
42
- it("should produce a solid when used", () => {
43
- const handle = part("solid-part", () => {
44
- sketch("xy", () => {
45
- circle(10);
46
- });
47
- extrude(20);
24
+ const scene = getCurrentScene();
25
+ const objects = scene.getAllSceneObjects();
26
+ const parts = objects.filter(o => o instanceof Part);
27
+ expect(parts).toHaveLength(1);
28
+ expect(parts[0].partName).toBe("my-part");
29
+ });
30
+ it("should produce a solid", () => {
31
+ part("solid-part", () => {
32
+ sketch("xy", () => {
33
+ circle(10);
48
34
  });
49
- use(handle);
50
- render();
51
- const scene = getCurrentScene();
52
- const objects = scene.getAllSceneObjects();
53
- const extrudeObj = objects.find(o => o instanceof Extrude);
54
- expect(extrudeObj).toBeDefined();
55
- const shapes = extrudeObj.getShapes();
56
- expect(shapes.length).toBeGreaterThan(0);
57
- expect(shapes[0].getType()).toBe("solid");
35
+ extrude(20);
58
36
  });
37
+ render();
38
+ const scene = getCurrentScene();
39
+ const objects = scene.getAllSceneObjects();
40
+ const extrudeObj = objects.find(o => o instanceof Extrude);
41
+ expect(extrudeObj).toBeDefined();
42
+ const shapes = extrudeObj.getShapes();
43
+ expect(shapes.length).toBeGreaterThan(0);
44
+ expect(shapes[0].getType()).toBe("solid");
59
45
  });
60
46
  describe("fusion isolation", () => {
61
47
  it("should keep two parts as separate solids", () => {
62
- const handle1 = part("part1", () => {
48
+ part("part1", () => {
63
49
  sketch("xy", () => {
64
50
  circle(10);
65
51
  });
66
52
  extrude(20);
67
53
  });
68
- const handle2 = part("part2", () => {
54
+ part("part2", () => {
69
55
  sketch("xy", () => {
70
56
  circle(10);
71
57
  });
72
58
  extrude(20);
73
59
  });
74
- use(handle1);
75
- use(handle2);
76
60
  render();
77
61
  const scene = getCurrentScene();
78
62
  const objects = scene.getAllSceneObjects();
79
63
  const extrudes = objects.filter(o => o instanceof Extrude);
80
64
  expect(extrudes).toHaveLength(2);
81
- // Each extrude should have its own solid — they should NOT be fused together
82
65
  const shapes1 = extrudes[0].getShapes();
83
66
  const shapes2 = extrudes[1].getShapes();
84
67
  expect(shapes1.length).toBeGreaterThan(0);
85
68
  expect(shapes2.length).toBeGreaterThan(0);
86
69
  });
87
70
  it("should fuse extrudes within the same part", () => {
88
- const handle = part("fusing-part", () => {
71
+ part("fusing-part", () => {
89
72
  sketch("xy", () => {
90
73
  circle([-5, 0], 10);
91
74
  });
@@ -95,14 +78,11 @@ describe("part", () => {
95
78
  });
96
79
  extrude(20);
97
80
  });
98
- use(handle);
99
81
  render();
100
82
  const scene = getCurrentScene();
101
83
  const objects = scene.getAllSceneObjects();
102
84
  const extrudes = objects.filter(o => o instanceof Extrude);
103
85
  expect(extrudes).toHaveLength(2);
104
- // The second extrude should have fused with the first (overlapping circles)
105
- // So the first extrude's shapes should have been removed (consumed by fusion)
106
86
  const shapes1 = extrudes[0].getShapes();
107
87
  const shapes2 = extrudes[1].getShapes();
108
88
  expect(shapes1).toHaveLength(0);
@@ -111,13 +91,12 @@ describe("part", () => {
111
91
  });
112
92
  describe("container structure", () => {
113
93
  it("should make sketch a child of the part", () => {
114
- const handle = part("container-test", () => {
94
+ part("container-test", () => {
115
95
  sketch("xy", () => {
116
96
  circle(10);
117
97
  });
118
98
  extrude(20);
119
99
  });
120
- use(handle);
121
100
  const scene = getCurrentScene();
122
101
  const objects = scene.getAllSceneObjects();
123
102
  const partObj = objects.find(o => o instanceof Part);
@@ -153,41 +132,35 @@ describe("part", () => {
153
132
  });
154
133
  const e2 = extrude(20);
155
134
  render();
156
- // Second extrude should fuse with first (overlapping)
157
135
  const shapes = e2.getShapes();
158
136
  expect(shapes.length).toBeGreaterThan(0);
159
137
  });
160
138
  });
161
139
  describe("repeat inside part", () => {
162
140
  it("should repeat with explicit objects", () => {
163
- const handle = part("repeat-explicit", () => {
141
+ part("repeat-explicit", () => {
164
142
  sketch("xy", () => {
165
143
  rect(50);
166
144
  });
167
145
  const e = extrude().new();
168
146
  repeat("linear", "x", { count: 3, offset: 80 }, e);
169
147
  });
170
- use(handle);
171
148
  const scene = render();
172
- // Original + 2 clones = 3 shapes
173
149
  expect(countShapes(scene)).toBe(3);
174
150
  });
175
151
  it("should repeat with default input (last object)", () => {
176
- const handle = part("repeat-default", () => {
152
+ part("repeat-default", () => {
177
153
  sketch("xy", () => {
178
154
  rect(50);
179
155
  });
180
156
  extrude().new();
181
157
  repeat("linear", "x", { count: 3, offset: 80 });
182
158
  });
183
- use(handle);
184
159
  const scene = render();
185
160
  expect(countShapes(scene)).toBe(3);
186
161
  });
187
162
  it("should not override clone parent-child relationships", () => {
188
- // Regression: cloned Sketch children (Rect) must keep Sketch as parent,
189
- // not be reparented to the Part container
190
- const handle = part("clone-parents", () => {
163
+ part("clone-parents", () => {
191
164
  sketch("xy", () => {
192
165
  rect(50);
193
166
  });
@@ -198,22 +171,19 @@ describe("part", () => {
198
171
  const c = cut();
199
172
  repeat("linear", "x", { count: 3, offset: 80 }, e, c);
200
173
  });
201
- use(handle);
202
174
  const scene = render();
203
- // Should produce 3 complete instances (original + 2 clones)
204
175
  expect(countShapes(scene)).toBe(3);
205
176
  });
206
177
  });
207
178
  describe("pick inside part", () => {
208
179
  it("should preserve pick meta shapes on extrude inside a part", () => {
209
- const handle = part("pick-test", () => {
180
+ part("pick-test", () => {
210
181
  sketch("xy", () => {
211
182
  rect(50);
212
183
  circle(20);
213
184
  });
214
185
  extrude().pick();
215
186
  });
216
- use(handle);
217
187
  const scene = render();
218
188
  const rendered = scene.getRenderedObjects();
219
189
  const extrudeRender = rendered.find(r => r.type === 'extrude');
@@ -222,78 +192,71 @@ describe("part", () => {
222
192
  expect(metaShapes.length).toBeGreaterThan(0);
223
193
  });
224
194
  it("should preserve pick meta shapes with multiple parts", () => {
225
- const handle1 = part("pick-part1", () => {
195
+ part("pick-part1", () => {
226
196
  sketch("xy", () => {
227
197
  rect(50);
228
198
  circle(20);
229
199
  });
230
200
  extrude().pick();
231
201
  });
232
- const handle2 = part("pick-part2", () => {
202
+ part("pick-part2", () => {
233
203
  sketch("xy", () => {
234
204
  circle(10);
235
205
  });
236
206
  extrude();
237
207
  });
238
- use(handle1);
239
- use(handle2);
240
208
  const scene = render();
241
209
  const rendered = scene.getRenderedObjects();
242
210
  const extrudeRenders = rendered.filter(r => r.type === 'extrude');
243
- // First extrude (pick) should still have meta shapes
244
211
  const metaShapes = extrudeRenders[0].sceneShapes.filter(s => s.isMetaShape);
245
212
  expect(metaShapes.length).toBeGreaterThan(0);
246
213
  });
247
214
  });
248
- describe("use() as transform target", () => {
215
+ describe("part() as transform target", () => {
249
216
  it("should return an ISceneObject (Part instance)", () => {
250
- const handle = part("ret-test", () => {
217
+ const result = part("ret-test", () => {
251
218
  sketch("xy", () => { circle(10); });
252
219
  extrude(20);
253
220
  });
254
- const result = use(handle);
255
221
  expect(result).toBeDefined();
256
222
  expect(result).toBeInstanceOf(Part);
257
223
  });
258
224
  it("should translate a Part target", () => {
259
- const handle = part("translate-part", () => {
225
+ const p = part("translate-part", () => {
260
226
  sketch("xy", () => { rect(20, 20); });
261
227
  extrude(10);
262
228
  });
263
- const p = use(handle);
264
229
  translate(50, 0, 0, p);
265
230
  const scene = render();
266
231
  expect(countShapes(scene)).toBe(1);
267
232
  });
268
233
  it("should translate-copy a Part target", () => {
269
- const handle = part("copy-part", () => {
234
+ const p = part("copy-part", () => {
270
235
  sketch("xy", () => { rect(20, 20); });
271
236
  extrude(10);
272
237
  });
273
- const p = use(handle);
274
238
  translate(50, 0, 0, true, p);
275
239
  const scene = render();
276
- // Original + copy = 2 shapes
277
240
  expect(countShapes(scene)).toBe(2);
278
241
  });
279
242
  it("should transform only the targeted Part", () => {
280
- const handle = part("multi", (options) => {
281
- sketch("xy", () => { rect(options.size, options.size); });
282
- extrude(options.size);
243
+ const p1 = part("multi-a", () => {
244
+ sketch("xy", () => { rect(10, 10); });
245
+ extrude(10);
246
+ });
247
+ part("multi-b", () => {
248
+ sketch("xy", () => { rect(20, 20); });
249
+ extrude(20);
283
250
  });
284
- const p1 = use(handle, { size: 10 });
285
- const p2 = use(handle, { size: 20 });
286
251
  translate(100, 0, 0, p1);
287
252
  const scene = render();
288
- // Both parts should still produce shapes (2 total)
289
253
  expect(countShapes(scene)).toBe(2);
290
254
  });
291
255
  it("should work inline with translate", () => {
292
- const handle = part("inline-part", () => {
256
+ translate(50, 0, 0, part("inline-part", () => {
293
257
  sketch("xy", () => { circle(10); });
294
258
  extrude(20);
295
- });
296
- translate(50, 0, 0, use(handle));
259
+ }));
297
260
  const scene = render();
298
261
  expect(countShapes(scene)).toBe(1);
299
262
  });
@@ -304,15 +267,17 @@ describe("part", () => {
304
267
  expect(p.isAlwaysVisible()).toBe(true);
305
268
  });
306
269
  });
307
- describe("options", () => {
308
- it("should pass options from use() to the part callback", () => {
309
- const handle = part("opts-part", (options) => {
310
- sketch("xy", () => {
311
- circle(options.radius);
270
+ describe("parameterized parts via closures", () => {
271
+ it("should support parameterization through closures", () => {
272
+ function makePart(radius, height) {
273
+ return part("opts-part", () => {
274
+ sketch("xy", () => {
275
+ circle(radius);
276
+ });
277
+ extrude(height);
312
278
  });
313
- extrude(options.height);
314
- });
315
- use(handle, { radius: 15, height: 30 });
279
+ }
280
+ makePart(15, 30);
316
281
  render();
317
282
  const scene = getCurrentScene();
318
283
  const objects = scene.getAllSceneObjects();
@@ -325,15 +290,17 @@ describe("part", () => {
325
290
  expect(shapes.length).toBeGreaterThan(0);
326
291
  expect(shapes[0].getType()).toBe("solid");
327
292
  });
328
- it("should allow reusing a part with different options", () => {
329
- const handle = part("reusable", (options) => {
330
- sketch("xy", () => {
331
- circle(options.size);
293
+ it("should create separate parts with different parameters", () => {
294
+ function makePart(name, size) {
295
+ return part(name, () => {
296
+ sketch("xy", () => {
297
+ circle(size);
298
+ });
299
+ extrude(size);
332
300
  });
333
- extrude(options.size);
334
- });
335
- use(handle, { size: 10 });
336
- use(handle, { size: 20 });
301
+ }
302
+ makePart("reusable-a", 10);
303
+ makePart("reusable-b", 20);
337
304
  render();
338
305
  const scene = getCurrentScene();
339
306
  const objects = scene.getAllSceneObjects();
@@ -341,16 +308,4 @@ describe("part", () => {
341
308
  expect(parts).toHaveLength(2);
342
309
  });
343
310
  });
344
- describe("PartHandle", () => {
345
- it("should have correct properties", () => {
346
- const handle = part("test-handle", () => { });
347
- expect(handle.__fluidcad_part).toBe(true);
348
- expect(handle.name).toBe("test-handle");
349
- expect(handle._callback).toBeInstanceOf(Function);
350
- });
351
- it("should throw when use() is given invalid input", () => {
352
- expect(() => use(null)).toThrow("use() expects a PartHandle");
353
- expect(() => use({})).toThrow("use() expects a PartHandle");
354
- });
355
- });
356
311
  });
@@ -222,7 +222,7 @@ describe("select", () => {
222
222
  rect(100, 50);
223
223
  });
224
224
  extrude(30);
225
- const sel = select(edge().onPlane("xy", 30));
225
+ const sel = select(edge().onPlane("xy", { offset: 30 }));
226
226
  render();
227
227
  expect(sel.getShapes()).toHaveLength(4);
228
228
  });
@@ -236,6 +236,104 @@ describe("select", () => {
236
236
  render();
237
237
  expect(sel.getShapes()).toHaveLength(8);
238
238
  });
239
+ it("should select edges on offset plane with bothDirections", () => {
240
+ sketch("xy", () => {
241
+ rect(100, 50);
242
+ });
243
+ extrude(30);
244
+ // Edges on z=15 or z=-15 — none exist on an extruded box
245
+ const sel = select(edge().onPlane("xy", { offset: 15, bothDirections: true }));
246
+ render();
247
+ expect(sel.getShapes()).toHaveLength(0);
248
+ });
249
+ it("should select edges with partial match", () => {
250
+ sketch("xy", () => {
251
+ rect(100, 50);
252
+ });
253
+ extrude(30);
254
+ // partial: true matches edges with at least one vertex on the plane
255
+ // Bottom 4 edges are fully on XY, plus the 4 vertical edges each have one vertex on XY
256
+ const sel = select(edge().onPlane("xy", { partial: true }));
257
+ render();
258
+ expect(sel.getShapes()).toHaveLength(8);
259
+ });
260
+ it("should exclude edges with partial notOnPlane", () => {
261
+ sketch("xy", () => {
262
+ rect(100, 50);
263
+ });
264
+ extrude(30);
265
+ // notOnPlane partial: true excludes edges where any vertex touches XY
266
+ // Only the 4 top edges (z=30) have no vertex on XY
267
+ const sel = select(edge().notOnPlane("xy", { partial: true }));
268
+ render();
269
+ expect(sel.getShapes()).toHaveLength(4);
270
+ });
271
+ });
272
+ describe("above / below", () => {
273
+ it("should select edges entirely above a plane", () => {
274
+ sketch("xy", () => {
275
+ rect(100, 50);
276
+ });
277
+ extrude(30);
278
+ // Edges above z=10: the 4 top edges (z=30) have both vertices above
279
+ // The 4 vertical edges straddle z=10, so they don't match
280
+ const sel = select(edge().above("xy", { offset: 10 }));
281
+ render();
282
+ expect(sel.getShapes()).toHaveLength(4);
283
+ });
284
+ it("should select edges entirely below a plane", () => {
285
+ sketch("xy", () => {
286
+ rect(100, 50);
287
+ });
288
+ extrude(30);
289
+ // Edges below z=10: the 4 bottom edges (z=0) have both vertices below
290
+ const sel = select(edge().below("xy", { offset: 10 }));
291
+ render();
292
+ expect(sel.getShapes()).toHaveLength(4);
293
+ });
294
+ it("should not match edges on the plane itself", () => {
295
+ sketch("xy", () => {
296
+ rect(100, 50);
297
+ });
298
+ extrude(30);
299
+ // Edges above z=0: bottom edges are ON the plane (dist=0), not above
300
+ // Only top 4 edges are above, vertical edges straddle
301
+ const sel = select(edge().above("xy"));
302
+ render();
303
+ expect(sel.getShapes()).toHaveLength(4);
304
+ });
305
+ it("should select partially above edges", () => {
306
+ sketch("xy", () => {
307
+ rect(100, 50);
308
+ });
309
+ extrude(30);
310
+ // partial: true — match edges where at least one vertex is above z=0
311
+ // Top 4 edges (both vertices above) + 4 vertical edges (one vertex above)
312
+ const sel = select(edge().above("xy", { partial: true }));
313
+ render();
314
+ expect(sel.getShapes()).toHaveLength(8);
315
+ });
316
+ it("should select partially below edges", () => {
317
+ sketch("xy", () => {
318
+ rect(100, 50);
319
+ });
320
+ extrude(30);
321
+ // partial: true — match edges where at least one vertex is below z=30
322
+ // Bottom 4 edges (both vertices below) + 4 vertical edges (one vertex below)
323
+ const sel = select(edge().below("xy", { offset: 30, partial: true }));
324
+ render();
325
+ expect(sel.getShapes()).toHaveLength(8);
326
+ });
327
+ it("should return empty when no edges match", () => {
328
+ sketch("xy", () => {
329
+ rect(100, 50);
330
+ });
331
+ extrude(30);
332
+ // Nothing is below z=0 on a box sitting on XY
333
+ const sel = select(edge().below("xy"));
334
+ render();
335
+ expect(sel.getShapes()).toHaveLength(0);
336
+ });
239
337
  });
240
338
  describe("parallelTo / notParallelTo", () => {
241
339
  it("should select edges parallel to a plane", () => {
@@ -369,7 +467,7 @@ describe("select", () => {
369
467
  extrude(30);
370
468
  // Faces that have both a line edge on XY AND a line edge on XY offset 30
371
469
  // The 4 side faces each have edges on both bottom and top planes
372
- const sel = select(face().hasEdge(edge().onPlane("xy"), edge().onPlane("xy", 30)));
470
+ const sel = select(face().hasEdge(edge().onPlane("xy"), edge().onPlane("xy", { offset: 30 })));
373
471
  render();
374
472
  expect(sel.getShapes()).toHaveLength(4);
375
473
  });
@@ -459,7 +557,7 @@ describe("select", () => {
459
557
  });
460
558
  extrude(30);
461
559
  // Edges on bottom OR edges on top
462
- const sel = select(edge().onPlane("xy"), edge().onPlane("xy", 30));
560
+ const sel = select(edge().onPlane("xy"), edge().onPlane("xy", { offset: 30 }));
463
561
  render();
464
562
  expect(sel.getShapes()).toHaveLength(8);
465
563
  });