fluidcad 0.0.7 → 0.0.9

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 (72) hide show
  1. package/bin/fluidcad.js +16 -0
  2. package/lib/dist/core/2d/arc.d.ts +15 -0
  3. package/lib/dist/core/2d/arc.js +10 -0
  4. package/lib/dist/core/2d/index.d.ts +0 -1
  5. package/lib/dist/core/2d/index.js +0 -1
  6. package/lib/dist/core/2d/tarc.d.ts +26 -2
  7. package/lib/dist/core/2d/tcircle.d.ts +26 -2
  8. package/lib/dist/core/2d/tline.d.ts +26 -6
  9. package/lib/dist/core/axis.d.ts +11 -4
  10. package/lib/dist/core/chamfer.d.ts +6 -6
  11. package/lib/dist/core/chamfer.js +12 -9
  12. package/lib/dist/core/cut.d.ts +27 -6
  13. package/lib/dist/core/extrude.d.ts +15 -3
  14. package/lib/dist/core/plane.d.ts +11 -4
  15. package/lib/dist/core/repeat.d.ts +16 -1
  16. package/lib/dist/core/repeat.js +39 -3
  17. package/lib/dist/core/sketch.d.ts +9 -3
  18. package/lib/dist/core/subtract.d.ts +5 -4
  19. package/lib/dist/core/subtract.js +9 -2
  20. package/lib/dist/features/2d/arc-three-points.d.ts +19 -0
  21. package/lib/dist/features/2d/arc-three-points.js +75 -0
  22. package/lib/dist/features/2d/constraints/geometry-qualifier.d.ts +16 -0
  23. package/lib/dist/features/2d/constraints/geometry-qualifier.js +16 -0
  24. package/lib/dist/features/2d/offset.js +10 -0
  25. package/lib/dist/features/2d/projection.js +9 -0
  26. package/lib/dist/features/2d/rect.js +19 -16
  27. package/lib/dist/features/chamfer.d.ts +4 -4
  28. package/lib/dist/features/chamfer.js +33 -15
  29. package/lib/dist/features/common2d.js +26 -8
  30. package/lib/dist/features/fuse2d.js +24 -6
  31. package/lib/dist/features/repeat-matrix.d.ts +14 -0
  32. package/lib/dist/features/repeat-matrix.js +41 -0
  33. package/lib/dist/features/select.d.ts +1 -0
  34. package/lib/dist/features/select.js +19 -0
  35. package/lib/dist/features/subtract2d.d.ts +14 -0
  36. package/lib/dist/features/subtract2d.js +80 -0
  37. package/lib/dist/filters/edge/belongs-to-face.d.ts +22 -0
  38. package/lib/dist/filters/edge/belongs-to-face.js +67 -0
  39. package/lib/dist/filters/edge/belongs-to-object.d.ts +18 -0
  40. package/lib/dist/filters/edge/belongs-to-object.js +37 -0
  41. package/lib/dist/filters/edge/edge-filter.d.ts +88 -3
  42. package/lib/dist/filters/edge/edge-filter.js +101 -4
  43. package/lib/dist/filters/edge/line-filter.d.ts +4 -1
  44. package/lib/dist/filters/edge/line-filter.js +14 -7
  45. package/lib/dist/filters/face/edge-count.d.ts +17 -0
  46. package/lib/dist/filters/face/edge-count.js +33 -0
  47. package/lib/dist/filters/face/face-filter.d.ts +93 -0
  48. package/lib/dist/filters/face/face-filter.js +112 -0
  49. package/lib/dist/filters/face/has-edge.d.ts +18 -0
  50. package/lib/dist/filters/face/has-edge.js +59 -0
  51. package/lib/dist/filters/face/has-object.d.ts +18 -0
  52. package/lib/dist/filters/face/has-object.js +37 -0
  53. package/lib/dist/filters/index.d.ts +6 -0
  54. package/lib/dist/filters/index.js +6 -0
  55. package/lib/dist/index.js +1 -1
  56. package/lib/dist/oc/edge-query.d.ts +2 -2
  57. package/lib/dist/oc/edge-query.js +13 -4
  58. package/lib/dist/tests/features/2d/arc.test.js +15 -1
  59. package/lib/dist/tests/features/select.test.js +249 -0
  60. package/lib/dist/tests/features/subtract2d.test.d.ts +1 -0
  61. package/lib/dist/tests/features/subtract2d.test.js +63 -0
  62. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  63. package/package.json +3 -2
  64. package/server/dist/fluidcad-server.js +3 -2
  65. package/server/dist/index.js +2 -1
  66. package/server/dist/normalize-path.d.ts +6 -0
  67. package/server/dist/normalize-path.js +8 -0
  68. package/server/dist/vite-manager.js +3 -2
  69. package/lib/dist/core/2d/wire.d.ts +0 -11
  70. package/lib/dist/core/2d/wire.js +0 -29
  71. package/lib/dist/features/2d/wire.d.ts +0 -12
  72. package/lib/dist/features/2d/wire.js +0 -76
