fluidcad 0.0.24 → 0.0.26

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 (52) hide show
  1. package/lib/dist/common/scene-object.d.ts +6 -2
  2. package/lib/dist/common/scene-object.js +28 -16
  3. package/lib/dist/common/transformable-primitive.d.ts +16 -0
  4. package/lib/dist/common/transformable-primitive.js +116 -0
  5. package/lib/dist/core/2d/tarc.d.ts +2 -2
  6. package/lib/dist/core/copy.js +5 -4
  7. package/lib/dist/core/cylinder.d.ts +2 -2
  8. package/lib/dist/core/index.d.ts +2 -1
  9. package/lib/dist/core/index.js +1 -0
  10. package/lib/dist/core/interfaces.d.ts +80 -21
  11. package/lib/dist/core/local.d.ts +12 -0
  12. package/lib/dist/core/local.js +18 -0
  13. package/lib/dist/core/mirror.d.ts +2 -2
  14. package/lib/dist/core/mirror.js +42 -60
  15. package/lib/dist/core/plane.d.ts +5 -7
  16. package/lib/dist/core/sphere.d.ts +3 -3
  17. package/lib/dist/features/2d/circle.js +2 -1
  18. package/lib/dist/features/2d/tarc-with-tangent.js +4 -2
  19. package/lib/dist/features/2d/tarc.js +4 -2
  20. package/lib/dist/features/axis-from-edge.js +2 -2
  21. package/lib/dist/features/axis-from-sketch.d.ts +18 -0
  22. package/lib/dist/features/axis-from-sketch.js +58 -0
  23. package/lib/dist/features/copy-linear2d.d.ts +4 -2
  24. package/lib/dist/features/copy-linear2d.js +23 -9
  25. package/lib/dist/features/cylinder.d.ts +2 -2
  26. package/lib/dist/features/cylinder.js +2 -2
  27. package/lib/dist/features/mirror-shape.js +10 -0
  28. package/lib/dist/features/plane-from-object.d.ts +1 -1
  29. package/lib/dist/features/plane-from-object.js +11 -6
  30. package/lib/dist/features/plane-mid.d.ts +1 -1
  31. package/lib/dist/features/plane.d.ts +1 -1
  32. package/lib/dist/features/select.js +0 -1
  33. package/lib/dist/features/sphere.d.ts +2 -2
  34. package/lib/dist/features/sphere.js +2 -2
  35. package/lib/dist/math/axis.d.ts +1 -0
  36. package/lib/dist/math/axis.js +4 -1
  37. package/lib/dist/math/index.d.ts +2 -2
  38. package/lib/dist/math/index.js +1 -1
  39. package/lib/dist/math/plane.d.ts +1 -2
  40. package/lib/dist/math/plane.js +20 -19
  41. package/lib/dist/rendering/render.js +8 -0
  42. package/lib/dist/tests/features/cut-two-distances.test.js +1 -1
  43. package/lib/dist/tests/features/cut.test.js +1 -1
  44. package/lib/dist/tests/features/mirror2d.test.js +34 -0
  45. package/lib/dist/tests/features/primitive-chain.test.d.ts +1 -0
  46. package/lib/dist/tests/features/primitive-chain.test.js +45 -0
  47. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  48. package/package.json +1 -1
  49. package/server/dist/code-editor.d.ts +3 -2
  50. package/server/dist/code-editor.js +244 -121
  51. package/ui/dist/assets/{index-CqP_mgZk.js → index-BeLxRMCv.js} +4 -4
  52. package/ui/dist/index.html +1 -1
@@ -27,6 +27,7 @@ export declare abstract class SceneObject implements Comparable<SceneObject>, Se
27
27
  private _id;
28
28
  private _order;
29
29
  private _transform;
30
+ private _appliedTransform;
30
31
  private _cloneSource;
31
32
  private _parent;
32
33
  private _alwaysVisible;
@@ -56,6 +57,8 @@ export declare abstract class SceneObject implements Comparable<SceneObject>, Se
56
57
  abstract serialize(scope?: Set<SceneObject>): any;
57
58
  abstract getType(): string;
58
59
  abstract build(context?: BuildSceneObjectContext): void;
