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.
Files changed (68) hide show
  1. package/lib/dist/common/scene-object.d.ts +3 -0
  2. package/lib/dist/common/scene-object.js +7 -0
  3. package/lib/dist/core/2d/arc.d.ts +14 -56
  4. package/lib/dist/core/2d/arc.js +8 -23
  5. package/lib/dist/core/2d/center.d.ts +9 -0
  6. package/lib/dist/core/2d/center.js +10 -0
  7. package/lib/dist/core/2d/index.d.ts +2 -0
  8. package/lib/dist/core/2d/index.js +2 -0
  9. package/lib/dist/core/2d/intersect.d.ts +17 -0
  10. package/lib/dist/core/2d/intersect.js +20 -0
  11. package/lib/dist/core/interfaces.d.ts +50 -2
  12. package/lib/dist/core/repeat.js +10 -3
  13. package/lib/dist/features/2d/arc.d.ts +27 -14
  14. package/lib/dist/features/2d/arc.js +269 -38
  15. package/lib/dist/features/2d/intersect.d.ts +15 -0
  16. package/lib/dist/features/2d/intersect.js +83 -0
  17. package/lib/dist/features/2d/plane-center.d.ts +13 -0
  18. package/lib/dist/features/2d/plane-center.js +40 -0
  19. package/lib/dist/features/2d/projection.js +3 -4
  20. package/lib/dist/features/2d/rect.d.ts +2 -2
  21. package/lib/dist/features/2d/rect.js +3 -3
  22. package/lib/dist/features/2d/sketch.d.ts +2 -2
  23. package/lib/dist/features/2d/sketch.js +13 -1
  24. package/lib/dist/features/2d/slot.d.ts +2 -2
  25. package/lib/dist/features/2d/slot.js +3 -3
  26. package/lib/dist/features/extrude-base.d.ts +4 -0
  27. package/lib/dist/features/extrude-base.js +27 -0
  28. package/lib/dist/features/extrude-to-face.d.ts +1 -0
  29. package/lib/dist/features/extrude-to-face.js +5 -1
  30. package/lib/dist/features/extrude-two-distances.d.ts +1 -0
  31. package/lib/dist/features/extrude-two-distances.js +5 -2
  32. package/lib/dist/features/extrude.d.ts +1 -0
  33. package/lib/dist/features/extrude.js +5 -1
  34. package/lib/dist/features/plane-renderable-base.d.ts +2 -1
  35. package/lib/dist/features/plane-renderable-base.js +1 -1
  36. package/lib/dist/features/plane.d.ts +1 -1
  37. package/lib/dist/filters/face/face-filter.d.ts +10 -0
  38. package/lib/dist/filters/face/face-filter.js +39 -0
  39. package/lib/dist/filters/face/intersects-with.d.ts +18 -0
  40. package/lib/dist/filters/face/intersects-with.js +41 -0
  41. package/lib/dist/helpers/clone-transform.js +1 -0
  42. package/lib/dist/oc/boolean-ops.js +0 -2
  43. package/lib/dist/oc/face-query.d.ts +1 -0
  44. package/lib/dist/oc/face-query.js +15 -0
  45. package/lib/dist/oc/index.d.ts +1 -0
  46. package/lib/dist/oc/index.js +1 -0
  47. package/lib/dist/oc/section-ops.d.ts +6 -0
  48. package/lib/dist/oc/section-ops.js +26 -0
  49. package/lib/dist/oc/thin-face-maker.d.ts +29 -0
  50. package/lib/dist/oc/thin-face-maker.js +163 -0
  51. package/lib/dist/rendering/render.js +16 -0
  52. package/lib/dist/tests/features/2d/arc.test.js +2 -2
  53. package/lib/dist/tests/features/2d/intersect.test.d.ts +1 -0
  54. package/lib/dist/tests/features/2d/intersect.test.js +22 -0
  55. package/lib/dist/tests/features/2d/rect.test.js +1 -1
  56. package/lib/dist/tests/features/loft.test.js +1 -1
  57. package/lib/dist/tests/features/select.test.js +49 -1
  58. package/lib/dist/tests/features/thin-extrude.test.d.ts +1 -0
  59. package/lib/dist/tests/features/thin-extrude.test.js +137 -0
  60. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +3 -3
  62. package/server/dist/index.js +23 -15
  63. package/ui/dist/assets/{index-mLcpjEcV.js → index-BJG141m7.js} +2 -2
  64. package/ui/dist/index.html +1 -1
  65. package/lib/dist/features/2d/arc-three-points.d.ts +0 -19
  66. package/lib/dist/features/2d/arc-three-points.js +0 -75
  67. package/lib/dist/features/2d/arc-to-point.d.ts +0 -17
  68. 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];
@@ -39,5 +39,6 @@ export declare class ExtrudeToFace extends ExtrudeBase {
39
39
  draft: [number, number];
40
40
  endOffset: number;
41
41
  face: string;
42
+ thin: [number, number] | [number];
42
43
  };
43
44
  }
@@ -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 = pickedFaces ?? FaceMaker2.getRegions(this.extrudable.getGeometries(), plane);
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
  }
@@ -24,5 +24,6 @@ export declare class ExtrudeTwoDistances extends ExtrudeBase {
24
24
  distance1: number;
25
25
  distance2: number;
26
26
  operationMode: "new" | "remove";
27
+ thin: [number, number] | [number];
27
28
  };
28
29
  }
@@ -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 = pickedFaces ?? FaceMaker2.getRegions(this.extrudable.getGeometries(), plane, this.getDrill());
23
- console.log("Extruding faces:", faces);
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
  }
@@ -28,5 +28,6 @@ export declare class Extrude extends ExtrudeBase {
28
28
  symmetric: true;
29
29
  draft: [number, number];
30
30
  endOffset: number;
31
+ thin: [number, number] | [number];
31
32
  };
32
33
  }
@@ -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 = pickedFaces ?? FaceMaker2.getRegions(this.extrudable.getGeometries(), plane, this.getDrill());
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(): any;
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: any;
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);
@@ -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';
@@ -19,3 +19,4 @@ export { Mesh } from "./mesh.js";
19
19
  export { OcIO } from "./io.js";
20
20
  export { ShapeProps } from './props.js';
21
21
  export { SweepOps } from "./sweep-ops.js";
22
+ export { SectionOps } from "./section-ops.js";
@@ -0,0 +1,6 @@
1
+ import { Plane } from "../math/plane.js";
2
+ import { Shape } from "../common/shape.js";
3
+ import { Edge } from "../common/edge.js";
4
+ export declare class SectionOps {
5
+ static sectionShapeWithPlane(plane: Plane, shape: Shape): Edge[];
6
+ }
@@ -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
+ }