fluidcad 0.0.29 → 0.0.31

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 (144) hide show
  1. package/lib/dist/common/build-error.d.ts +13 -0
  2. package/lib/dist/common/build-error.js +18 -0
  3. package/lib/dist/common/describe-error.d.ts +6 -0
  4. package/lib/dist/common/describe-error.js +26 -0
  5. package/lib/dist/common/operand-check.d.ts +19 -0
  6. package/lib/dist/common/operand-check.js +38 -0
  7. package/lib/dist/common/scene-object.d.ts +9 -0
  8. package/lib/dist/common/scene-object.js +13 -0
  9. package/lib/dist/common/shape-factory.d.ts +1 -1
  10. package/lib/dist/core/2d/aline.d.ts +12 -12
  11. package/lib/dist/core/2d/aline.js +13 -8
  12. package/lib/dist/core/2d/arc.d.ts +4 -2
  13. package/lib/dist/core/2d/back.d.ts +12 -0
  14. package/lib/dist/core/2d/back.js +11 -0
  15. package/lib/dist/core/2d/hline.d.ts +19 -12
  16. package/lib/dist/core/2d/hline.js +24 -10
  17. package/lib/dist/core/2d/hmove.d.ts +8 -1
  18. package/lib/dist/core/2d/hmove.js +6 -2
  19. package/lib/dist/core/2d/index.d.ts +1 -0
  20. package/lib/dist/core/2d/index.js +1 -0
  21. package/lib/dist/core/2d/pmove.d.ts +13 -3
  22. package/lib/dist/core/2d/pmove.js +6 -2
  23. package/lib/dist/core/2d/vline.d.ts +19 -12
  24. package/lib/dist/core/2d/vline.js +20 -10
  25. package/lib/dist/core/2d/vmove.d.ts +8 -1
  26. package/lib/dist/core/2d/vmove.js +8 -4
  27. package/lib/dist/core/interfaces.d.ts +54 -0
  28. package/lib/dist/core/mirror.d.ts +7 -7
  29. package/lib/dist/core/rotate.d.ts +5 -5
  30. package/lib/dist/core/translate.d.ts +9 -9
  31. package/lib/dist/features/2d/aline.d.ts +8 -5
  32. package/lib/dist/features/2d/aline.js +75 -19
  33. package/lib/dist/features/2d/arc.d.ts +3 -0
  34. package/lib/dist/features/2d/arc.js +28 -1
  35. package/lib/dist/features/2d/back.d.ts +14 -0
  36. package/lib/dist/features/2d/back.js +35 -0
  37. package/lib/dist/features/2d/hline.d.ts +9 -4
  38. package/lib/dist/features/2d/hline.js +69 -14
  39. package/lib/dist/features/2d/hmove.d.ts +2 -2
  40. package/lib/dist/features/2d/hmove.js +32 -7
  41. package/lib/dist/features/2d/intersect.js +17 -10
  42. package/lib/dist/features/2d/pmove.d.ts +2 -2
  43. package/lib/dist/features/2d/pmove.js +47 -7
  44. package/lib/dist/features/2d/projection.d.ts +1 -1
  45. package/lib/dist/features/2d/projection.js +25 -15
  46. package/lib/dist/features/2d/sketch.d.ts +3 -2
  47. package/lib/dist/features/2d/sketch.js +25 -4
  48. package/lib/dist/features/2d/tarc-to-point.js +0 -3
  49. package/lib/dist/features/2d/tarc.js +0 -3
  50. package/lib/dist/features/2d/tline.js +0 -3
  51. package/lib/dist/features/2d/vline.d.ts +9 -4
  52. package/lib/dist/features/2d/vline.js +71 -15
  53. package/lib/dist/features/2d/vmove.d.ts +2 -2
  54. package/lib/dist/features/2d/vmove.js +32 -7
  55. package/lib/dist/features/axis-from-edge.d.ts +1 -0
  56. package/lib/dist/features/axis-from-edge.js +8 -0
  57. package/lib/dist/features/chamfer.d.ts +1 -0
  58. package/lib/dist/features/chamfer.js +6 -0
  59. package/lib/dist/features/color.d.ts +1 -0
  60. package/lib/dist/features/color.js +6 -0
  61. package/lib/dist/features/common.d.ts +1 -0
  62. package/lib/dist/features/common.js +9 -0
  63. package/lib/dist/features/common2d.d.ts +1 -0
  64. package/lib/dist/features/common2d.js +9 -0
  65. package/lib/dist/features/draft.d.ts +1 -0
  66. package/lib/dist/features/draft.js +6 -0
  67. package/lib/dist/features/fillet.d.ts +1 -0
  68. package/lib/dist/features/fillet.js +6 -0
  69. package/lib/dist/features/fillet2d.d.ts +1 -0
  70. package/lib/dist/features/fillet2d.js +9 -0
  71. package/lib/dist/features/fuse.d.ts +1 -0
  72. package/lib/dist/features/fuse.js +6 -0
  73. package/lib/dist/features/fuse2d.d.ts +1 -0
  74. package/lib/dist/features/fuse2d.js +9 -0
  75. package/lib/dist/features/lazy-scene-object.d.ts +1 -0
  76. package/lib/dist/features/lazy-scene-object.js +3 -0
  77. package/lib/dist/features/lazy-vertex.d.ts +1 -0
  78. package/lib/dist/features/lazy-vertex.js +3 -0
  79. package/lib/dist/features/loft.d.ts +1 -0
  80. package/lib/dist/features/loft.js +6 -0
  81. package/lib/dist/features/mirror-shape.d.ts +3 -0
  82. package/lib/dist/features/mirror-shape.js +44 -8
  83. package/lib/dist/features/mirror-shape2d.d.ts +2 -0
  84. package/lib/dist/features/mirror-shape2d.js +22 -1
  85. package/lib/dist/features/plane-from-object.d.ts +1 -0
  86. package/lib/dist/features/plane-from-object.js +8 -0
  87. package/lib/dist/features/rotate.d.ts +3 -0
  88. package/lib/dist/features/rotate.js +25 -0
  89. package/lib/dist/features/rotate2d.d.ts +2 -0
  90. package/lib/dist/features/rotate2d.js +16 -0
  91. package/lib/dist/features/shell.d.ts +1 -0
  92. package/lib/dist/features/shell.js +6 -0
  93. package/lib/dist/features/subtract.d.ts +1 -0
  94. package/lib/dist/features/subtract.js +5 -0
  95. package/lib/dist/features/subtract2d.d.ts +1 -0
  96. package/lib/dist/features/subtract2d.js +5 -0
  97. package/lib/dist/features/sweep.d.ts +1 -0
  98. package/lib/dist/features/sweep.js +4 -0
  99. package/lib/dist/features/translate.d.ts +3 -0
  100. package/lib/dist/features/translate.js +32 -2
  101. package/lib/dist/oc/boolean-ops.d.ts +2 -2
  102. package/lib/dist/oc/edge-ops.d.ts +17 -0
  103. package/lib/dist/oc/edge-ops.js +60 -0
  104. package/lib/dist/oc/face-query.js +17 -13
  105. package/lib/dist/oc/ray-intersect.d.ts +17 -0
  106. package/lib/dist/oc/ray-intersect.js +89 -0
  107. package/lib/dist/oc/shell-ops.js +15 -2
  108. package/lib/dist/oc/thin-face-maker.d.ts +15 -0
  109. package/lib/dist/oc/thin-face-maker.js +48 -7
  110. package/lib/dist/oc/wire-ops.d.ts +14 -0
  111. package/lib/dist/oc/wire-ops.js +38 -0
  112. package/lib/dist/rendering/render.js +10 -5
  113. package/lib/dist/tests/common/describe-error.test.d.ts +1 -0
  114. package/lib/dist/tests/common/describe-error.test.js +36 -0
  115. package/lib/dist/tests/features/2d/back.test.d.ts +1 -0
  116. package/lib/dist/tests/features/2d/back.test.js +60 -0
  117. package/lib/dist/tests/features/2d/constrained.test.js +4 -4
  118. package/lib/dist/tests/features/2d/intersect.test.js +43 -0
  119. package/lib/dist/tests/features/2d/line.test.js +88 -2
  120. package/lib/dist/tests/features/2d/move.test.js +72 -1
  121. package/lib/dist/tests/features/2d/project-regression.test.js +35 -0
  122. package/lib/dist/tests/features/2d/slot-from-edge.test.js +1 -1
  123. package/lib/dist/tests/features/color-lineage.test.js +24 -0
  124. package/lib/dist/tests/features/cylinder-curve-filter.test.d.ts +1 -0
  125. package/lib/dist/tests/features/cylinder-curve-filter.test.js +99 -0
  126. package/lib/dist/tests/features/mirror.test.js +132 -0
  127. package/lib/dist/tests/features/mirror2d.test.js +63 -0
  128. package/lib/dist/tests/features/rotate.test.js +62 -0
  129. package/lib/dist/tests/features/rotate2d.test.js +47 -0
  130. package/lib/dist/tests/features/subtract-consumed-input.test.d.ts +1 -0
  131. package/lib/dist/tests/features/subtract-consumed-input.test.js +28 -0
  132. package/lib/dist/tests/features/thin-extrude-offset-fix.test.d.ts +1 -0
  133. package/lib/dist/tests/features/thin-extrude-offset-fix.test.js +34 -0
  134. package/lib/dist/tests/features/thin-revolve.test.js +2 -2
  135. package/lib/dist/tests/features/translate.test.js +63 -0
  136. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  137. package/package.json +2 -3
  138. package/server/dist/index.js +77 -45
  139. package/server/dist/ws-protocol.d.ts +11 -0
  140. package/ui/dist/assets/index-B15vMQZ2.js +4946 -0
  141. package/ui/dist/assets/index-DR7c2Qk9.css +2 -0
  142. package/ui/dist/index.html +2 -2
  143. package/ui/dist/assets/index-VKkXzLfR.css +0 -2
  144. package/ui/dist/assets/index-VY48_dgc.js +0 -4909
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Error thrown by feature pre-build validation when an operand or input is
3
+ * unsuitable for the operation. Carries an optional hint so the user can be
4
+ * told both what went wrong and how to fix it.
5
+ *
6
+ * Extends Error so the renderer's existing try/catch in `buildObject` and
7
+ * `describeError` keep working unchanged.
8
+ */
9
+ export declare class BuildError extends Error {
10
+ readonly diagnostic: string;
11
+ readonly hint?: string;
12
+ constructor(diagnostic: string, hint?: string);
13
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Error thrown by feature pre-build validation when an operand or input is
3
+ * unsuitable for the operation. Carries an optional hint so the user can be
4
+ * told both what went wrong and how to fix it.
5
+ *
6
+ * Extends Error so the renderer's existing try/catch in `buildObject` and
7
+ * `describeError` keep working unchanged.
8
+ */
9
+ export class BuildError extends Error {
10
+ diagnostic;
11
+ hint;
12
+ constructor(diagnostic, hint) {
13
+ super(hint ? `${diagnostic}\nHint: ${hint}` : diagnostic);
14
+ this.diagnostic = diagnostic;
15
+ this.hint = hint;
16
+ this.name = 'BuildError';
17
+ }
18
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Convert any thrown value to a readable string. OCC throws come through
3
+ * Emscripten as raw numeric pointers; decode those via OCJS.getStandard_FailureData
4
+ * so the actual reason ("Offset wire is not closed.", etc.) reaches the user.
5
+ */
6
+ export declare function describeError(error: unknown): string;
@@ -0,0 +1,26 @@
1
+ import { getOC } from "../oc/init.js";
2
+ /**
3
+ * Convert any thrown value to a readable string. OCC throws come through
4
+ * Emscripten as raw numeric pointers; decode those via OCJS.getStandard_FailureData
5
+ * so the actual reason ("Offset wire is not closed.", etc.) reaches the user.
6
+ */
7
+ export function describeError(error) {
8
+ if (error instanceof Error) {
9
+ return error.message;
10
+ }
11
+ if (typeof error === 'number' && Number.isFinite(error)) {
12
+ try {
13
+ const oc = getOC();
14
+ const failure = oc.OCJS.getStandard_FailureData(error);
15
+ const msg = failure.GetMessageString();
16
+ if (msg) {
17
+ return `OCC: ${msg}`;
18
+ }
19
+ return `OCC error (ptr=${error})`;
20
+ }
21
+ catch (e) {
22
+ console.log("[describeError] decode failed:", e);
23
+ }
24
+ }
25
+ return String(error);
26
+ }
@@ -0,0 +1,19 @@
1
+ import { SceneObject } from "./scene-object.js";
2
+ import { Shape } from "./shape.js";
3
+ interface RequireShapesOpts {
4
+ /** Require exactly this many shapes. */
5
+ count?: number;
6
+ /** Require every shape to be of this type. */
7
+ type?: string;
8
+ }
9
+ /**
10
+ * Validate that an operand SceneObject still owns shapes the consumer can use,
11
+ * and produce a uniform diagnostic when it doesn't.
12
+ *
13
+ * The most common failure mode is a SceneObject whose geometry was consumed by
14
+ * an earlier op (e.g. `translate(amount, target)` moves the shape from
15
+ * `target` onto the translate object). `getRemovedShapes()` records who took
16
+ * each shape, so we can name the consumer in the error.
17
+ */
18
+ export declare function requireShapes(obj: SceneObject, operandLabel: string, consumerType: string, opts?: RequireShapesOpts): Shape[];
19
+ export {};
@@ -0,0 +1,38 @@
1
+ import { BuildError } from "./build-error.js";
2
+ /**
3
+ * Validate that an operand SceneObject still owns shapes the consumer can use,
4
+ * and produce a uniform diagnostic when it doesn't.
5
+ *
6
+ * The most common failure mode is a SceneObject whose geometry was consumed by
7
+ * an earlier op (e.g. `translate(amount, target)` moves the shape from
8
+ * `target` onto the translate object). `getRemovedShapes()` records who took
9
+ * each shape, so we can name the consumer in the error.
10
+ */
11
+ export function requireShapes(obj, operandLabel, consumerType, opts) {
12
+ // Lazy operands (LazySelectionSceneObject, LazyVertex) only populate their
13
+ // shapes during build. Their pre-build emptiness is expected, so skip —
14
+ // the build itself still validates them.
15
+ if (obj.isLazy()) {
16
+ return [];
17
+ }
18
+ const shapes = obj.getShapes();
19
+ if (shapes.length === 0) {
20
+ const removed = obj.getRemovedShapes();
21
+ const objLabel = `${operandLabel} (${obj.getType()})`;
22
+ if (removed.length > 0) {
23
+ const consumers = [...new Set(removed.map(r => r.removedBy.getType()))].join(", ");
24
+ throw new BuildError(`${consumerType}: ${objLabel} has no shapes — its geometry was consumed by ${consumers}.`, `Reference the result of ${consumers} as the operand instead of ${obj.getType()}.`);
25
+ }
26
+ throw new BuildError(`${consumerType}: ${objLabel} has no shapes.`, `Make sure the upstream operation produced geometry.`);
27
+ }
28
+ if (opts?.count !== undefined && shapes.length !== opts.count) {
29
+ throw new BuildError(`${consumerType}: ${operandLabel} has ${shapes.length} shapes, expected ${opts.count}.`);
30
+ }
31
+ if (opts?.type) {
32
+ const wrong = shapes.find(s => s.getType() !== opts.type);
33
+ if (wrong) {
34
+ throw new BuildError(`${consumerType}: ${operandLabel} has a shape of type '${wrong.getType()}', expected '${opts.type}'.`);
35
+ }
36
+ }
37
+ return shapes;
38
+ }
@@ -69,11 +69,20 @@ export declare abstract class SceneObject implements Comparable<SceneObject>, Se
69
69
  getPreviousSibling(obj: SceneObject): SceneObject | null;
70
70
  getPreviousSiblings(obj: SceneObject): SceneObject[];
71
71
  isContainer(): boolean;
72
+ isLazy(): boolean;
72
73
  saveShapesSnapshot(context: BuildSceneObjectContext): void;
73
74
  getSnapshot(): Map<SceneObject, Shape[]>;
74
75
  abstract serialize(scope?: Set<SceneObject>): any;
75
76
  abstract getType(): string;
76
77
  abstract build(context?: BuildSceneObjectContext): void;
78
+ /**
79
+ * Pre-build validation hook. The renderer calls this before `build()` so
80
+ * features can fail fast with a clear diagnostic (typically a `BuildError`)
81
+ * before any OC work runs. Default is a no-op; overrides should not mutate
82
+ * state. Existing per-feature checks inside `build()` are intentionally not
83
+ * removed — this hook is additive.
84
+ */
85
+ validate(): void;
77
86
  getAppliedTransform(): Matrix4 | null;
78
87
  protected composeAppliedTransform(matrix: Matrix4): void;
79
88
  compareTo(other: SceneObject): boolean;
@@ -85,6 +85,9 @@ export class SceneObject {
85
85
  isContainer() {
86
86
  return false;
87
87
  }
88
+ isLazy() {
89
+ return false;
90
+ }
88
91
  // called by containers to save the shapes state up to this object
89
92
  saveShapesSnapshot(context) {
90
93
  const upToHere = context.getSceneObjects();
@@ -97,6 +100,16 @@ export class SceneObject {
97
100
  getSnapshot() {
98
101
  return this.getState('snapshot') || [];
99
102
  }
103
+ /**
104
+ * Pre-build validation hook. The renderer calls this before `build()` so
105
+ * features can fail fast with a clear diagnostic (typically a `BuildError`)
106
+ * before any OC work runs. Default is a no-op; overrides should not mutate
107
+ * state. Existing per-feature checks inside `build()` are intentionally not
108
+ * removed — this hook is additive.
109
+ */
110
+ validate() {
111
+ // Override in subclasses to add operand checks via `requireShapes` etc.
112
+ }
100
113
  getAppliedTransform() {
101
114
  return this._appliedTransform;
102
115
  }
@@ -4,5 +4,5 @@ import { Wire } from "./wire.js";
4
4
  import { Face } from "./face.js";
5
5
  import { Edge } from "./edge.js";
6
6
  export declare class ShapeFactory {
7
- static fromShape(shape: TopoDS_Shape): Solid | Face | Edge | Wire;
7
+ static fromShape(shape: TopoDS_Shape): Edge | Wire | Solid | Face;
8
8
  }
@@ -1,28 +1,28 @@
1
1
  import { PlaneLike } from "../../math/plane.js";
2
- import { IGeometry, ISceneObject } from "../interfaces.js";
2
+ import { IALine, ISceneObject } from "../interfaces.js";
3
3
  interface ALineFunction {
4
4
  /**
5
5
  * Draws a line at the given angle with the given length.
6
- * @param length - The line length
6
+ * Chain `.centered()` to center the line on the current position.
7
7
  * @param angle - The angle in degrees
8
- * @param centered - Whether to center the line on the current position
8
+ * @param length - The line length
9
9
  */
10
- (length: number, angle: number, centered?: boolean): IGeometry;
10
+ (angle: number, length: number): IALine;
11
11
  /**
12
- * Draws a line at the given angle on a specific plane.
13
- * @param targetPlane - The plane to draw on
14
- * @param length - The line length
12
+ * Draws a line at the given angle that ends where it intersects the target
13
+ * geometry. The nearest intersection along the line's direction (in either
14
+ * sign) is used.
15
15
  * @param angle - The angle in degrees
16
+ * @param target - The geometry to intersect with
16
17
  */
17
- (targetPlane: PlaneLike | ISceneObject, length: number, angle: number): IGeometry;
18
+ (angle: number, target: ISceneObject): IALine;
18
19
  /**
19
- * Draws a centered line at the given angle on a specific plane.
20
+ * Draws a line at the given angle on a specific plane.
20
21
  * @param targetPlane - The plane to draw on
21
- * @param length - The line length
22
22
  * @param angle - The angle in degrees
23
- * @param centered - Whether to center the line on the current position
23
+ * @param length - The line length
24
24
  */
25
- (targetPlane: PlaneLike | ISceneObject, length: number, angle: number, centered: boolean): IGeometry;
25
+ (targetPlane: PlaneLike | ISceneObject, angle: number, length: number): IALine;
26
26
  }
27
27
  declare const _default: ALineFunction;
28
28
  export default _default;
@@ -7,22 +7,27 @@ function build(context) {
7
7
  return function line() {
8
8
  let planeObj = null;
9
9
  let argOffset = 0;
10
- // Detect plane as first argument (only valid outside a sketch)
10
+ const inSketch = context.getActiveSketch() !== null;
11
11
  if (arguments.length > 0) {
12
12
  const firstArg = arguments[0];
13
- if (isPlaneLike(firstArg) || firstArg instanceof SceneObject) {
14
- if (context.getActiveSketch() !== null) {
13
+ if (isPlaneLike(firstArg)) {
14
+ if (inSketch) {
15
15
  throw new Error("aLine(plane, ...) cannot be used inside a sketch. Use aLine(...) instead.");
16
16
  }
17
17
  planeObj = resolvePlane(firstArg, context);
18
18
  argOffset = 1;
19
19
  }
20
+ else if (!inSketch && firstArg instanceof SceneObject) {
21
+ planeObj = resolvePlane(firstArg, context);
22
+ argOffset = 1;
23
+ }
20
24
  }
21
- const argCount = arguments.length - argOffset;
22
- const length = arguments[argOffset];
23
- const angle = arguments[argOffset + 1];
24
- const centered = argCount >= 3 ? arguments[argOffset + 2] : false;
25
- const aline = new AngledLine(length, angle, centered, planeObj);
25
+ const angle = arguments[argOffset];
26
+ const second = arguments[argOffset + 1];
27
+ const lengthOrTarget = second instanceof SceneObject
28
+ ? second
29
+ : second;
30
+ const aline = new AngledLine(angle, lengthOrTarget, planeObj);
26
31
  context.addSceneObject(aline);
27
32
  return aline;
28
33
  };
@@ -18,10 +18,12 @@ interface ArcFunction {
18
18
  (startPoint: Point2DLike, endPoint: Point2DLike): IArcPoints;
19
19
  /**
20
20
  * Draws an arc by radius and angle range at the current position.
21
+ * Angles are measured relative to the current tangent (the tangent of the previous
22
+ * geometry, or +X when there is none).
21
23
  * Chain `.centered()` to center the arc symmetrically around the start angle.
22
24
  * @param radius - The arc radius
23
- * @param startAngle - The start angle in degrees (defaults to 0)
24
- * @param endAngle - The end angle in degrees (defaults to 180)
25
+ * @param startAngle - The start angle in degrees, relative to the current tangent (defaults to 0)
26
+ * @param endAngle - The end angle in degrees, relative to the current tangent (defaults to 180)
25
27
  */
26
28
  (radius: number, startAngle?: number, endAngle?: number): IArcAngles;
27
29
  /**
@@ -0,0 +1,12 @@
1
+ import { IGeometry } from "../interfaces.js";
2
+ interface BackFunction {
3
+ /** Reverts the cursor to the previous position. */
4
+ (): IGeometry;
5
+ /**
6
+ * Reverts the cursor `count` position-changes back.
7
+ * @param count - How many prior cursor changes to undo (default 1).
8
+ */
9
+ (count: number): IGeometry;
10
+ }
11
+ declare const _default: BackFunction;
12
+ export default _default;
@@ -0,0 +1,11 @@
1
+ import { Back } from "../../features/2d/back.js";
2
+ import { registerBuilder } from "../../index.js";
3
+ function build(context) {
4
+ return function back() {
5
+ const count = arguments[0] ?? 1;
6
+ const obj = new Back(count);
7
+ context.addSceneObject(obj);
8
+ return obj;
9
+ };
10
+ }
11
+ export default registerBuilder(build);
@@ -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 { IHLine, ISceneObject } from "../interfaces.js";
4
4
  interface HLineFunction {
5
5
  /**
6
6
  * Draws a horizontal 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): IHLine;
11
+ /**
12
+ * Draws a horizontal line that ends where it intersects the target geometry.
13
+ * The nearest intersection (in either direction along the X axis) is used.
14
+ * @param target - The geometry to intersect with
15
+ */
16
+ (target: ISceneObject): IHLine;
11
17
  /**
12
18
  * Draws a horizontal 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): IHLine;
18
24
  /**
19
- * Draws a horizontal line on a specific plane.
20
- * @param targetPlane - The plane to draw on
21
- * @param distance - The line length
25
+ * Draws a horizontal line from a start point that ends where it intersects
26
+ * the target geometry. The nearest intersection (in either direction along
27
+ * the X axis) is used.
28
+ * @param start - The start point
29
+ * @param target - The geometry to intersect with
22
30
  */
23
- (targetPlane: PlaneLike | ISceneObject, distance: number): IGeometry;
31
+ (start: Point2DLike, target: ISceneObject): IHLine;
24
32
  /**
25
- * Draws a horizontal line with centering on a specific plane.
33
+ * Draws a horizontal line on a specific plane.
26
34
  * @param targetPlane - The plane to draw on
27
35
  * @param distance - The line length
28
- * @param centered - Whether to center the line on the current position
29
36
  */
30
- (targetPlane: PlaneLike | ISceneObject, distance: number, centered: boolean): IGeometry;
37
+ (targetPlane: PlaneLike | ISceneObject, distance: number): IHLine;
31
38
  }
32
39
  declare const _default: HLineFunction;
33
40
  export default _default;
@@ -10,30 +10,44 @@ function build(context) {
10
10
  return function line() {
11
11
  let planeObj = null;
12
12
  let argOffset = 0;
13
- // Detect plane as first argument (only valid outside a sketch)
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.
14
18
  if (arguments.length > 0) {
15
19
  const firstArg = arguments[0];
16
- if (isPlaneLike(firstArg) || (firstArg instanceof SceneObject && !isPoint2DLike(firstArg))) {
17
- if (context.getActiveSketch() !== null) {
20
+ if (isPlaneLike(firstArg)) {
21
+ if (inSketch) {
18
22
  throw new Error("hLine(plane, ...) cannot be used inside a sketch. Use hLine(...) instead.");
19
23
  }
20
24
  planeObj = resolvePlane(firstArg, context);
21
25
  argOffset = 1;
22
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;
23
37
  }
24
- const argCount = arguments.length - argOffset;
25
38
  if (argOffset === 0 && typeof arguments[0] !== 'number') {
26
- // hline(start, distance) or hline(start, distance, centered)
39
+ // hLine(start, distance) or hLine(start, target)
27
40
  const start = normalizePoint2D(arguments[0]);
28
- const distance = arguments[1];
29
- const centered = argCount >= 3 ? arguments[2] : false;
30
- 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);
31
46
  context.addSceneObjects([new Move(start), hline]);
32
47
  return hline;
33
48
  }
34
49
  const distance = arguments[argOffset];
35
- const centered = argCount >= 2 ? arguments[argOffset + 1] : false;
36
- const hline = new HorizontalLine(distance, centered, planeObj);
50
+ const hline = new HorizontalLine(distance, planeObj);
37
51
  context.addSceneObject(hline);
38
52
  return hline;
39
53
  };
@@ -1,9 +1,16 @@
1
+ import { IGeometry, ISceneObject } from "../interfaces.js";
1
2
  interface HMoveFunction {
2
3
  /**
3
4
  * Moves the cursor horizontally by the given distance.
4
5
  * @param distance - The horizontal distance to move
5
6
  */
6
- (distance: number): void;
7
+ (distance: number): IGeometry;
8
+ /**
9
+ * Moves the cursor horizontally to the nearest intersection with the target geometry.
10
+ * The nearest intersection (in either direction along the X axis) is used.
11
+ * @param target - The geometry to intersect with
12
+ */
13
+ (target: ISceneObject): IGeometry;
7
14
  }
8
15
  declare const _default: HMoveFunction;
9
16
  export default _default;
@@ -1,9 +1,13 @@
1
1
  import { HMove } from "../../features/2d/hmove.js";
2
+ import { SceneObject } from "../../common/scene-object.js";
2
3
  import { registerBuilder } from "../../index.js";
3
4
  function build(context) {
4
5
  return function move() {
5
- const distance = arguments[0];
6
- const hmove = new HMove(distance);
6
+ const arg = arguments[0];
7
+ const distanceOrTarget = arg instanceof SceneObject
8
+ ? arg
9
+ : arg;
10
+ const hmove = new HMove(distanceOrTarget);
7
11
  context.addSceneObject(hmove);
8
12
  return hmove;
9
13
  };
@@ -22,3 +22,4 @@ export { default as project } from './project.js';
22
22
  export { default as intersect } from './intersect.js';
23
23
  export { default as bezier } from './bezier.js';
24
24
  export { default as center } from './center.js';
25
+ export { default as back } from './back.js';
@@ -22,3 +22,4 @@ export { default as project } from './project.js';
22
22
  export { default as intersect } from './intersect.js';
23
23
  export { default as bezier } from './bezier.js';
24
24
  export { default as center } from './center.js';
25
+ export { default as back } from './back.js';
@@ -1,11 +1,21 @@
1
- import { IGeometry } from "../interfaces.js";
1
+ import { IGeometry, ISceneObject } from "../interfaces.js";
2
2
  interface PolarMoveFunction {
3
3
  /**
4
- * Moves the cursor by polar coordinates.
4
+ * Moves the cursor by polar coordinates, relative to the current tangent.
5
+ * The angle is measured from the tangent of the previous geometry (defaults to +X
6
+ * when there is none).
5
7
  * @param radius - The distance to move
6
- * @param angle - The angle in degrees
8
+ * @param angle - The angle in degrees, relative to the current tangent
7
9
  */
8
10
  (radius: number, angle: number): IGeometry;
11
+ /**
12
+ * Moves the cursor along the given angle (relative to the current tangent) to the
13
+ * nearest intersection with the target geometry. The nearest intersection (in either
14
+ * direction along the ray) is used.
15
+ * @param target - The geometry to intersect with
16
+ * @param angle - The angle in degrees, relative to the current tangent
17
+ */
18
+ (target: ISceneObject, angle: number): IGeometry;
9
19
  }
10
20
  declare const _default: PolarMoveFunction;
11
21
  export default _default;
@@ -1,10 +1,14 @@
1
1
  import { PolarMove } from "../../features/2d/pmove.js";
2
+ import { SceneObject } from "../../common/scene-object.js";
2
3
  import { registerBuilder } from "../../index.js";
3
4
  function build(context) {
4
5
  return function pmove() {
5
- const radius = arguments[0];
6
+ const arg0 = arguments[0];
6
7
  const angle = arguments[1] * Math.PI / 180;
7
- const pmove = new PolarMove(radius, angle);
8
+ const radiusOrTarget = arg0 instanceof SceneObject
9
+ ? arg0
10
+ : arg0;
11
+ const pmove = new PolarMove(radiusOrTarget, angle);
8
12
  context.addSceneObject(pmove);
9
13
  return pmove;
10
14
  };
@@ -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 targetPlane - The plane to draw on
21
- * @param distance - The line length
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
- (targetPlane: PlaneLike | ISceneObject, distance: number): IGeometry;
31
+ (start: Point2DLike, target: ISceneObject): IVLine;
24
32
  /**
25
- * Draws a vertical line with centering on a specific plane.
33
+ * Draws a vertical line on a specific plane.
26
34
  * @param targetPlane - The plane to draw on
27
35
  * @param distance - The line length
28
- * @param centered - Whether to center the line on the current position
29
36
  */
30
- (targetPlane: PlaneLike | ISceneObject, distance: number, centered: boolean): IGeometry;
37
+ (targetPlane: PlaneLike | ISceneObject, distance: number): IVLine;
31
38
  }
32
39
  declare const _default: VLineFunction;
33
40
  export default _default;
@@ -10,30 +10,40 @@ function build(context) {
10
10
  return function line() {
11
11
  let planeObj = null;
12
12
  let argOffset = 0;
13
- // Detect plane as first argument (only valid outside a sketch)
13
+ const inSketch = context.getActiveSketch() !== null;
14
14
  if (arguments.length > 0) {
15
15
  const firstArg = arguments[0];
16
- if (isPlaneLike(firstArg) || (firstArg instanceof SceneObject && !isPoint2DLike(firstArg))) {
17
- if (context.getActiveSketch() !== null) {
16
+ if (isPlaneLike(firstArg)) {
17
+ if (inSketch) {
18
18
  throw new Error("vLine(plane, ...) cannot be used inside a sketch. Use vLine(...) instead.");
19
19
  }
20
20
  planeObj = resolvePlane(firstArg, context);
21
21
  argOffset = 1;
22
22
  }
23
+ else if (!inSketch && firstArg instanceof SceneObject && !isPoint2DLike(firstArg)) {
24
+ planeObj = resolvePlane(firstArg, context);
25
+ argOffset = 1;
26
+ }
27
+ }
28
+ if (argOffset === 0 && inSketch && arguments[0] instanceof SceneObject && !isPoint2DLike(arguments[0])) {
29
+ // vLine(target)
30
+ const vline = new VerticalLine(arguments[0], null);
31
+ context.addSceneObject(vline);
32
+ return vline;
23
33
  }
24
- const argCount = arguments.length - argOffset;
25
34
  if (argOffset === 0 && typeof arguments[0] !== 'number') {
26
- // vline(start, distance) or vline(start, distance, centered)
35
+ // vLine(start, distance) or vLine(start, target)
27
36
  const start = normalizePoint2D(arguments[0]);
28
- const distance = arguments[1];
29
- const centered = argCount >= 3 ? arguments[2] : false;
30
- const vline = new VerticalLine(distance, centered, planeObj);
37
+ const second = arguments[1];
38
+ const distanceOrTarget = second instanceof SceneObject
39
+ ? second
40
+ : second;
41
+ const vline = new VerticalLine(distanceOrTarget, planeObj);
31
42
  context.addSceneObjects([new Move(start), vline]);
32
43
  return vline;
33
44
  }
34
45
  const distance = arguments[argOffset];
35
- const centered = argCount >= 2 ? arguments[argOffset + 1] : false;
36
- const vline = new VerticalLine(distance, centered, planeObj);
46
+ const vline = new VerticalLine(distance, planeObj);
37
47
  context.addSceneObject(vline);
38
48
  return vline;
39
49
  };
@@ -1,9 +1,16 @@
1
+ import { IGeometry, ISceneObject } from "../interfaces.js";
1
2
  interface VMoveFunction {
2
3
  /**
3
4
  * Moves the cursor vertically by the given distance.
4
5
  * @param distance - The vertical distance to move
5
6
  */
6
- (distance: number): void;
7
+ (distance: number): IGeometry;
8
+ /**
9
+ * Moves the cursor vertically to the nearest intersection with the target geometry.
10
+ * The nearest intersection (in either direction along the Y axis) is used.
11
+ * @param target - The geometry to intersect with
12
+ */
13
+ (target: ISceneObject): IGeometry;
7
14
  }
8
15
  declare const _default: VMoveFunction;
9
16
  export default _default;