fluidcad 0.0.38 → 0.0.39

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 (98) hide show
  1. package/lib/dist/core/2d/aline.d.ts +0 -8
  2. package/lib/dist/core/2d/aline.js +3 -22
  3. package/lib/dist/core/2d/arc.d.ts +1 -23
  4. package/lib/dist/core/2d/arc.js +11 -28
  5. package/lib/dist/core/2d/circle.d.ts +1 -8
  6. package/lib/dist/core/2d/circle.js +7 -24
  7. package/lib/dist/core/2d/ellipse.d.ts +1 -17
  8. package/lib/dist/core/2d/ellipse.js +7 -45
  9. package/lib/dist/core/2d/hline.d.ts +0 -7
  10. package/lib/dist/core/2d/hline.js +5 -27
  11. package/lib/dist/core/2d/intersect.d.ts +0 -7
  12. package/lib/dist/core/2d/intersect.js +0 -13
  13. package/lib/dist/core/2d/line.d.ts +1 -8
  14. package/lib/dist/core/2d/line.js +6 -24
  15. package/lib/dist/core/2d/offset.d.ts +1 -11
  16. package/lib/dist/core/2d/offset.js +0 -16
  17. package/lib/dist/core/2d/polygon.d.ts +1 -17
  18. package/lib/dist/core/2d/polygon.js +18 -37
  19. package/lib/dist/core/2d/project.d.ts +0 -7
  20. package/lib/dist/core/2d/project.js +0 -13
  21. package/lib/dist/core/2d/rect.d.ts +1 -9
  22. package/lib/dist/core/2d/rect.js +0 -19
  23. package/lib/dist/core/2d/slot.d.ts +0 -23
  24. package/lib/dist/core/2d/slot.js +18 -37
  25. package/lib/dist/core/2d/text.d.ts +0 -7
  26. package/lib/dist/core/2d/text.js +0 -14
  27. package/lib/dist/core/2d/vline.d.ts +0 -7
  28. package/lib/dist/core/2d/vline.js +5 -23
  29. package/lib/dist/core/fillet.js +7 -0
  30. package/lib/dist/core/index.d.ts +1 -1
  31. package/lib/dist/core/interfaces.d.ts +43 -0
  32. package/lib/dist/features/2d/connect.d.ts +2 -0
  33. package/lib/dist/features/2d/connect.js +3 -0
  34. package/lib/dist/features/2d/intersect.js +4 -1
  35. package/lib/dist/features/frozen-geometry.d.ts +29 -0
  36. package/lib/dist/features/frozen-geometry.js +62 -0
  37. package/lib/dist/features/loft.d.ts +38 -1
  38. package/lib/dist/features/loft.js +133 -8
  39. package/lib/dist/features/rib.js +11 -1
  40. package/lib/dist/features/shell.d.ts +1 -1
  41. package/lib/dist/features/shell.js +9 -0
  42. package/lib/dist/features/sweep.d.ts +11 -1
  43. package/lib/dist/features/sweep.js +43 -7
  44. package/lib/dist/helpers/clone-transform.js +62 -5
  45. package/lib/dist/helpers/scene-helpers.d.ts +12 -0
  46. package/lib/dist/helpers/scene-helpers.js +26 -0
  47. package/lib/dist/math/bspline-interpolation.d.ts +33 -6
  48. package/lib/dist/math/bspline-interpolation.js +187 -84
  49. package/lib/dist/oc/loft/constrained-loft.d.ts +32 -0
  50. package/lib/dist/oc/loft/constrained-loft.js +61 -0
  51. package/lib/dist/oc/loft/curve-data.d.ts +18 -0
  52. package/lib/dist/oc/loft/curve-data.js +54 -0
  53. package/lib/dist/oc/loft/curve-eval.d.ts +23 -0
  54. package/lib/dist/oc/loft/curve-eval.js +84 -0
  55. package/lib/dist/oc/loft/guided-loft.d.ts +89 -0
  56. package/lib/dist/oc/loft/guided-loft.js +368 -0
  57. package/lib/dist/oc/loft/section-compatibility.d.ts +131 -0
  58. package/lib/dist/oc/loft/section-compatibility.js +481 -0
  59. package/lib/dist/oc/loft/section-curve.d.ts +53 -0
  60. package/lib/dist/oc/loft/section-curve.js +264 -0
  61. package/lib/dist/oc/loft/skinning.d.ts +104 -0
  62. package/lib/dist/oc/loft/skinning.js +299 -0
  63. package/lib/dist/oc/loft-ops.d.ts +28 -1
  64. package/lib/dist/oc/loft-ops.js +31 -1
  65. package/lib/dist/oc/ncollection.d.ts +19 -0
  66. package/lib/dist/oc/ncollection.js +69 -0
  67. package/lib/dist/oc/rib-ops.js +5 -17
  68. package/lib/dist/oc/wire-extend-ops.d.ts +12 -0
  69. package/lib/dist/oc/wire-extend-ops.js +42 -0
  70. package/lib/dist/oc/wire-ops.d.ts +11 -0
  71. package/lib/dist/oc/wire-ops.js +33 -0
  72. package/lib/dist/oc/wrap-ops.js +8 -15
  73. package/lib/dist/tests/features/2d/circle.test.js +0 -10
  74. package/lib/dist/tests/features/2d/ellipse.test.js +0 -30
  75. package/lib/dist/tests/features/2d/line.test.js +0 -5
  76. package/lib/dist/tests/features/2d/offset.test.js +0 -10
  77. package/lib/dist/tests/features/2d/polygon.test.js +0 -16
  78. package/lib/dist/tests/features/2d/rect.test.js +0 -11
  79. package/lib/dist/tests/features/2d/slot.test.js +0 -8
  80. package/lib/dist/tests/features/fillet2d.test.js +37 -1
  81. package/lib/dist/tests/features/loft-conditions.test.d.ts +1 -0
  82. package/lib/dist/tests/features/loft-conditions.test.js +246 -0
  83. package/lib/dist/tests/features/loft-guides.test.d.ts +1 -0
  84. package/lib/dist/tests/features/loft-guides.test.js +276 -0
  85. package/lib/dist/tests/features/repeat-sweep-borrowed-path.test.d.ts +1 -0
  86. package/lib/dist/tests/features/repeat-sweep-borrowed-path.test.js +35 -0
  87. package/lib/dist/tests/features/rib.test.js +50 -0
  88. package/lib/dist/tests/features/shell.test.js +35 -1
  89. package/lib/dist/tests/features/sweep.test.js +81 -0
  90. package/lib/dist/tests/features/text.test.js +33 -14
  91. package/lib/dist/tests/math/bspline-interpolation.test.js +97 -1
  92. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  93. package/llm-docs/api/index.json +1 -1
  94. package/llm-docs/api/types/loft.md +36 -1
  95. package/llm-docs/api/types/sweep.md +13 -1
  96. package/llm-docs/api/types/text.md +2 -2
  97. package/llm-docs/index.json +5 -5
  98. package/package.json +1 -1