@@ -0,0 +1,75 @@
1
+ import { Vertex } from "../../common/vertex.js";
2
+ import { Geometry } from "../../oc/geometry.js";
3
+ import { Point2D } from "../../math/point.js";
4
+ import { GeometrySceneObject } from "./geometry.js";
5
+ export class ArcThreePoints extends GeometrySceneObject {
6
+ startPoint;
7
+ endPoint;
8
+ center;
9
+ targetPlane;
10
+ constructor(startPoint, endPoint, center, targetPlane = null) {
11
+ super();
12
+ this.startPoint = startPoint;
13
+ this.endPoint = endPoint;
14
+ this.center = center;
15
+ this.targetPlane = targetPlane;
16
+ }
17
+ build() {
18
+ const plane = this.targetPlane?.getPlane() || this.sketch.getPlane();
19
+ const startPt = this.startPoint.asPoint2D();
20
+ const endPt = this.endPoint.asPoint2D();
21
+ const centerPt = this.center.asPoint2D();
22
+ const dx = startPt.x - centerPt.x;
23
+ const dy = startPt.y - centerPt.y;
24
+ const radius = Math.sqrt(dx * dx + dy * dy);
25
+ const endAngle = Math.atan2(endPt.y - centerPt.y, endPt.x - centerPt.x);
26
+ const center = plane.localToWorld(centerPt);
27
+ const start = plane.localToWorld(startPt);
28
+ const end = plane.localToWorld(endPt);
29
+ const arc = Geometry.makeArc(center, radius, plane.normal, start, end);
30
+ const edge = Geometry.makeEdgeFromCurve(arc);
31
+ // Tangent at end: perpendicular to radius direction at end point (CCW)
32
+ const tx = -Math.sin(endAngle);
33
+ const ty = Math.cos(endAngle);
34
+ this.setTangent(new Point2D(tx, ty));
35
+ this.setState('start', Vertex.fromPoint2D(startPt));
36
+ this.setState('end', Vertex.fromPoint2D(endPt));
37
+ this.addShape(edge);
38
+ if (this.sketch) {
39
+ this.setCurrentPosition(endPt);
40
+ }
41
+ if (this.targetPlane) {
42
+ this.targetPlane.removeShapes(this);
43
+ }
44
+ }
45
+ compareTo(other) {
46
+ if (!(other instanceof ArcThreePoints)) {
47
+ return false;
48
+ }
49
+ if (!super.compareTo(other)) {
50
+ return false;
51
+ }
52
+ if (this.targetPlane?.constructor !== other.targetPlane?.constructor) {
53
+ return false;
54
+ }
55
+ if (this.targetPlane && other.targetPlane && !this.targetPlane.compareTo(other.targetPlane)) {
56
+ return false;
57
+ }
58
+ return this.startPoint.compareTo(other.startPoint) &&
59
+ this.endPoint.compareTo(other.endPoint) &&
60
+ this.center.compareTo(other.center);
61
+ }
62
+ getType() {
63
+ return 'arc';
64
+ }
65
+ getUniqueType() {
66
+ return 'arc-three-points';
67
+ }
68
+ serialize() {
69
+ return {
70
+ startPoint: this.startPoint.serialize(),
71
+ endPoint: this.endPoint.serialize(),
72
+ center: this.center.serialize()
73
+ };
74
+ }
75
+ }
@@ -1,6 +1,22 @@
1
1
  import { ISceneObject } from "../../../core/interfaces.js";
2
2
  import { QualifiedSceneObject } from "./qualified-geometry.js";
3
+ /**
4
+ * Qualifies a geometry as being on the outside of the constraining object.
5
+ * @param sceneObject - The geometry to qualify.
6
+ */
3
7
  export declare function outside(sceneObject: ISceneObject): QualifiedSceneObject;
8
+ /**
9
+ * Qualifies a geometry as being enclosed by the constraining object.
10
+ * @param sceneObject - The geometry to qualify.
11
+ */
4
12
  export declare function enclosed(sceneObject: ISceneObject): QualifiedSceneObject;
