fluidcad 0.0.13 → 0.0.15
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/scene-object.d.ts +3 -0
- package/lib/dist/common/scene-object.js +7 -0
- package/lib/dist/core/2d/arc.d.ts +14 -56
- package/lib/dist/core/2d/arc.js +8 -23
- package/lib/dist/core/2d/center.d.ts +9 -0
- package/lib/dist/core/2d/center.js +10 -0
- package/lib/dist/core/2d/index.d.ts +2 -0
- package/lib/dist/core/2d/index.js +2 -0
- package/lib/dist/core/2d/intersect.d.ts +17 -0
- package/lib/dist/core/2d/intersect.js +20 -0
- package/lib/dist/core/interfaces.d.ts +50 -2
- package/lib/dist/core/repeat.js +10 -3
- package/lib/dist/features/2d/arc.d.ts +27 -14
- package/lib/dist/features/2d/arc.js +269 -38
- package/lib/dist/features/2d/intersect.d.ts +15 -0
- package/lib/dist/features/2d/intersect.js +83 -0
- package/lib/dist/features/2d/plane-center.d.ts +13 -0
- package/lib/dist/features/2d/plane-center.js +40 -0
- package/lib/dist/features/2d/projection.js +3 -4
- package/lib/dist/features/2d/rect.d.ts +2 -2
- package/lib/dist/features/2d/rect.js +3 -3
- package/lib/dist/features/2d/sketch.d.ts +2 -2
- package/lib/dist/features/2d/sketch.js +13 -1
- package/lib/dist/features/2d/slot.d.ts +2 -2
- package/lib/dist/features/2d/slot.js +3 -3
- package/lib/dist/features/extrude-base.d.ts +4 -0
- package/lib/dist/features/extrude-base.js +27 -0
- package/lib/dist/features/extrude-to-face.d.ts +1 -0
- package/lib/dist/features/extrude-to-face.js +5 -1
- package/lib/dist/features/extrude-two-distances.d.ts +1 -0
- package/lib/dist/features/extrude-two-distances.js +5 -2
- package/lib/dist/features/extrude.d.ts +1 -0
- package/lib/dist/features/extrude.js +5 -1
- package/lib/dist/features/plane-renderable-base.d.ts +2 -1
- package/lib/dist/features/plane-renderable-base.js +1 -1
- package/lib/dist/features/plane.d.ts +1 -1
- package/lib/dist/filters/face/face-filter.d.ts +10 -0
- package/lib/dist/filters/face/face-filter.js +39 -0
- package/lib/dist/filters/face/intersects-with.d.ts +18 -0
- package/lib/dist/filters/face/intersects-with.js +41 -0
- package/lib/dist/helpers/clone-transform.js +1 -0
- package/lib/dist/oc/boolean-ops.js +0 -2
- package/lib/dist/oc/face-query.d.ts +1 -0
- package/lib/dist/oc/face-query.js +15 -0
- package/lib/dist/oc/index.d.ts +1 -0
- package/lib/dist/oc/index.js +1 -0
- package/lib/dist/oc/section-ops.d.ts +6 -0
- package/lib/dist/oc/section-ops.js +26 -0
- package/lib/dist/oc/thin-face-maker.d.ts +29 -0
- package/lib/dist/oc/thin-face-maker.js +163 -0
- package/lib/dist/rendering/render.js +16 -0
- package/lib/dist/tests/features/2d/arc.test.js +2 -2
- package/lib/dist/tests/features/2d/intersect.test.d.ts +1 -0
- package/lib/dist/tests/features/2d/intersect.test.js +22 -0
- package/lib/dist/tests/features/2d/rect.test.js +1 -1
- package/lib/dist/tests/features/loft.test.js +1 -1
- package/lib/dist/tests/features/select.test.js +49 -1
- package/lib/dist/tests/features/thin-extrude.test.d.ts +1 -0
- package/lib/dist/tests/features/thin-extrude.test.js +137 -0
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/server/dist/index.js +23 -15
- package/ui/dist/assets/{index-mLcpjEcV.js → index-BJG141m7.js} +2 -2
- package/ui/dist/index.html +1 -1
- package/lib/dist/features/2d/arc-three-points.d.ts +0 -19
- package/lib/dist/features/2d/arc-three-points.js +0 -75
- package/lib/dist/features/2d/arc-to-point.d.ts +0 -17
- package/lib/dist/features/2d/arc-to-point.js +0 -95
|
@@ -14,6 +14,7 @@ export declare abstract class ExtrudeBase extends SceneObject implements IExtrud
|
|
|
14
14
|
protected _drill?: boolean;
|
|
15
15
|
protected _picking: boolean;
|
|
16
16
|
protected _pickPoints: LazyVertex[];
|
|
17
|
+
protected _thin?: [number] | [number, number];
|
|
17
18
|
constructor(extrudable?: Extrudable);
|
|
18
19
|
get extrudable(): Extrudable;
|
|
19
20
|
startFaces(...args: number[] | FaceFilterBuilder[]): SceneObject;
|
|
@@ -35,6 +36,9 @@ export declare abstract class ExtrudeBase extends SceneObject implements IExtrud
|
|
|
35
36
|
draft(value: number | [number, number]): this;
|
|
36
37
|
endOffset(value: number): this;
|
|
37
38
|
drill(value?: boolean): this;
|
|
39
|
+
thin(offset1: number, offset2?: number): this;
|
|
40
|
+
isThin(): boolean;
|
|
41
|
+
getThinOffsets(): [number] | [number, number] | undefined;
|
|
38
42
|
protected serializePickFields(): {
|
|
39
43
|
picking: true;
|
|
40
44
|
pickPoints: number[][];
|
|
@@ -13,6 +13,7 @@ export class ExtrudeBase extends SceneObject {
|
|
|
13
13
|
_drill = true;
|
|
14
14
|
_picking = false;
|
|
15
15
|
_pickPoints = [];
|
|
16
|
+
_thin;
|
|
16
17
|
constructor(extrudable) {
|
|
17
18
|
super();
|
|
18
19
|
this._extrudable = extrudable ?? null;
|
|
@@ -198,6 +199,16 @@ export class ExtrudeBase extends SceneObject {
|
|
|
198
199
|
this._drill = value;
|
|
199
200
|
return this;
|
|
200
201
|
}
|
|
202
|
+
thin(offset1, offset2) {
|
|
203
|
+
this._thin = offset2 !== undefined ? [offset1, offset2] : [offset1];
|
|
204
|
+
return this;
|
|
205
|
+
}
|
|
206
|
+
isThin() {
|
|
207
|
+
return this._thin !== undefined;
|
|
208
|
+
}
|
|
209
|
+
getThinOffsets() {
|
|
210
|
+
return this._thin;
|
|
211
|
+
}
|
|
201
212
|
serializePickFields() {
|
|
202
213
|
const plane = this._extrudable?.getPlane();
|
|
203
214
|
return {
|
|
@@ -307,6 +318,7 @@ export class ExtrudeBase extends SceneObject {
|
|
|
307
318
|
this._symmetric = other._symmetric;
|
|
308
319
|
this._picking = other._picking;
|
|
309
320
|
this._pickPoints = other._pickPoints;
|
|
321
|
+
this._thin = other._thin;
|
|
310
322
|
return this;
|
|
311
323
|
}
|
|
312
324
|
compareTo(other) {
|
|
@@ -328,6 +340,21 @@ export class ExtrudeBase extends SceneObject {
|
|
|
328
340
|
return false;
|
|
329
341
|
}
|
|
330
342
|
}
|
|
343
|
+
const thisThin = this._thin;
|
|
344
|
+
const otherThin = other._thin;
|
|
345
|
+
if ((thisThin === undefined) !== (otherThin === undefined)) {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
if (thisThin && otherThin) {
|
|
349
|
+
if (thisThin.length !== otherThin.length) {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
for (let i = 0; i < thisThin.length; i++) {
|
|
353
|
+
if (thisThin[i] !== otherThin[i]) {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
331
358
|
let thisDraft = this._draft || [0, 0];
|
|
332
359
|
let otherDraft = other._draft || [0, 0];
|
|
333
360
|
thisDraft = this._draft instanceof Array ? this._draft : [this._draft, this._draft];
|
|
@@ -9,6 +9,7 @@ import { BooleanOps } from "../oc/boolean-ops.js";
|
|
|
9
9
|
import { ShapeOps } from "../oc/shape-ops.js";
|
|
10
10
|
import { Explorer } from "../oc/explorer.js";
|
|
11
11
|
import { FaceMaker2 } from "../oc/face-maker2.js";
|
|
12
|
+
import { ThinFaceMaker } from "../oc/thin-face-maker.js";
|
|
12
13
|
import { Point } from "../math/point.js";
|
|
13
14
|
export class ExtrudeToFace extends ExtrudeBase {
|
|
14
15
|
face;
|
|
@@ -31,7 +32,9 @@ export class ExtrudeToFace extends ExtrudeBase {
|
|
|
31
32
|
const allEndFaces = [];
|
|
32
33
|
const allSideFaces = [];
|
|
33
34
|
const allInternalFaces = [];
|
|
34
|
-
const faces =
|
|
35
|
+
const faces = this.isThin()
|
|
36
|
+
? ThinFaceMaker.make(this.extrudable.getGeometries(), plane, this._thin[0], this._thin[1])
|
|
37
|
+
: pickedFaces ?? FaceMaker2.getRegions(this.extrudable.getGeometries(), plane);
|
|
35
38
|
for (const startFace of faces) {
|
|
36
39
|
if (isPlanar && FaceQuery.areFacePlanesParallel(startFace, targetFace)) {
|
|
37
40
|
const { shapes, extruder } = this.createSimpleExtrude(startFace, targetFace, plane);
|
|
@@ -269,6 +272,7 @@ export class ExtrudeToFace extends ExtrudeBase {
|
|
|
269
272
|
draft: this.getDraft(),
|
|
270
273
|
endOffset: this.getEndOffset(),
|
|
271
274
|
face: typeof (this.face) === 'string' ? this.face : 'selection',
|
|
275
|
+
thin: this._thin,
|
|
272
276
|
...this.serializePickFields(),
|
|
273
277
|
};
|
|
274
278
|
}
|
|
@@ -4,6 +4,7 @@ import { BooleanOps } from "../oc/boolean-ops.js";
|
|
|
4
4
|
import { Explorer } from "../oc/explorer.js";
|
|
5
5
|
import { FaceMaker2 } from "../oc/face-maker2.js";
|
|
6
6
|
import { Extruder } from "./simple-extruder.js";
|
|
7
|
+
import { ThinFaceMaker } from "../oc/thin-face-maker.js";
|
|
7
8
|
export class ExtrudeTwoDistances extends ExtrudeBase {
|
|
8
9
|
distance1;
|
|
9
10
|
distance2;
|
|
@@ -19,8 +20,9 @@ export class ExtrudeTwoDistances extends ExtrudeBase {
|
|
|
19
20
|
if (pickedFaces !== null && pickedFaces.length === 0) {
|
|
20
21
|
return;
|
|
21
22
|
}
|
|
22
|
-
const faces =
|
|
23
|
-
|
|
23
|
+
const faces = this.isThin()
|
|
24
|
+
? ThinFaceMaker.make(this.extrudable.getGeometries(), plane, this._thin[0], this._thin[1])
|
|
25
|
+
: pickedFaces ?? FaceMaker2.getRegions(this.extrudable.getGeometries(), plane, this.getDrill());
|
|
24
26
|
const extruder1 = new Extruder(faces, plane, this.distance1, this.getDraft(), this.getEndOffset());
|
|
25
27
|
const extrusions1 = extruder1.extrude();
|
|
26
28
|
const startFaces = extruder1.getEndFaces();
|
|
@@ -110,6 +112,7 @@ export class ExtrudeTwoDistances extends ExtrudeBase {
|
|
|
110
112
|
distance1: this.distance1,
|
|
111
113
|
distance2: this.distance2,
|
|
112
114
|
operationMode: this._operationMode !== 'add' ? this._operationMode : undefined,
|
|
115
|
+
thin: this._thin,
|
|
113
116
|
...this.serializePickFields(),
|
|
114
117
|
};
|
|
115
118
|
}
|
|
@@ -5,6 +5,7 @@ import { FaceMaker2 } from "../oc/face-maker2.js";
|
|
|
5
5
|
import { BooleanOps } from "../oc/boolean-ops.js";
|
|
6
6
|
import { Explorer } from "../oc/explorer.js";
|
|
7
7
|
import { ExtrudeThroughAll } from "./infinite-extrude.js";
|
|
8
|
+
import { ThinFaceMaker } from "../oc/thin-face-maker.js";
|
|
8
9
|
export class Extrude extends ExtrudeBase {
|
|
9
10
|
distance;
|
|
10
11
|
constructor(distance, extrudable) {
|
|
@@ -17,7 +18,9 @@ export class Extrude extends ExtrudeBase {
|
|
|
17
18
|
if (pickedFaces !== null && pickedFaces.length === 0) {
|
|
18
19
|
return;
|
|
19
20
|
}
|
|
20
|
-
const faces =
|
|
21
|
+
const faces = this.isThin()
|
|
22
|
+
? ThinFaceMaker.make(this.extrudable.getGeometries(), plane, this._thin[0], this._thin[1])
|
|
23
|
+
: pickedFaces ?? FaceMaker2.getRegions(this.extrudable.getGeometries(), plane, this.getDrill());
|
|
21
24
|
if (this._operationMode === 'remove') {
|
|
22
25
|
this.buildRemove(faces, plane, context);
|
|
23
26
|
}
|
|
@@ -176,6 +179,7 @@ export class Extrude extends ExtrudeBase {
|
|
|
176
179
|
symmetric: this._symmetric || undefined,
|
|
177
180
|
draft: this.getDraft(),
|
|
178
181
|
endOffset: this.getEndOffset(),
|
|
182
|
+
thin: this._thin,
|
|
179
183
|
...this.serializePickFields(),
|
|
180
184
|
};
|
|
181
185
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Plane } from "../math/plane.js";
|
|
2
2
|
import { SceneObject } from "../common/scene-object.js";
|
|
3
3
|
import { IPlane } from "../core/interfaces.js";
|
|
4
|
+
import { Point } from "../math/point.js";
|
|
4
5
|
export declare abstract class PlaneObjectBase extends SceneObject implements IPlane {
|
|
5
6
|
constructor();
|
|
6
7
|
getPlane(): Plane;
|
|
7
|
-
getPlaneCenter():
|
|
8
|
+
getPlaneCenter(): Point;
|
|
8
9
|
getType(): string;
|
|
9
10
|
}
|
|
@@ -7,7 +7,7 @@ export class PlaneObjectBase extends SceneObject {
|
|
|
7
7
|
return this.getState('plane');
|
|
8
8
|
}
|
|
9
9
|
getPlaneCenter() {
|
|
10
|
-
return this.getState('plane-center') || this.getPlane().origin;
|
|
10
|
+
return (this.getState('plane-center') || this.getPlane().origin);
|
|
11
11
|
}
|
|
12
12
|
getType() {
|
|
13
13
|
return 'plane';
|
|
@@ -15,7 +15,7 @@ export declare class PlaneObject extends PlaneObjectBase {
|
|
|
15
15
|
xDirection: import("../math/vector3d.js").Vector3d;
|
|
16
16
|
yDirection: import("../math/vector3d.js").Vector3d;
|
|
17
17
|
normal: import("../math/vector3d.js").Vector3d;
|
|
18
|
-
center:
|
|
18
|
+
center: import("../math/point.js").Point;
|
|
19
19
|
options: PlaneRenderableOptions;
|
|
20
20
|
};
|
|
21
21
|
}
|
|
@@ -80,6 +80,16 @@ export declare class FaceFilterBuilder extends FilterBuilderBase<Face> {
|
|
|
80
80
|
* Excludes conical faces.
|
|
81
81
|
*/
|
|
82
82
|
notCone(): this;
|
|
83
|
+
/**
|
|
84
|
+
* Selects faces that intersect with the given plane.
|
|
85
|
+
* @param plane - The reference plane to test intersection against.
|
|
86
|
+
*/
|
|
87
|
+
intersectsWith(plane: PlaneLike | PlaneObjectBase): this;
|
|
88
|
+
/**
|
|
89
|
+
* Excludes faces that intersect with the given plane.
|
|
90
|
+
* @param plane - The reference plane to test intersection against.
|
|
91
|
+
*/
|
|
92
|
+
notIntersectsWith(plane: PlaneLike | PlaneObjectBase): this;
|
|
83
93
|
/**
|
|
84
94
|
* Selects faces that share an edge with the given scene object.
|
|
85
95
|
* @param sceneObject - A scene object whose edges are matched against.
|
|
@@ -12,6 +12,7 @@ import { AtIndexFilter, NotAtIndexFilter } from "./at-index.js";
|
|
|
12
12
|
import { HasEdgeFilter, NotHasEdgeFilter } from "./has-edge.js";
|
|
13
13
|
import { HasEdgeFromSceneObjectFilter, NotHasEdgeFromSceneObjectFilter } from "./has-object.js";
|
|
14
14
|
import { EdgeCountFilter, NotEdgeCountFilter } from "./edge-count.js";
|
|
15
|
+
import { IntersectsWithFilter, NotIntersectsWithFilter } from "./intersects-with.js";
|
|
15
16
|
import { SceneObject } from "../../common/scene-object.js";
|
|
16
17
|
export class FaceFilterBuilder extends FilterBuilderBase {
|
|
17
18
|
constructor() {
|
|
@@ -195,6 +196,44 @@ export class FaceFilterBuilder extends FilterBuilderBase {
|
|
|
195
196
|
this.filters.push(filter);
|
|
196
197
|
return this;
|
|
197
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Selects faces that intersect with the given plane.
|
|
201
|
+
* @param plane - The reference plane to test intersection against.
|
|
202
|
+
*/
|
|
203
|
+
intersectsWith(plane) {
|
|
204
|
+
if (!plane) {
|
|
205
|
+
throw new Error('Plane is required');
|
|
206
|
+
}
|
|
207
|
+
let planeObj;
|
|
208
|
+
if (plane instanceof PlaneObjectBase) {
|
|
209
|
+
planeObj = plane;
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
planeObj = new PlaneObject(normalizePlane(plane));
|
|
213
|
+
}
|
|
214
|
+
const filter = new IntersectsWithFilter(planeObj);
|
|
215
|
+
this.filters.push(filter);
|
|
216
|
+
return this;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Excludes faces that intersect with the given plane.
|
|
220
|
+
* @param plane - The reference plane to test intersection against.
|
|
221
|
+
*/
|
|
222
|
+
notIntersectsWith(plane) {
|
|
223
|
+
if (!plane) {
|
|
224
|
+
throw new Error('Plane is required');
|
|
225
|
+
}
|
|
226
|
+
let planeObj;
|
|
227
|
+
if (plane instanceof PlaneObjectBase) {
|
|
228
|
+
planeObj = plane;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
planeObj = new PlaneObject(normalizePlane(plane));
|
|
232
|
+
}
|
|
233
|
+
const filter = new NotIntersectsWithFilter(planeObj);
|
|
234
|
+
this.filters.push(filter);
|
|
235
|
+
return this;
|
|
236
|
+
}
|
|
198
237
|
hasEdge(...args) {
|
|
199
238
|
const filterBuilders = [];
|
|
200
239
|
for (const arg of args) {
|
|
@@ -0,0 +1,18 @@
|
|
|
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 IntersectsWithFilter extends FilterBase<Face> {
|
|
6
|
+
private plane;
|
|
7
|
+
constructor(plane: PlaneObjectBase);
|
|
8
|
+
match(shape: Face): boolean;
|
|
9
|
+
compareTo(other: IntersectsWithFilter): boolean;
|
|
10
|
+
transform(matrix: Matrix4): IntersectsWithFilter;
|
|
11
|
+
}
|
|
12
|
+
export declare class NotIntersectsWithFilter extends FilterBase<Face> {
|
|
13
|
+
private plane;
|
|
14
|
+
constructor(plane: PlaneObjectBase);
|
|
15
|
+
match(shape: Face): boolean;
|
|
16
|
+
compareTo(other: NotIntersectsWithFilter): boolean;
|
|
17
|
+
transform(matrix: Matrix4): NotIntersectsWithFilter;
|
|
18
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { FilterBase } from "../filter-base.js";
|
|
2
|
+
import { FaceQuery } from "../../oc/face-query.js";
|
|
3
|
+
import { PlaneObject } from "../../features/plane.js";
|
|
4
|
+
export class IntersectsWithFilter extends FilterBase {
|
|
5
|
+
plane;
|
|
6
|
+
constructor(plane) {
|
|
7
|
+
super();
|
|
8
|
+
this.plane = plane;
|
|
9
|
+
}
|
|
10
|
+
match(shape) {
|
|
11
|
+
const plane = this.plane.getPlane();
|
|
12
|
+
return FaceQuery.doesFaceIntersectPlane(shape, plane);
|
|
13
|
+
}
|
|
14
|
+
compareTo(other) {
|
|
15
|
+
return this.plane.compareTo(other.plane);
|
|
16
|
+
}
|
|
17
|
+
transform(matrix) {
|
|
18
|
+
const plane = this.plane.getPlane();
|
|
19
|
+
const planeObj = new PlaneObject(plane.applyMatrix(matrix));
|
|
20
|
+
return new IntersectsWithFilter(planeObj);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export class NotIntersectsWithFilter extends FilterBase {
|
|
24
|
+
plane;
|
|
25
|
+
constructor(plane) {
|
|
26
|
+
super();
|
|
27
|
+
this.plane = plane;
|
|
28
|
+
}
|
|
29
|
+
match(shape) {
|
|
30
|
+
const plane = this.plane.getPlane();
|
|
31
|
+
return !FaceQuery.doesFaceIntersectPlane(shape, plane);
|
|
32
|
+
}
|
|
33
|
+
compareTo(other) {
|
|
34
|
+
return this.plane.compareTo(other.plane);
|
|
35
|
+
}
|
|
36
|
+
transform(matrix) {
|
|
37
|
+
const plane = this.plane.getPlane();
|
|
38
|
+
const planeObj = new PlaneObject(plane.applyMatrix(matrix));
|
|
39
|
+
return new NotIntersectsWithFilter(planeObj);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -33,6 +33,7 @@ export function cloneWithTransform(objects, transform, container) {
|
|
|
33
33
|
const copy = obj.createCopy(remap);
|
|
34
34
|
remap.set(obj, copy);
|
|
35
35
|
copy.setTransform(transform);
|
|
36
|
+
copy.setCloneSource(obj);
|
|
36
37
|
allCloned.push(copy);
|
|
37
38
|
const parent = obj.getParent();
|
|
38
39
|
if (parent && remap.has(parent)) {
|
|
@@ -137,7 +137,6 @@ export class BooleanOps {
|
|
|
137
137
|
builder.Build(progress);
|
|
138
138
|
builder.SimplifyResult(false, true, oc.Precision.Angular());
|
|
139
139
|
const resultShape = builder.Shape();
|
|
140
|
-
console.log('FuseMultiShape: Result shape type:', Explorer.getShapeType(resultShape));
|
|
141
140
|
const rawShapes = Explorer.findAllShapes(resultShape);
|
|
142
141
|
console.log('FuseMultiShape: Result shapes count:', rawShapes.length);
|
|
143
142
|
const result = rawShapes.map(s => ShapeFactory.fromShape(s));
|
|
@@ -175,7 +174,6 @@ export class BooleanOps {
|
|
|
175
174
|
const progress = new oc.Message_ProgressRange();
|
|
176
175
|
builder.Build(progress);
|
|
177
176
|
const resultShape = builder.Shape();
|
|
178
|
-
console.log('FuseMultiShape: Result shape type:', Explorer.getShapeType(resultShape));
|
|
179
177
|
const unify = new oc.ShapeUpgrade_UnifySameDomain(resultShape, true, true, false);
|
|
180
178
|
unify.Build();
|
|
181
179
|
const mergedShape = unify.Shape();
|
|
@@ -9,6 +9,7 @@ export declare class FaceQuery {
|
|
|
9
9
|
static isCylinderFace(face: Shape, diameter?: number): boolean;
|
|
10
10
|
static isCylinderCurveFace(face: Shape, diameter?: number): boolean;
|
|
11
11
|
static isFaceOnPlane(face: Shape, plane: Plane): boolean;
|
|
12
|
+
static doesFaceIntersectPlane(face: Shape, plane: Plane): boolean;
|
|
12
13
|
static isFaceParallelToPlane(face: Shape, plane: Plane): boolean;
|
|
13
14
|
static isPlanarFace(face: Shape): boolean;
|
|
14
15
|
static getSurfaceType(face: Shape): string;
|
|
@@ -23,6 +23,21 @@ export class FaceQuery {
|
|
|
23
23
|
dispose();
|
|
24
24
|
return result;
|
|
25
25
|
}
|
|
26
|
+
static doesFaceIntersectPlane(face, plane) {
|
|
27
|
+
const oc = getOC();
|
|
28
|
+
const [gpPln, dispose] = Convert.toGpPln(plane);
|
|
29
|
+
const planeFace = FaceOps.makeFaceFromPlane(gpPln);
|
|
30
|
+
const tool = new oc.IntTools_FaceFace();
|
|
31
|
+
tool.Perform(oc.TopoDS.Face(face.getShape()), planeFace, false);
|
|
32
|
+
let result = false;
|
|
33
|
+
if (tool.IsDone()) {
|
|
34
|
+
result = tool.Lines().Length() > 0 || tool.Points().Length() > 0;
|
|
35
|
+
}
|
|
36
|
+
tool.delete();
|
|
37
|
+
planeFace.delete();
|
|
38
|
+
dispose();
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
26
41
|
static isFaceParallelToPlane(face, plane) {
|
|
27
42
|
const [gpPln, dispose] = Convert.toGpPln(plane);
|
|
28
43
|
const result = FaceQuery.isFaceParallelToPlaneRaw(face.getShape(), gpPln);
|
package/lib/dist/oc/index.d.ts
CHANGED
|
@@ -21,4 +21,5 @@ export type { MeshData } from "./mesh.js";
|
|
|
21
21
|
export { OcIO } from "./io.js";
|
|
22
22
|
export { ShapeProps } from './props.js';
|
|
23
23
|
export { SweepOps } from "./sweep-ops.js";
|
|
24
|
+
export { SectionOps } from "./section-ops.js";
|
|
24
25
|
export type { ShapeProperties } from './props.js';
|
package/lib/dist/oc/index.js
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getOC } from "./init.js";
|
|
2
|
+
import { Convert } from "./convert.js";
|
|
3
|
+
import { Explorer } from "./explorer.js";
|
|
4
|
+
import { Edge } from "../common/edge.js";
|
|
5
|
+
export class SectionOps {
|
|
6
|
+
static sectionShapeWithPlane(plane, shape) {
|
|
7
|
+
const oc = getOC();
|
|
8
|
+
const [pln, disposePln] = Convert.toGpPln(plane);
|
|
9
|
+
const progress = new oc.Message_ProgressRange();
|
|
10
|
+
const section = new oc.BRepAlgoAPI_Section(shape.getShape(), pln, false);
|
|
11
|
+
section.Build(progress);
|
|
12
|
+
if (!section.IsDone()) {
|
|
13
|
+
section.delete();
|
|
14
|
+
progress.delete();
|
|
15
|
+
disposePln();
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
const result = section.Shape();
|
|
19
|
+
const rawEdges = Explorer.findShapes(result, oc.TopAbs_ShapeEnum.TopAbs_EDGE);
|
|
20
|
+
const edges = rawEdges.map(e => Edge.fromTopoDSEdge(Explorer.toEdge(e)));
|
|
21
|
+
section.delete();
|
|
22
|
+
progress.delete();
|
|
23
|
+
disposePln();
|
|
24
|
+
return edges;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Edge } from "../common/edge.js";
|
|
2
|
+
import { Face } from "../common/face.js";
|
|
3
|
+
import { Wire } from "../common/wire.js";
|
|
4
|
+
import { Plane } from "../math/plane.js";
|
|
5
|
+
export declare class ThinFaceMaker {
|
|
6
|
+
static make(edges: (Wire | Edge)[], plane: Plane, offset1: number, offset2?: number): Face[];
|
|
7
|
+
private static makeSingleOffsetFace;
|
|
8
|
+
private static makeDualOffsetFace;
|
|
9
|
+
/**
|
|
10
|
+
* Offsets a wire by the given distance, handling both closed and open wires.
|
|
11
|
+
* For closed wires, WireOps.offsetWire handles negative distances natively.
|
|
12
|
+
* For open wires, negative distances are handled by reversing the wire,
|
|
13
|
+
* offsetting with the absolute value, then reversing back.
|
|
14
|
+
*/
|
|
15
|
+
private static doOffset;
|
|
16
|
+
/**
|
|
17
|
+
* Offsets an open wire on a given plane, using a planar face as reference
|
|
18
|
+
* so that BRepOffsetAPI_MakeOffset knows the offset direction.
|
|
19
|
+
* Only handles positive distances — use doOffset for sign handling.
|
|
20
|
+
*/
|
|
21
|
+
private static offsetWireOnPlane;
|
|
22
|
+
private static makeLineEdge;
|
|
23
|
+
/**
|
|
24
|
+
* Creates a closed face from two open wires by capping the ends with straight lines.
|
|
25
|
+
* wire1 goes A->B, wire2 goes C->D.
|
|
26
|
+
* Result: A->B (wire1) + B->D (cap) + D->C (reversed wire2) + C->A (cap)
|
|
27
|
+
*/
|
|
28
|
+
private static makeOpenFaceWithCaps;
|
|
29
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { Edge } from "../common/edge.js";
|
|
2
|
+
import { Face } from "../common/face.js";
|
|
3
|
+
import { Wire } from "../common/wire.js";
|
|
4
|
+
import { WireOps } from "./wire-ops.js";
|
|
5
|
+
import { FaceOps } from "./face-ops.js";
|
|
6
|
+
import { Explorer } from "./explorer.js";
|
|
7
|
+
import { Convert } from "./convert.js";
|
|
8
|
+
import { getOC } from "./init.js";
|
|
9
|
+
export class ThinFaceMaker {
|
|
10
|
+
static make(edges, plane, offset1, offset2) {
|
|
11
|
+
const edgesOnly = [];
|
|
12
|
+
for (const shape of edges) {
|
|
13
|
+
if (shape instanceof Wire) {
|
|
14
|
+
for (const edge of shape.getEdges()) {
|
|
15
|
+
edgesOnly.push(edge);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
edgesOnly.push(shape);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const groups = WireOps.groupConnectedEdges(edgesOnly);
|
|
23
|
+
const faces = [];
|
|
24
|
+
for (const group of groups) {
|
|
25
|
+
const wire = WireOps.makeWireFromEdges(group);
|
|
26
|
+
const isClosed = wire.isClosed();
|
|
27
|
+
if (offset2 !== undefined) {
|
|
28
|
+
faces.push(this.makeDualOffsetFace(wire, isClosed, plane, offset1, offset2));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
faces.push(this.makeSingleOffsetFace(wire, isClosed, plane, offset1));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return faces;
|
|
35
|
+
}
|
|
36
|
+
static makeSingleOffsetFace(wire, isClosed, plane, offset) {
|
|
37
|
+
const offsetWire = this.doOffset(wire, plane, offset, isClosed);
|
|
38
|
+
if (isClosed) {
|
|
39
|
+
// Determine which wire is outer (larger) vs inner based on offset sign
|
|
40
|
+
const [outer, inner] = offset >= 0
|
|
41
|
+
? [offsetWire, wire]
|
|
42
|
+
: [wire, offsetWire];
|
|
43
|
+
const reversedInner = WireOps.reverseWire(inner);
|
|
44
|
+
return Face.fromTopoDSFace(FaceOps.makeFaceFromWires([
|
|
45
|
+
outer.getShape(),
|
|
46
|
+
reversedInner.getShape(),
|
|
47
|
+
]));
|
|
48
|
+
}
|
|
49
|
+
return this.makeOpenFaceWithCaps(wire, offsetWire);
|
|
50
|
+
}
|
|
51
|
+
static makeDualOffsetFace(wire, isClosed, plane, offset1, offset2) {
|
|
52
|
+
// Ensure offset2 goes in the opposite direction of offset1
|
|
53
|
+
if (Math.sign(offset1) === Math.sign(offset2)) {
|
|
54
|
+
offset2 = -offset2;
|
|
55
|
+
}
|
|
56
|
+
const wire1 = this.doOffset(wire, plane, offset1, isClosed);
|
|
57
|
+
const wire2 = this.doOffset(wire, plane, offset2, isClosed);
|
|
58
|
+
if (isClosed) {
|
|
59
|
+
// The wire with the larger offset is the outer boundary
|
|
60
|
+
const [outer, inner] = offset1 >= offset2
|
|
61
|
+
? [wire1, wire2]
|
|
62
|
+
: [wire2, wire1];
|
|
63
|
+
const reversedInner = WireOps.reverseWire(inner);
|
|
64
|
+
return Face.fromTopoDSFace(FaceOps.makeFaceFromWires([
|
|
65
|
+
outer.getShape(),
|
|
66
|
+
reversedInner.getShape(),
|
|
67
|
+
]));
|
|
68
|
+
}
|
|
69
|
+
return this.makeOpenFaceWithCaps(wire1, wire2);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Offsets a wire by the given distance, handling both closed and open wires.
|
|
73
|
+
* For closed wires, WireOps.offsetWire handles negative distances natively.
|
|
74
|
+
* For open wires, negative distances are handled by reversing the wire,
|
|
75
|
+
* offsetting with the absolute value, then reversing back.
|
|
76
|
+
*/
|
|
77
|
+
static doOffset(wire, plane, distance, isClosed) {
|
|
78
|
+
if (isClosed) {
|
|
79
|
+
return WireOps.offsetWire(wire, distance, false);
|
|
80
|
+
}
|
|
81
|
+
if (distance < 0) {
|
|
82
|
+
const reversed = WireOps.reverseWire(wire);
|
|
83
|
+
const offsetResult = this.offsetWireOnPlane(reversed, plane, -distance, true);
|
|
84
|
+
return WireOps.reverseWire(offsetResult);
|
|
85
|
+
}
|
|
86
|
+
return this.offsetWireOnPlane(wire, plane, distance, true);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Offsets an open wire on a given plane, using a planar face as reference
|
|
90
|
+
* so that BRepOffsetAPI_MakeOffset knows the offset direction.
|
|
91
|
+
* Only handles positive distances — use doOffset for sign handling.
|
|
92
|
+
*/
|
|
93
|
+
static offsetWireOnPlane(wire, plane, distance, isOpen) {
|
|
94
|
+
const oc = getOC();
|
|
95
|
+
const [pln, disposePlane] = Convert.toGpPln(plane);
|
|
96
|
+
const faceMaker = new oc.BRepBuilderAPI_MakeFace(pln);
|
|
97
|
+
if (!faceMaker.IsDone()) {
|
|
98
|
+
faceMaker.delete();
|
|
99
|
+
disposePlane();
|
|
100
|
+
throw new Error("Failed to create reference face for thin offset");
|
|
101
|
+
}
|
|
102
|
+
const face = faceMaker.Face();
|
|
103
|
+
faceMaker.delete();
|
|
104
|
+
disposePlane();
|
|
105
|
+
const maker = new oc.BRepOffsetAPI_MakeOffset();
|
|
106
|
+
maker.Init(face, oc.GeomAbs_JoinType.GeomAbs_Arc, isOpen);
|
|
107
|
+
maker.AddWire(wire.getShape());
|
|
108
|
+
maker.Perform(distance, 0);
|
|
109
|
+
if (!maker.IsDone()) {
|
|
110
|
+
maker.delete();
|
|
111
|
+
throw new Error("Failed to offset wire for thin extrude");
|
|
112
|
+
}
|
|
113
|
+
const result = maker.Shape();
|
|
114
|
+
maker.delete();
|
|
115
|
+
if (Explorer.isWire(result)) {
|
|
116
|
+
return Wire.fromTopoDSWire(oc.TopoDS.Wire(result));
|
|
117
|
+
}
|
|
118
|
+
const wires = Explorer.findShapes(result, oc.TopAbs_ShapeEnum.TopAbs_WIRE);
|
|
119
|
+
if (wires.length === 0) {
|
|
120
|
+
throw new Error("Thin offset produced no usable wire");
|
|
121
|
+
}
|
|
122
|
+
return Wire.fromTopoDSWire(oc.TopoDS.Wire(wires[0]));
|
|
123
|
+
}
|
|
124
|
+
static makeLineEdge(p1, p2) {
|
|
125
|
+
const oc = getOC();
|
|
126
|
+
const [gp1, dispose1] = Convert.toGpPnt(p1);
|
|
127
|
+
const [gp2, dispose2] = Convert.toGpPnt(p2);
|
|
128
|
+
const edgeMaker = new oc.BRepBuilderAPI_MakeEdge(gp1, gp2);
|
|
129
|
+
if (!edgeMaker.IsDone()) {
|
|
130
|
+
edgeMaker.delete();
|
|
131
|
+
dispose1();
|
|
132
|
+
dispose2();
|
|
133
|
+
throw new Error("Failed to create cap edge for thin extrude");
|
|
134
|
+
}
|
|
135
|
+
const edge = edgeMaker.Edge();
|
|
136
|
+
edgeMaker.delete();
|
|
137
|
+
dispose1();
|
|
138
|
+
dispose2();
|
|
139
|
+
return Edge.fromTopoDSEdge(edge);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Creates a closed face from two open wires by capping the ends with straight lines.
|
|
143
|
+
* wire1 goes A->B, wire2 goes C->D.
|
|
144
|
+
* Result: A->B (wire1) + B->D (cap) + D->C (reversed wire2) + C->A (cap)
|
|
145
|
+
*/
|
|
146
|
+
static makeOpenFaceWithCaps(wire1, wire2) {
|
|
147
|
+
const wire1End = wire1.getLastVertex().toPoint();
|
|
148
|
+
const wire2Start = wire2.getFirstVertex().toPoint();
|
|
149
|
+
const wire2End = wire2.getLastVertex().toPoint();
|
|
150
|
+
const wire1Start = wire1.getFirstVertex().toPoint();
|
|
151
|
+
const cap1 = this.makeLineEdge(wire1End, wire2End);
|
|
152
|
+
const cap2 = this.makeLineEdge(wire2Start, wire1Start);
|
|
153
|
+
const reversedWire2 = WireOps.reverseWire(wire2);
|
|
154
|
+
const allEdges = [
|
|
155
|
+
...wire1.getEdges(),
|
|
156
|
+
cap1,
|
|
157
|
+
...reversedWire2.getEdges(),
|
|
158
|
+
cap2,
|
|
159
|
+
];
|
|
160
|
+
const closedWire = WireOps.makeWireFromEdges(allEdges);
|
|
161
|
+
return Face.fromTopoDSFace(FaceOps.makeFaceFromWires([closedWire.getShape()]));
|
|
162
|
+
}
|
|
163
|
+
}
|