fluidcad 0.0.30 → 0.0.32
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/extrude.d.ts +17 -4
- package/lib/dist/core/extrude.js +8 -6
- package/lib/dist/core/interfaces.d.ts +16 -6
- package/lib/dist/core/mirror.d.ts +5 -5
- 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/line.d.ts +2 -0
- package/lib/dist/features/2d/line.js +4 -0
- 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/extrude-to-face.d.ts +5 -1
- package/lib/dist/features/extrude-to-face.js +50 -8
- package/lib/dist/features/extrude.js +19 -28
- 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 +11 -2
- package/lib/dist/features/select.d.ts +1 -0
- package/lib/dist/features/select.js +40 -12
- package/lib/dist/features/shell.d.ts +1 -0
- package/lib/dist/features/shell.js +6 -0
- package/lib/dist/features/simple-extruder.js +6 -3
- 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/filters/face/above-below.d.ts +20 -0
- package/lib/dist/filters/face/above-below.js +57 -0
- package/lib/dist/filters/face/face-filter.d.ts +26 -0
- package/lib/dist/filters/face/face-filter.js +64 -0
- package/lib/dist/filters/face/planar-filter.d.ts +15 -0
- package/lib/dist/filters/face/planar-filter.js +30 -0
- package/lib/dist/filters/from-object.d.ts +1 -0
- package/lib/dist/filters/from-object.js +3 -0
- package/lib/dist/oc/boolean-ops.d.ts +2 -2
- package/lib/dist/oc/boolean-ops.js +8 -3
- package/lib/dist/oc/edge-ops.d.ts +17 -0
- package/lib/dist/oc/edge-ops.js +60 -0
- package/lib/dist/oc/face-maker2.d.ts +8 -0
- package/lib/dist/oc/face-maker2.js +42 -1
- package/lib/dist/oc/face-ops.d.ts +6 -1
- package/lib/dist/oc/face-ops.js +3 -2
- package/lib/dist/oc/face-query.js +19 -15
- 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/cut.test.js +40 -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/extrude-to-face.test.js +52 -0
- package/lib/dist/tests/features/extrude.test.js +46 -8
- package/lib/dist/tests/features/mirror.test.js +74 -0
- package/lib/dist/tests/features/select.test.js +141 -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-DMw0OYCF.js} +70 -70
- package/ui/dist/assets/index-DR7c2Qk9.css +2 -0
- package/ui/dist/index.html +2 -2
- package/lib/dist/features/infinite-extrude.d.ts +0 -13
- package/lib/dist/features/infinite-extrude.js +0 -79
- package/ui/dist/assets/index-DRKfe6N9.css +0 -2
|
@@ -3,6 +3,7 @@ import { BooleanOps } from "../oc/boolean-ops.js";
|
|
|
3
3
|
import { Explorer } from "../oc/explorer.js";
|
|
4
4
|
import { ShapeOps } from "../oc/shape-ops.js";
|
|
5
5
|
import { Solid } from "../common/shapes.js";
|
|
6
|
+
import { requireShapes } from "../common/operand-check.js";
|
|
6
7
|
export class Subtract extends SceneObject {
|
|
7
8
|
solid1;
|
|
8
9
|
solid2;
|
|
@@ -11,6 +12,10 @@ export class Subtract extends SceneObject {
|
|
|
11
12
|
this.solid1 = solid1;
|
|
12
13
|
this.solid2 = solid2;
|
|
13
14
|
}
|
|
15
|
+
validate() {
|
|
16
|
+
requireShapes(this.solid1, "first operand", "subtract");
|
|
17
|
+
requireShapes(this.solid2, "second operand", "subtract");
|
|
18
|
+
}
|
|
14
19
|
build(context) {
|
|
15
20
|
const p = context.getProfiler();
|
|
16
21
|
const stock = this.solid1.getShapes();
|
|
@@ -4,6 +4,7 @@ export declare class Subtract2D extends GeometrySceneObject {
|
|
|
4
4
|
target1: GeometrySceneObject;
|
|
5
5
|
target2: GeometrySceneObject;
|
|
6
6
|
constructor(target1: GeometrySceneObject, target2: GeometrySceneObject);
|
|
7
|
+
validate(): void;
|
|
7
8
|
private collectEdges;
|
|
8
9
|
build(context: BuildSceneObjectContext): void;
|
|
9
10
|
getDependencies(): SceneObject[];
|
|
@@ -6,6 +6,7 @@ import { BooleanOps } from "../oc/boolean-ops.js";
|
|
|
6
6
|
import { ShapeOps } from "../oc/shape-ops.js";
|
|
7
7
|
import { Explorer } from "../oc/explorer.js";
|
|
8
8
|
import { FaceMaker2 } from "../oc/face-maker2.js";
|
|
9
|
+
import { requireShapes } from "../common/operand-check.js";
|
|
9
10
|
export class Subtract2D extends GeometrySceneObject {
|
|
10
11
|
target1;
|
|
11
12
|
target2;
|
|
@@ -14,6 +15,10 @@ export class Subtract2D extends GeometrySceneObject {
|
|
|
14
15
|
this.target1 = target1;
|
|
15
16
|
this.target2 = target2;
|
|
16
17
|
}
|
|
18
|
+
validate() {
|
|
19
|
+
requireShapes(this.target1, "first operand", "subtract2d");
|
|
20
|
+
requireShapes(this.target2, "second operand", "subtract2d");
|
|
21
|
+
}
|
|
17
22
|
collectEdges(target) {
|
|
18
23
|
const edges = new Map();
|
|
19
24
|
for (const shape of target.getShapes()) {
|
|
@@ -6,6 +6,7 @@ export declare class Sweep extends ExtrudeBase implements ISweep {
|
|
|
6
6
|
private _path;
|
|
7
7
|
constructor(path: SceneObject, extrudable?: Extrudable);
|
|
8
8
|
get path(): SceneObject;
|
|
9
|
+
validate(): void;
|
|
9
10
|
build(context: BuildSceneObjectContext): void;
|
|
10
11
|
/** Plain sweep: classify by inner-wire detection on the start face. */
|
|
11
12
|
private buildSweep;
|
|
@@ -5,6 +5,7 @@ import { FaceMaker2 } from "../oc/face-maker2.js";
|
|
|
5
5
|
import { ExtrudeBase } from "./extrude-base.js";
|
|
6
6
|
import { cutWithSceneObjects } from "../helpers/scene-helpers.js";
|
|
7
7
|
import { ThinFaceMaker } from "../oc/thin-face-maker.js";
|
|
8
|
+
import { requireShapes } from "../common/operand-check.js";
|
|
8
9
|
export class Sweep extends ExtrudeBase {
|
|
9
10
|
_path;
|
|
10
11
|
constructor(path, extrudable) {
|
|
@@ -14,6 +15,9 @@ export class Sweep extends ExtrudeBase {
|
|
|
14
15
|
get path() {
|
|
15
16
|
return this._path;
|
|
16
17
|
}
|
|
18
|
+
validate() {
|
|
19
|
+
requireShapes(this._path, "path", "sweep");
|
|
20
|
+
}
|
|
17
21
|
build(context) {
|
|
18
22
|
const p = context.getProfiler();
|
|
19
23
|
const plane = this.extrudable.getPlane();
|
|
@@ -8,6 +8,7 @@ export declare class Translate extends SceneObject {
|
|
|
8
8
|
constructor(amount: LazyVertex, copy?: boolean, ...targets: SceneObject[]);
|
|
9
9
|
get targetObjects(): SceneObject[];
|
|
10
10
|
exclude(...objects: SceneObject[]): this;
|
|
11
|
+
validate(): void;
|
|
11
12
|
build(context: BuildSceneObjectContext): void;
|
|
12
13
|
getDependencies(): SceneObject[];
|
|
13
14
|
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SceneObject } from "../common/scene-object.js";
|
|
2
2
|
import { Matrix4 } from "../math/matrix4.js";
|
|
3
3
|
import { ShapeOps } from "../oc/shape-ops.js";
|
|
4
|
+
import { requireShapes } from "../common/operand-check.js";
|
|
4
5
|
export class Translate extends SceneObject {
|
|
5
6
|
amount;
|
|
6
7
|
copy;
|
|
@@ -19,6 +20,14 @@ export class Translate extends SceneObject {
|
|
|
19
20
|
this._excludedObjects.push(...objects);
|
|
20
21
|
return this;
|
|
21
22
|
}
|
|
23
|
+
validate() {
|
|
24
|
+
if (!this._targetObjects) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
for (let i = 0; i < this._targetObjects.length; i++) {
|
|
28
|
+
requireShapes(this._targetObjects[i], `target ${i + 1}`, "translate");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
22
31
|
build(context) {
|
|
23
32
|
let objects = this.targetObjects || context.getSceneObjects();
|
|
24
33
|
if (this._excludedObjects.length > 0) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Matrix4 } from "../../math/matrix4.js";
|
|
2
|
+
import { Face } from "../../common/shapes.js";
|
|
3
|
+
import { FilterBase } from "../filter-base.js";
|
|
4
|
+
import { PlaneObjectBase } from "../../features/plane-renderable-base.js";
|
|
5
|
+
export declare class AboveFacePlaneFilter extends FilterBase<Face> {
|
|
6
|
+
private plane;
|
|
7
|
+
private partial;
|
|
8
|
+
constructor(plane: PlaneObjectBase, partial?: boolean);
|
|
9
|
+
match(shape: Face): boolean;
|
|
10
|
+
compareTo(other: AboveFacePlaneFilter): boolean;
|
|
11
|
+
transform(matrix: Matrix4): AboveFacePlaneFilter;
|
|
12
|
+
}
|
|
13
|
+
export declare class BelowFacePlaneFilter extends FilterBase<Face> {
|
|
14
|
+
private plane;
|
|
15
|
+
private partial;
|
|
16
|
+
constructor(plane: PlaneObjectBase, partial?: boolean);
|
|
17
|
+
match(shape: Face): boolean;
|
|
18
|
+
compareTo(other: BelowFacePlaneFilter): boolean;
|
|
19
|
+
transform(matrix: Matrix4): BelowFacePlaneFilter;
|
|
20
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { FilterBase } from "../filter-base.js";
|
|
2
|
+
import { EdgeOps } from "../../oc/edge-ops.js";
|
|
3
|
+
import { PlaneObject } from "../../features/plane.js";
|
|
4
|
+
function getBoundaryPoints(face) {
|
|
5
|
+
return face.getEdges().flatMap(edge => [
|
|
6
|
+
EdgeOps.getVertexPoint(EdgeOps.getFirstVertex(edge)),
|
|
7
|
+
EdgeOps.getVertexPoint(EdgeOps.getLastVertex(edge)),
|
|
8
|
+
]);
|
|
9
|
+
}
|
|
10
|
+
export class AboveFacePlaneFilter extends FilterBase {
|
|
11
|
+
plane;
|
|
12
|
+
partial;
|
|
13
|
+
constructor(plane, partial = false) {
|
|
14
|
+
super();
|
|
15
|
+
this.plane = plane;
|
|
16
|
+
this.partial = partial;
|
|
17
|
+
}
|
|
18
|
+
match(shape) {
|
|
19
|
+
const plane = this.plane.getPlane();
|
|
20
|
+
const flags = getBoundaryPoints(shape).map(p => plane.signedDistanceToPoint(p) > 0);
|
|
21
|
+
if (flags.length === 0) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
return this.partial ? flags.some(Boolean) : flags.every(Boolean);
|
|
25
|
+
}
|
|
26
|
+
compareTo(other) {
|
|
27
|
+
return this.plane.compareTo(other.plane) && this.partial === other.partial;
|
|
28
|
+
}
|
|
29
|
+
transform(matrix) {
|
|
30
|
+
const transformedPlane = this.plane.getPlane().applyMatrix(matrix);
|
|
31
|
+
return new AboveFacePlaneFilter(new PlaneObject(transformedPlane), this.partial);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export class BelowFacePlaneFilter extends FilterBase {
|
|
35
|
+
plane;
|
|
36
|
+
partial;
|
|
37
|
+
constructor(plane, partial = false) {
|
|
38
|
+
super();
|
|
39
|
+
this.plane = plane;
|
|
40
|
+
this.partial = partial;
|
|
41
|
+
}
|
|
42
|
+
match(shape) {
|
|
43
|
+
const plane = this.plane.getPlane();
|
|
44
|
+
const flags = getBoundaryPoints(shape).map(p => plane.signedDistanceToPoint(p) < 0);
|
|
45
|
+
if (flags.length === 0) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return this.partial ? flags.some(Boolean) : flags.every(Boolean);
|
|
49
|
+
}
|
|
50
|
+
compareTo(other) {
|
|
51
|
+
return this.plane.compareTo(other.plane) && this.partial === other.partial;
|
|
52
|
+
}
|
|
53
|
+
transform(matrix) {
|
|
54
|
+
const transformedPlane = this.plane.getPlane().applyMatrix(matrix);
|
|
55
|
+
return new BelowFacePlaneFilter(new PlaneObject(transformedPlane), this.partial);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -70,6 +70,14 @@ export declare class FaceFilterBuilder extends FilterBuilderBase<Face> {
|
|
|
70
70
|
* @param minorRadius - Optional radius of the tube itself.
|
|
71
71
|
*/
|
|
72
72
|
notTorus(majorRadius?: number, minorRadius?: number): this;
|
|
73
|
+
/**
|
|
74
|
+
* Selects planar (flat) faces.
|
|
75
|
+
*/
|
|
76
|
+
planar(): this;
|
|
77
|
+
/**
|
|
78
|
+
* Excludes planar (flat) faces.
|
|
79
|
+
*/
|
|
80
|
+
notPlanar(): this;
|
|
73
81
|
/**
|
|
74
82
|
* Selects conical faces.
|
|
75
83
|
*/
|
|
@@ -119,6 +127,24 @@ export declare class FaceFilterBuilder extends FilterBuilderBase<Face> {
|
|
|
119
127
|
* @param count - The number of edges to exclude.
|
|
120
128
|
*/
|
|
121
129
|
notEdgeCount(count: number): this;
|
|
130
|
+
/**
|
|
131
|
+
* Selects faces that are entirely above the given plane (in the direction of its normal).
|
|
132
|
+
* @param plane - The reference plane.
|
|
133
|
+
* @param offsetOrOptions - Offset distance, or an options object with `offset` and `partial`.
|
|
134
|
+
*/
|
|
135
|
+
above(plane: PlaneLike | PlaneObjectBase, offsetOrOptions?: number | {
|
|
136
|
+
offset?: number;
|
|
137
|
+
partial?: boolean;
|
|
138
|
+
}): this;
|
|
139
|
+
/**
|
|
140
|
+
* Selects faces that are entirely below the given plane (opposite to its normal direction).
|
|
141
|
+
* @param plane - The reference plane.
|
|
142
|
+
* @param offsetOrOptions - Offset distance, or an options object with `offset` and `partial`.
|
|
143
|
+
*/
|
|
144
|
+
below(plane: PlaneLike | PlaneObjectBase, offsetOrOptions?: number | {
|
|
145
|
+
offset?: number;
|
|
146
|
+
partial?: boolean;
|
|
147
|
+
}): this;
|
|
122
148
|
/**
|
|
123
149
|
* Restricts the selection to faces originating from the given scene objects.
|
|
124
150
|
* Recursive: passing a container picks up faces from its descendants.
|
|
@@ -5,6 +5,7 @@ import { ConeFilter, NotConeFilter } from "./cone-filter.js";
|
|
|
5
5
|
import { CylinderCurveFilter, NotCylinderCurveFilter } from "./cylinder-curve.js";
|
|
6
6
|
import { CylinderFilter, NotCylinderFilter } from "./cylinder.js";
|
|
7
7
|
import { TorusFilter, NotTorusFilter } from "./torus-filter.js";
|
|
8
|
+
import { PlanarFilter, NotPlanarFilter } from "./planar-filter.js";
|
|
8
9
|
import { NotOnPlaneFilter, OnPlaneFilter } from "./on-plane.js";
|
|
9
10
|
import { NotParallelFilter, ParallelFilter } from "./parallel.js";
|
|
10
11
|
import { PlaneObject } from "../../features/plane.js";
|
|
@@ -15,6 +16,7 @@ import { HasEdgeFromSceneObjectFilter, NotHasEdgeFromSceneObjectFilter } from ".
|
|
|
15
16
|
import { FromSceneObjectFilter } from "../from-object.js";
|
|
16
17
|
import { EdgeCountFilter, NotEdgeCountFilter } from "./edge-count.js";
|
|
17
18
|
import { IntersectsWithFilter, NotIntersectsWithFilter } from "./intersects-with.js";
|
|
19
|
+
import { AboveFacePlaneFilter, BelowFacePlaneFilter } from "./above-below.js";
|
|
18
20
|
import { SceneObject } from "../../common/scene-object.js";
|
|
19
21
|
export class FaceFilterBuilder extends FilterBuilderBase {
|
|
20
22
|
constructor() {
|
|
@@ -204,6 +206,22 @@ export class FaceFilterBuilder extends FilterBuilderBase {
|
|
|
204
206
|
this.filters.push(filter);
|
|
205
207
|
return this;
|
|
206
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Selects planar (flat) faces.
|
|
211
|
+
*/
|
|
212
|
+
planar() {
|
|
213
|
+
const filter = new PlanarFilter();
|
|
214
|
+
this.filters.push(filter);
|
|
215
|
+
return this;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Excludes planar (flat) faces.
|
|
219
|
+
*/
|
|
220
|
+
notPlanar() {
|
|
221
|
+
const filter = new NotPlanarFilter();
|
|
222
|
+
this.filters.push(filter);
|
|
223
|
+
return this;
|
|
224
|
+
}
|
|
207
225
|
/**
|
|
208
226
|
* Selects conical faces.
|
|
209
227
|
*/
|
|
@@ -306,6 +324,52 @@ export class FaceFilterBuilder extends FilterBuilderBase {
|
|
|
306
324
|
this.filters.push(filter);
|
|
307
325
|
return this;
|
|
308
326
|
}
|
|
327
|
+
/**
|
|
328
|
+
* Selects faces that are entirely above the given plane (in the direction of its normal).
|
|
329
|
+
* @param plane - The reference plane.
|
|
330
|
+
* @param offsetOrOptions - Offset distance, or an options object with `offset` and `partial`.
|
|
331
|
+
*/
|
|
332
|
+
above(plane, offsetOrOptions) {
|
|
333
|
+
if (!plane) {
|
|
334
|
+
throw new Error('Plane is required');
|
|
335
|
+
}
|
|
336
|
+
const opts = typeof offsetOrOptions === 'number' ? { offset: offsetOrOptions } : (offsetOrOptions ?? {});
|
|
337
|
+
const { offset = 0, partial = false } = opts;
|
|
338
|
+
let planeObj;
|
|
339
|
+
if (plane instanceof PlaneObjectBase) {
|
|
340
|
+
planeObj = plane;
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
let normalized = normalizePlane(plane);
|
|
344
|
+
planeObj = offset ? new PlaneObject(normalized.offset(offset)) : new PlaneObject(normalized);
|
|
345
|
+
}
|
|
346
|
+
const filter = new AboveFacePlaneFilter(planeObj, partial);
|
|
347
|
+
this.filters.push(filter);
|
|
348
|
+
return this;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Selects faces that are entirely below the given plane (opposite to its normal direction).
|
|
352
|
+
* @param plane - The reference plane.
|
|
353
|
+
* @param offsetOrOptions - Offset distance, or an options object with `offset` and `partial`.
|
|
354
|
+
*/
|
|
355
|
+
below(plane, offsetOrOptions) {
|
|
356
|
+
if (!plane) {
|
|
357
|
+
throw new Error('Plane is required');
|
|
358
|
+
}
|
|
359
|
+
const opts = typeof offsetOrOptions === 'number' ? { offset: offsetOrOptions } : (offsetOrOptions ?? {});
|
|
360
|
+
const { offset = 0, partial = false } = opts;
|
|
361
|
+
let planeObj;
|
|
362
|
+
if (plane instanceof PlaneObjectBase) {
|
|
363
|
+
planeObj = plane;
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
let normalized = normalizePlane(plane);
|
|
367
|
+
planeObj = offset ? new PlaneObject(normalized.offset(offset)) : new PlaneObject(normalized);
|
|
368
|
+
}
|
|
369
|
+
const filter = new BelowFacePlaneFilter(planeObj, partial);
|
|
370
|
+
this.filters.push(filter);
|
|
371
|
+
return this;
|
|
372
|
+
}
|
|
309
373
|
/**
|
|
310
374
|
* Restricts the selection to faces originating from the given scene objects.
|
|
311
375
|
* Recursive: passing a container picks up faces from its descendants.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Matrix4 } from "../../math/matrix4.js";
|
|
2
|
+
import { Face } from "../../common/shapes.js";
|
|
3
|
+
import { FilterBase } from "../filter-base.js";
|
|
4
|
+
export declare class PlanarFilter extends FilterBase<Face> {
|
|
5
|
+
constructor();
|
|
6
|
+
match(shape: Face): boolean;
|
|
7
|
+
compareTo(other: PlanarFilter): boolean;
|
|
8
|
+
transform(matrix: Matrix4): PlanarFilter;
|
|
9
|
+
}
|
|
10
|
+
export declare class NotPlanarFilter extends FilterBase<Face> {
|
|
11
|
+
constructor();
|
|
12
|
+
match(shape: Face): boolean;
|
|
13
|
+
compareTo(other: NotPlanarFilter): boolean;
|
|
14
|
+
transform(matrix: Matrix4): NotPlanarFilter;
|
|
15
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { FilterBase } from "../filter-base.js";
|
|
2
|
+
import { FaceQuery } from "../../oc/face-query.js";
|
|
3
|
+
export class PlanarFilter extends FilterBase {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
}
|
|
7
|
+
match(shape) {
|
|
8
|
+
return FaceQuery.isPlanarFace(shape);
|
|
9
|
+
}
|
|
10
|
+
compareTo(other) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
transform(matrix) {
|
|
14
|
+
return new PlanarFilter();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class NotPlanarFilter extends FilterBase {
|
|
18
|
+
constructor() {
|
|
19
|
+
super();
|
|
20
|
+
}
|
|
21
|
+
match(shape) {
|
|
22
|
+
return !FaceQuery.isPlanarFace(shape);
|
|
23
|
+
}
|
|
24
|
+
compareTo(other) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
transform(matrix) {
|
|
28
|
+
return new NotPlanarFilter();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -7,6 +7,7 @@ export declare class FromSceneObjectFilter<TShape extends Shape> extends FilterB
|
|
|
7
7
|
private sceneObjects;
|
|
8
8
|
private shapeType;
|
|
9
9
|
constructor(sceneObjects: SceneObject[], shapeType: ShapeType);
|
|
10
|
+
getSceneObjects(): SceneObject[];
|
|
10
11
|
match(shape: TShape): boolean;
|
|
11
12
|
compareTo(other: FromSceneObjectFilter<TShape>): boolean;
|
|
12
13
|
transform(_matrix: Matrix4): FromSceneObjectFilter<TShape>;
|
|
@@ -7,6 +7,9 @@ export class FromSceneObjectFilter extends FilterBase {
|
|
|
7
7
|
this.sceneObjects = sceneObjects;
|
|
8
8
|
this.shapeType = shapeType;
|
|
9
9
|
}
|
|
10
|
+
getSceneObjects() {
|
|
11
|
+
return this.sceneObjects;
|
|
12
|
+
}
|
|
10
13
|
match(shape) {
|
|
11
14
|
for (const obj of this.sceneObjects) {
|
|
12
15
|
const subShapes = obj.getShapes().flatMap(s => s.getSubShapes(this.shapeType));
|
|
@@ -8,8 +8,8 @@ export declare class BooleanOps {
|
|
|
8
8
|
static cutShapes(shape: Shape, tool: Shape): Shape;
|
|
9
9
|
static cutShapesRaw(shape: TopoDS_Shape, tool: TopoDS_Shape): TopoDS_Shape;
|
|
10
10
|
static cutMultiShape(stocks: Shape[], tools: Shape[], plane?: Plane, cutDistance?: number): {
|
|
11
|
-
result:
|
|
12
|
-
modified: (shape: Shape) => (
|
|
11
|
+
result: Edge | import("../common/wire.js").Wire | Solid | Face;
|
|
12
|
+
modified: (shape: Shape) => (Edge | import("../common/wire.js").Wire | Solid | Face)[];
|
|
13
13
|
sectionEdges: Edge[];
|
|
14
14
|
startEdges: Edge[];
|
|
15
15
|
endEdges: Edge[];
|
|
@@ -168,10 +168,15 @@ export class BooleanOps {
|
|
|
168
168
|
else if (opts?.glue === 'shift') {
|
|
169
169
|
builder.SetGlue(oc.BOPAlgo_GlueEnum.BOPAlgo_GlueShift);
|
|
170
170
|
}
|
|
171
|
+
// Wrap all stocks in a single compound argument. OCC's pave-filling step
|
|
172
|
+
// computes intersections between distinct arguments — so two touching
|
|
173
|
+
// stocks passed as separate args end up merged with each other even when
|
|
174
|
+
// the tool doesn't touch them. Bundling them under one TopoDS_Compound
|
|
175
|
+
// keeps stock-to-stock relationships out of the result; only stock↔tool
|
|
176
|
+
// interactions are computed.
|
|
177
|
+
const stockCompound = ShapeOps.makeCompoundRaw(stock.map(s => s.getShape()));
|
|
171
178
|
const stockList = new oc.TopTools_ListOfShape();
|
|
172
|
-
|
|
173
|
-
stockList.Append(s.getShape());
|
|
174
|
-
}
|
|
179
|
+
stockList.Append(stockCompound);
|
|
175
180
|
const toolList = new oc.TopTools_ListOfShape();
|
|
176
181
|
for (const t of tools) {
|
|
177
182
|
toolList.Append(t.getShape());
|
|
@@ -35,4 +35,21 @@ export declare class EdgeOps {
|
|
|
35
35
|
static findNearestEdgeIndex(edges: Edge[], point: Point, tolerance?: number): number;
|
|
36
36
|
static findNearestEdgeIndices(edges: Edge[], point: Point, tolerance?: number): number[];
|
|
37
37
|
static isClosed(edge: Edge): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Removes coincident edges from an edge set and splits any pairs that
|
|
40
|
+
* intersect, while preserving each input edge's original boundaries.
|
|
41
|
+
*
|
|
42
|
+
* Uses OCCT's General Fuse: it splits inputs at mutual intersections and
|
|
43
|
+
* detects same-domain (coincident) pieces, keeping a single representative
|
|
44
|
+
* per group. `SimplifyResult` is intentionally not called — its
|
|
45
|
+
* edge-unification step concatenates tangent-continuous chains (line + arc
|
|
46
|
+
* fillets, etc.) into single BSpline curves, which destroys the per-edge
|
|
47
|
+
* structure callers need for downstream filter/trim operations.
|
|
48
|
+
*
|
|
49
|
+
* `fuzzy` overrides the matching tolerance — defaults to a value loose enough
|
|
50
|
+
* to absorb the vertex-tolerance drift produced by `BRepAlgo_NormalProjection`
|
|
51
|
+
* on coincident edges from independent face projections.
|
|
52
|
+
*/
|
|
53
|
+
static unifyCoincident(edges: Edge[], fuzzy?: number): Edge[];
|
|
54
|
+
static unifyCoincidentRaw(edges: TopoDS_Edge[], fuzzy?: number): TopoDS_Edge[];
|
|
38
55
|
}
|
package/lib/dist/oc/edge-ops.js
CHANGED
|
@@ -320,4 +320,64 @@ export class EdgeOps {
|
|
|
320
320
|
adaptor.delete();
|
|
321
321
|
return closed;
|
|
322
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Removes coincident edges from an edge set and splits any pairs that
|
|
325
|
+
* intersect, while preserving each input edge's original boundaries.
|
|
326
|
+
*
|
|
327
|
+
* Uses OCCT's General Fuse: it splits inputs at mutual intersections and
|
|
328
|
+
* detects same-domain (coincident) pieces, keeping a single representative
|
|
329
|
+
* per group. `SimplifyResult` is intentionally not called — its
|
|
330
|
+
* edge-unification step concatenates tangent-continuous chains (line + arc
|
|
331
|
+
* fillets, etc.) into single BSpline curves, which destroys the per-edge
|
|
332
|
+
* structure callers need for downstream filter/trim operations.
|
|
333
|
+
*
|
|
334
|
+
* `fuzzy` overrides the matching tolerance — defaults to a value loose enough
|
|
335
|
+
* to absorb the vertex-tolerance drift produced by `BRepAlgo_NormalProjection`
|
|
336
|
+
* on coincident edges from independent face projections.
|
|
337
|
+
*/
|
|
338
|
+
static unifyCoincident(edges, fuzzy) {
|
|
339
|
+
const raw = edges.map(e => e.getShape());
|
|
340
|
+
const unique = EdgeOps.unifyCoincidentRaw(raw, fuzzy);
|
|
341
|
+
return unique.map(e => Edge.fromTopoDSEdge(e));
|
|
342
|
+
}
|
|
343
|
+
static unifyCoincidentRaw(edges, fuzzy) {
|
|
344
|
+
const oc = getOC();
|
|
345
|
+
// Filter out degenerate edges (e.g. projection of an edge parallel to the
|
|
346
|
+
// projection direction collapses to zero length). These confuse the fuse.
|
|
347
|
+
const live = edges.filter(e => !oc.BRep_Tool.Degenerated(e));
|
|
348
|
+
if (live.length <= 1) {
|
|
349
|
+
return live;
|
|
350
|
+
}
|
|
351
|
+
const fuzzValue = fuzzy ?? Math.max(oc.Precision.Confusion() * 100, 1e-6);
|
|
352
|
+
const args = new oc.TopTools_ListOfShape();
|
|
353
|
+
let builder = null;
|
|
354
|
+
let progress = null;
|
|
355
|
+
try {
|
|
356
|
+
for (const e of live) {
|
|
357
|
+
args.Append(e);
|
|
358
|
+
}
|
|
359
|
+
builder = new oc.BRepAlgoAPI_BuilderAlgo();
|
|
360
|
+
builder.SetArguments(args);
|
|
361
|
+
builder.SetNonDestructive(true);
|
|
362
|
+
builder.SetFuzzyValue(fuzzValue);
|
|
363
|
+
progress = new oc.Message_ProgressRange();
|
|
364
|
+
builder.Build(progress);
|
|
365
|
+
const result = builder.Shape();
|
|
366
|
+
const unique = Explorer.findShapes(result, oc.TopAbs_ShapeEnum.TopAbs_EDGE).map(s => oc.TopoDS.Edge(s));
|
|
367
|
+
return unique;
|
|
368
|
+
}
|
|
369
|
+
catch (err) {
|
|
370
|
+
console.error('EdgeOps.unifyCoincidentRaw failed, falling back to input edges:', err);
|
|
371
|
+
return live;
|
|
372
|
+
}
|
|
373
|
+
finally {
|
|
374
|
+
args.delete();
|
|
375
|
+
if (builder) {
|
|
376
|
+
builder.delete();
|
|
377
|
+
}
|
|
378
|
+
if (progress) {
|
|
379
|
+
progress.delete();
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
323
383
|
}
|
|
@@ -6,5 +6,13 @@ export declare class FaceMaker2 {
|
|
|
6
6
|
static getRegions(shapes: Array<Wire | Edge>, plane: Plane, drill?: boolean): Face[];
|
|
7
7
|
private static getDrilledFaces;
|
|
8
8
|
private static getFaces;
|
|
9
|
+
/**
|
|
10
|
+
* Sizes the bounded plane face used by `getFaces`'s splitter so it always
|
|
11
|
+
* encloses the input edges with margin. The default ±1000 face used to
|
|
12
|
+
* silently swallow sketches placed far from origin: edges that crossed the
|
|
13
|
+
* boundary produced regions touching `boundaryEdges`, which the filter at
|
|
14
|
+
* the end of `getFaces` then dropped — leaving an extrude with zero faces.
|
|
15
|
+
*/
|
|
16
|
+
private static computePlaneFaceBounds;
|
|
9
17
|
private static getSplitEdges;
|
|
10
18
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Edge } from "../common/edge.js";
|
|
2
|
+
import { Point } from "../math/point.js";
|
|
2
3
|
import { Explorer } from "./explorer.js";
|
|
3
4
|
import { getOC } from "./init.js";
|
|
4
5
|
import { Convert } from "./convert.js";
|
|
@@ -67,7 +68,7 @@ export class FaceMaker2 {
|
|
|
67
68
|
static getFaces(edges, plane) {
|
|
68
69
|
const [gpPln, dispose] = Convert.toGpPln(plane);
|
|
69
70
|
const oc = getOC();
|
|
70
|
-
const planeFace = FaceOps.makeFaceFromPlane2(gpPln);
|
|
71
|
+
const planeFace = FaceOps.makeFaceFromPlane2(gpPln, this.computePlaneFaceBounds(edges, plane));
|
|
71
72
|
// Collect boundary edges of the big face before splitting
|
|
72
73
|
const boundaryEdges = Explorer.findShapes(planeFace, oc.TopAbs_ShapeEnum.TopAbs_EDGE);
|
|
73
74
|
const splitter = new oc.BRepAlgoAPI_Splitter();
|
|
@@ -108,6 +109,46 @@ export class FaceMaker2 {
|
|
|
108
109
|
dispose();
|
|
109
110
|
return filtered.map(f => Face.fromTopoDSFace(oc.TopoDS.Face(f)));
|
|
110
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Sizes the bounded plane face used by `getFaces`'s splitter so it always
|
|
114
|
+
* encloses the input edges with margin. The default ±1000 face used to
|
|
115
|
+
* silently swallow sketches placed far from origin: edges that crossed the
|
|
116
|
+
* boundary produced regions touching `boundaryEdges`, which the filter at
|
|
117
|
+
* the end of `getFaces` then dropped — leaving an extrude with zero faces.
|
|
118
|
+
*/
|
|
119
|
+
static computePlaneFaceBounds(edges, plane) {
|
|
120
|
+
let uMin = Infinity, uMax = -Infinity, vMin = Infinity, vMax = -Infinity;
|
|
121
|
+
for (const edge of edges) {
|
|
122
|
+
const bbox = ShapeOps.getBoundingBox(edge);
|
|
123
|
+
const corners = [
|
|
124
|
+
new Point(bbox.minX, bbox.minY, bbox.minZ),
|
|
125
|
+
new Point(bbox.maxX, bbox.minY, bbox.minZ),
|
|
126
|
+
new Point(bbox.minX, bbox.maxY, bbox.minZ),
|
|
127
|
+
new Point(bbox.maxX, bbox.maxY, bbox.minZ),
|
|
128
|
+
new Point(bbox.minX, bbox.minY, bbox.maxZ),
|
|
129
|
+
new Point(bbox.maxX, bbox.minY, bbox.maxZ),
|
|
130
|
+
new Point(bbox.minX, bbox.maxY, bbox.maxZ),
|
|
131
|
+
new Point(bbox.maxX, bbox.maxY, bbox.maxZ),
|
|
132
|
+
];
|
|
133
|
+
for (const c of corners) {
|
|
134
|
+
const uv = plane.worldToLocal(c);
|
|
135
|
+
if (uv.x < uMin) {
|
|
136
|
+
uMin = uv.x;
|
|
137
|
+
}
|
|
138
|
+
if (uv.x > uMax) {
|
|
139
|
+
uMax = uv.x;
|
|
140
|
+
}
|
|
141
|
+
if (uv.y < vMin) {
|
|
142
|
+
vMin = uv.y;
|
|
143
|
+
}
|
|
144
|
+
if (uv.y > vMax) {
|
|
145
|
+
vMax = uv.y;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const half = Math.max(Math.abs(uMin), Math.abs(uMax), Math.abs(vMin), Math.abs(vMax), 1000) * 1.5;
|
|
150
|
+
return { uMin: -half, uMax: half, vMin: -half, vMax: half };
|
|
151
|
+
}
|
|
111
152
|
static getSplitEdges(shapes) {
|
|
112
153
|
const oc = getOC();
|
|
113
154
|
console.log('Getting split edges for shapes:', shapes.length);
|
|
@@ -20,7 +20,12 @@ export declare class FaceOps {
|
|
|
20
20
|
static fixFaceOrientation(face: Face | TopoDS_Face): Face;
|
|
21
21
|
static makeFaceWithHoles(outerWire: Wire, holes: Wire[]): Face;
|
|
22
22
|
static isPointInsideFace(point: Point, face: Face | TopoDS_Face): boolean;
|
|
23
|
-
static makeFaceFromPlane2(plane: gp_Pln
|
|
23
|
+
static makeFaceFromPlane2(plane: gp_Pln, bounds?: {
|
|
24
|
+
uMin: number;
|
|
25
|
+
uMax: number;
|
|
26
|
+
vMin: number;
|
|
27
|
+
vMax: number;
|
|
28
|
+
}): TopoDS_Face;
|
|
24
29
|
static makeFaceFromPlane(plane: gp_Pln): TopoDS_Face;
|
|
25
30
|
static makeFaceFromCylinder(cylinder: gp_Cylinder): TopoDS_Face;
|
|
26
31
|
static planeToFace(plane: Plane, center?: Point): Face;
|
package/lib/dist/oc/face-ops.js
CHANGED
|
@@ -197,9 +197,10 @@ export class FaceOps {
|
|
|
197
197
|
disposePnt();
|
|
198
198
|
return isInside;
|
|
199
199
|
}
|
|
200
|
-
static makeFaceFromPlane2(plane) {
|
|
200
|
+
static makeFaceFromPlane2(plane, bounds) {
|
|
201
201
|
const oc = getOC();
|
|
202
|
-
const
|
|
202
|
+
const b = bounds ?? { uMin: -1000, uMax: 1000, vMin: -1000, vMax: 1000 };
|
|
203
|
+
const faceMaker = new oc.BRepBuilderAPI_MakeFace(plane, b.uMin, b.uMax, b.vMin, b.vMax);
|
|
203
204
|
const face = faceMaker.Face();
|
|
204
205
|
faceMaker.delete();
|
|
205
206
|
return face;
|