60
+ getAppliedTransform(): Matrix4 | null;
61
+ protected composeAppliedTransform(matrix: Matrix4): void;
59
62
  compareTo(other: SceneObject): boolean;
60
63
  getDependencies(): SceneObject[];
61
64
  createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
@@ -104,9 +107,10 @@ export declare abstract class SceneObject implements Comparable<SceneObject>, Se
104
107
  getOperationMode(): OperationMode;
105
108
  isSymmetric(): boolean;
106
109
  resolveFusionScope(sceneObjects: SceneObject[]): SceneObject[];
107
- add(...objects: ISceneObject[]): this;
110
+ add(): this;
108
111
  new(): this;
109
- remove(...objects: ISceneObject[]): this;
112
+ remove(): this;
113
+ scope(...objects: ISceneObject[]): this;
110
114
  symmetric(): this;
111
115
  /**
112
116
  * Called after all objects have been built. Override to perform
@@ -5,6 +5,7 @@ export class SceneObject {
5
5
  _id;
6
6
  _order = 0;
7
7
  _transform = null;
8
+ _appliedTransform = null;
8
9
  _cloneSource = null;
9
10
  _parent = null;
10
11
  _alwaysVisible = false;
@@ -89,6 +90,14 @@ export class SceneObject {
89
90
  getSnapshot() {
90
91
  return this.getState('snapshot') || [];
91
92
  }
93
+ getAppliedTransform() {
94
+ return this._appliedTransform;
95
+ }
96
+ composeAppliedTransform(matrix) {
97
+ this._appliedTransform = this._appliedTransform
98
+ ? matrix.multiply(this._appliedTransform)
99
+ : matrix;
100
+ }
92
101
  compareTo(other) {
93
102
  const match = this._guide === other._guide && this._reusable === other._reusable;
94
103
  if (!match) {
@@ -100,6 +109,13 @@ export class SceneObject {
100
109
  if (this._symmetric !== other._symmetric) {
101
110
  return false;
102
111
  }
112
+ if (!this._appliedTransform !== !other._appliedTransform) {
113
+ return false;
114
+ }
115
+ if (this._appliedTransform && other._appliedTransform
116
+ && !this._appliedTransform.equals(other._appliedTransform)) {
117
+ return false;
118
+ }
103
119
  if (typeof (this._fusionScope) !== typeof (other._fusionScope)) {
104
120
  return false;
105
121
  }
@@ -148,6 +164,9 @@ export class SceneObject {
148
164
  const result = [];
149
165
  for (const obj of ordered) {
150
166
  const copy = obj.createCopy(remap);
167
+ if (obj._appliedTransform) {
168
+ copy._appliedTransform = obj._appliedTransform;
169
+ }
151
170
  remap.set(obj, copy);
152
171
  result.push(copy);
153
172
  const parent = obj.getParent();
@@ -351,17 +370,9 @@ export class SceneObject {
351
370
  }
352
371
  return sceneObjects;
353
372
  }
354
- add(...objects) {
373
+ add() {
355
374
  this._operationMode = 'add';
356
- if (objects.length === 0) {
357
- this._fusionScope = 'all';
358
- }
359
- else if (objects.length === 1) {
360
- this._fusionScope = objects[0];
361
- }
362
- else {
363
- this._fusionScope = objects;
364
- }
375
+ this._fusionScope = 'all';
365
376
  return this;
366
377
  }
367
378
  new() {
@@ -369,15 +380,16 @@ export class SceneObject {
369
380
  this._fusionScope = 'none';
370
381
  return this;
371
382
  }
372
- remove(...objects) {
383
+ remove() {
373
384
  this._operationMode = 'remove';
374
- if (objects.length === 0) {
375
- this._fusionScope = 'all';
376
- }
377
- else if (objects.length === 1) {
385
+ this._fusionScope = 'all';
386
+ return this;
387
+ }
388
+ scope(...objects) {
389
+ if (objects.length === 1) {
378
390
  this._fusionScope = objects[0];
379
391
  }
380
- else {
392
+ else if (objects.length > 1) {
381
393
  this._fusionScope = objects;
382
394
  }
383
395
  return this;
@@ -0,0 +1,16 @@
1
+ import { SceneObject } from "./scene-object.js";
2
+ import { Matrix4 } from "../math/matrix4.js";
3
+ import type { AxisLike } from "../math/axis.js";
4
+ import type { PlaneLike } from "../math/plane.js";
5
+ import type { PointLike } from "../math/point.js";
6
+ export declare abstract class TransformablePrimitive extends SceneObject {
7
+ transform(matrix: Matrix4): this;
8
+ translate(x: number): this;
9
+ translate(x: number, y: number): this;
10
+ translate(x: number, y: number, z: number): this;
11
+ translate(p: PointLike): this;
12
+ rotate(angle: number): this;
13
+ rotate(axis: AxisLike, angle: number): this;
14
+ mirror(plane: PlaneLike): this;
15
+ mirror(axis: AxisLike): this;
16
+ }
@@ -0,0 +1,116 @@
1
+ import { SceneObject } from "./scene-object.js";
2
+ import { Matrix4 } from "../math/matrix4.js";
3
+ import { Point } from "../math/point.js";
4
+ import { Vector3d } from "../math/vector3d.js";
5
+ import { rad } from "../helpers/math-helpers.js";
6
+ export class TransformablePrimitive extends SceneObject {
7
+ transform(matrix) {
8
+ this.composeAppliedTransform(matrix);
9
+ return this;
10
+ }
11
+ translate(a, b, c) {
12
+ let x, y, z;
13
+ if (typeof a === 'number') {
14
+ x = a;
15
+ y = b ?? 0;
16
+ z = c ?? 0;
17
+ }
18
+ else if (Array.isArray(a)) {
19
+ x = a[0] ?? 0;
20
+ y = a[1] ?? 0;
21
+ z = a[2] ?? 0;
22
+ }
23
+ else {
24
+ x = a.x;
25
+ y = a.y;
26
+ z = a.z;
27
+ }
28
+ return this.transform(Matrix4.fromTranslation(x, y, z));
29
+ }
30
+ rotate(a, b) {
31
+ let origin;
32
+ let direction;
33
+ let angleDeg;
34
+ if (typeof a === 'number') {
35
+ origin = new Point(0, 0, 0);
36
+ direction = Vector3d.unitZ();
37
+ angleDeg = a;
38
+ }
39
+ else {
40
+ const resolved = resolveAxisLike(a);
41
+ origin = resolved.origin;
42
+ direction = resolved.direction;
43
+ angleDeg = b;
44
+ }
45
+ return this.transform(Matrix4.fromRotationAroundAxis(origin, direction, rad(angleDeg)));
46
+ }
47
+ mirror(arg) {
48
+ if (isAxisLikeArg(arg)) {
49
+ const axis = resolveAxisLike(arg);
50
+ return this.transform(Matrix4.mirrorAxis(axis.origin, axis.direction));
51
+ }
52
+ const plane = resolvePlaneLike(arg);
53
+ return this.transform(Matrix4.mirrorPlane(plane.normal, plane.origin));
54
+ }
55
+ }
56
+ function isAxisLikeArg(arg) {
57
+ if (arg === 'x' || arg === 'y' || arg === 'z') {
58
+ return true;
59
+ }
60
+ if (arg && typeof arg === 'object') {
61
+ if (typeof arg.getAxis === 'function') {
62
+ return true;
63
+ }
64
+ if (arg.origin && arg.direction) {
65
+ return true;
66
+ }
67
+ }
68
+ return false;
69
+ }
70
+ function resolveAxisLike(arg) {
71
+ if (arg === 'x') {
72
+ return { origin: new Point(0, 0, 0), direction: Vector3d.unitX() };
73
+ }
74
+ if (arg === 'y') {
75
+ return { origin: new Point(0, 0, 0), direction: Vector3d.unitY() };
76
+ }
77
+ if (arg === 'z') {
78
+ return { origin: new Point(0, 0, 0), direction: Vector3d.unitZ() };
79
+ }
80
+ const a = arg;
81
+ if (typeof a.getAxis === 'function') {
82
+ const axis = a.getAxis();
83
+ return { origin: axis.origin, direction: axis.direction };
84
+ }
85
+ return { origin: a.origin, direction: a.direction };
86
+ }
87
+ function resolvePlaneLike(arg) {
88
+ if (typeof arg === 'string') {
89
+ switch (arg) {
90
+ case 'xy':
91
+ case 'top':
92
+ return { origin: new Point(0, 0, 0), normal: Vector3d.unitZ() };
93
+ case '-xy':
94
+ case 'bottom':
95
+ return { origin: new Point(0, 0, 0), normal: Vector3d.unitZ().multiply(-1) };
96
+ case 'xz':
97
+ case 'front':
98
+ return { origin: new Point(0, 0, 0), normal: Vector3d.unitY().multiply(-1) };
99
+ case '-xz':
100
+ case 'back':
101
+ return { origin: new Point(0, 0, 0), normal: Vector3d.unitY() };
102
+ case 'yz':
103
+ case 'right':
104
+ return { origin: new Point(0, 0, 0), normal: Vector3d.unitX() };
105
+ case '-yz':
106
+ case 'left':
107
+ return { origin: new Point(0, 0, 0), normal: Vector3d.unitX().multiply(-1) };
108
+ }
109
+ }
110
+ const p = arg;
111
+ if (typeof p.getPlane === 'function') {
112
+ const plane = p.getPlane();
113
+ return { origin: plane.origin, normal: plane.normal };
114
+ }
115
+ return { origin: p.origin, normal: p.normal };
116
+ }
@@ -4,13 +4,13 @@ import { IGeometry, ISceneObject, ITangentArcTwoObjects } from "../interfaces.js
4
4
  interface TArcFunction {
5
5
  /**
6
6
  * Draws a tangent arc with a given radius and end angle.
7
- * @param radius - The arc radius (defaults to 100)
7
+ * @param radius - The arc radius (defaults to 100). A negative value flips the sweep direction.
8
8
  * @param endAngle - The sweep angle in degrees (defaults to 90)
9
9
  */