13
+ /**
14
+ * Qualifies a geometry as enclosing the constraining object.
15
+ * @param sceneObject - The geometry to qualify.
16
+ */
5
17
  export declare function enclosing(sceneObject: ISceneObject): QualifiedSceneObject;
18
+ /**
19
+ * Removes any existing constraint qualification from a geometry.
20
+ * @param sceneObject - The geometry to unqualify.
21
+ */
6
22
  export declare function unqualified(sceneObject: ISceneObject): QualifiedSceneObject;
@@ -1,13 +1,29 @@
1
1
  import { QualifiedSceneObject } from "./qualified-geometry.js";
2
+ /**
3
+ * Qualifies a geometry as being on the outside of the constraining object.
4
+ * @param sceneObject - The geometry to qualify.
5
+ */
2
6
  export function outside(sceneObject) {
3
7
  return new QualifiedSceneObject(sceneObject, 'outside');
4
8
  }
9
+ /**
10
+ * Qualifies a geometry as being enclosed by the constraining object.
11
+ * @param sceneObject - The geometry to qualify.
12
+ */
5
13
  export function enclosed(sceneObject) {
6
14
  return new QualifiedSceneObject(sceneObject, 'enclosed');
7
15
  }
16
+ /**
17
+ * Qualifies a geometry as enclosing the constraining object.
18
+ * @param sceneObject - The geometry to qualify.
19
+ */
8
20
  export function enclosing(sceneObject) {
9
21
  return new QualifiedSceneObject(sceneObject, 'enclosing');
10
22
  }
23
+ /**
24
+ * Removes any existing constraint qualification from a geometry.
25
+ * @param sceneObject - The geometry to unqualify.
26
+ */
11
27
  export function unqualified(sceneObject) {
12
28
  return new QualifiedSceneObject(sceneObject, 'unqualified');
13
29
  }
@@ -1,5 +1,6 @@
1
1
  import { WireOps } from "../../oc/wire-ops.js";
2
2
  import { Edge } from "../../common/edge.js";
3
+ import { Vertex } from "../../common/vertex.js";
3
4
  import { Wire } from "../../common/wire.js";
4
5
  import { ExtrudableGeometryBase } from "./extrudable-base.js";
5
6
  export class Offset extends ExtrudableGeometryBase {
@@ -44,9 +45,11 @@ export class Offset extends ExtrudableGeometryBase {
44
45
  edges: new Map(group.map(edge => [edge, sourceObjects.get(edge)]))
45
46
  });
46
47
  }
48
+ let lastOffsetWire = null;
47
49
  for (const wireInfo of wires) {
48
50
  const isOpen = !wireInfo.wire.isClosed();
49
51
  const offsetWire = WireOps.offsetWire(wireInfo.wire, this.distance, isOpen);
52
+ lastOffsetWire = offsetWire;
50
53
  const edges = offsetWire.getEdges();
51
54
  for (const edge of edges) {
52
55
  this.addShape(edge);
@@ -57,6 +60,13 @@ export class Offset extends ExtrudableGeometryBase {
57
60
  }
58
61
  }
59
62
  }
63
+ if (lastOffsetWire) {
64
+ const plane = this.getPlane();
65
+ const localStart = plane.worldToLocal(lastOffsetWire.getFirstVertex().toPoint());
66
+ const localEnd = plane.worldToLocal(lastOffsetWire.getLastVertex().toPoint());
67
+ this.setState('start', Vertex.fromPoint2D(localStart));
68
+ this.setState('end', Vertex.fromPoint2D(localEnd));
69
+ }
60
70
  }
