fluidcad 0.0.30 → 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.
- package/lib/dist/common/build-error.d.ts +13 -0
- package/lib/dist/common/build-error.js +18 -0
- package/lib/dist/common/describe-error.d.ts +6 -0
- package/lib/dist/common/describe-error.js +26 -0
- package/lib/dist/common/operand-check.d.ts +19 -0
- package/lib/dist/common/operand-check.js +38 -0
- package/lib/dist/common/scene-object.d.ts +8 -0
- package/lib/dist/common/scene-object.js +10 -0
- package/lib/dist/common/shape-factory.d.ts +1 -1
- package/lib/dist/core/2d/arc.d.ts +4 -2
- package/lib/dist/core/2d/hmove.d.ts +8 -1
- package/lib/dist/core/2d/hmove.js +6 -2
- package/lib/dist/core/2d/pmove.d.ts +13 -3
- package/lib/dist/core/2d/pmove.js +6 -2
- package/lib/dist/core/2d/vmove.d.ts +8 -1
- package/lib/dist/core/2d/vmove.js +8 -4
- package/lib/dist/core/interfaces.d.ts +1 -1
- package/lib/dist/features/2d/aline.js +6 -2
- package/lib/dist/features/2d/arc.d.ts +3 -0
- package/lib/dist/features/2d/arc.js +28 -1
- package/lib/dist/features/2d/hline.js +5 -1
- package/lib/dist/features/2d/hmove.d.ts +2 -2
- package/lib/dist/features/2d/hmove.js +32 -7
- package/lib/dist/features/2d/intersect.js +17 -10
- package/lib/dist/features/2d/pmove.d.ts +2 -2
- package/lib/dist/features/2d/pmove.js +47 -7
- package/lib/dist/features/2d/projection.d.ts +1 -1
- package/lib/dist/features/2d/projection.js +25 -15
- package/lib/dist/features/2d/sketch.d.ts +2 -2
- package/lib/dist/features/2d/sketch.js +10 -4
- package/lib/dist/features/2d/tarc-to-point.js +0 -3
- package/lib/dist/features/2d/tarc.js +0 -3
- package/lib/dist/features/2d/tline.js +0 -3
- package/lib/dist/features/2d/vline.js +5 -1
- package/lib/dist/features/2d/vmove.d.ts +2 -2
- package/lib/dist/features/2d/vmove.js +32 -7
- package/lib/dist/features/axis-from-edge.d.ts +1 -0
- package/lib/dist/features/axis-from-edge.js +8 -0
- package/lib/dist/features/chamfer.d.ts +1 -0
- package/lib/dist/features/chamfer.js +6 -0
- package/lib/dist/features/color.d.ts +1 -0
- package/lib/dist/features/color.js +6 -0
- package/lib/dist/features/common.d.ts +1 -0
- package/lib/dist/features/common.js +9 -0
- package/lib/dist/features/common2d.d.ts +1 -0
- package/lib/dist/features/common2d.js +9 -0
- package/lib/dist/features/draft.d.ts +1 -0
- package/lib/dist/features/draft.js +6 -0
- package/lib/dist/features/fillet.d.ts +1 -0
- package/lib/dist/features/fillet.js +6 -0
- package/lib/dist/features/fillet2d.d.ts +1 -0
- package/lib/dist/features/fillet2d.js +9 -0
- package/lib/dist/features/fuse.d.ts +1 -0
- package/lib/dist/features/fuse.js +6 -0
- package/lib/dist/features/fuse2d.d.ts +1 -0
- package/lib/dist/features/fuse2d.js +9 -0
- package/lib/dist/features/loft.d.ts +1 -0
- package/lib/dist/features/loft.js +6 -0
- package/lib/dist/features/mirror-shape.d.ts +1 -0
- package/lib/dist/features/mirror-shape.js +28 -8
- package/lib/dist/features/plane-from-object.d.ts +1 -0
- package/lib/dist/features/plane-from-object.js +8 -0
- package/lib/dist/features/rotate.d.ts +1 -0
- package/lib/dist/features/rotate.js +9 -0
- package/lib/dist/features/shell.d.ts +1 -0
- package/lib/dist/features/shell.js +6 -0
- package/lib/dist/features/subtract.d.ts +1 -0
- package/lib/dist/features/subtract.js +5 -0
- package/lib/dist/features/subtract2d.d.ts +1 -0
- package/lib/dist/features/subtract2d.js +5 -0
- package/lib/dist/features/sweep.d.ts +1 -0
- package/lib/dist/features/sweep.js +4 -0
- package/lib/dist/features/translate.d.ts +1 -0
- package/lib/dist/features/translate.js +9 -0
- package/lib/dist/oc/boolean-ops.d.ts +2 -2
- package/lib/dist/oc/edge-ops.d.ts +17 -0
- package/lib/dist/oc/edge-ops.js +60 -0
- package/lib/dist/oc/face-query.js +17 -13
- package/lib/dist/oc/ray-intersect.d.ts +3 -2
- package/lib/dist/oc/ray-intersect.js +2 -4
- package/lib/dist/oc/shell-ops.js +15 -2
- package/lib/dist/oc/thin-face-maker.d.ts +15 -0
- package/lib/dist/oc/thin-face-maker.js +48 -7
- package/lib/dist/oc/wire-ops.d.ts +14 -0
- package/lib/dist/oc/wire-ops.js +38 -0
- package/lib/dist/rendering/render.js +6 -4
- package/lib/dist/tests/common/describe-error.test.d.ts +1 -0
- package/lib/dist/tests/common/describe-error.test.js +36 -0
- package/lib/dist/tests/features/2d/intersect.test.js +43 -0
- package/lib/dist/tests/features/2d/move.test.js +72 -1
- package/lib/dist/tests/features/2d/project-regression.test.js +35 -0
- package/lib/dist/tests/features/color-lineage.test.js +24 -0
- package/lib/dist/tests/features/cylinder-curve-filter.test.d.ts +1 -0
- package/lib/dist/tests/features/cylinder-curve-filter.test.js +99 -0
- package/lib/dist/tests/features/mirror.test.js +74 -0
- package/lib/dist/tests/features/subtract-consumed-input.test.d.ts +1 -0
- package/lib/dist/tests/features/subtract-consumed-input.test.js +28 -0
- package/lib/dist/tests/features/thin-extrude-offset-fix.test.d.ts +1 -0
- package/lib/dist/tests/features/thin-extrude-offset-fix.test.js +34 -0
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -3
- package/ui/dist/assets/{index-6Ep4GPxf.js → index-B15vMQZ2.js} +102 -102
- package/ui/dist/assets/index-DR7c2Qk9.css +2 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-DRKfe6N9.css +0 -2
|
@@ -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
|
+
}
|
|
@@ -75,6 +75,14 @@ export declare abstract class SceneObject implements Comparable<SceneObject>, Se
|
|
|
75
75
|
abstract serialize(scope?: Set<SceneObject>): any;
|
|
76
76
|
abstract getType(): string;
|
|
77
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;
|
|
78
86
|
getAppliedTransform(): Matrix4 | null;
|
|
79
87
|
protected composeAppliedTransform(matrix: Matrix4): void;
|
|
80
88
|
compareTo(other: SceneObject): boolean;
|
|
@@ -100,6 +100,16 @@ export class SceneObject {
|
|
|
100
100
|
getSnapshot() {
|
|
101
101
|
return this.getState('snapshot') || [];
|
|
102
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
|
+
}
|
|
103
113
|
getAppliedTransform() {
|
|
104
114
|
return this._appliedTransform;
|
|
105
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):
|
|
7
|
+
static fromShape(shape: TopoDS_Shape): Edge | Wire | Solid | Face;
|
|
8
8
|
}
|
|
@@ -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
|
/**
|
|
@@ -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):
|
|
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
|
|
6
|
-
const
|
|
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
|
};
|
|
@@ -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
|
|
6
|
+
const arg0 = arguments[0];
|
|
6
7
|
const angle = arguments[1] * Math.PI / 180;
|
|
7
|
-
const
|
|
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,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):
|
|
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;
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { VMove } from "../../features/2d/vmove.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
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const arg = arguments[0];
|
|
7
|
+
const distanceOrTarget = arg instanceof SceneObject
|
|
8
|
+
? arg
|
|
9
|
+
: arg;
|
|
10
|
+
const vmove = new VMove(distanceOrTarget);
|
|
11
|
+
context.addSceneObject(vmove);
|
|
12
|
+
return vmove;
|
|
9
13
|
};
|
|
10
14
|
}
|
|
11
15
|
export default registerBuilder(build);
|
|
@@ -656,7 +656,7 @@ export interface ISweep extends IBooleanOperation {
|
|
|
656
656
|
*/
|
|
657
657
|
capEdges(...args: (number | EdgeFilterBuilder)[]): ISceneObject;
|
|
658
658
|
}
|
|
659
|
-
export interface IMirror extends
|
|
659
|
+
export interface IMirror extends IBooleanOperation {
|
|
660
660
|
/**
|
|
661
661
|
* Excludes the given objects from the mirror operation. Useful when
|
|
662
662
|
* mirroring "everything" but a few specific objects should be skipped,
|
|
@@ -22,7 +22,7 @@ export class AngledLine extends GeometrySceneObject {
|
|
|
22
22
|
}
|
|
23
23
|
build() {
|
|
24
24
|
const plane = this.targetPlane?.getPlane() || this.sketch.getPlane();
|
|
25
|
-
let tangent = this.sketch?.getTangentAt(this)
|
|
25
|
+
let tangent = this.sketch?.getTangentAt(this) ?? new Point2D(1, 0);
|
|
26
26
|
tangent = tangent.normalize();
|
|
27
27
|
const angleRad = rad(this.angle);
|
|
28
28
|
// 2D rotation of tangent by angle
|
|
@@ -48,7 +48,11 @@ export class AngledLine extends GeometrySceneObject {
|
|
|
48
48
|
throw new Error('aLine: .centered() cannot be combined with a target geometry');
|
|
49
49
|
}
|
|
50
50
|
startPoint = currentPos;
|
|
51
|
-
|
|
51
|
+
const hit = findNearestRayIntersection(plane, startPoint, direction, this.lengthOrTarget);
|
|
52
|
+
if (!hit) {
|
|
53
|
+
throw new Error("Line does not intersect target geometry");
|
|
54
|
+
}
|
|
55
|
+
endPoint = hit;
|
|
52
56
|
}
|
|
53
57
|
const start = plane.localToWorld(startPoint);
|
|
54
58
|
const end = plane.localToWorld(endPoint);
|
|
@@ -3,6 +3,7 @@ import { PlaneObjectBase } from "../plane-renderable-base.js";
|
|
|
3
3
|
import { GeometrySceneObject } from "./geometry.js";
|
|
4
4
|
import { LazyVertex } from "../lazy-vertex.js";
|
|
5
5
|
import { IArcPoints, IArcAngles } from "../../core/interfaces.js";
|
|
6
|
+
import { SceneObject } from "../../common/scene-object.js";
|
|
6
7
|
export declare class Arc extends GeometrySceneObject implements IArcPoints, IArcAngles {
|
|
7
8
|
private _startPoint;
|
|
8
9
|
private _endPoint;
|
|
@@ -27,6 +28,8 @@ export declare class Arc extends GeometrySceneObject implements IArcPoints, IArc
|
|
|
27
28
|
private buildWithCenter;
|
|
28
29
|
private buildFromAngles;
|
|
29
30
|
getType(): string;
|
|
31
|
+
getDependencies(): SceneObject[];
|
|
32
|
+
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
30
33
|
compareTo(other: Arc): boolean;
|
|
31
34
|
serialize(): Record<string, unknown>;
|
|
32
35
|
}
|
|
@@ -167,7 +167,6 @@ export class Arc extends GeometrySceneObject {
|
|
|
167
167
|
const d = Math.sqrt(r * r - (chordLen / 2) * (chordLen / 2));
|
|
168
168
|
const sign = cw ? -1 : 1;
|
|
169
169
|
const centerPoint = new Point2D(mx + sign * d * px, my + sign * d * py);
|
|
170
|
-
const startAngle = Math.atan2(startPoint.y - centerPoint.y, startPoint.x - centerPoint.x);
|
|
171
170
|
const endAngle = Math.atan2(targetPoint.y - centerPoint.y, targetPoint.x - centerPoint.x);
|
|
172
171
|
const normal = cw ? plane.normal.negate() : plane.normal;
|
|
173
172
|
const center = plane.localToWorld(centerPoint);
|
|
@@ -224,6 +223,10 @@ export class Arc extends GeometrySceneObject {
|
|
|
224
223
|
const centerPoint = this._targetPlane
|
|
225
224
|
? plane.worldToLocal(this._targetPlane.getPlaneCenter())
|
|
226
225
|
: this.getCurrentPosition();
|
|
226
|
+
// Angles are measured relative to the current tangent (defaults to +X).
|
|
227
|
+
const tangent = (this._targetPlane ? null : this.sketch?.getTangentAt(this))
|
|
228
|
+
?? new Point2D(1, 0);
|
|
229
|
+
const tangentAngle = Math.atan2(tangent.y, tangent.x);
|
|
227
230
|
const cw = this._endAngle < 0;
|
|
228
231
|
const absStartAngle = Math.abs(this._startAngle);
|
|
229
232
|
const absEndAngle = Math.abs(this._endAngle);
|
|
@@ -239,6 +242,8 @@ export class Arc extends GeometrySceneObject {
|
|
|
239
242
|
startAngleRad = rad(absStartAngle);
|
|
240
243
|
endAngleRad = rad(absEndAngle);
|
|
241
244
|
}
|
|
245
|
+
startAngleRad += tangentAngle;
|
|
246
|
+
endAngleRad += tangentAngle;
|
|
242
247
|
const normal = cw ? plane.normal.negate() : plane.normal;
|
|
243
248
|
const startPoint = Geometry.getPointOnCircle(centerPoint, radius, startAngleRad);
|
|
244
249
|
const endPoint = Geometry.getPointOnCircle(centerPoint, radius, endAngleRad);
|
|
@@ -261,6 +266,28 @@ export class Arc extends GeometrySceneObject {
|
|
|
261
266
|
getType() {
|
|
262
267
|
return 'arc';
|
|
263
268
|
}
|
|
269
|
+
getDependencies() {
|
|
270
|
+
return this._targetPlane ? [this._targetPlane] : [];
|
|
271
|
+
}
|
|
272
|
+
createCopy(remap) {
|
|
273
|
+
const targetPlane = this._targetPlane
|
|
274
|
+
? (remap.get(this._targetPlane) || this._targetPlane)
|
|
275
|
+
: null;
|
|
276
|
+
let copy;
|
|
277
|
+
if (this._startPoint && this._endPoint) {
|
|
278
|
+
copy = Arc.twoPoints(this._startPoint, this._endPoint, targetPlane);
|
|
279
|
+
}
|
|
280
|
+
else if (this._endPoint) {
|
|
281
|
+
copy = Arc.toPoint(this._endPoint, targetPlane);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
copy = Arc.fromAngles(this._arcRadius, this._startAngle, this._endAngle, targetPlane);
|
|
285
|
+
}
|
|
286
|
+
copy._bulgeRadius = this._bulgeRadius;
|
|
287
|
+
copy._centerPoint = this._centerPoint;
|
|
288
|
+
copy._centered = this._centered;
|
|
289
|
+
return copy;
|
|
290
|
+
}
|
|
264
291
|
compareTo(other) {
|
|
265
292
|
if (!(other instanceof Arc)) {
|
|
266
293
|
return false;
|
|
@@ -38,7 +38,11 @@ export class HorizontalLine extends GeometrySceneObject {
|
|
|
38
38
|
throw new Error('hLine: .centered() cannot be combined with a target geometry');
|
|
39
39
|
}
|
|
40
40
|
startPoint = currentPos;
|
|
41
|
-
|
|
41
|
+
const hit = findNearestRayIntersection(plane, startPoint, new Point2D(1, 0), this.distanceOrTarget);
|
|
42
|
+
if (!hit) {
|
|
43
|
+
throw new Error("Line does not intersect target geometry");
|
|
44
|
+
}
|
|
45
|
+
endPoint = hit;
|
|
42
46
|
signedLength = endPoint.x - startPoint.x;
|
|
43
47
|
}
|
|
44
48
|
const start = plane.localToWorld(startPoint);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { SceneObject } from "../../common/scene-object.js";
|
|
2
2
|
import { GeometrySceneObject } from "./geometry.js";
|
|
3
3
|
export declare class HMove extends GeometrySceneObject {
|
|
4
|
-
|
|
5
|
-
constructor(
|
|
4
|
+
distanceOrTarget: number | SceneObject;
|
|
5
|
+
constructor(distanceOrTarget: number | SceneObject);
|
|
6
6
|
getType(): string;
|
|
7
7
|
build(): void;
|
|
8
8
|
getDependencies(): SceneObject[];
|
|
@@ -1,24 +1,43 @@
|
|
|
1
1
|
import { Point2D } from "../../math/point.js";
|
|
2
|
+
import { SceneObject } from "../../common/scene-object.js";
|
|
2
3
|
import { GeometrySceneObject } from "./geometry.js";
|
|
4
|
+
import { findNearestRayIntersection } from "../../oc/ray-intersect.js";
|
|
3
5
|
export class HMove extends GeometrySceneObject {
|
|
4
|
-
|
|
5
|
-
constructor(
|
|
6
|
+
distanceOrTarget;
|
|
7
|
+
constructor(distanceOrTarget) {
|
|
6
8
|
super();
|
|
7
|
-
this.
|
|
9
|
+
this.distanceOrTarget = distanceOrTarget;
|
|
8
10
|
}
|
|
9
11
|
getType() {
|
|
10
12
|
return 'hmove';
|
|
11
13
|
}
|
|
12
14
|
build() {
|
|
13
15
|
const pos = this.getCurrentPosition();
|
|
14
|
-
|
|
16
|
+
let newPos;
|
|
17
|
+
if (typeof this.distanceOrTarget === 'number') {
|
|
18
|
+
newPos = new Point2D(pos.x + this.distanceOrTarget, pos.y);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const plane = this.sketch.getPlane();
|
|
22
|
+
const hit = findNearestRayIntersection(plane, pos, new Point2D(1, 0), this.distanceOrTarget);
|
|
23
|
+
if (!hit) {
|
|
24
|
+
throw new Error("Cannot move horizontally up to the specified geometry");
|
|
25
|
+
}
|
|
26
|
+
newPos = hit;
|
|
27
|
+
}
|
|
15
28
|
this.setCurrentPosition(newPos);
|
|
16
29
|
}
|
|
17
30
|
getDependencies() {
|
|
31
|
+
if (this.distanceOrTarget instanceof SceneObject) {
|
|
32
|
+
return [this.distanceOrTarget];
|
|
33
|
+
}
|
|
18
34
|
return [];
|
|
19
35
|
}
|
|
20
36
|
createCopy(remap) {
|
|
21
|
-
|
|
37
|
+
const distanceOrTarget = this.distanceOrTarget instanceof SceneObject
|
|
38
|
+
? (remap.get(this.distanceOrTarget) || this.distanceOrTarget)
|
|
39
|
+
: this.distanceOrTarget;
|
|
40
|
+
return new HMove(distanceOrTarget);
|
|
22
41
|
}
|
|
23
42
|
compareTo(other) {
|
|
24
43
|
if (!(other instanceof HMove)) {
|
|
@@ -27,11 +46,17 @@ export class HMove extends GeometrySceneObject {
|
|
|
27
46
|
if (!super.compareTo(other)) {
|
|
28
47
|
return false;
|
|
29
48
|
}
|
|
30
|
-
|
|
49
|
+
if (typeof this.distanceOrTarget !== typeof other.distanceOrTarget) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (this.distanceOrTarget instanceof SceneObject && other.distanceOrTarget instanceof SceneObject) {
|
|
53
|
+
return this.distanceOrTarget.compareTo(other.distanceOrTarget);
|
|
54
|
+
}
|
|
55
|
+
return this.distanceOrTarget === other.distanceOrTarget;
|
|
31
56
|
}
|
|
32
57
|
serialize() {
|
|
33
58
|
return {
|
|
34
|
-
distance: this.
|
|
59
|
+
distance: typeof this.distanceOrTarget === 'number' ? this.distanceOrTarget : null
|
|
35
60
|
};
|
|
36
61
|
}
|
|
37
62
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Vertex } from "../../common/vertex.js";
|
|
2
2
|
import { SectionOps } from "../../oc/section-ops.js";
|
|
3
|
+
import { WireOps } from "../../oc/wire-ops.js";
|
|
3
4
|
import { ExtrudableGeometryBase } from "./extrudable-base.js";
|
|
4
5
|
export class Intersect extends ExtrudableGeometryBase {
|
|
5
6
|
sourceObjects;
|
|
@@ -11,19 +12,25 @@ export class Intersect extends ExtrudableGeometryBase {
|
|
|
11
12
|
const plane = this.targetPlane?.getPlane() || this.sketch.getPlane();
|
|
12
13
|
const shapes = this.sourceObjects.flatMap(obj => obj.getShapes());
|
|
13
14
|
const transform = context?.getTransform() ?? null;
|
|
14
|
-
|
|
15
|
-
for (
|
|
15
|
+
const allEdges = [];
|
|
16
|
+
for (const shape of shapes) {
|
|
16
17
|
const edges = SectionOps.sectionShapeWithPlane(plane, shape);
|
|
17
|
-
|
|
18
|
-
lastEdge = edge;
|
|
19
|
-
}
|
|
18
|
+
allEdges.push(...edges);
|
|
20
19
|
this.addShapes(edges);
|
|
21
20
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
// Section across multiple source faces yields an unordered edge set that
|
|
22
|
+
// may form one connected chain, several disjoint chains, or closed loops.
|
|
23
|
+
// Take the first connected group and use its actual chain endpoints —
|
|
24
|
+
// not an arbitrary edge's vertices, which can land on interior junctions.
|
|
25
|
+
if (allEdges.length > 0) {
|
|
26
|
+
const groups = WireOps.groupConnectedEdges(allEdges);
|
|
27
|
+
const endpoints = WireOps.findChainEndpoints(groups[0]);
|
|
28
|
+
if (endpoints) {
|
|
29
|
+
const localStart = plane.worldToLocal(endpoints.start.toPoint());
|
|
30
|
+
const localEnd = plane.worldToLocal(endpoints.end.toPoint());
|
|
31
|
+
this.setState('start', Vertex.fromPoint2D(localStart));
|
|
32
|
+
this.setState('end', Vertex.fromPoint2D(localEnd));
|
|
33
|
+
}
|
|
27
34
|
}
|
|
28
35
|
for (const obj of this.sourceObjects) {
|
|
29
36
|
obj.removeShapes(this);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { SceneObject } from "../../common/scene-object.js";
|
|
2
2
|
import { GeometrySceneObject } from "./geometry.js";
|
|
3
3
|
export declare class PolarMove extends GeometrySceneObject {
|
|
4
|
-
|
|
4
|
+
radiusOrTarget: number | SceneObject;
|
|
5
5
|
angle: number;
|
|
6
|
-
constructor(
|
|
6
|
+
constructor(radiusOrTarget: number | SceneObject, angle: number);
|
|
7
7
|
getType(): string;
|
|
8
8
|
build(): void;
|
|
9
9
|
getDependencies(): SceneObject[];
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Point2D } from "../../math/point.js";
|
|
2
|
+
import { SceneObject } from "../../common/scene-object.js";
|
|
2
3
|
import { GeometrySceneObject } from "./geometry.js";
|
|
4
|
+
import { findNearestRayIntersection } from "../../oc/ray-intersect.js";
|
|
3
5
|
export class PolarMove extends GeometrySceneObject {
|
|
4
|
-
|
|
6
|
+
radiusOrTarget;
|
|
5
7
|
angle;
|
|
6
|
-
constructor(
|
|
8
|
+
constructor(radiusOrTarget, angle) {
|
|
7
9
|
super();
|
|
8
|
-
this.
|
|
10
|
+
this.radiusOrTarget = radiusOrTarget;
|
|
9
11
|
this.angle = angle;
|
|
10
12
|
}
|
|
11
13
|
getType() {
|
|
@@ -13,14 +15,43 @@ export class PolarMove extends GeometrySceneObject {
|
|
|
13
15
|
}
|
|
14
16
|
build() {
|
|
15
17
|
const pos = this.getCurrentPosition();
|
|
16
|
-
const
|
|
18
|
+
const tangent = (this.sketch?.getTangentAt(this) ?? new Point2D(1, 0)).normalize();
|
|
19
|
+
const cos = Math.cos(this.angle);
|
|
20
|
+
const sin = Math.sin(this.angle);
|
|
21
|
+
const direction = new Point2D(cos * tangent.x - sin * tangent.y, sin * tangent.x + cos * tangent.y);
|
|
22
|
+
let newPos;
|
|
23
|
+
if (typeof this.radiusOrTarget === 'number') {
|
|
24
|
+
newPos = new Point2D(pos.x + this.radiusOrTarget * direction.x, pos.y + this.radiusOrTarget * direction.y);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
const plane = this.sketch.getPlane();
|
|
28
|
+
const hit = findNearestRayIntersection(plane, pos, direction, this.radiusOrTarget);
|
|
29
|
+
if (!hit) {
|
|
30
|
+
throw new Error("Cannot move at the specified angle up to the geometry");
|
|
31
|
+
}
|
|
32
|
+
newPos = hit;
|
|
33
|
+
}
|
|
17
34
|
this.setCurrentPosition(newPos);
|
|
35
|
+
const moveVec = newPos.subtract(pos);
|
|
36
|
+
const moveLen = Math.hypot(moveVec.x, moveVec.y);
|
|
37
|
+
if (moveLen > 1e-12) {
|
|
38
|
+
this.setTangent(new Point2D(moveVec.x / moveLen, moveVec.y / moveLen));
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this.setTangent(direction);
|
|
42
|
+
}
|
|
18
43
|
}
|
|
19
44
|
getDependencies() {
|
|
45
|
+
if (this.radiusOrTarget instanceof SceneObject) {
|
|
46
|
+
return [this.radiusOrTarget];
|
|
47
|
+
}
|
|
20
48
|
return [];
|
|
21
49
|
}
|
|
22
50
|
createCopy(remap) {
|
|
23
|
-
|
|
51
|
+
const radiusOrTarget = this.radiusOrTarget instanceof SceneObject
|
|
52
|
+
? (remap.get(this.radiusOrTarget) || this.radiusOrTarget)
|
|
53
|
+
: this.radiusOrTarget;
|
|
54
|
+
return new PolarMove(radiusOrTarget, this.angle);
|
|
24
55
|
}
|
|
25
56
|
compareTo(other) {
|
|
26
57
|
if (!(other instanceof PolarMove)) {
|
|
@@ -29,11 +60,20 @@ export class PolarMove extends GeometrySceneObject {
|
|
|
29
60
|
if (!super.compareTo(other)) {
|
|
30
61
|
return false;
|
|
31
62
|
}
|
|
32
|
-
|
|
63
|
+
if (this.angle !== other.angle) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (typeof this.radiusOrTarget !== typeof other.radiusOrTarget) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
if (this.radiusOrTarget instanceof SceneObject && other.radiusOrTarget instanceof SceneObject) {
|
|
70
|
+
return this.radiusOrTarget.compareTo(other.radiusOrTarget);
|
|
71
|
+
}
|
|
72
|
+
return this.radiusOrTarget === other.radiusOrTarget;
|
|
33
73
|
}
|
|
34
74
|
serialize() {
|
|
35
75
|
return {
|
|
36
|
-
radius: this.
|
|
76
|
+
radius: typeof this.radiusOrTarget === 'number' ? this.radiusOrTarget : null,
|
|
37
77
|
angle: this.angle
|
|
38
78
|
};
|
|
39
79
|
}
|