10
10
  (radius?: number, endAngle?: number): IGeometry;
11
11
  /**
12
12
  * Draws a tangent arc with a given radius, angle, and start tangent direction.
13
- * @param radius - The arc radius
13
+ * @param radius - The arc radius. A negative value flips the sweep direction.
14
14
  * @param angle - The sweep angle in degrees
15
15
  * @param tangent - The start tangent direction
16
16
  */
@@ -4,6 +4,7 @@ import { CopyLinear } from "../features/copy-linear.js";
4
4
  import { CopyCircular } from "../features/copy-circular.js";
5
5
  import { CopyLinear2D } from "../features/copy-linear2d.js";
6
6
  import { CopyCircular2D } from "../features/copy-circular2d.js";
7
+ import { AxisObjectBase } from "../features/axis-renderable-base.js";
7
8
  function build(context) {
8
9
  return function copy() {
9
10
  const args = Array.from(arguments);
@@ -19,14 +20,14 @@ function build(context) {
19
20
  : null;
20
21
  if (type === 'linear') {
21
22
  const axisArg = args[1];
22
- const axes = Array.isArray(axisArg)
23
- ? axisArg.map(a => normalizeAxis(a))
24
- : [normalizeAxis(axisArg)];
23
+ const axisList = Array.isArray(axisArg) ? axisArg : [axisArg];
25
24
  if (activeSketch) {
26
- const copy = new CopyLinear2D(axes, options, restObjects.length > 0 ? restObjects : null);
25
+ const sketchAxes = axisList.map(a => a instanceof AxisObjectBase ? a : normalizeAxis(a));
26
+ const copy = new CopyLinear2D(sketchAxes, options, restObjects.length > 0 ? restObjects : null);
27
27
  context.addSceneObject(copy);
28
28
  return copy;
29
29
  }
30
+ const axes = axisList.map(a => normalizeAxis(a));
30
31
  const copy = new CopyLinear(axes, options, objects);
31
32
  context.addSceneObject(copy);
32
33
  return copy;
@@ -1,11 +1,11 @@
1
- import { ISceneObject } from "./interfaces.js";
1
+ import { ITransformable } from "./interfaces.js";
2
2
  interface CylinderFunction {
3
3
  /**
4
4
  * Creates a cylinder with the given radius and height.
5
5
  * @param radius - The cylinder radius
6
6
  * @param height - The cylinder height
7
7
  */
8
- (radius: number, height: number): ISceneObject;
8
+ (radius: number, height: number): ITransformable;
9
9
  }
10
10
  declare const _default: CylinderFunction;
11
11
  export default _default;
@@ -1,5 +1,6 @@
1
- export type { ISceneObject, IFuseable, IPlane, IAxis, ISelect, IGeometry, IExtrudableGeometry, IRect, ISlot, IPolygon, ITwoObjectsTangentLine, ITangentArcTwoObjects, IExtrude, ICut, ICommon, ISweep, ILoft, IRevolve, IDraft } from "./interfaces.js";
1
+ export type { ISceneObject, ITransformable, IBooleanOperation, IPlane, IAxis, ISelect, IGeometry, IExtrudableGeometry, IRect, ISlot, IPolygon, ITwoObjectsTangentLine, ITangentArcTwoObjects, IExtrude, ICut, ICommon, ISweep, ILoft, IRevolve, IDraft } from "./interfaces.js";
2
2
  export { default as axis } from "./axis.js";
3
+ export { default as local } from "./local.js";
3
4
  export { default as plane } from "./plane.js";
4
5
  export { default as sketch } from "./sketch.js";
5
6
  export { default as fuse } from "./fuse.js";
@@ -1,4 +1,5 @@
1
1
  export { default as axis } from "./axis.js";
2
+ export { default as local } from "./local.js";
2
3
  export { default as plane } from "./plane.js";
3
4
  export { default as sketch } from "./sketch.js";
4
5
  export { default as fuse } from "./fuse.js";
@@ -1,7 +1,10 @@
1
- import { LazyVertex } from "../features/lazy-vertex.js";
2
- import { Point2DLike } from "../math/point.js";
3
- import { FaceFilterBuilder } from "../filters/face/face-filter.js";
4
- import { EdgeFilterBuilder } from "../filters/edge/edge-filter.js";
1
+ import type { LazyVertex } from "../features/lazy-vertex.js";
2
+ import type { Point2DLike, PointLike } from "../math/point.js";
3
+ import type { FaceFilterBuilder } from "../filters/face/face-filter.js";
4
+ import type { EdgeFilterBuilder } from "../filters/edge/edge-filter.js";
5
+ import type { Matrix4 } from "../math/matrix4.js";
6
+ import type { AxisLike } from "../math/axis.js";
7
+ import type { PlaneLike } from "../math/plane.js";
5
8
  export interface ISceneObject {
6
9
  /**
7
10
  * Sets a custom display name for this object, overriding the default type-based name.
@@ -21,26 +24,81 @@ export interface ISceneObject {
21
24
  */
22
25
  reusable(): this;
23
26
  }
24
- export interface IFuseable extends ISceneObject {
27
+ export interface IBooleanOperation extends ISceneObject {
25
28
  /**
26
- * Additive boolean operation — fuses the result with existing shapes.
27
- * When called with no arguments, fuses with all intersecting scene objects.
28
- * When called with specific objects, fuses only with those objects.
29
- * @param objects - Optional target objects to fuse with.
29
+ * Additive boolean operation — fuses the result with all intersecting scene objects.
30
+ * Use `.scope()` to target specific objects.
30
31
  */
31
- add(...objects: ISceneObject[]): this;
32
+ add(): this;
32
33
  /**
33
34
  * No boolean operation — keeps the result as a standalone shape,
34
35
  * separate from all other scene objects.
35
36
  */
36
37
  'new'(): this;
37
38
  /**
38
- * Subtractive boolean operation — cuts the result from existing shapes.
39
- * When called with no arguments, cuts from all intersecting scene objects.
40
- * When called with specific objects, cuts only from those objects.
41
- * @param objects - Optional target objects to cut from.
39
+ * Subtractive boolean operation — cuts the result from all intersecting scene objects.
40
+ * Use `.scope()` to target specific objects.
42
41
  */
43
- remove(...objects: ISceneObject[]): this;
42
+ remove(): this;
43
+ /**
44
+ * Narrows the boolean operation scope to specific target objects.
45
+ * Must be chained after `.add()` or `.remove()`.
46
+ * @param objects - The target objects to operate on.
47
+ */
48
+ scope(...objects: ISceneObject[]): this;
49
+ }
50
+ /**
51
+ * Scene objects that can be chained with world-space transformations.
52
+ * The chained form `obj.translate(...)` / `obj.rotate(...)` / `obj.mirror(...)`
53
+ * applies the transform to the object's built shapes; it does not create
54
+ * a separate history entry like the free-function `translate()` does.
55
+ *
56
+ * Container objects (sketches, parts, repeat/mirror features) deliberately
57
+ * do not expose this interface — apply transforms to their contents instead.
58
+ */
59
+ export interface ITransformable extends ISceneObject {
60
+ /**
61
+ * Composes a 4x4 transformation matrix onto this object. Applied to the
62
+ * object's own shapes after build. Chained calls compose left-to-right:
63
+ * `.translate(T).rotate(R)` applies translation first, then rotation.
64
+ */
65
+ transform(matrix: Matrix4): this;
66
+ /**
67
+ * Translate along X.
68
+ * @param x - Distance along world X.
69
+ */
70
+ translate(x: number): this;
71
+ /**
72
+ * Translate along X and Y.
73
+ */
74
+ translate(x: number, y: number): this;
75
+ /**
76
+ * Translate along X, Y, and Z.
77
+ */
78
+ translate(x: number, y: number, z: number): this;
79
+ /**
80
+ * Translate by a point-like offset in world space.
81
+ */
82
+ translate(offset: PointLike): this;
83
+ /**
84
+ * Rotate by an angle around world Z through the origin.
85
+ * @param angle - Rotation in degrees.
86
+ */
87
+ rotate(angle: number): this;
88
+ /**
89
+ * Rotate around an axis by an angle.
90
+ * @param axis - The axis to rotate around. Use `local(...)` to reference a sketch-local axis.
91
+ * @param angle - Rotation in degrees.
92
+ */
93
+ rotate(axis: AxisLike, angle: number): this;
94
+ /**
95
+ * Mirror across a plane.
96
+ */
97
+ mirror(plane: PlaneLike): this;
98
+ /**
99
+ * Mirror across an axis (primarily useful for 2D geometry).
100
+ */
101
+ mirror(axis: AxisLike): this;
44
102
  }
45
103
  export interface IPlane extends ISceneObject {
46
104
  }
@@ -205,7 +263,7 @@ export interface ICommon extends ISceneObject {
205
263
  */
206
264
  keepOriginal(value?: boolean): this;
207
265
  }
208
- export interface IExtrude extends IFuseable {
266
+ export interface IExtrude extends IBooleanOperation {
209
267
  /**
210
268
  * Enables symmetric mode — extrudes equally in both directions from the sketch plane.
211
269
  */
@@ -303,10 +361,11 @@ export interface ICut extends ISceneObject {
303
361
  */
304
362
  symmetric(): this;
305
363
  /**
306
- * Narrows the cut scope to specific objects instead of all scene objects.
364
+ * Narrows the cut scope to specific target objects.
365
+ * Must be chained after `.remove()`.
307
366
  * @param objects - The target objects to cut from.
308
367
  */
309
- remove(...objects: ISceneObject[]): this;
368
+ scope(...objects: ISceneObject[]): this;
310
369
  /**
311
370
  * Applies a draft (taper) angle to the cut walls.
312
371
  * @param value - A single angle for uniform draft, or a `[start, end]` tuple for asymmetric draft.
@@ -357,7 +416,7 @@ export interface ICut extends ISceneObject {
357
416
  */
358
417
  thin(offset1: number, offset2: number): this;
359
418
  }
360
- export interface IRevolve extends IFuseable {
419
+ export interface IRevolve extends IBooleanOperation {
361
420
  /**
362
421
  * Enables symmetric mode — revolves equally in both directions from the sketch plane.
363
422
  */
@@ -405,7 +464,7 @@ export interface IRevolve extends IFuseable {
405
464
  */
406
465
  capEdges(...args: (number | EdgeFilterBuilder)[]): ISceneObject;
407
466
  }
408
- export interface ILoft extends IFuseable {
467
+ export interface ILoft extends IBooleanOperation {
409
468
  /**
410
469
  * Selects faces on the first profile plane of the loft.
411
470
  * @param args - Numeric indices or {@link FaceFilterBuilder} instances to filter the selection.
@@ -474,7 +533,7 @@ export interface ILoft extends IFuseable {
474
533
  */
475
534
  capEdges(...args: (number | EdgeFilterBuilder)[]): ISceneObject;
476
535
  }
477
- export interface ISweep extends IFuseable {
536
+ export interface ISweep extends IBooleanOperation {
478
537
  /**
479
538
  * Selects faces at the start (profile plane) of the sweep.
480
539
  * @param args - Numeric indices or {@link FaceFilterBuilder} instances to filter the selection.
@@ -0,0 +1,12 @@
1
+ import { StandardAxis } from "../math/axis.js";
2
+ import { IAxis } from "./interfaces.js";
3
+ interface LocalFunction {
4
+ /**
5
+ * Creates an axis in the active sketch's local coordinate system.
6
+ * @param axis - One of the standard axes ('x', 'y', 'z') to interpret
7
+ * relative to the active sketch's plane.
8
+ */
9
+ (axis: StandardAxis): IAxis;
10
+ }
11
+ declare const _default: LocalFunction;
12
+ export default _default;
@@ -0,0 +1,18 @@
1
+ import { registerBuilder } from "../index.js";
2
+ import { AxisFromSketch } from "../features/axis-from-sketch.js";
3
+ import { isStandardAxis } from "../math/axis.js";
4
+ function build(context) {
5
+ return function local(axis) {
6
+ if (!isStandardAxis(axis)) {
7
+ throw new Error("local() accepts only 'x', 'y', or 'z'");
8
+ }
9
+ const sketch = context.getActiveSketch();
10
+ if (!sketch) {
11
+ throw new Error("local() can only be used inside a sketch");
12
+ }
13
+ const ax = new AxisFromSketch(sketch, axis);
14
+ context.addSceneObject(ax);
15
+ return ax;
16
+ };
17
+ }
18
+ export default registerBuilder(build);
@@ -9,7 +9,7 @@ interface MirrorFunction {
9
9
  (line: ISceneObject): ISceneObject;
10
10
  /**
11
11
  * [2D] Mirror all sketch geometries across a given axis.
12
- * @param axis The axis to mirror across
12
+ * @param axis The local axis to mirror across
13
13
  */
14
14
  (axis: AxisLike): ISceneObject;
15
15
  /**
@@ -20,7 +20,7 @@ interface MirrorFunction {
20
20
  (line: ISceneObject, ...geometries: ISceneObject[]): ISceneObject;
21
21
  /**
22
22
  * [2D] Mirror given sketch geometries across a given axis.
23
- * @param axis The axis to mirror across
23
+ * @param axis The local axis to mirror across
24
24
  * @param geometries The geometries to mirror
25
25
  */
26
26
  (axis: AxisLike, ...geometries: ISceneObject[]): ISceneObject;
@@ -4,87 +4,69 @@ import { SceneObject } from "../common/scene-object.js";
4
4
  import { MirrorShape } from "../features/mirror-shape.js";
5
5
  import { PlaneObjectBase } from "../features/plane-renderable-base.js";
6
6
  import { PlaneObject } from "../features/plane.js";
7
- import { isAxisLike } from "../math/axis.js";
7
+ import { isAxisLike, isStandardAxis } from "../math/axis.js";
8
8
  import { MirrorShape2D } from "../features/mirror-shape2d.js";
9
9
  import { AxisObjectBase } from "../features/axis-renderable-base.js";
10
10
  import { AxisObject } from "../features/axis.js";
11
11
  import { AxisFromEdge } from "../features/axis-from-edge.js";
12
+ const axisToPlaneName = { x: "yz", y: "xz", z: "xy" };
13
+ function resolveAxis(arg, context) {
14
+ if (arg instanceof AxisObjectBase) {
15
+ return arg;
16
+ }
17
+ if (arg instanceof SceneObject) {
18
+ const axis = new AxisFromEdge(arg);
19
+ context.addSceneObject(axis);
20
+ return axis;
21
+ }
22
+ const a = normalizeAxis(arg);
23
+ const axis = new AxisObject(a);
24
+ context.addSceneObject(axis);
25
+ return axis;
26
+ }
27
+ function resolvePlane(arg, context) {
28
+ if (arg instanceof PlaneObjectBase) {
29
+ return arg;
30
+ }
31
+ const normalizedPlane = normalizePlane(arg);
32
+ const planeObj = new PlaneObject(normalizedPlane);
33
+ context.addSceneObject(planeObj);
34
+ return planeObj;
35
+ }
12
36
  function build(context) {
13
37
  return function mirror() {
38
+ const activeSketch = context.getActiveSketch();
14
39
  if (arguments.length === 1) {
15
- if (isAxisLike(arguments[0] || arguments[0] instanceof SceneObject)) {
16
- let axis = null;
17
- if (arguments[0] instanceof AxisObjectBase) {
18
- axis = arguments[0];
19
- }
20
- else if (arguments[0] instanceof SceneObject) {
21
- const line = arguments[0];
22
- axis = new AxisFromEdge(line);
23
- context.addSceneObject(axis);
24
- }
25
- else {
26
- const a = normalizeAxis(arguments[0]);
27
- axis = new AxisObject(a);
28
- context.addSceneObject(axis);
29
- }
40
+ if (activeSketch && (isAxisLike(arguments[0]) || arguments[0] instanceof SceneObject)) {
41
+ const axis = resolveAxis(arguments[0], context);
30
42
  const mirror = new MirrorShape2D(axis);
31
43
  context.addSceneObject(mirror);
32
44
  return mirror;
33
45
  }
34
- else {
35
- const pln = arguments[0];
36
- let planeObj;
37
- if (!(pln instanceof PlaneObjectBase)) {
38
- const normalizedPlane = normalizePlane(arguments[0]);
39
- planeObj = new PlaneObject(normalizedPlane);
40
- context.addSceneObject(planeObj);
41
- }
42
- else {
43
- planeObj = pln;
44
- ;
45
- }
46
- const mirror = new MirrorShape(planeObj);
47
- context.addSceneObject(mirror);
48
- return mirror;
46
+ let planeArg = arguments[0];
47
+ if (isStandardAxis(planeArg)) {
48
+ planeArg = axisToPlaneName[planeArg];
49
49
  }
50
+ const planeObj = resolvePlane(planeArg, context);
51
+ const mirror = new MirrorShape(planeObj);
52
+ context.addSceneObject(mirror);
53
+ return mirror;
50
54
  }
51
55
  if (arguments.length >= 2) {
52
56
  const args = Array.from(arguments);
53
- // 2D mirror with target objects: mirror(axis/line, geometries[])
54
- if (isAxisLike(args[0]) || args[0] instanceof SceneObject) {
55
- let axis = null;
56
- if (args[0] instanceof AxisObjectBase) {
57
- axis = args[0];
58
- }
59
- else if (args[0] instanceof SceneObject) {
60
- const line = args[0];
61
- axis = new AxisFromEdge(line);
62
- context.addSceneObject(axis);
63
- }
64
- else {
65
- const a = normalizeAxis(args[0]);
66
- axis = new AxisObject(a);
67
- context.addSceneObject(axis);
68
- }
57
+ if (activeSketch && (isAxisLike(args[0]) || args[0] instanceof SceneObject)) {
58
+ const axis = resolveAxis(args[0], context);
69
59
  const targetObjects = args.slice(1);
70
60
  const mirror = new MirrorShape2D(axis, targetObjects);
71
- if (!(args[0] instanceof AxisObjectBase) && !(args[0] instanceof SceneObject)) {
72
- context.addSceneObject(axis);
73
- }
74
61
  context.addSceneObject(mirror);
75
62
  return mirror;
76
63
  }
77
- // 3D shape mirror: mirror(plane, ...objects)
78
- const targetObjects = args.slice(1);
79
- let planeObj;
80
- if (!(args[0] instanceof PlaneObjectBase)) {
81
- const normalizedPlane = normalizePlane(args[0]);
82
- planeObj = new PlaneObject(normalizedPlane);
83
- context.addSceneObject(planeObj);
84
- }
85
- else {
86
- planeObj = args[0];
64
+ let planeArg = args[0];
65
+ if (isStandardAxis(planeArg)) {
66
+ planeArg = axisToPlaneName[planeArg];
87
67
  }
68
+ const planeObj = resolvePlane(planeArg, context);
69
+ const targetObjects = args.slice(1);
88
70
  const mirror = new MirrorShape(planeObj, targetObjects);
89
71
  context.addSceneObject(mirror);
90
72
  return mirror;