61
71
  getDependencies() {
62
72
  const deps = [];
@@ -1,6 +1,7 @@
1
1
  import { Face } from "../../common/face.js";
2
2
  import { ShapeOps } from "../../oc/shape-ops.js";
3
3
  import { Edge } from "../../common/edge.js";
4
+ import { Vertex } from "../../common/vertex.js";
4
5
  import { ProjectionOps } from "../../oc/intersection.js";
5
6
  import { Wire } from "../../common/wire.js";
6
7
  import { ExtrudableGeometryBase } from "./extrudable-base.js";
@@ -14,6 +15,7 @@ export class Projection extends ExtrudableGeometryBase {
14
15
  const plane = this.targetPlane?.getPlane() || this.sketch.getPlane();
15
16
  const shapes = this.sourceObjects.flatMap(obj => obj.getShapes());
16
17
  const transform = context?.getTransform() ?? null;
18
+ let lastWire = null;
17
19
  console.log('Projection: building with shapes:', shapes.length);
18
20
  for (let shape of shapes) {
19
21
  if (transform) {
@@ -31,9 +33,16 @@ export class Projection extends ExtrudableGeometryBase {
31
33
  wires = ProjectionOps.projectEdgeOntoPlane(plane, shape);
32
34
  }
33
35
  for (const wire of wires) {
36
+ lastWire = wire;
34
37
  this.addShapes(wire.getEdges());
35
38
  }
36
39
  }
40
+ if (lastWire) {
41
+ const localStart = plane.worldToLocal(lastWire.getFirstVertex().toPoint());
42
+ const localEnd = plane.worldToLocal(lastWire.getLastVertex().toPoint());
43
+ this.setState('start', Vertex.fromPoint2D(localStart));
44
+ this.setState('end', Vertex.fromPoint2D(localEnd));
45
+ }
37
46
  for (const obj of this.sourceObjects) {
38
47
  obj.removeShapes(this);
39
48
  }
@@ -77,41 +77,44 @@ export class Rect extends ExtrudableGeometryBase {
77
77
  const [bottomLeftRadius, bottomRightRadius, topRightRadius, topLeftRadius] = radius;
78
78
  const bottomLeft = start;
79
79
  const topRight = new Point2D(bottomLeft.x + this.width, bottomLeft.y + this.height);
80
- const bottomLeftArcCenter = new Point2D(bottomLeft.x + bottomLeftRadius, bottomLeft.y + bottomLeftRadius);
81
- const bottomRightArcCenter = new Point2D(topRight.x - bottomRightRadius, bottomLeft.y + bottomRightRadius);
82
- const topRightArcCenter = new Point2D(topRight.x - topRightRadius, topRight.y - topRightRadius);
83
- const topLeftArcCenter = new Point2D(bottomLeft.x + topLeftRadius, topRight.y - topLeftRadius);
84
- const bottomLineStart = new Point2D(bottomLeft.x + bottomLeftRadius, bottomLeft.y);
85
- const bottomLineEnd = new Point2D(topRight.x - bottomRightRadius, bottomLeft.y);
86
- const rightLineStart = new Point2D(topRight.x, bottomLeft.y + bottomRightRadius);
87
- const rightLineEnd = new Point2D(topRight.x, topRight.y - topRightRadius);
88
- const topLineStart = new Point2D(topRight.x - topRightRadius, topRight.y);
89
- const topLineEnd = new Point2D(bottomLeft.x + topLeftRadius, topRight.y);
90
- const leftLineStart = new Point2D(bottomLeft.x, topRight.y - topLeftRadius);
91
- const leftLineEnd = new Point2D(bottomLeft.x, bottomLeft.y + bottomLeftRadius);
80
+ const signW = Math.sign(this.width);
81
+ const signH = Math.sign(this.height);
82
+ const bottomLeftArcCenter = new Point2D(bottomLeft.x + bottomLeftRadius * signW, bottomLeft.y + bottomLeftRadius * signH);
83
+ const bottomRightArcCenter = new Point2D(topRight.x - bottomRightRadius * signW, bottomLeft.y + bottomRightRadius * signH);
84
+ const topRightArcCenter = new Point2D(topRight.x - topRightRadius * signW, topRight.y - topRightRadius * signH);
85
+ const topLeftArcCenter = new Point2D(bottomLeft.x + topLeftRadius * signW, topRight.y - topLeftRadius * signH);
86
+ const bottomLineStart = new Point2D(bottomLeft.x + bottomLeftRadius * signW, bottomLeft.y);
87
+ const bottomLineEnd = new Point2D(topRight.x - bottomRightRadius * signW, bottomLeft.y);
88
+ const rightLineStart = new Point2D(topRight.x, bottomLeft.y + bottomRightRadius * signH);
89
+ const rightLineEnd = new Point2D(topRight.x, topRight.y - topRightRadius * signH);
90
+ const topLineStart = new Point2D(topRight.x - topRightRadius * signW, topRight.y);
91
+ const topLineEnd = new Point2D(bottomLeft.x + topLeftRadius * signW, topRight.y);
92
+ const leftLineStart = new Point2D(bottomLeft.x, topRight.y - topLeftRadius * signH);
93
+ const leftLineEnd = new Point2D(bottomLeft.x, bottomLeft.y + bottomLeftRadius * signH);
92
94
  const localToWorld = plane.localToWorld.bind(plane);
95
+ const arcNormal = signW * signH < 0 ? plane.normal.negate() : plane.normal;
93
96
  const bottomLineSegment = Geometry.makeSegment(localToWorld(bottomLineStart), localToWorld(bottomLineEnd));
94
97
  const rightLineSegment = Geometry.makeSegment(localToWorld(rightLineStart), localToWorld(rightLineEnd));
95
98
  const topLineSegment = Geometry.makeSegment(localToWorld(topLineStart), localToWorld(topLineEnd));
96
99
  const leftLineSegment = Geometry.makeSegment(localToWorld(leftLineStart), localToWorld(leftLineEnd));
97
100
  let bottomRightArcEdge = null;
98
101
  if (bottomRightRadius > 0) {
99
- const arc = Geometry.makeArc(localToWorld(bottomRightArcCenter), bottomRightRadius, plane.normal, localToWorld(bottomLineEnd), localToWorld(rightLineStart));
102
+ const arc = Geometry.makeArc(localToWorld(bottomRightArcCenter), bottomRightRadius, arcNormal, localToWorld(bottomLineEnd), localToWorld(rightLineStart));
100
103
  bottomRightArcEdge = Geometry.makeEdgeFromCurve(arc);
101
104
  }
102
105
  let topRightArcEdge = null;
103
106
  if (topRightRadius > 0) {
104
- const arc = Geometry.makeArc(localToWorld(topRightArcCenter), topRightRadius, plane.normal, localToWorld(rightLineEnd), localToWorld(topLineStart));
107
+ const arc = Geometry.makeArc(localToWorld(topRightArcCenter), topRightRadius, arcNormal, localToWorld(rightLineEnd), localToWorld(topLineStart));
105
108
  topRightArcEdge = Geometry.makeEdgeFromCurve(arc);
106
109
  }
107
110
  let topLeftArcEdge = null;
108
111
  if (topLeftRadius > 0) {
109
- const arc = Geometry.makeArc(localToWorld(topLeftArcCenter), topLeftRadius, plane.normal, localToWorld(topLineEnd), localToWorld(leftLineStart));
112
+ const arc = Geometry.makeArc(localToWorld(topLeftArcCenter), topLeftRadius, arcNormal, localToWorld(topLineEnd), localToWorld(leftLineStart));
110
113
  topLeftArcEdge = Geometry.makeEdgeFromCurve(arc);
111
114
  }
112
115
  let bottomLeftArcEdge = null;
113
116
  if (bottomLeftRadius > 0) {
114
- const arc = Geometry.makeArc(localToWorld(bottomLeftArcCenter), bottomLeftRadius, plane.normal, localToWorld(leftLineEnd), localToWorld(bottomLineStart));
117
+ const arc = Geometry.makeArc(localToWorld(bottomLeftArcCenter), bottomLeftRadius, arcNormal, localToWorld(leftLineEnd), localToWorld(bottomLineStart));
115
118
  bottomLeftArcEdge = Geometry.makeEdgeFromCurve(arc);
116
119
  }
117
120
  const bottomEdge = Geometry.makeEdge(bottomLineSegment);
@@ -3,16 +3,16 @@ export declare class Chamfer extends SceneObject {
3
3
  private distance;
4
4
  private distance2;
5
5
  private isAngle;
6
- private _selection;
7
- constructor(distance: number, distance2: number, isAngle?: boolean, selection?: SceneObject);
8
- get selection(): SceneObject;
6
+ private _selections;
7
+ constructor(distance: number, distance2: number, isAngle?: boolean, ...selections: SceneObject[]);
8
+ get selections(): SceneObject[];
9
9
  build(context: BuildSceneObjectContext): void;
10
10
  getDependencies(): SceneObject[];
11
11
  createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
12
12
  compareTo(other: SceneObject): boolean;
13
13
  getType(): string;
14
14
  serialize(): {
15
- edges: any;
15
+ edges: any[];
16
16
  distance: number;
17
17
  distance2: number;
18
18
  isAngle: boolean;
@@ -6,16 +6,16 @@ export class Chamfer extends SceneObject {
6
6
  distance;
7
7
  distance2;
8
8
  isAngle;
9
- _selection = null;
10
- constructor(distance, distance2, isAngle = false, selection) {
9
+ _selections = [];
10
+ constructor(distance, distance2, isAngle = false, ...selections) {
11
11
  super();
12
12
  this.distance = distance;
13
13
  this.distance2 = distance2;
14
14
  this.isAngle = isAngle;
15
- this._selection = selection ?? null;
15
+ this._selections = selections;
16
16
  }
17
- get selection() {
18
- return this._selection;
17
+ get selections() {
18
+ return this._selections;
19
19
  }
20
20
  build(context) {
21
21
  let sceneObjects;
@@ -26,8 +26,18 @@ export class Chamfer extends SceneObject {
26
26
  sceneObjects.set(obj, shapes);
27
27
  }
28
28
  }
29
- const allEdgeShapes = this.selection.getShapes();
30
- let edges = allEdgeShapes;
29
+ let edges = [];
30
+ for (const selection of this.selections) {
31
+ const allEdgeShapes = selection.getShapes();
32
+ for (const shape of allEdgeShapes) {
33
+ if (shape.isEdge()) {
34
+ edges.push(shape);
35
+ }
36
+ else {
37
+ edges.push(...Explorer.findEdgesWrapped(shape));
38
+ }
39
+ }
40
+ }
31
41
  const newShapes = [];
32
42
  const shapeObjectMap = new Map();
33
43
  for (const [obj, shapes] of sceneObjects) {
@@ -82,17 +92,20 @@ export class Chamfer extends SceneObject {
82
92
  continue;
83
93
  }
84
94
  }
85
- this.selection.removeShapes(this);
95
+ for (const selection of this.selections) {
96
+ const shapes = selection.getShapes();
97
+ for (const shape of shapes) {
98
+ selection.removeShape(shape, this);
99
+ }
100
+ }
86
101
  this.addShapes(newShapes);
87
102
  }
88
103
  getDependencies() {
89
- return this.selection ? [this.selection] : [];
104
+ return [...this.selections];
90
105
  }
91
106
  createCopy(remap) {
92
- const selection = this.selection
93
- ? (remap.get(this.selection) || this.selection)
94
- : undefined;
95
- return new Chamfer(this.distance, this.distance2, this.isAngle, selection);
107
+ const selections = this.selections.map(s => remap.get(s) || s);
108
+ return new Chamfer(this.distance, this.distance2, this.isAngle, ...selections);
96
109
  }
97
110
  compareTo(other) {
98
111
  if (!(other instanceof Chamfer)) {
@@ -110,9 +123,14 @@ export class Chamfer extends SceneObject {
110
123
  if (this.isAngle !== other.isAngle) {
111
124
  return false;
112
125
  }
113
- if (!this.selection.compareTo(other.selection)) {
126
+ if (this.selections.length !== other.selections.length) {
114
127
  return false;
115
128
  }
129
+ for (let i = 0; i < this.selections.length; i++) {
130
+ if (!this.selections[i].compareTo(other.selections[i])) {
131
+ return false;
132
+ }
133
+ }
116
134
  return true;
117
135
  }
118
136
  getType() {
@@ -120,7 +138,7 @@ export class Chamfer extends SceneObject {
120
138
  }
121
139
  serialize() {
122
140
  return {
123
- edges: this.selection.serialize(),
141
+ edges: this.selections.map(s => s.serialize()),
124
142
  distance: this.distance,
125
143
  distance2: this.distance2,
126
144
  isAngle: this.isAngle
@@ -2,8 +2,7 @@ import { Wire } from "../common/wire.js";
2
2
  import { Edge } from "../common/edge.js";
3
3
  import { GeometrySceneObject } from "./2d/geometry.js";
4
4
  import { BooleanOps } from "../oc/boolean-ops.js";
5
- import { FaceOps } from "../oc/face-ops.js";
6
- import { WireOps } from "../oc/wire-ops.js";
5
+ import { FaceMaker2 } from "../oc/face-maker2.js";
7
6
  export class Common2D extends GeometrySceneObject {
8
7
  _targetObjects = null;
9
8
  _keepOriginal = false;
@@ -38,20 +37,39 @@ export class Common2D extends GeometrySceneObject {
38
37
  }
39
38
  }
40
39
  }
41
- const allEdges = Array.from(sourceEdges.keys()).filter(edge => edge.isClosed());
42
- const wires = allEdges.map(edge => WireOps.makeWireFromEdges([edge]));
43
- const faces = wires.map(wire => FaceOps.makeFaceWrapped(wire));
40
+ const plane = this.sketch.getPlane();
41
+ // Group edges by owner
42
+ const ownerToEdges = new Map();
43
+ for (const [edge, owner] of sourceEdges) {
44
+ if (!ownerToEdges.has(owner)) {
45
+ ownerToEdges.set(owner, []);
46
+ }
47
+ ownerToEdges.get(owner).push(edge);
48
+ }
49
+ // Convert each owner's edges to faces via FaceMaker2
50
+ const faces = [];
51
+ const processedOwners = new Set();
52
+ for (const [owner, edges] of ownerToEdges) {
53
+ const ownerFaces = FaceMaker2.getRegions(edges, plane);
54
+ if (ownerFaces.length > 0) {
55
+ faces.push(...ownerFaces);
56
+ processedOwners.add(owner);
57
+ }
58
+ }
59
+ if (faces.length === 0) {
60
+ return;
61
+ }
44
62
  const { newShapes } = BooleanOps.common(faces);
45
63
  if (newShapes.length === 0) {
46
64
  return;
47
65
  }
48
66
  const newEdges = newShapes.flatMap((face) => face.getEdges());
49
67
  if (!this._keepOriginal) {
50
- for (const [wire, owner] of sourceEdges) {
51
- if (!allEdges.includes(wire)) {
68
+ for (const [edge, owner] of sourceEdges) {
69
+ if (!processedOwners.has(owner)) {
52
70
  continue;
53
71
  }
54
- owner.removeShape(wire, this);
72
+ owner.removeShape(edge, this);
55
73
  }
56
74
  }
57
75
  this.addShapes(newEdges);
@@ -2,8 +2,7 @@ import { Wire } from "../common/wire.js";
2
2
  import { Edge } from "../common/edge.js";
3
3
  import { GeometrySceneObject } from "./2d/geometry.js";
4
4
  import { BooleanOps } from "../oc/boolean-ops.js";
5
- import { WireOps } from "../oc/wire-ops.js";
6
- import { FaceOps } from "../oc/face-ops.js";
5
+ import { FaceMaker2 } from "../oc/face-maker2.js";
7
6
  export class Fuse2D extends GeometrySceneObject {
8
7
  _targetObjects = null;
9
8
  constructor(...targets) {
@@ -33,16 +32,35 @@ export class Fuse2D extends GeometrySceneObject {
33
32
  }
34
33
  }
35
34
  }
36
- const allEdges = Array.from(sourceEdges.keys()).filter(edge => edge.isClosed());
37
- const wires = allEdges.map(edge => WireOps.makeWireFromEdges([edge]));
38
- const faces = wires.map(wire => FaceOps.makeFaceWrapped(wire));
35
+ const plane = this.sketch.getPlane();
36
+ // Group edges by owner
37
+ const ownerToEdges = new Map();
38
+ for (const [edge, owner] of sourceEdges) {
39
+ if (!ownerToEdges.has(owner)) {
40
+ ownerToEdges.set(owner, []);
41
+ }
42
+ ownerToEdges.get(owner).push(edge);
43
+ }
44
+ // Convert each owner's edges to faces via FaceMaker2
45
+ const faces = [];
46
+ const processedOwners = new Set();
47
+ for (const [owner, edges] of ownerToEdges) {
48
+ const ownerFaces = FaceMaker2.getRegions(edges, plane);
49
+ if (ownerFaces.length > 0) {
50
+ faces.push(...ownerFaces);
51
+ processedOwners.add(owner);
52
+ }
53
+ }
54
+ if (faces.length === 0) {
55
+ return;
56
+ }
39
57
  const { newShapes } = BooleanOps.fuseFaces(faces);
40
58
  if (newShapes.length === 0) {
41
59
  return;
42
60
  }
43
61
  const newEdges = newShapes.flatMap((face) => face.getEdges());
44
62
  for (const [edge, owner] of sourceEdges) {
45
- if (!allEdges.includes(edge)) {
63
+ if (!processedOwners.has(owner)) {
46
64
  continue;
47
65
  }
48
66
  owner.removeShape(edge, this);
@@ -0,0 +1,14 @@
1
+ import { BuildSceneObjectContext, SceneObject } from "../common/scene-object.js";
2
+ import { Matrix4 } from "../math/matrix4.js";
3
+ export declare class RepeatMatrix extends SceneObject {
4
+ private _matrix;
5
+ targetObjects: SceneObject[];
6
+ constructor(_matrix: Matrix4, targetObjects: SceneObject[]);
7
+ getTransformMatrix(): Matrix4 | null;
8
+ isContainer(): boolean;
9
+ build(context: BuildSceneObjectContext): void;
10
+ compareTo(other: RepeatMatrix): boolean;
11
+ getType(): string;
12
+ getUniqueType(): string;
13
+ serialize(): {};
14
+ }
@@ -0,0 +1,41 @@
1
+ import { SceneObject } from "../common/scene-object.js";
2
+ export class RepeatMatrix extends SceneObject {
3
+ _matrix;
4
+ targetObjects;
5
+ constructor(_matrix, targetObjects) {
6
+ super();
7
+ this._matrix = _matrix;
8
+ this.targetObjects = targetObjects;
9
+ this.setAlwaysVisible();
10
+ }
11
+ getTransformMatrix() {
12
+ return this._matrix;
13
+ }
14
+ isContainer() {
15
+ return true;
16
+ }
17
+ build(context) {
18
+ this.saveShapesSnapshot(context);
19
+ }
20
+ compareTo(other) {
21
+ if (!(other instanceof RepeatMatrix)) {
22
+ return false;
23
+ }
24
+ if (!super.compareTo(other)) {
25
+ return false;
26
+ }
27
+ if (!this._matrix.equals(other._matrix)) {
28
+ return false;
29
+ }
30
+ return true;
31
+ }
32
+ getType() {
33
+ return "repeat-matrix";
34
+ }
35
+ getUniqueType() {
36
+ return "repeat-matrix";
37
+ }
38
+ serialize() {
39
+ return {};
40
+ }
41
+ }
@@ -15,6 +15,7 @@ export declare class SelectSceneObject extends SceneObject implements ISelect {
15
15
  getDependencies(): SceneObject[];
16
16
  createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
17
17
  transform(matrix: Matrix4): SelectSceneObject;
18
+ private injectScopeFaces;
18
19
  applyFilters(shapes: Shape[], filters: FilterBuilderBase<Shape>[]): Shape[];
19
20
  compareTo(other: SelectSceneObject): boolean;
20
21
  shapeType(): string;
@@ -1,6 +1,7 @@
1
1
  import { FaceFilterBuilder } from "../filters/face/face-filter.js";
2
2
  import { ShapeFilter } from "../filters/filter.js";
3
3
  import { SceneObject } from "../common/scene-object.js";
4
+ import { BelongsToFaceFilter, NotBelongsToFaceFilter } from "../filters/edge/belongs-to-face.js";
4
5
  export class SelectSceneObject extends SceneObject {
5
6
  filters;
6
7
  constraintObject;
@@ -32,6 +33,9 @@ export class SelectSceneObject extends SceneObject {
32
33
  }
33
34
  }
34
35
  const allShapes = this.constraintObject ? this.constraintObject.getShapes() : this.getAllShapes(sceneObjects, excludedObjects);
36
+ if (this.type === "edge") {
37
+ this.injectScopeFaces(filters, sceneObjects);
38
+ }
35
39
  const filteredShapes = this.applyFilters(allShapes, filters);
36
40
  console.log(`SelectSceneObject: shapes after filtering: ${filteredShapes[0]}`);
37
41
  this.addShapes(filteredShapes);
@@ -64,6 +68,21 @@ export class SelectSceneObject extends SceneObject {
64
68
  console.log('SelectSceneObject: transform applied to selection filters.', mirroredFilters);
65
69
  return new SelectSceneObject(mirroredFilters, this.constraintObject);
66
70
  }
71
+ injectScopeFaces(filters, sceneObjects) {
72
+ let scopeFaces = null;
73
+ for (const builder of filters) {
74
+ for (const filter of builder.getFilters()) {
75
+ if (filter instanceof BelongsToFaceFilter || filter instanceof NotBelongsToFaceFilter) {
76
+ if (!scopeFaces) {
77
+ scopeFaces = this.constraintObject
78
+ ? this.constraintObject.getShapes().flatMap(s => s.getSubShapes("face"))
79
+ : sceneObjects.flatMap(obj => obj.getShapes({}, 'solid').flatMap(s => s.getSubShapes("face")));
80
+ }
81
+ filter.setScopeFaces(scopeFaces);
82
+ }
83
+ }
84
+ }
85
+ }
67
86
  applyFilters(shapes, filters) {
68
87
  const shapeFilter = new ShapeFilter(shapes, ...filters);
69
88
  return shapeFilter.apply();
@@ -0,0 +1,14 @@
1
+ import { BuildSceneObjectContext, SceneObject } from "../common/scene-object.js";
2
+ import { GeometrySceneObject } from "./2d/geometry.js";
3
+ export declare class Subtract2D extends GeometrySceneObject {
4
+ target1: GeometrySceneObject;
5
+ target2: GeometrySceneObject;
6
+ constructor(target1: GeometrySceneObject, target2: GeometrySceneObject);
7
+ private collectEdges;
8
+ build(context: BuildSceneObjectContext): void;
9
+ getDependencies(): SceneObject[];
10
+ createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
11
+ compareTo(other: Subtract2D): boolean;
12
+ getType(): string;
13
+ serialize(): {};
14
+ }