@@ -1,11 +1,7 @@
1
- import { isPoint2DLike } from "../../math/point.js";
2
1
  import { Polygon } from "../../features/2d/polygon.js";
3
2
  import { Move } from "../../features/2d/move.js";
4
3
  import { normalizePoint2D } from "../../helpers/normalize.js";
5
4
  import { registerBuilder } from "../../index.js";
6
- import { isPlaneLike } from "../../math/plane.js";
7
- import { SceneObject } from "../../common/scene-object.js";
8
- import { resolvePlane } from "../../helpers/resolve.js";
9
5
  import { isNumberParam, resolveParam } from "../param.js";
10
6
  function build(context) {
11
7
  return function polygon() {
@@ -14,52 +10,37 @@ function build(context) {
14
10
  let mode;
15
11
  let center;
16
12
  let poly;
17
- let planeObj = null;
18
- let argOffset = 0;
19
- // Detect plane as first argument (only valid outside a sketch)
20
- // PolygonMode strings ('inscribed'/'circumscribed') don't overlap with StandardPlane strings
21
- if (arguments.length > 0) {
22
- const firstArg = arguments[0];
23
- if (isPlaneLike(firstArg) || (firstArg instanceof SceneObject && !isPoint2DLike(firstArg))) {
24
- if (context.getActiveSketch() !== null) {
25
- throw new Error("polygon(plane, ...) cannot be used inside a sketch. Use polygon(...) instead.");
26
- }
27
- planeObj = resolvePlane(firstArg, context);
28
- context.addSceneObject(planeObj);
29
- argOffset = 1;
30
- }
31
- }
32
- const argCount = arguments.length - argOffset;
13
+ const argCount = arguments.length;
33
14
  if (argCount === 2) {
34
- numberOfSides = resolveParam(arguments[argOffset]);
35
- diameter = resolveParam(arguments[argOffset + 1]);
15
+ numberOfSides = resolveParam(arguments[0]);
16
+ diameter = resolveParam(arguments[1]);
36
17
  mode = 'inscribed';
37
- poly = new Polygon(numberOfSides, diameter, mode, planeObj);
18
+ poly = new Polygon(numberOfSides, diameter, mode, null);
38
19
  context.addSceneObject(poly);
39
20
  }
40
21
  else if (argCount === 3) {
41
- if (isNumberParam(arguments[argOffset])) {
42
- numberOfSides = resolveParam(arguments[argOffset]);
43
- diameter = resolveParam(arguments[argOffset + 1]);
44
- mode = arguments[argOffset + 2];
45
- poly = new Polygon(numberOfSides, diameter, mode, planeObj);
22
+ if (isNumberParam(arguments[0])) {
23
+ numberOfSides = resolveParam(arguments[0]);
24
+ diameter = resolveParam(arguments[1]);
25
+ mode = arguments[2];
26
+ poly = new Polygon(numberOfSides, diameter, mode, null);
46
27
  context.addSceneObject(poly);
47
28
  }
48
29
  else {
49
- center = normalizePoint2D(arguments[argOffset]);
50
- numberOfSides = resolveParam(arguments[argOffset + 1]);
51
- diameter = resolveParam(arguments[argOffset + 2]);
30
+ center = normalizePoint2D(arguments[0]);
31
+ numberOfSides = resolveParam(arguments[1]);
32
+ diameter = resolveParam(arguments[2]);
52
33
  mode = 'inscribed';
53
- poly = new Polygon(numberOfSides, diameter, mode, planeObj);
34
+ poly = new Polygon(numberOfSides, diameter, mode, null);
54
35
  context.addSceneObjects([new Move(center), poly]);
55
36
  }
56
37
  }
57
38
  else if (argCount === 4) {
58
- center = normalizePoint2D(arguments[argOffset]);
59
- numberOfSides = resolveParam(arguments[argOffset + 1]);
60
- diameter = resolveParam(arguments[argOffset + 2]);
61
- mode = arguments[argOffset + 3];
62
- poly = new Polygon(numberOfSides, diameter, mode, planeObj);
39
+ center = normalizePoint2D(arguments[0]);
40
+ numberOfSides = resolveParam(arguments[1]);
41
+ diameter = resolveParam(arguments[2]);
42
+ mode = arguments[3];
43
+ poly = new Polygon(numberOfSides, diameter, mode, null);
63
44
  context.addSceneObjects([new Move(center), poly]);
64
45
  }
65
46
  return poly;
@@ -1,4 +1,3 @@
1
- import { PlaneLike } from "../../math/plane.js";
2
1
  import { IExtrudableGeometry, ISceneObject } from "../interfaces.js";
3
2
  interface ProjectFunction {
4
3
  /**
@@ -6,12 +5,6 @@ interface ProjectFunction {
6
5
  * @param sourceObjects - The 3D objects to project
7
6
  */
8
7
  (...sourceObjects: ISceneObject[]): IExtrudableGeometry;
9
- /**
10
- * Projects 3D objects onto a target plane.
11
- * @param targetPlane - The plane to project onto
12
- * @param sourceObjects - The 3D objects to project
13
- */
14
- (targetPlane: PlaneLike | ISceneObject, sourceObjects: ISceneObject[]): IExtrudableGeometry;
15
8
  }
16
9
  declare const _default: ProjectFunction;
17
10
  export default _default;
@@ -1,20 +1,7 @@
1
1
  import { Projection } from "../../features/2d/projection.js";
2
- import { resolvePlane } from "../../helpers/resolve.js";
3
2
  import { registerBuilder } from "../../index.js";
4
3
  function build(context) {
5
4
  return function project(...args) {
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];
13
- context.addSceneObjects(sourceObjects);
14
- const projection = new Projection(sourceObjects, planeObj);
15
- context.addSceneObject(projection);
16
- return projection;
17
- }
18
5
  const projection = new Projection(args);
19
6
  context.addSceneObjects(args);
20
7
  context.addSceneObject(projection);
@@ -1,6 +1,5 @@
1
1
  import { Point2DLike } from "../../math/point.js";
2
- import { PlaneLike } from "../../math/plane.js";
3
- import { IRect, ISceneObject } from "../interfaces.js";
2
+ import { IRect } from "../interfaces.js";
4
3
  import { type NumberParam } from "../param.js";
5
4
  interface RectFunction {
6
5
  /**
@@ -16,13 +15,6 @@ interface RectFunction {
16
15
  * @param height - The rectangle height (defaults to width)
17
16
  */
18
17
  (start: Point2DLike, width: NumberParam, height?: NumberParam): IRect;
19
- /**
20
- * Draws a rectangle with given dimensions on a specific plane.
21
- * @param targetPlane - The plane to draw on
22
- * @param width - The rectangle width
23
- * @param height - The rectangle height
24
- */
25
- (targetPlane: PlaneLike | ISceneObject, width: NumberParam, height: NumberParam): IRect;
26
18
  }
27
19
  declare const _default: RectFunction;
28
20
  export default _default;
@@ -1,29 +1,10 @@
1
- import { isPoint2DLike } from "../../math/point.js";
2
1
  import { Move } from "../../features/2d/move.js";
3
2
  import { Rect } from "../../features/2d/rect.js";
4
3
  import { normalizePoint2D } from "../../helpers/normalize.js";
5
4
  import { registerBuilder } from "../../index.js";
6
- import { isPlaneLike } from "../../math/plane.js";
7
- import { SceneObject } from "../../common/scene-object.js";
8
- import { resolvePlane } from "../../helpers/resolve.js";
9
5
  import { isNumberParam, resolveParam } from "../param.js";
10
6
  function build(context) {
11
7
  return function cRect() {
12
- // Detect plane as first argument (only valid outside a sketch)
13
- if (arguments.length > 0) {
14
- const firstArg = arguments[0];
15
- if (isPlaneLike(firstArg) || (firstArg instanceof SceneObject && !isPoint2DLike(firstArg))) {
16
- if (context.getActiveSketch() !== null) {
17
- throw new Error("rect(plane, ...) cannot be used inside a sketch. Use rect(...) instead.");
18
- }
19
- const planeObj = resolvePlane(firstArg, context);
20
- const width = resolveParam(arguments[1]);
21
- const height = resolveParam(arguments[2]);
22
- const rect = new Rect(width, height, planeObj);
23
- context.addSceneObject(rect);
24
- return rect;
25
- }
26
- }
27
8
  const argCount = arguments.length;
28
9
  if (argCount === 1) {
29
10
  const width = resolveParam(arguments[0]);
@@ -1,5 +1,4 @@
1
1
  import { Point2DLike } from "../../math/point.js";
2
- import { PlaneLike } from "../../math/plane.js";
3
2
  import { ISlot, ISceneObject } from "../interfaces.js";
4
3
  import { type NumberParam, type BooleanParam } from "../param.js";
5
4
  interface SlotFunction {
@@ -9,13 +8,6 @@ interface SlotFunction {
9
8
  * @param radius - The end cap radius
10
9
  */
11
10
  (distance: NumberParam, radius: NumberParam): ISlot;
12
- /**
13
- * Draws a slot on a specific plane.
14
- * @param targetPlane - The plane to draw on
15
- * @param distance - The slot length
16
- * @param radius - The end cap radius
17
- */
18
- (targetPlane: PlaneLike | ISceneObject, distance: NumberParam, radius: NumberParam): ISlot;
19
11
  /**
20
12
  * Draws a slot from a start point with the given length and end radius.
21
13
  * @param start - The start point
@@ -37,21 +29,6 @@ interface SlotFunction {
37
29
  * @param deleteSource - Whether to delete the source geometry (defaults to true)
38
30
  */
39
31
  (geometry: ISceneObject, radius: NumberParam, deleteSource?: BooleanParam): ISlot;
40
- /**
41
- * Creates a slot from a geometry edge on a specific plane.
42
- * @param targetPlane - The plane to draw on
43
- * @param geometry - The source geometry edge
44
- * @param radius - The end cap radius
45
- */
46
- (targetPlane: PlaneLike | ISceneObject, geometry: ISceneObject, radius: NumberParam): ISlot;
47
- /**
48
- * Creates a slot from a geometry edge, optionally keeping the source, on a specific plane.
49
- * @param targetPlane - The plane to draw on
50
- * @param geometry - The source geometry edge
51
- * @param radius - The end cap radius
52
- * @param deleteSource - Whether to delete the source geometry
53
- */
54
- (targetPlane: PlaneLike | ISceneObject, geometry: ISceneObject, radius: NumberParam, deleteSource: BooleanParam): ISlot;
55
32
  }
56
33
  declare const _default: SlotFunction;
57
34
  export default _default;
@@ -5,65 +5,46 @@ import { SlotFromEdge } from "../../features/2d/slot-from-edge.js";
5
5
  import { normalizePoint2D } from "../../helpers/normalize.js";
6
6
  import { registerBuilder } from "../../index.js";
7
7
  import { SceneObject } from "../../common/scene-object.js";
8
- import { isPlaneLike } from "../../math/plane.js";
9
- import { resolvePlane } from "../../helpers/resolve.js";
10
8
  import { isNumberParam, isBooleanParam, resolveParam } from "../param.js";
11
9
  function build(context) {
12
10
  return function slot() {
13
- const inSketch = context.getActiveSketch() !== null;
14
- // Detect plane as first argument (only valid outside a sketch).
15
- // Inside a sketch, a SceneObject at position 0 means SlotFromEdge geometry, not plane.
16
- let planeObj = null;
17
- let argOffset = 0;
18
- if (arguments.length > 0) {
19
- const firstArg = arguments[0];
20
- const looksLikePlane = isPlaneLike(firstArg) ||
21
- (firstArg instanceof SceneObject && !isPoint2DLike(firstArg) && !inSketch);
22
- if (looksLikePlane) {
23
- if (inSketch) {
24
- throw new Error("slot(plane, ...) cannot be used inside a sketch. Use slot(...) instead.");
25
- }
26
- planeObj = resolvePlane(firstArg, context);
27
- argOffset = 1;
28
- }
29
- }
30
- // SlotFromEdge path: first non-plane arg is a SceneObject (geometry)
31
- if (arguments[argOffset] instanceof SceneObject && !isPoint2DLike(arguments[argOffset])) {
32
- const geometry = arguments[argOffset];
33
- const radius = resolveParam(arguments[argOffset + 1]);
11
+ // SlotFromEdge path: first arg is a SceneObject (geometry)
12
+ if (arguments[0] instanceof SceneObject && !isPoint2DLike(arguments[0])) {
13
+ const geometry = arguments[0];
14
+ const radius = resolveParam(arguments[1]);
34
15
  let deleteSource = true;
35
- if (arguments.length > argOffset + 2 && isBooleanParam(arguments[argOffset + 2])) {
36
- deleteSource = resolveParam(arguments[argOffset + 2]);
16
+ if (arguments.length > 2 && isBooleanParam(arguments[2])) {
17
+ deleteSource = resolveParam(arguments[2]);
37
18
  }
38
- const slotFromEdge = new SlotFromEdge(geometry, radius, deleteSource, planeObj);
19
+ const slotFromEdge = new SlotFromEdge(geometry, radius, deleteSource, null);
39
20
  context.addSceneObject(slotFromEdge);
40
21
  return slotFromEdge;
41
22
  }
42
- const argCount = arguments.length - argOffset;
23
+ const argCount = arguments.length;
43
24
  // slot(distance, radius)
44
- if (argCount === 2 && isNumberParam(arguments[argOffset])) {
45
- const distance = resolveParam(arguments[argOffset]);
46
- const radius = resolveParam(arguments[argOffset + 1]);
47
- const s = new Slot(distance, radius, planeObj);
25
+ if (argCount === 2 && isNumberParam(arguments[0])) {
26
+ const distance = resolveParam(arguments[0]);
27
+ const radius = resolveParam(arguments[1]);
28
+ const s = new Slot(distance, radius, null);
48
29
  context.addSceneObject(s);
49
30
  return s;
50
31
  }
51
- // slot(start, end, radius) — in-sketch only
52
- if (argCount === 3 && argOffset === 0
32
+ // slot(start, end, radius)
33
+ if (argCount === 3
53
34
  && isPoint2DLike(arguments[0]) && isPoint2DLike(arguments[1])) {
54
35
  const start = normalizePoint2D(arguments[0]);
55
36
  const end = normalizePoint2D(arguments[1]);
56
37
  const radius = arguments[2];
57
- const s = Slot.fromTwoPoints(start, end, radius, planeObj);
38
+ const s = Slot.fromTwoPoints(start, end, radius, null);
58
39
  context.addSceneObjects([new Move(start), s]);
59
40
  return s;
60
41
  }
61
- // slot(start, distance, radius) — in-sketch only
62
- if (argCount === 3 && argOffset === 0) {
42
+ // slot(start, distance, radius)
43
+ if (argCount === 3) {
63
44
  const start = normalizePoint2D(arguments[0]);
64
45
  const distance = resolveParam(arguments[1]);
65
46
  const radius = resolveParam(arguments[2]);
66
- const s = new Slot(distance, radius, planeObj);
47
+ const s = new Slot(distance, radius, null);
67
48
  context.addSceneObjects([new Move(start), s]);
68
49
  return s;
69
50
  }
@@ -1,4 +1,3 @@
1
- import { PlaneLike } from "../../math/plane.js";
2
1
  import { IText, ISceneObject } from "../interfaces.js";
3
2
  interface TextFunction {
4
3
  /**
@@ -7,12 +6,6 @@ interface TextFunction {
7
6
  * @param text - The string to render.
8
7
  */
9
8
  (text: string): IText;
10
- /**
11
- * Renders a text string on a specific plane (standalone, outside a sketch).
12
- * @param plane - The plane (e.g. "xy") or face to render the text on.
13
- * @param text - The string to render.
14
- */
15
- (plane: PlaneLike | ISceneObject, text: string): IText;
16
9
  /**
17
10
  * Renders a text string following a planar curve. Each glyph is placed
18
11
  * upright along the path's arc length; the text plane is the path's plane.
@@ -1,8 +1,6 @@
1
1
  import { Text } from "../../features/2d/text.js";
2
2
  import { registerBuilder } from "../../index.js";
3
- import { isPlaneLike } from "../../math/plane.js";
4
3
  import { SceneObject } from "../../common/scene-object.js";
5
- import { resolvePlane } from "../../helpers/resolve.js";
6
4
  function build(context) {
7
5
  return function text() {
8
6
  const first = arguments[0];
@@ -17,18 +15,6 @@ function build(context) {
17
15
  context.addSceneObject(obj);
18
16
  return obj;
19
17
  }
20
- // A leading plane/face is only valid standalone and only when a string
21
- // follows it; `text("xy")` (one arg) renders the literal string "xy".
22
- const standalone = arguments.length >= 2 && (isPlaneLike(first) || first instanceof SceneObject);
23
- if (standalone) {
24
- if (context.getActiveSketch() !== null) {
25
- throw new Error("text(plane, ...) cannot be used inside a sketch. Use text(...) instead.");
26
- }
27
- const planeObj = resolvePlane(first, context);
28
- const obj = new Text(String(arguments[1] ?? ""), planeObj);
29
- context.addSceneObject(obj);
30
- return obj;
31
- }
32
18
  const obj = new Text(String(first ?? ""));
33
19
  context.addSceneObject(obj);
34
20
  return obj;
@@ -1,5 +1,4 @@
1
1
  import { Point2DLike } from "../../math/point.js";
2
- import { PlaneLike } from "../../math/plane.js";
3
2
  import { IVLine, ISceneObject } from "../interfaces.js";
4
3
  import { type NumberParam } from "../param.js";
5
4
  interface VLineFunction {
@@ -30,12 +29,6 @@ interface VLineFunction {
30
29
  * @param target - The geometry to intersect with
31
30
  */
32
31
  (start: Point2DLike, target: ISceneObject): IVLine;
33
- /**
34
- * Draws a vertical line on a specific plane.
35
- * @param targetPlane - The plane to draw on
36
- * @param distance - The line length
37
- */
38
- (targetPlane: PlaneLike | ISceneObject, distance: NumberParam): IVLine;
39
32
  }
40
33
  declare const _default: VLineFunction;
41
34
  export default _default;
@@ -3,49 +3,31 @@ import { Move } from "../../features/2d/move.js";
3
3
  import { VerticalLine } from "../../features/2d/vline.js";
4
4
  import { normalizePoint2D } from "../../helpers/normalize.js";
5
5
  import { registerBuilder } from "../../index.js";
6
- import { isPlaneLike } from "../../math/plane.js";
7
6
  import { SceneObject } from "../../common/scene-object.js";
8
- import { resolvePlane } from "../../helpers/resolve.js";
9
7
  import { isNumberParam, resolveParam } from "../param.js";
10
8
  function build(context) {
11
9
  return function line() {
12
- let planeObj = null;
13
- let argOffset = 0;
14
10
  const inSketch = context.getActiveSketch() !== null;
15
- if (arguments.length > 0) {
16
- const firstArg = arguments[0];
17
- if (isPlaneLike(firstArg)) {
18
- if (inSketch) {
19
- throw new Error("vLine(plane, ...) cannot be used inside a sketch. Use vLine(...) instead.");
20
- }
21
- planeObj = resolvePlane(firstArg, context);
22
- argOffset = 1;
23
- }
24
- else if (!inSketch && firstArg instanceof SceneObject && !isPoint2DLike(firstArg)) {
25
- planeObj = resolvePlane(firstArg, context);
26
- argOffset = 1;
27
- }
28
- }
29
- if (argOffset === 0 && inSketch && arguments[0] instanceof SceneObject && !isPoint2DLike(arguments[0])) {
11
+ if (inSketch && arguments[0] instanceof SceneObject && !isPoint2DLike(arguments[0])) {
30
12
  // vLine(target)
31
13
  const vline = new VerticalLine(arguments[0], null);
32
14
  context.addSceneObject(vline);
33
15
  return vline;
34
16
  }
35
- if (argOffset === 0 && !isNumberParam(arguments[0])) {
17
+ if (!isNumberParam(arguments[0])) {
36
18
  // vLine(start, distance) or vLine(start, target)
37
19
  const start = normalizePoint2D(arguments[0]);
38
20
  const second = arguments[1];
39
21
  const distanceOrTarget = second instanceof SceneObject
40
22
  ? second
41
23
  : resolveParam(second);
42
- const vline = new VerticalLine(distanceOrTarget, planeObj);
24
+ const vline = new VerticalLine(distanceOrTarget, null);
43
25
  vline.setHasExplicitStart();
44
26
  context.addSceneObjects([new Move(start), vline]);
45
27
  return vline;
46
28
  }
47
- const distance = resolveParam(arguments[argOffset]);
48
- const vline = new VerticalLine(distance, planeObj);
29
+ const distance = resolveParam(arguments[0]);
30
+ const vline = new VerticalLine(distance, null);
49
31
  context.addSceneObject(vline);
50
32
  return vline;
51
33
  };
@@ -33,6 +33,13 @@ function build(context) {
33
33
  context.addSceneObject(fillet);
34
34
  return fillet;
35
35
  }
36
+ if (arguments.length >= 2 && isNumberParam(arguments[0])) {
37
+ const radius = resolveParam(arguments[0]);
38
+ const objects = Array.from(arguments).slice(1);
39
+ const fillet = new Fillet2D(radius, ...objects);
40
+ context.addSceneObject(fillet);
41
+ return fillet;
42
+ }
36
43
  }
37
44
  else {
38
45
  const args = Array.from(arguments);
@@ -1,4 +1,4 @@
1
- export type { ISceneObject, ITransformable, IBooleanOperation, IPlane, IAxis, ISelect, IGeometry, IExtrudableGeometry, IText, IRect, ISlot, IPolygon, ITwoObjectsTangentLine, ITangentArcTwoObjects, IExtrude, ICut, ICommon, ISweep, ILoft, IRevolve, IDraft, IRib, IHelix, IWrap } from "./interfaces.js";
1
+ export type { ISceneObject, ITransformable, IBooleanOperation, IPlane, IAxis, ISelect, IGeometry, IExtrudableGeometry, IText, IRect, ISlot, IPolygon, ITwoObjectsTangentLine, ITangentArcTwoObjects, IExtrude, ICut, ICommon, ISweep, ILoft, LoftConditionType, IRevolve, IDraft, IRib, IHelix, IWrap } from "./interfaces.js";
2
2
  export { default as axis } from "./axis.js";
3
3
  export { default as local } from "./local.js";
4
4
  export { default as plane } from "./plane.js";
@@ -618,7 +618,40 @@ export interface IRevolve extends IBooleanOperation {
618
618
  */
619
619
  capEdges(...args: (number | EdgeFilterBuilder)[]): ISceneObject;
620
620
  }
621
+ /**
622
+ * How a loft leaves (or arrives at) an end profile:
623
+ * - `'none'` — no constraint (default).
624
+ * - `'normal'` — the surface takes off perpendicular to the profile plane.
625
+ * - `'tangent'` — the surface takes off inside the profile plane, directed
626
+ * outward, so the profile plane becomes a tangency plane.
627
+ */
628
+ export type LoftConditionType = 'none' | 'normal' | 'tangent';
621
629
  export interface ILoft extends IBooleanOperation {
630
+ /**
631
+ * Adds side guide curves (rails) the loft surface must follow. Supports one
632
+ * or two guides in total; a single argument may carry several separate
633
+ * curves (e.g. a sketch holding a curve and its mirror) — each connected
634
+ * chain counts as one guide. Every guide must pass through every profile.
635
+ * Composes with start/end conditions (the condition fades out around each
636
+ * guide's contact point — rails win locally, the condition shapes the
637
+ * rest). Cannot be combined with thin mode.
638
+ * @param guides - Sketches or edges forming the guide curves.
639
+ */
640
+ guides(...guides: ISceneObject[]): this;
641
+ /**
642
+ * Constrains how the surface leaves the first profile.
643
+ * @param type - `'none'`, `'normal'` or `'tangent'` — see {@link LoftConditionType}.
644
+ * @param magnitude - Scales the takeoff strength; defaults to 1. Negative
645
+ * values flip the direction (e.g. inward instead of outward for `'tangent'`).
646
+ */
647
+ startCondition(type: LoftConditionType, magnitude?: NumberParam): this;
648
+ /**
649
+ * Constrains how the surface arrives at the last profile.
650
+ * @param type - `'none'`, `'normal'` or `'tangent'` — see {@link LoftConditionType}.
651
+ * @param magnitude - Scales the arrival strength; defaults to 1. Negative
652
+ * values flip the direction (e.g. inward instead of outward for `'tangent'`).
653
+ */
654
+ endCondition(type: LoftConditionType, magnitude?: NumberParam): this;
622
655
  /**
623
656
  * Selects faces on the first profile plane of the loft.
624
657
  * @param args - Numeric indices or {@link FaceFilterBuilder} instances to filter the selection.
@@ -687,6 +720,8 @@ export interface ILoft extends IBooleanOperation {
687
720
  */
688
721
  capEdges(...args: (number | EdgeFilterBuilder)[]): ISceneObject;
689
722
  }
723
+ /** Which end of a sweep path to extend. */
724
+ export type SweepSide = "start" | "end";
690
725
  export interface ISweep extends IBooleanOperation {
691
726
  /**
692
727
  * Selects faces at the start (profile plane) of the sweep.
@@ -738,6 +773,14 @@ export interface ISweep extends IBooleanOperation {
738
773
  * @param value - The offset distance.
739
774
  */
740
775
  endOffset(value: NumberParam): this;
776
+ /**
777
+ * Extends the swept solid beyond the path at the given end by `amount`,
778
+ * continuing straight along the path's tangent direction there. Chain twice to
779
+ * extend both ends, e.g. `.extend('start', 10).extend('end', 5)`.
780
+ * @param side - Which end of the path to extend: `'start'` or `'end'`.
781
+ * @param amount - Distance to extend, in mm (positive; non-positive is a no-op).
782
+ */
783
+ extend(side: SweepSide, amount: NumberParam): this;
741
784
  /**
742
785
  * Enables or disables drill mode.
743
786
  * @param value - `true` to enable (default), `false` to disable.
@@ -1,9 +1,11 @@
1
+ import { SceneObject } from "../../common/scene-object.js";
1
2
  import { GeometrySceneObject } from "./geometry.js";
2
3
  export type ConnectMode = 'line' | 'arc';
3
4
  export declare class Connect extends GeometrySceneObject {
4
5
  private mode;
5
6
  constructor(mode?: ConnectMode);
6
7
  build(): void;
8
+ createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
7
9
  compareTo(other: Connect): boolean;
8
10
  getType(): string;
9
11
  serialize(): {
@@ -85,6 +85,9 @@ export class Connect extends GeometrySceneObject {
85
85
  }
86
86
  this.addShape(wire);
87
87
  }
88
+ createCopy(remap) {
89
+ return new Connect(this.mode);
90
+ }
88
91
  compareTo(other) {
89
92
  if (!(other instanceof Connect)) {
90
93
  return false;
@@ -2,6 +2,7 @@ import { Vertex } from "../../common/vertex.js";
2
2
  import { SectionOps } from "../../oc/section-ops.js";
3
3
  import { WireOps } from "../../oc/wire-ops.js";
4
4
  import { ExtrudableGeometryBase } from "./extrudable-base.js";
5
+ import { SelectSceneObject } from "../select.js";
5
6
  export class Intersect extends ExtrudableGeometryBase {
6
7
  sourceObjects;
7
8
  constructor(sourceObjects, targetPlane = null) {
@@ -33,7 +34,9 @@ export class Intersect extends ExtrudableGeometryBase {
33
34
  }
34
35
  }
35
36
  for (const obj of this.sourceObjects) {
36
- obj.removeShapes(this);
37
+ if (obj instanceof SelectSceneObject) {
38
+ obj.removeShapes(this);
39
+ }
37
40
  }
38
41
  if (this.targetPlane) {
39
42
  this.targetPlane.removeShapes(this);
@@ -0,0 +1,29 @@
1
+ import { BuildSceneObjectContext, SceneObject } from "../common/scene-object.js";
2
+ import { TransformablePrimitive } from "../common/transformable-primitive.js";
3
+ /**
4
+ * A clone-only stand-in for sketch geometry that a repeated feature references
5
+ * but that lives in a sketch *outside* the cloned set — e.g. a `sweep` whose
6
+ * path is `otherSketch.regions.foo`.
7
+ *
8
+ * Such geometry cannot be rebuilt during the clone: a 2D curve resolves its
9
+ * plane by walking up to its owning `Sketch` (see GeometrySceneObject.sketch),
10
+ * and its pen position depends on sibling curves that are not part of the
11
+ * clone. Cloning it out of its sketch leaves it parented under the repeat
12
+ * container with no sketch ancestor, so `this.sketch` is `null` and build
13
+ * throws "Cannot read properties of null (reading 'getPlane')".
14
+ *
15
+ * Instead we freeze the source's already-built edges (which are final,
16
+ * world-space, and already incorporate any fillet/trim) and bake in the
17
+ * per-instance clone transform — exactly like cloned primitives
18
+ * (cylinder/sphere) do via {@link TransformablePrimitive.addPrimitiveShape}.
19
+ */
20
+ export declare class FrozenGeometry extends TransformablePrimitive {
21
+ private source;
22
+ constructor(source: SceneObject);
23
+ build(context?: BuildSceneObjectContext): void;
24
+ getDependencies(): SceneObject[];
25
+ createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
26
+ compareTo(other: SceneObject): boolean;
27
+ getType(): string;
28
+ serialize(): {};
29
+ }
@@ -0,0 +1,62 @@
1
+ import { TransformablePrimitive } from "../common/transformable-primitive.js";
2
+ /**
3
+ * A clone-only stand-in for sketch geometry that a repeated feature references
4
+ * but that lives in a sketch *outside* the cloned set — e.g. a `sweep` whose
5
+ * path is `otherSketch.regions.foo`.
6
+ *
7
+ * Such geometry cannot be rebuilt during the clone: a 2D curve resolves its
8
+ * plane by walking up to its owning `Sketch` (see GeometrySceneObject.sketch),
9
+ * and its pen position depends on sibling curves that are not part of the
10
+ * clone. Cloning it out of its sketch leaves it parented under the repeat
11
+ * container with no sketch ancestor, so `this.sketch` is `null` and build
12
+ * throws "Cannot read properties of null (reading 'getPlane')".
13
+ *
14
+ * Instead we freeze the source's already-built edges (which are final,
15
+ * world-space, and already incorporate any fillet/trim) and bake in the
16
+ * per-instance clone transform — exactly like cloned primitives
17
+ * (cylinder/sphere) do via {@link TransformablePrimitive.addPrimitiveShape}.
18
+ */
19
+ export class FrozenGeometry extends TransformablePrimitive {
20
+ source;
21
+ constructor(source) {
22
+ super();
23
+ this.source = source;
24
+ }
25
+ build(context) {
26
+ // Read the source's raw built edges rather than getShapes(): the original
27
+ // feature that consumes this geometry (e.g. the un-cloned sweep) may have
28
+ // marked it removed via removeShapes(), which getShapes() filters out. The
29
+ // edges themselves are unchanged, so we still want them — skipping only
30
+ // meta/guide shapes, matching how a spine/region is normally read.
31
+ for (const shape of this.source.getAddedShapes()) {
32
+ if (shape.isMetaShape() || shape.isGuideShape()) {
33
+ continue;
34
+ }
35
+ this.addPrimitiveShape(shape, context);
36
+ }
37
+ }
38
+ getDependencies() {
39
+ // The source is the pre-existing original, not part of the clone set; it is
40
+ // built earlier in the scene order, so we read its shapes at build time
41
+ // rather than depending on a clone of it.
42
+ return [];
43
+ }
44
+ createCopy(remap) {
45
+ return new FrozenGeometry(this.source).syncPrimitiveWith(this);
46
+ }
47
+ compareTo(other) {
48
+ if (!(other instanceof FrozenGeometry)) {
49
+ return false;
50
+ }
51
+ if (!super.compareTo(other)) {
52
+ return false;
53
+ }
54
+ return this.source.compareTo(other.source);
55
+ }
56
+ getType() {
57
+ return "frozen-geometry";
58
+ }
59
+ serialize() {
60
+ return {};
61
+ }
62
+ }