fluidcad 0.0.28 → 0.0.30

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 (145) hide show
  1. package/lib/dist/common/profiler.d.ts +12 -0
  2. package/lib/dist/common/profiler.js +35 -0
  3. package/lib/dist/common/scene-object.d.ts +3 -0
  4. package/lib/dist/common/scene-object.js +3 -0
  5. package/lib/dist/common/shape-history-tracker.d.ts +9 -1
  6. package/lib/dist/common/shape-history-tracker.js +37 -23
  7. package/lib/dist/core/2d/aline.d.ts +13 -13
  8. package/lib/dist/core/2d/aline.js +20 -11
  9. package/lib/dist/core/2d/arc.d.ts +6 -6
  10. package/lib/dist/core/2d/arc.js +19 -15
  11. package/lib/dist/core/2d/back.d.ts +12 -0
  12. package/lib/dist/core/2d/back.js +11 -0
  13. package/lib/dist/core/2d/circle.d.ts +2 -2
  14. package/lib/dist/core/2d/circle.js +14 -10
  15. package/lib/dist/core/2d/ellipse.d.ts +35 -0
  16. package/lib/dist/core/2d/ellipse.js +65 -0
  17. package/lib/dist/core/2d/hline.d.ts +20 -13
  18. package/lib/dist/core/2d/hline.js +33 -15
  19. package/lib/dist/core/2d/index.d.ts +2 -0
  20. package/lib/dist/core/2d/index.js +2 -0
  21. package/lib/dist/core/2d/intersect.d.ts +2 -2
  22. package/lib/dist/core/2d/intersect.js +7 -3
  23. package/lib/dist/core/2d/line.d.ts +2 -2
  24. package/lib/dist/core/2d/line.js +14 -10
  25. package/lib/dist/core/2d/offset.d.ts +4 -4
  26. package/lib/dist/core/2d/offset.js +9 -5
  27. package/lib/dist/core/2d/polygon.d.ts +4 -4
  28. package/lib/dist/core/2d/polygon.js +24 -20
  29. package/lib/dist/core/2d/project.d.ts +2 -2
  30. package/lib/dist/core/2d/project.js +7 -3
  31. package/lib/dist/core/2d/rect.d.ts +2 -2
  32. package/lib/dist/core/2d/rect.js +22 -21
  33. package/lib/dist/core/2d/slot.d.ts +6 -6
  34. package/lib/dist/core/2d/slot.js +29 -32
  35. package/lib/dist/core/2d/vline.d.ts +20 -13
  36. package/lib/dist/core/2d/vline.js +29 -15
  37. package/lib/dist/core/interfaces.d.ts +62 -0
  38. package/lib/dist/core/mirror.d.ts +7 -7
  39. package/lib/dist/core/mirror.js +17 -11
  40. package/lib/dist/core/part.d.ts +3 -1
  41. package/lib/dist/core/part.js +1 -1
  42. package/lib/dist/core/rotate.d.ts +5 -5
  43. package/lib/dist/core/rotate.js +4 -1
  44. package/lib/dist/core/sketch.d.ts +3 -1
  45. package/lib/dist/core/sketch.js +1 -1
  46. package/lib/dist/core/translate.d.ts +9 -9
  47. package/lib/dist/features/2d/aline.d.ts +8 -5
  48. package/lib/dist/features/2d/aline.js +70 -18
  49. package/lib/dist/features/2d/back.d.ts +14 -0
  50. package/lib/dist/features/2d/back.js +35 -0
  51. package/lib/dist/features/2d/ellipse.d.ts +23 -0
  52. package/lib/dist/features/2d/ellipse.js +75 -0
  53. package/lib/dist/features/2d/hline.d.ts +9 -4
  54. package/lib/dist/features/2d/hline.js +65 -14
  55. package/lib/dist/features/2d/offset.d.ts +3 -0
  56. package/lib/dist/features/2d/offset.js +27 -3
  57. package/lib/dist/features/2d/sketch.d.ts +1 -0
  58. package/lib/dist/features/2d/sketch.js +15 -0
  59. package/lib/dist/features/2d/vline.d.ts +9 -4
  60. package/lib/dist/features/2d/vline.js +67 -15
  61. package/lib/dist/features/common.js +2 -1
  62. package/lib/dist/features/extrude-base.d.ts +19 -1
  63. package/lib/dist/features/extrude-base.js +75 -12
  64. package/lib/dist/features/extrude-two-distances.js +32 -27
  65. package/lib/dist/features/extrude.d.ts +39 -0
  66. package/lib/dist/features/extrude.js +196 -156
  67. package/lib/dist/features/fuse.js +2 -1
  68. package/lib/dist/features/lazy-scene-object.d.ts +1 -0
  69. package/lib/dist/features/lazy-scene-object.js +3 -0
  70. package/lib/dist/features/lazy-vertex.d.ts +1 -0
  71. package/lib/dist/features/lazy-vertex.js +3 -0
  72. package/lib/dist/features/loft.js +11 -8
  73. package/lib/dist/features/mirror-shape.d.ts +2 -0
  74. package/lib/dist/features/mirror-shape.js +16 -0
  75. package/lib/dist/features/mirror-shape2d.d.ts +2 -0
  76. package/lib/dist/features/mirror-shape2d.js +22 -1
  77. package/lib/dist/features/revolve.d.ts +31 -0
  78. package/lib/dist/features/revolve.js +178 -95
  79. package/lib/dist/features/rotate.d.ts +2 -0
  80. package/lib/dist/features/rotate.js +16 -0
  81. package/lib/dist/features/rotate2d.d.ts +2 -0
  82. package/lib/dist/features/rotate2d.js +16 -0
  83. package/lib/dist/features/select.js +2 -1
  84. package/lib/dist/features/simple-extruder.d.ts +3 -1
  85. package/lib/dist/features/simple-extruder.js +13 -9
  86. package/lib/dist/features/subtract.d.ts +2 -2
  87. package/lib/dist/features/subtract.js +3 -3
  88. package/lib/dist/features/sweep.d.ts +14 -0
  89. package/lib/dist/features/sweep.js +93 -80
  90. package/lib/dist/features/translate.d.ts +2 -0
  91. package/lib/dist/features/translate.js +23 -2
  92. package/lib/dist/filters/edge/edge-filter.d.ts +6 -0
  93. package/lib/dist/filters/edge/edge-filter.js +11 -0
  94. package/lib/dist/filters/face/face-filter.d.ts +6 -0
  95. package/lib/dist/filters/face/face-filter.js +11 -0
  96. package/lib/dist/filters/filter-base.d.ts +7 -1
  97. package/lib/dist/filters/filter-base.js +8 -0
  98. package/lib/dist/filters/filter-builder-base.js +11 -0
  99. package/lib/dist/filters/from-object.d.ts +14 -0
  100. package/lib/dist/filters/from-object.js +40 -0
  101. package/lib/dist/helpers/scene-helpers.d.ts +2 -0
  102. package/lib/dist/helpers/scene-helpers.js +68 -48
  103. package/lib/dist/oc/color-transfer.js +6 -0
  104. package/lib/dist/oc/edge-ops.d.ts +1 -0
  105. package/lib/dist/oc/edge-ops.js +17 -0
  106. package/lib/dist/oc/extrude-ops.d.ts +18 -1
  107. package/lib/dist/oc/extrude-ops.js +34 -1
  108. package/lib/dist/oc/geometry.d.ts +1 -0
  109. package/lib/dist/oc/geometry.js +27 -0
  110. package/lib/dist/oc/mesh.js +11 -9
  111. package/lib/dist/oc/ray-intersect.d.ts +16 -0
  112. package/lib/dist/oc/ray-intersect.js +91 -0
  113. package/lib/dist/oc/thin-face-maker.d.ts +0 -1
  114. package/lib/dist/oc/thin-face-maker.js +2 -20
  115. package/lib/dist/rendering/render.d.ts +2 -1
  116. package/lib/dist/rendering/render.js +72 -33
  117. package/lib/dist/rendering/scene.d.ts +4 -0
  118. package/lib/dist/tests/features/2d/back.test.d.ts +1 -0
  119. package/lib/dist/tests/features/2d/back.test.js +60 -0
  120. package/lib/dist/tests/features/2d/circle.test.js +1 -1
  121. package/lib/dist/tests/features/2d/constrained.test.js +4 -4
  122. package/lib/dist/tests/features/2d/ellipse.test.d.ts +1 -0
  123. package/lib/dist/tests/features/2d/ellipse.test.js +100 -0
  124. package/lib/dist/tests/features/2d/line.test.js +89 -3
  125. package/lib/dist/tests/features/2d/offset.test.js +1 -1
  126. package/lib/dist/tests/features/2d/polygon.test.js +2 -2
  127. package/lib/dist/tests/features/2d/rect.test.js +1 -1
  128. package/lib/dist/tests/features/2d/slot-from-edge.test.js +1 -1
  129. package/lib/dist/tests/features/2d/slot.test.js +1 -1
  130. package/lib/dist/tests/features/mirror.test.js +58 -0
  131. package/lib/dist/tests/features/mirror2d.test.js +63 -0
  132. package/lib/dist/tests/features/rotate.test.js +62 -0
  133. package/lib/dist/tests/features/rotate2d.test.js +47 -0
  134. package/lib/dist/tests/features/thin-revolve.test.js +37 -1
  135. package/lib/dist/tests/features/translate.test.js +63 -0
  136. package/lib/dist/tests/perf/record-fusion-history.bench.test.d.ts +1 -0
  137. package/lib/dist/tests/perf/record-fusion-history.bench.test.js +77 -0
  138. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  139. package/package.json +1 -1
  140. package/server/dist/index.js +77 -45
  141. package/server/dist/ws-protocol.d.ts +11 -0
  142. package/ui/dist/assets/{index-BrW_x4uc.js → index-6Ep4GPxf.js} +131 -77
  143. package/ui/dist/assets/index-DRKfe6N9.css +2 -0
  144. package/ui/dist/index.html +2 -2
  145. package/ui/dist/assets/index-gPoNOiIs.css +0 -2
@@ -9,27 +9,45 @@ import { resolvePlane } from "../../helpers/resolve.js";
9
9
  function build(context) {
10
10
  return function line() {
11
11
  let planeObj = null;
12
- let argCount = arguments.length;
13
- // Detect plane as last argument
14
- if (argCount > 0) {
15
- const lastArg = arguments[argCount - 1];
16
- if (isPlaneLike(lastArg) || (lastArg instanceof SceneObject && !isPoint2DLike(lastArg))) {
17
- planeObj = resolvePlane(lastArg, context);
18
- argCount--;
12
+ let argOffset = 0;
13
+ const inSketch = context.getActiveSketch() !== null;
14
+ // Detect plane as first argument (only valid outside a sketch).
15
+ // Inside a sketch, a SceneObject in the first position is a target geometry,
16
+ // and a true PlaneLike is an error since drawing on another plane mid-sketch
17
+ // is not supported.
18
+ if (arguments.length > 0) {
19
+ const firstArg = arguments[0];
20
+ if (isPlaneLike(firstArg)) {
21
+ if (inSketch) {
22
+ throw new Error("hLine(plane, ...) cannot be used inside a sketch. Use hLine(...) instead.");
23
+ }
24
+ planeObj = resolvePlane(firstArg, context);
25
+ argOffset = 1;
19
26
  }
27
+ else if (!inSketch && firstArg instanceof SceneObject && !isPoint2DLike(firstArg)) {
28
+ planeObj = resolvePlane(firstArg, context);
29
+ argOffset = 1;
30
+ }
31
+ }
32
+ if (argOffset === 0 && inSketch && arguments[0] instanceof SceneObject && !isPoint2DLike(arguments[0])) {
33
+ // hLine(target)
34
+ const hline = new HorizontalLine(arguments[0], null);
35
+ context.addSceneObject(hline);
36
+ return hline;
20
37
  }
21
- if (typeof arguments[0] !== 'number') {
22
- // hline(start, distance) or hline(start, distance, centered)
38
+ if (argOffset === 0 && typeof arguments[0] !== 'number') {
39
+ // hLine(start, distance) or hLine(start, target)
23
40
  const start = normalizePoint2D(arguments[0]);
24
- const distance = arguments[1];
25
- const centered = argCount >= 3 ? arguments[2] : false;
26
- const hline = new HorizontalLine(distance, centered, planeObj);
41
+ const second = arguments[1];
42
+ const distanceOrTarget = second instanceof SceneObject
43
+ ? second
44
+ : second;
45
+ const hline = new HorizontalLine(distanceOrTarget, planeObj);
27
46
  context.addSceneObjects([new Move(start), hline]);
28
47
  return hline;
29
48
  }
30
- const distance = arguments[0];
31
- const centered = argCount >= 2 ? arguments[1] : false;
32
- const hline = new HorizontalLine(distance, centered, planeObj);
49
+ const distance = arguments[argOffset];
50
+ const hline = new HorizontalLine(distance, planeObj);
33
51
  context.addSceneObject(hline);
34
52
  return hline;
35
53
  };
@@ -1,5 +1,6 @@
1
1
  export { default as line } from './line.js';
2
2
  export { default as circle } from './circle.js';
3
+ export { default as ellipse } from './ellipse.js';
3
4
  export { default as rect } from './rect.js';
4
5
  export { default as hMove } from './hmove.js';
5
6
  export { default as vMove } from './vmove.js';
@@ -21,3 +22,4 @@ export { default as project } from './project.js';
21
22
  export { default as intersect } from './intersect.js';
22
23
  export { default as bezier } from './bezier.js';
23
24
  export { default as center } from './center.js';
25
+ export { default as back } from './back.js';
@@ -1,5 +1,6 @@
1
1
  export { default as line } from './line.js';
2
2
  export { default as circle } from './circle.js';
3
+ export { default as ellipse } from './ellipse.js';
3
4
  export { default as rect } from './rect.js';
4
5
  export { default as hMove } from './hmove.js';
5
6
  export { default as vMove } from './vmove.js';
@@ -21,3 +22,4 @@ export { default as project } from './project.js';
21
22
  export { default as intersect } from './intersect.js';
22
23
  export { default as bezier } from './bezier.js';
23
24
  export { default as center } from './center.js';
25
+ export { default as back } from './back.js';
@@ -8,10 +8,10 @@ interface IntersectFunction {
8
8
  (...sourceObjects: ISceneObject[]): IExtrudableGeometry;
9
9
  /**
10
10
  * Intersects 3D objects with a target plane, producing cross-section edges.
11
- * @param sourceObjects - The 3D objects to intersect
12
11
  * @param targetPlane - The plane to intersect with
12
+ * @param sourceObjects - The 3D objects to intersect
13
13
  */
14
- (sourceObjects: ISceneObject[], targetPlane: PlaneLike | ISceneObject): IExtrudableGeometry;
14
+ (targetPlane: PlaneLike | ISceneObject, sourceObjects: ISceneObject[]): IExtrudableGeometry;
15
15
  }
16
16
  declare const _default: IntersectFunction;
17
17
  export default _default;
@@ -3,10 +3,14 @@ import { resolvePlane } from "../../helpers/resolve.js";
3
3
  import { registerBuilder } from "../../index.js";
4
4
  function build(context) {
5
5
  return function intersect(...args) {
6
- if (Array.isArray(args[0])) {
7
- const sourceObjects = args[0];
6
+ // Plane-first mode: intersect(plane, sources[])
7
+ if (args.length === 2 && Array.isArray(args[1])) {
8
+ if (context.getActiveSketch() !== null) {
9
+ throw new Error("intersect(plane, sources[]) cannot be used inside a sketch. Use intersect(...sources) instead.");
10
+ }
11
+ const planeObj = resolvePlane(args[0], context);
12
+ const sourceObjects = args[1];
8
13
  context.addSceneObjects(sourceObjects);
9
- const planeObj = resolvePlane(args[1], context);
10
14
  const result = new Intersect(sourceObjects, planeObj);
11
15
  context.addSceneObject(result);
12
16
  return result;
@@ -15,10 +15,10 @@ interface LineFunction {
15
15
  (start: Point2DLike, end: Point2DLike): IGeometry;
16
16
  /**
17
17
  * Draws a line to the given point on a specific plane.
18
- * @param end - The end point
19
18
  * @param targetPlane - The plane to draw on
19
+ * @param end - The end point
20
20
  */
21
- (end: Point2DLike, targetPlane: PlaneLike | ISceneObject): IGeometry;
21
+ (targetPlane: PlaneLike | ISceneObject, end: Point2DLike): IGeometry;
22
22
  }
23
23
  declare const _default: LineFunction;
24
24
  export default _default;
@@ -10,24 +10,28 @@ function build(context) {
10
10
  return function line() {
11
11
  let line;
12
12
  let planeObj = null;
13
- let argCount = arguments.length;
14
- // Detect plane as last argument
13
+ let argOffset = 0;
14
+ // Detect plane as first argument (only valid outside a sketch)
15
15
  // Point2DLike is not plane-like so no conflict
16
- if (argCount > 0) {
17
- const lastArg = arguments[argCount - 1];
18
- if (isPlaneLike(lastArg) || (lastArg instanceof SceneObject && !isPoint2DLike(lastArg))) {
19
- planeObj = resolvePlane(lastArg, context);
20
- argCount--;
16
+ if (arguments.length > 0) {
17
+ const firstArg = arguments[0];
18
+ if (isPlaneLike(firstArg) || (firstArg instanceof SceneObject && !isPoint2DLike(firstArg))) {
19
+ if (context.getActiveSketch() !== null) {
20
+ throw new Error("line(plane, ...) cannot be used inside a sketch. Use line(...) instead.");
21
+ }
22
+ planeObj = resolvePlane(firstArg, context);
23
+ argOffset = 1;
21
24
  }
22
25
  }
26
+ const argCount = arguments.length - argOffset;
23
27
  if (argCount === 1) {
24
- const vertex = normalizePoint2D(arguments[0]);
28
+ const vertex = normalizePoint2D(arguments[argOffset]);
25
29
  line = new LineTo(vertex, planeObj);
26
30
  context.addSceneObject(line);
27
31
  }
28
32
  else if (argCount === 2) {
29
- const start = normalizePoint2D(arguments[0]);
30
- const end = normalizePoint2D(arguments[1]);
33
+ const start = normalizePoint2D(arguments[argOffset]);
34
+ const end = normalizePoint2D(arguments[argOffset + 1]);
31
35
  line = new LineTo(end, planeObj);
32
36
  context.addSceneObjects([new Move(start), line]);
33
37
  }
@@ -1,5 +1,5 @@
1
1
  import { PlaneLike } from "../../math/plane.js";
2
- import { IExtrudableGeometry, ISceneObject } from "../interfaces.js";
2
+ import { IOffset, ISceneObject } from "../interfaces.js";
3
3
  import { Extrudable } from "../../helpers/types.js";
4
4
  interface OffsetFunction {
5
5
  /**
@@ -7,15 +7,15 @@ interface OffsetFunction {
7
7
  * @param distance - The offset distance (defaults to 1)
8
8
  * @param removeOriginal - Whether to remove the original geometry
9
9
  */
10
- (distance?: number, removeOriginal?: boolean): IExtrudableGeometry;
10
+ (distance?: number, removeOriginal?: boolean): IOffset;
11
11
  /**
12
12
  * Offsets source geometries onto a target plane.
13
+ * @param targetPlane - The plane to offset onto
13
14
  * @param distance - The offset distance
14
15
  * @param removeOriginal - Whether to remove the original geometry
15
- * @param targetPlane - The plane to offset onto
16
16
  * @param sourceGeometries - The geometries to offset
17
17
  */
18
- (distance: number, removeOriginal: boolean, targetPlane: PlaneLike | ISceneObject, ...sourceGeometries: Extrudable[]): IExtrudableGeometry;
18
+ (targetPlane: PlaneLike | ISceneObject, distance: number, removeOriginal: boolean, ...sourceGeometries: Extrudable[]): IOffset;
19
19
  }
20
20
  declare const _default: OffsetFunction;
21
21
  export default _default;
@@ -3,11 +3,15 @@ import { registerBuilder } from "../../index.js";
3
3
  import { resolvePlane } from "../../helpers/resolve.js";
4
4
  function build(context) {
5
5
  return function offset(...args) {
6
- // Outside-sketch mode: offset(distance, removeOriginal, plane, ...sourceGeometries)
7
- if (args.length >= 3 && !Array.isArray(args[0])) {
8
- const distance = args[0] ?? 1;
9
- const removeOriginal = args[1] ?? false;
10
- const planeObj = resolvePlane(args[2], context);
6
+ // Plane-first mode: offset(plane, distance, removeOriginal, ...sourceGeometries)
7
+ // Detected when first arg is not a number/undefined.
8
+ if (args.length > 0 && args[0] !== undefined && typeof args[0] !== 'number' && typeof args[0] !== 'boolean') {
9
+ if (context.getActiveSketch() !== null) {
10
+ throw new Error("offset(plane, ...) cannot be used inside a sketch. Use offset(...) instead.");
11
+ }
12
+ const planeObj = resolvePlane(args[0], context);
13
+ const distance = args[1] ?? 1;
14
+ const removeOriginal = args[2] ?? false;
11
15
  const sourceObjects = args.slice(3);
12
16
  const off = new Offset(distance, removeOriginal, sourceObjects, planeObj);
13
17
  context.addSceneObject(off);
@@ -20,19 +20,19 @@ interface PolygonFunction {
20
20
  (center: Point2DLike, numberOfSides: number, diameter: number, mode?: PolygonMode): IPolygon;
21
21
  /**
22
22
  * Draws a regular polygon on a specific plane.
23
+ * @param targetPlane - The plane to draw on
23
24
  * @param numberOfSides - The number of sides
24
25
  * @param diameter - The circumscribed or inscribed diameter
25
- * @param targetPlane - The plane to draw on
26
26
  */
27
- (numberOfSides: number, diameter: number, targetPlane: PlaneLike | ISceneObject): IPolygon;
27
+ (targetPlane: PlaneLike | ISceneObject, numberOfSides: number, diameter: number): IPolygon;
28
28
  /**
29
29
  * Draws a regular polygon with a given mode on a specific plane.
30
+ * @param targetPlane - The plane to draw on
30
31
  * @param numberOfSides - The number of sides
31
32
  * @param diameter - The circumscribed or inscribed diameter
32
33
  * @param mode - `'inscribed'` or `'circumscribed'`
33
- * @param targetPlane - The plane to draw on
34
34
  */
35
- (numberOfSides: number, diameter: number, mode: PolygonMode, targetPlane: PlaneLike | ISceneObject): IPolygon;
35
+ (targetPlane: PlaneLike | ISceneObject, numberOfSides: number, diameter: number, mode: PolygonMode): IPolygon;
36
36
  }
37
37
  declare const _default: PolygonFunction;
38
38
  export default _default;
@@ -14,46 +14,50 @@ function build(context) {
14
14
  let center;
15
15
  let poly;
16
16
  let planeObj = null;
17
- let argCount = arguments.length;
18
- // Detect plane as last argument
17
+ let argOffset = 0;
18
+ // Detect plane as first argument (only valid outside a sketch)
19
19
  // PolygonMode strings ('inscribed'/'circumscribed') don't overlap with StandardPlane strings
20
- if (argCount > 0) {
21
- const lastArg = arguments[argCount - 1];
22
- if (isPlaneLike(lastArg) || (lastArg instanceof SceneObject && !isPoint2DLike(lastArg))) {
23
- planeObj = resolvePlane(lastArg, context);
20
+ if (arguments.length > 0) {
21
+ const firstArg = arguments[0];
22
+ if (isPlaneLike(firstArg) || (firstArg instanceof SceneObject && !isPoint2DLike(firstArg))) {
23
+ if (context.getActiveSketch() !== null) {
24
+ throw new Error("polygon(plane, ...) cannot be used inside a sketch. Use polygon(...) instead.");
25
+ }
26
+ planeObj = resolvePlane(firstArg, context);
24
27
  context.addSceneObject(planeObj);
25
- argCount--;
28
+ argOffset = 1;
26
29
  }
27
30
  }
31
+ const argCount = arguments.length - argOffset;
28
32
  if (argCount === 2) {
29
- numberOfSides = arguments[0];
30
- diameter = arguments[1];
33
+ numberOfSides = arguments[argOffset];
34
+ diameter = arguments[argOffset + 1];
31
35
  mode = 'inscribed';
32
36
  poly = new Polygon(numberOfSides, diameter, mode, planeObj);
33
37
  context.addSceneObject(poly);
34
38
  }
35
39
  else if (argCount === 3) {
36
- if (typeof arguments[0] === 'number') {
37
- numberOfSides = arguments[0];
38
- diameter = arguments[1];
39
- mode = arguments[2];
40
+ if (typeof arguments[argOffset] === 'number') {
41
+ numberOfSides = arguments[argOffset];
42
+ diameter = arguments[argOffset + 1];
43
+ mode = arguments[argOffset + 2];
40
44
  poly = new Polygon(numberOfSides, diameter, mode, planeObj);
41
45
  context.addSceneObject(poly);
42
46
  }
43
47
  else {
44
- center = normalizePoint2D(arguments[0]);
45
- numberOfSides = arguments[1];
46
- diameter = arguments[2];
48
+ center = normalizePoint2D(arguments[argOffset]);
49
+ numberOfSides = arguments[argOffset + 1];
50
+ diameter = arguments[argOffset + 2];
47
51
  mode = 'inscribed';
48
52
  poly = new Polygon(numberOfSides, diameter, mode, planeObj);
49
53
  context.addSceneObjects([new Move(center), poly]);
50
54
  }
51
55
  }
52
56
  else if (argCount === 4) {
53
- center = normalizePoint2D(arguments[0]);
54
- numberOfSides = arguments[1];
55
- diameter = arguments[2];
56
- mode = arguments[3];
57
+ center = normalizePoint2D(arguments[argOffset]);
58
+ numberOfSides = arguments[argOffset + 1];
59
+ diameter = arguments[argOffset + 2];
60
+ mode = arguments[argOffset + 3];
57
61
  poly = new Polygon(numberOfSides, diameter, mode, planeObj);
58
62
  context.addSceneObjects([new Move(center), poly]);
59
63
  }
@@ -8,10 +8,10 @@ interface ProjectFunction {
8
8
  (...sourceObjects: ISceneObject[]): IExtrudableGeometry;
9
9
  /**
10
10
  * Projects 3D objects onto a target plane.
11
- * @param sourceObjects - The 3D objects to project
12
11
  * @param targetPlane - The plane to project onto
12
+ * @param sourceObjects - The 3D objects to project
13
13
  */
14
- (sourceObjects: ISceneObject[], targetPlane: PlaneLike | ISceneObject): IExtrudableGeometry;
14
+ (targetPlane: PlaneLike | ISceneObject, sourceObjects: ISceneObject[]): IExtrudableGeometry;
15
15
  }
16
16
  declare const _default: ProjectFunction;
17
17
  export default _default;
@@ -3,10 +3,14 @@ import { resolvePlane } from "../../helpers/resolve.js";
3
3
  import { registerBuilder } from "../../index.js";
4
4
  function build(context) {
5
5
  return function project(...args) {
6
- if (Array.isArray(args[0])) {
7
- const sourceObjects = args[0];
6
+ // Plane-first mode: project(plane, sources[])
7
+ if (args.length === 2 && Array.isArray(args[1])) {
8
+ if (context.getActiveSketch() !== null) {
9
+ throw new Error("project(plane, sources[]) cannot be used inside a sketch. Use project(...sources) instead.");
10
+ }
11
+ const planeObj = resolvePlane(args[0], context);
12
+ const sourceObjects = args[1];
8
13
  context.addSceneObjects(sourceObjects);
9
- const planeObj = resolvePlane(args[1], context);
10
14
  const projection = new Projection(sourceObjects, planeObj);
11
15
  context.addSceneObject(projection);
12
16
  return projection;
@@ -17,11 +17,11 @@ interface RectFunction {
17
17
  (start: Point2DLike, width: number, height?: number): IRect;
18
18
  /**
19
19
  * Draws a rectangle with given dimensions on a specific plane.
20
+ * @param targetPlane - The plane to draw on
20
21
  * @param width - The rectangle width
21
22
  * @param height - The rectangle height
22
- * @param targetPlane - The plane to draw on
23
23
  */
24
- (width: number, height: number, targetPlane: PlaneLike | ISceneObject): IRect;
24
+ (targetPlane: PlaneLike | ISceneObject, width: number, height: number): IRect;
25
25
  }
26
26
  declare const _default: RectFunction;
27
27
  export default _default;
@@ -8,7 +8,22 @@ import { SceneObject } from "../../common/scene-object.js";
8
8
  import { resolvePlane } from "../../helpers/resolve.js";
9
9
  function build(context) {
10
10
  return function cRect() {
11
- let argCount = arguments.length;
11
+ // Detect plane as first argument (only valid outside a sketch)
12
+ if (arguments.length > 0) {
13
+ const firstArg = arguments[0];
14
+ if (isPlaneLike(firstArg) || (firstArg instanceof SceneObject && !isPoint2DLike(firstArg))) {
15
+ if (context.getActiveSketch() !== null) {
16
+ throw new Error("rect(plane, ...) cannot be used inside a sketch. Use rect(...) instead.");
17
+ }
18
+ const planeObj = resolvePlane(firstArg, context);
19
+ const width = arguments[1];
20
+ const height = arguments[2];
21
+ const rect = new Rect(width, height, planeObj);
22
+ context.addSceneObject(rect);
23
+ return rect;
24
+ }
25
+ }
26
+ const argCount = arguments.length;
12
27
  if (argCount === 1) {
13
28
  const width = arguments[0];
14
29
  const rect = new Rect(width, width);
@@ -32,26 +47,12 @@ function build(context) {
32
47
  }
33
48
  }
34
49
  else if (argCount === 3) {
35
- if (typeof arguments[0] === 'number') {
36
- const width = arguments[0];
37
- const height = arguments[1];
38
- const lastArg = arguments[argCount - 1];
39
- let planeObj;
40
- if (isPlaneLike(lastArg) || (lastArg instanceof SceneObject && !isPoint2DLike(lastArg))) {
41
- planeObj = resolvePlane(lastArg, context);
42
- }
43
- const rect = new Rect(width, height, planeObj);
44
- context.addSceneObject(rect);
45
- return rect;
46
- }
47
- else {
48
- const start = normalizePoint2D(arguments[0]);
49
- const width = arguments[1];
50
- const height = arguments[2];
51
- const rect = new Rect(width, height);
52
- context.addSceneObjects([new Move(start), rect]);
53
- return rect;
54
- }
50
+ const start = normalizePoint2D(arguments[0]);
51
+ const width = arguments[1];
52
+ const height = arguments[2];
53
+ const rect = new Rect(width, height);
54
+ context.addSceneObjects([new Move(start), rect]);
55
+ return rect;
55
56
  }
56
57
  };
57
58
  }
@@ -10,11 +10,11 @@ interface SlotFunction {
10
10
  (distance: number, radius: number): ISlot;
11
11
  /**
12
12
  * Draws a slot on a specific plane.
13
+ * @param targetPlane - The plane to draw on
13
14
  * @param distance - The slot length
14
15
  * @param radius - The end cap radius
15
- * @param targetPlane - The plane to draw on
16
16
  */
17
- (distance: number, radius: number, targetPlane: PlaneLike | ISceneObject): ISlot;
17
+ (targetPlane: PlaneLike | ISceneObject, distance: number, radius: number): ISlot;
18
18
  /**
19
19
  * Draws a slot from a start point with the given length and end radius.
20
20
  * @param start - The start point
@@ -31,19 +31,19 @@ interface SlotFunction {
31
31
  (geometry: ISceneObject, radius: number, deleteSource?: boolean): ISlot;
32
32
  /**
33
33
  * Creates a slot from a geometry edge on a specific plane.
34
+ * @param targetPlane - The plane to draw on
34
35
  * @param geometry - The source geometry edge
35
36
  * @param radius - The end cap radius
36
- * @param targetPlane - The plane to draw on
37
37
  */
38
- (geometry: ISceneObject, radius: number, targetPlane: PlaneLike | ISceneObject): ISlot;
38
+ (targetPlane: PlaneLike | ISceneObject, geometry: ISceneObject, radius: number): ISlot;
39
39
  /**
40
40
  * Creates a slot from a geometry edge, optionally keeping the source, on a specific plane.
41
+ * @param targetPlane - The plane to draw on
41
42
  * @param geometry - The source geometry edge
42
43
  * @param radius - The end cap radius
43
44
  * @param deleteSource - Whether to delete the source geometry
44
- * @param targetPlane - The plane to draw on
45
45
  */
46
- (geometry: ISceneObject, radius: number, deleteSource: boolean, targetPlane: PlaneLike | ISceneObject): ISlot;
46
+ (targetPlane: PlaneLike | ISceneObject, geometry: ISceneObject, radius: number, deleteSource: boolean): ISlot;
47
47
  }
48
48
  declare const _default: SlotFunction;
49
49
  export default _default;
@@ -9,49 +9,46 @@ import { isPlaneLike } from "../../math/plane.js";
9
9
  import { resolvePlane } from "../../helpers/resolve.js";
10
10
  function build(context) {
11
11
  return function slot() {
12
- // SlotFromEdge path: first arg is a SceneObject (geometry)
13
- if (arguments[0] instanceof SceneObject) {
14
- const geometry = arguments[0];
15
- const radius = arguments[1];
16
- let deleteSource = true;
17
- let planeObj = null;
18
- let argIdx = 2;
19
- // Check if there's a deleteSource boolean before a potential plane
20
- if (argIdx < arguments.length && typeof arguments[argIdx] === 'boolean') {
21
- deleteSource = arguments[argIdx];
22
- argIdx++;
23
- }
24
- // Check if there's a plane arg remaining
25
- if (argIdx < arguments.length) {
26
- const planeArg = arguments[argIdx];
27
- if (isPlaneLike(planeArg) || planeArg instanceof SceneObject) {
28
- planeObj = resolvePlane(planeArg, context);
12
+ const inSketch = context.getActiveSketch() !== null;
13
+ // Detect plane as first argument (only valid outside a sketch).
14
+ // Inside a sketch, a SceneObject at position 0 means SlotFromEdge geometry, not plane.
15
+ let planeObj = null;
16
+ let argOffset = 0;
17
+ if (arguments.length > 0) {
18
+ const firstArg = arguments[0];
19
+ const looksLikePlane = isPlaneLike(firstArg) ||
20
+ (firstArg instanceof SceneObject && !isPoint2DLike(firstArg) && !inSketch);
21
+ if (looksLikePlane) {
22
+ if (inSketch) {
23
+ throw new Error("slot(plane, ...) cannot be used inside a sketch. Use slot(...) instead.");
29
24
  }
25
+ planeObj = resolvePlane(firstArg, context);
26
+ argOffset = 1;
27
+ }
28
+ }
29
+ // SlotFromEdge path: first non-plane arg is a SceneObject (geometry)
30
+ if (arguments[argOffset] instanceof SceneObject && !isPoint2DLike(arguments[argOffset])) {
31
+ const geometry = arguments[argOffset];
32
+ const radius = arguments[argOffset + 1];
33
+ let deleteSource = true;
34
+ if (arguments.length > argOffset + 2 && typeof arguments[argOffset + 2] === 'boolean') {
35
+ deleteSource = arguments[argOffset + 2];
30
36
  }
31
37
  const slotFromEdge = new SlotFromEdge(geometry, radius, deleteSource, planeObj);
32
38
  context.addSceneObject(slotFromEdge);
33
39
  return slotFromEdge;
34
40
  }
35
- let planeObj = null;
36
- let argCount = arguments.length;
37
- // Detect plane as last argument
38
- if (argCount > 0) {
39
- const lastArg = arguments[argCount - 1];
40
- if (isPlaneLike(lastArg) || (lastArg instanceof SceneObject && !isPoint2DLike(lastArg))) {
41
- planeObj = resolvePlane(lastArg, context);
42
- argCount--;
43
- }
44
- }
41
+ const argCount = arguments.length - argOffset;
45
42
  // slot(distance, radius)
46
- if (argCount === 2 && typeof arguments[0] === 'number') {
47
- const distance = arguments[0];
48
- const radius = arguments[1];
43
+ if (argCount === 2 && typeof arguments[argOffset] === 'number') {
44
+ const distance = arguments[argOffset];
45
+ const radius = arguments[argOffset + 1];
49
46
  const s = new Slot(distance, radius, planeObj);
50
47
  context.addSceneObject(s);
51
48
  return s;
52
49
  }
53
- // slot(start, distance, radius)
54
- if (argCount === 3) {
50
+ // slot(start, distance, radius) — in-sketch only
51
+ if (argCount === 3 && argOffset === 0) {
55
52
  const start = normalizePoint2D(arguments[0]);
56
53
  const distance = arguments[1];
57
54
  const radius = arguments[2];
@@ -1,33 +1,40 @@
1
1
  import { Point2DLike } from "../../math/point.js";
2
2
  import { PlaneLike } from "../../math/plane.js";
3
- import { IGeometry, ISceneObject } from "../interfaces.js";
3
+ import { IVLine, ISceneObject } from "../interfaces.js";
4
4
  interface VLineFunction {
5
5
  /**
6
6
  * Draws a vertical line of the given distance.
7
+ * Chain `.centered()` to center the line on the current position.
7
8
  * @param distance - The line length
8
- * @param centered - Whether to center the line on the current position
9
9
  */
10
- (distance: number, centered?: boolean): IGeometry;
10
+ (distance: number): IVLine;
11
+ /**
12
+ * Draws a vertical line that ends where it intersects the target geometry.
13
+ * The nearest intersection (in either direction along the Y axis) is used.
14
+ * @param target - The geometry to intersect with
15
+ */
16
+ (target: ISceneObject): IVLine;
11
17
  /**
12
18
  * Draws a vertical line from a start point.
19
+ * Chain `.centered()` to center the line on the start point.
13
20
  * @param start - The start point
14
21
  * @param distance - The line length
15
- * @param centered - Whether to center the line on the start point
16
22
  */
17
- (start: Point2DLike, distance: number, centered?: boolean): IGeometry;
23
+ (start: Point2DLike, distance: number): IVLine;
18
24
  /**
19
- * Draws a vertical line on a specific plane.
20
- * @param distance - The line length
21
- * @param targetPlane - The plane to draw on
25
+ * Draws a vertical line from a start point that ends where it intersects
26
+ * the target geometry. The nearest intersection (in either direction along
27
+ * the Y axis) is used.
28
+ * @param start - The start point
29
+ * @param target - The geometry to intersect with
22
30
  */
23
- (distance: number, targetPlane: PlaneLike | ISceneObject): IGeometry;
31
+ (start: Point2DLike, target: ISceneObject): IVLine;
24
32
  /**
25
- * Draws a vertical line with centering on a specific plane.
26
- * @param distance - The line length
27
- * @param centered - Whether to center the line on the current position
33
+ * Draws a vertical line on a specific plane.
28
34
  * @param targetPlane - The plane to draw on
35
+ * @param distance - The line length
29
36
  */
30
- (distance: number, centered: boolean, targetPlane: PlaneLike | ISceneObject): IGeometry;
37
+ (targetPlane: PlaneLike | ISceneObject, distance: number): IVLine;
31
38
  }
32
39
  declare const _default: VLineFunction;
33
40
  export default _default;