fluidcad 0.0.22 → 0.0.24
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/core/trim.d.ts +12 -5
- package/lib/dist/core/trim.js +7 -2
- package/lib/dist/features/2d/sketch.d.ts +0 -1
- package/lib/dist/features/2d/sketch.js +22 -33
- package/lib/dist/features/extrude-base.js +1 -0
- package/lib/dist/features/remove.d.ts +2 -0
- package/lib/dist/features/remove.js +7 -0
- package/lib/dist/features/trim2d.d.ts +16 -4
- package/lib/dist/features/trim2d.js +80 -29
- package/lib/dist/filters/edge/above-below.d.ts +20 -0
- package/lib/dist/filters/edge/above-below.js +57 -0
- package/lib/dist/filters/edge/edge-filter.d.ts +40 -6
- package/lib/dist/filters/edge/edge-filter.js +76 -8
- package/lib/dist/filters/edge/intersects-with.d.ts +18 -0
- package/lib/dist/filters/edge/intersects-with.js +38 -0
- package/lib/dist/filters/edge/on-plane.d.ts +4 -2
- package/lib/dist/filters/edge/on-plane.js +37 -12
- package/lib/dist/filters/filter-builder-base.d.ts +0 -5
- package/lib/dist/filters/filter-builder-base.js +12 -0
- package/lib/dist/helpers/clone-transform.js +3 -0
- package/lib/dist/oc/edge-query.d.ts +5 -1
- package/lib/dist/oc/edge-query.js +40 -0
- package/lib/dist/rendering/render.js +3 -2
- package/lib/dist/tests/features/chamfer.test.js +1 -1
- package/lib/dist/tests/features/fillet.test.js +1 -1
- package/lib/dist/tests/features/select.test.js +101 -3
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/ui/dist/assets/{index-C0JwQ8Bk.js → index-CqP_mgZk.js} +14 -7
- package/ui/dist/index.html +1 -1
package/lib/dist/core/trim.d.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { Point2DLike } from "../math/point.js";
|
|
2
|
-
import {
|
|
2
|
+
import { EdgeFilterBuilder } from "../filters/edge/edge-filter.js";
|
|
3
|
+
interface ITrim {
|
|
4
|
+
/**
|
|
5
|
+
* Enters interactive trimming mode, optionally trimming edges at the given points.
|
|
6
|
+
* @param points - Points where geometry should be trimmed; the nearest edge segment to each point is removed.
|
|
7
|
+
*/
|
|
8
|
+
pick(...points: Point2DLike[]): ITrim;
|
|
9
|
+
}
|
|
3
10
|
interface TrimFunction {
|
|
4
11
|
/** Trims all sketch geometry segments. */
|
|
5
|
-
():
|
|
12
|
+
(): ITrim;
|
|
6
13
|
/**
|
|
7
|
-
* Trims sketch geometry segments
|
|
8
|
-
* @param
|
|
14
|
+
* Trims sketch geometry segments matching the given edge filters.
|
|
15
|
+
* @param filters - Edge filters that select which edges to remove
|
|
9
16
|
*/
|
|
10
|
-
(...
|
|
17
|
+
(...filters: EdgeFilterBuilder[]): ITrim;
|
|
11
18
|
}
|
|
12
19
|
declare const _default: TrimFunction;
|
|
13
20
|
export default _default;
|
package/lib/dist/core/trim.js
CHANGED
|
@@ -9,10 +9,15 @@ function build(context) {
|
|
|
9
9
|
}
|
|
10
10
|
const trim2d = new Trim2D();
|
|
11
11
|
if (args.length > 0) {
|
|
12
|
-
trim2d.
|
|
12
|
+
trim2d.setFilters(...args);
|
|
13
13
|
}
|
|
14
14
|
context.addSceneObject(trim2d);
|
|
15
|
-
return
|
|
15
|
+
return {
|
|
16
|
+
pick(...points) {
|
|
17
|
+
trim2d.pick(...points.map(p => normalizePoint2D(p)));
|
|
18
|
+
return this;
|
|
19
|
+
},
|
|
20
|
+
};
|
|
16
21
|
};
|
|
17
22
|
}
|
|
18
23
|
export default registerBuilder(build);
|
|
@@ -18,7 +18,6 @@ export declare class Sketch extends SceneObject implements Extrudable {
|
|
|
18
18
|
build(context?: BuildSceneObjectContext): void;
|
|
19
19
|
getEdges(): Edge[];
|
|
20
20
|
getEdgesWithOwner(): Map<Edge, GeometrySceneObject>;
|
|
21
|
-
getAllEdges(): Edge[];
|
|
22
21
|
getGeometriesWithOwner(): Map<Edge, GeometrySceneObject>;
|
|
23
22
|
getGeometries(): Edge[];
|
|
24
23
|
getDependencies(): SceneObject[];
|
|
@@ -83,16 +83,31 @@ export class Sketch extends SceneObject {
|
|
|
83
83
|
const source = this.getCloneSource();
|
|
84
84
|
const transform = context?.getTransform();
|
|
85
85
|
if (source instanceof Sketch && transform) {
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
|
|
86
|
+
const sourceChildren = source.getChildren();
|
|
87
|
+
const clonedChildren = this.getChildren();
|
|
88
|
+
for (let i = 0; i < sourceChildren.length; i++) {
|
|
89
|
+
const sourceChild = sourceChildren[i];
|
|
90
|
+
const clonedChild = clonedChildren[i];
|
|
91
|
+
if (!clonedChild) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const shapes = sourceChild.getAddedShapes();
|
|
95
|
+
const removedShapes = sourceChild.getRemovedShapes();
|
|
96
|
+
for (const shape of shapes) {
|
|
97
|
+
if (shape.isMetaShape() || shape.isGuideShape()) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const isRemovedBySibling = removedShapes.some(s => s.shape === shape && s.removedBy?.parentId === source.id);
|
|
101
|
+
if (isRemovedBySibling) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const transformed = ShapeOps.transform(shape, transform);
|
|
105
|
+
clonedChild.addShape(transformed);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
89
108
|
}
|
|
90
109
|
}
|
|
91
110
|
getEdges() {
|
|
92
|
-
const clonedEdges = this.getState('cloned-edges');
|
|
93
|
-
if (clonedEdges) {
|
|
94
|
-
return clonedEdges;
|
|
95
|
-
}
|
|
96
111
|
return [...this.getEdgesWithOwner().keys()];
|
|
97
112
|
}
|
|
98
113
|
getEdgesWithOwner() {
|
|
@@ -113,32 +128,6 @@ export class Sketch extends SceneObject {
|
|
|
113
128
|
}
|
|
114
129
|
return result;
|
|
115
130
|
}
|
|
116
|
-
getAllEdges() {
|
|
117
|
-
const children = this.getChildren();
|
|
118
|
-
const result = [];
|
|
119
|
-
for (const child of children) {
|
|
120
|
-
const shapes = child.getAddedShapes();
|
|
121
|
-
const removedShapes = child.getRemovedShapes();
|
|
122
|
-
for (const shape of shapes) {
|
|
123
|
-
if (shape.isMetaShape() || shape.isGuideShape()) {
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
const isRemovedBySibling = removedShapes.some(s => s.shape === shape && s.removedBy?.parentId === this.id);
|
|
127
|
-
if (isRemovedBySibling) {
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
if (shape instanceof Edge) {
|
|
131
|
-
result.push(shape);
|
|
132
|
-
}
|
|
133
|
-
else if (shape instanceof Wire) {
|
|
134
|
-
for (const edge of shape.getEdges()) {
|
|
135
|
-
result.push(edge);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return result;
|
|
141
|
-
}
|
|
142
131
|
getGeometriesWithOwner() {
|
|
143
132
|
return this.getEdgesWithOwner();
|
|
144
133
|
}
|
|
@@ -4,6 +4,8 @@ export declare class Remove extends SceneObject {
|
|
|
4
4
|
constructor(objects: SceneObject[]);
|
|
5
5
|
build(): void;
|
|
6
6
|
compareTo(other: Remove): boolean;
|
|
7
|
+
getDependencies(): SceneObject[];
|
|
8
|
+
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
7
9
|
getType(): string;
|
|
8
10
|
serialize(): {};
|
|
9
11
|
}
|
|
@@ -24,6 +24,13 @@ export class Remove extends SceneObject {
|
|
|
24
24
|
}
|
|
25
25
|
return true;
|
|
26
26
|
}
|
|
27
|
+
getDependencies() {
|
|
28
|
+
return this.objects;
|
|
29
|
+
}
|
|
30
|
+
createCopy(remap) {
|
|
31
|
+
const remappedObjects = this.objects.map(obj => remap.get(obj) || obj);
|
|
32
|
+
return new Remove(remappedObjects);
|
|
33
|
+
}
|
|
27
34
|
getType() {
|
|
28
35
|
return 'remove';
|
|
29
36
|
}
|
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
import { SceneObject } from "../common/scene-object.js";
|
|
2
2
|
import { GeometrySceneObject } from "./2d/geometry.js";
|
|
3
3
|
import { LazyVertex } from "./lazy-vertex.js";
|
|
4
|
+
import { EdgeFilterBuilder } from "../filters/edge/edge-filter.js";
|
|
4
5
|
export declare class Trim2D extends GeometrySceneObject {
|
|
5
|
-
private
|
|
6
|
+
private _filters;
|
|
7
|
+
private _picking;
|
|
8
|
+
private _pickPoints;
|
|
6
9
|
constructor();
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
setFilters(...fs: EdgeFilterBuilder[]): this;
|
|
11
|
+
pick(...ps: LazyVertex[]): this;
|
|
12
|
+
isPicking(): boolean;
|
|
13
|
+
getPickPoints(): LazyVertex[];
|
|
14
|
+
get filters(): EdgeFilterBuilder[];
|
|
9
15
|
build(): void;
|
|
16
|
+
private buildWithFilters;
|
|
17
|
+
private buildWithPicking;
|
|
10
18
|
getDependencies(): SceneObject[];
|
|
11
19
|
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
12
20
|
compareTo(other: Trim2D): boolean;
|
|
13
21
|
getType(): string;
|
|
14
|
-
serialize(): {
|
|
22
|
+
serialize(): {
|
|
23
|
+
trigger: "trim-picking";
|
|
24
|
+
picking: true;
|
|
25
|
+
pickPoints: number[][];
|
|
26
|
+
};
|
|
15
27
|
}
|
|
@@ -2,22 +2,35 @@ 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 { EdgeOps } from "../oc/edge-ops.js";
|
|
5
|
+
import { ShapeFilter } from "../filters/filter.js";
|
|
5
6
|
export class Trim2D extends GeometrySceneObject {
|
|
6
|
-
|
|
7
|
+
_filters = [];
|
|
8
|
+
_picking = false;
|
|
9
|
+
_pickPoints = [];
|
|
7
10
|
constructor() {
|
|
8
11
|
super();
|
|
9
12
|
}
|
|
10
|
-
|
|
11
|
-
this.
|
|
13
|
+
setFilters(...fs) {
|
|
14
|
+
this._filters = fs;
|
|
12
15
|
return this;
|
|
13
16
|
}
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
pick(...ps) {
|
|
18
|
+
this._picking = true;
|
|
19
|
+
this._pickPoints = ps;
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
isPicking() {
|
|
23
|
+
return this._picking;
|
|
24
|
+
}
|
|
25
|
+
getPickPoints() {
|
|
26
|
+
return this._pickPoints;
|
|
27
|
+
}
|
|
28
|
+
get filters() {
|
|
29
|
+
return this._filters;
|
|
16
30
|
}
|
|
17
31
|
build() {
|
|
18
32
|
const plane = this.sketch.getPlane();
|
|
19
33
|
const sourceWires = this.sketch.getGeometriesWithOwner();
|
|
20
|
-
// Collect all individual edges from wires/edges in the sketch
|
|
21
34
|
const allEdges = [];
|
|
22
35
|
const edgeToOwner = new Map();
|
|
23
36
|
for (const [wireOrEdge, owner] of sourceWires) {
|
|
@@ -35,25 +48,49 @@ export class Trim2D extends GeometrySceneObject {
|
|
|
35
48
|
if (allEdges.length === 0) {
|
|
36
49
|
return;
|
|
37
50
|
}
|
|
51
|
+
if (this._filters.length > 0) {
|
|
52
|
+
this.buildWithFilters(allEdges, edgeToOwner);
|
|
53
|
+
}
|
|
54
|
+
if (this._picking) {
|
|
55
|
+
let pickableEdges;
|
|
56
|
+
if (this._filters.length > 0) {
|
|
57
|
+
const matched = new Set(new ShapeFilter(allEdges, ...this._filters).apply());
|
|
58
|
+
pickableEdges = allEdges.filter(e => !matched.has(e));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
pickableEdges = allEdges;
|
|
62
|
+
}
|
|
63
|
+
this.buildWithPicking(pickableEdges, edgeToOwner, plane);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
buildWithFilters(allEdges, edgeToOwner) {
|
|
67
|
+
const matchedEdges = new ShapeFilter(allEdges, ...this._filters).apply();
|
|
68
|
+
const removedWires = new Set();
|
|
69
|
+
for (const edge of matchedEdges) {
|
|
70
|
+
const entry = edgeToOwner.get(edge);
|
|
71
|
+
if (!removedWires.has(entry.wire)) {
|
|
72
|
+
removedWires.add(entry.wire);
|
|
73
|
+
entry.owner.removeShape(entry.wire, this);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
buildWithPicking(pickableEdges, edgeToOwner, plane) {
|
|
38
78
|
const TRIM_TOLERANCE = 50;
|
|
39
|
-
|
|
40
|
-
const splitResult = EdgeOps.splitEdgesWithMapping(allEdges);
|
|
79
|
+
const splitResult = EdgeOps.splitEdgesWithMapping(pickableEdges);
|
|
41
80
|
const splitEdges = splitResult.edges;
|
|
42
81
|
const sourceIndex = splitResult.sourceIndex;
|
|
43
|
-
// Find split edges to remove
|
|
44
82
|
const splitEdgesToRemove = new Set();
|
|
45
|
-
if (this.
|
|
46
|
-
for (const lazyPoint of this.
|
|
83
|
+
if (this._pickPoints.length > 0) {
|
|
84
|
+
for (const lazyPoint of this._pickPoints) {
|
|
47
85
|
const point2d = lazyPoint.asPoint2D();
|
|
48
86
|
const point3d = plane.localToWorld(point2d);
|
|
49
87
|
for (const idx of EdgeOps.findNearestEdgeIndices(splitEdges, point3d, TRIM_TOLERANCE)) {
|
|
50
88
|
splitEdgesToRemove.add(idx);
|
|
51
89
|
}
|
|
52
90
|
}
|
|
53
|
-
// Remove affected original wires and re-add surviving split edges
|
|
54
91
|
const removedWires = new Set();
|
|
55
92
|
for (const idx of splitEdgesToRemove) {
|
|
56
|
-
const origEdge =
|
|
93
|
+
const origEdge = pickableEdges[sourceIndex[idx]];
|
|
57
94
|
const entry = edgeToOwner.get(origEdge);
|
|
58
95
|
if (!removedWires.has(entry.wire)) {
|
|
59
96
|
removedWires.add(entry.wire);
|
|
@@ -64,7 +101,7 @@ export class Trim2D extends GeometrySceneObject {
|
|
|
64
101
|
if (splitEdgesToRemove.has(i)) {
|
|
65
102
|
continue;
|
|
66
103
|
}
|
|
67
|
-
const origEdge =
|
|
104
|
+
const origEdge = pickableEdges[sourceIndex[i]];
|
|
68
105
|
const entry = edgeToOwner.get(origEdge);
|
|
69
106
|
if (removedWires.has(entry.wire)) {
|
|
70
107
|
this.addShape(splitEdges[i]);
|
|
@@ -72,11 +109,8 @@ export class Trim2D extends GeometrySceneObject {
|
|
|
72
109
|
}
|
|
73
110
|
}
|
|
74
111
|
// --- Meta shapes for segment-level hover ---
|
|
75
|
-
// Originals with trims need the first-pass split (to preserve trim boundaries).
|
|
76
|
-
// Originals without trims are re-split against only surviving edges
|
|
77
|
-
// (so ghost intersections from fully-removed edges disappear).
|
|
78
112
|
const origSurvives = new Set();
|
|
79
|
-
const origHasTrim = new Array(
|
|
113
|
+
const origHasTrim = new Array(pickableEdges.length).fill(false);
|
|
80
114
|
for (let i = 0; i < splitEdges.length; i++) {
|
|
81
115
|
if (!splitEdgesToRemove.has(i)) {
|
|
82
116
|
origSurvives.add(sourceIndex[i]);
|
|
@@ -85,17 +119,15 @@ export class Trim2D extends GeometrySceneObject {
|
|
|
85
119
|
origHasTrim[sourceIndex[i]] = true;
|
|
86
120
|
}
|
|
87
121
|
}
|
|
88
|
-
// Re-split only surviving originals (for clean meta shapes of untrimmed edges)
|
|
89
122
|
const metaInputEdges = [];
|
|
90
123
|
const metaInputToOrig = [];
|
|
91
|
-
for (let i = 0; i <
|
|
124
|
+
for (let i = 0; i < pickableEdges.length; i++) {
|
|
92
125
|
if (origSurvives.has(i)) {
|
|
93
|
-
metaInputEdges.push(
|
|
126
|
+
metaInputEdges.push(pickableEdges[i]);
|
|
94
127
|
metaInputToOrig.push(i);
|
|
95
128
|
}
|
|
96
129
|
}
|
|
97
130
|
const metaSplit = EdgeOps.splitEdgesWithMapping(metaInputEdges);
|
|
98
|
-
// Untrimmed originals: use re-split result (clean, no ghost intersections)
|
|
99
131
|
for (let i = 0; i < metaSplit.edges.length; i++) {
|
|
100
132
|
const origIdx = metaInputToOrig[metaSplit.sourceIndex[i]];
|
|
101
133
|
if (origHasTrim[origIdx]) {
|
|
@@ -105,7 +137,6 @@ export class Trim2D extends GeometrySceneObject {
|
|
|
105
137
|
metaEdge.markAsMetaShape('trim');
|
|
106
138
|
this.addShape(metaEdge);
|
|
107
139
|
}
|
|
108
|
-
// Trimmed originals: use first-pass surviving split edges (preserve boundaries)
|
|
109
140
|
for (let i = 0; i < splitEdges.length; i++) {
|
|
110
141
|
if (splitEdgesToRemove.has(i)) {
|
|
111
142
|
continue;
|
|
@@ -123,8 +154,11 @@ export class Trim2D extends GeometrySceneObject {
|
|
|
123
154
|
}
|
|
124
155
|
createCopy(remap) {
|
|
125
156
|
const copy = new Trim2D();
|
|
126
|
-
if (this.
|
|
127
|
-
copy.
|
|
157
|
+
if (this._filters.length > 0) {
|
|
158
|
+
copy.setFilters(...this._filters);
|
|
159
|
+
}
|
|
160
|
+
if (this._picking) {
|
|
161
|
+
copy.pick(...this._pickPoints);
|
|
128
162
|
}
|
|
129
163
|
return copy;
|
|
130
164
|
}
|
|
@@ -135,11 +169,22 @@ export class Trim2D extends GeometrySceneObject {
|
|
|
135
169
|
if (!super.compareTo(other)) {
|
|
136
170
|
return false;
|
|
137
171
|
}
|
|
138
|
-
if (this.
|
|
172
|
+
if (this._filters.length !== other._filters.length) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
for (let i = 0; i < this._filters.length; i++) {
|
|
176
|
+
if (!this._filters[i].equals(other._filters[i])) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (this._picking !== other._picking) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
if (this._pickPoints.length !== other._pickPoints.length) {
|
|
139
184
|
return false;
|
|
140
185
|
}
|
|
141
|
-
for (let i = 0; i < this.
|
|
142
|
-
if (!this.
|
|
186
|
+
for (let i = 0; i < this._pickPoints.length; i++) {
|
|
187
|
+
if (!this._pickPoints[i].compareTo(other._pickPoints[i])) {
|
|
143
188
|
return false;
|
|
144
189
|
}
|
|
145
190
|
}
|
|
@@ -149,6 +194,12 @@ export class Trim2D extends GeometrySceneObject {
|
|
|
149
194
|
return "trim2d";
|
|
150
195
|
}
|
|
151
196
|
serialize() {
|
|
152
|
-
return {
|
|
197
|
+
return {
|
|
198
|
+
trigger: 'trim-picking',
|
|
199
|
+
picking: this._picking || undefined,
|
|
200
|
+
pickPoints: this._picking
|
|
201
|
+
? this._pickPoints.map(p => { const pt = p.asPoint2D(); return [pt.x, pt.y]; })
|
|
202
|
+
: undefined,
|
|
203
|
+
};
|
|
153
204
|
}
|
|
154
205
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Matrix4 } from "../../math/matrix4.js";
|
|
2
|
+
import { Edge } from "../../common/shapes.js";
|
|
3
|
+
import { FilterBase } from "../filter-base.js";
|
|
4
|
+
import { PlaneObjectBase } from "../../features/plane-renderable-base.js";
|
|
5
|
+
export declare class AbovePlaneFilter extends FilterBase<Edge> {
|
|
6
|
+
private plane;
|
|
7
|
+
private partial;
|
|
8
|
+
constructor(plane: PlaneObjectBase, partial?: boolean);
|
|
9
|
+
match(shape: Edge): boolean;
|
|
10
|
+
compareTo(other: AbovePlaneFilter): boolean;
|
|
11
|
+
transform(matrix: Matrix4): AbovePlaneFilter;
|
|
12
|
+
}
|
|
13
|
+
export declare class BelowPlaneFilter extends FilterBase<Edge> {
|
|
14
|
+
private plane;
|
|
15
|
+
private partial;
|
|
16
|
+
constructor(plane: PlaneObjectBase, partial?: boolean);
|
|
17
|
+
match(shape: Edge): boolean;
|
|
18
|
+
compareTo(other: BelowPlaneFilter): boolean;
|
|
19
|
+
transform(matrix: Matrix4): BelowPlaneFilter;
|
|
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
|
+
export class AbovePlaneFilter extends FilterBase {
|
|
5
|
+
plane;
|
|
6
|
+
partial;
|
|
7
|
+
constructor(plane, partial = false) {
|
|
8
|
+
super();
|
|
9
|
+
this.plane = plane;
|
|
10
|
+
this.partial = partial;
|
|
11
|
+
}
|
|
12
|
+
match(shape) {
|
|
13
|
+
const plane = this.plane.getPlane();
|
|
14
|
+
const firstPoint = EdgeOps.getVertexPoint(EdgeOps.getFirstVertex(shape));
|
|
15
|
+
const lastPoint = EdgeOps.getVertexPoint(EdgeOps.getLastVertex(shape));
|
|
16
|
+
const firstAbove = plane.signedDistanceToPoint(firstPoint) > 0;
|
|
17
|
+
const lastAbove = plane.signedDistanceToPoint(lastPoint) > 0;
|
|
18
|
+
if (this.partial) {
|
|
19
|
+
return firstAbove || lastAbove;
|
|
20
|
+
}
|
|
21
|
+
return firstAbove && lastAbove;
|
|
22
|
+
}
|
|
23
|
+
compareTo(other) {
|
|
24
|
+
return this.plane.compareTo(other.plane) && this.partial === other.partial;
|
|
25
|
+
}
|
|
26
|
+
transform(matrix) {
|
|
27
|
+
const transformedPlane = this.plane.getPlane().applyMatrix(matrix);
|
|
28
|
+
return new AbovePlaneFilter(new PlaneObject(transformedPlane), this.partial);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export class BelowPlaneFilter extends FilterBase {
|
|
32
|
+
plane;
|
|
33
|
+
partial;
|
|
34
|
+
constructor(plane, partial = false) {
|
|
35
|
+
super();
|
|
36
|
+
this.plane = plane;
|
|
37
|
+
this.partial = partial;
|
|
38
|
+
}
|
|
39
|
+
match(shape) {
|
|
40
|
+
const plane = this.plane.getPlane();
|
|
41
|
+
const firstPoint = EdgeOps.getVertexPoint(EdgeOps.getFirstVertex(shape));
|
|
42
|
+
const lastPoint = EdgeOps.getVertexPoint(EdgeOps.getLastVertex(shape));
|
|
43
|
+
const firstBelow = plane.signedDistanceToPoint(firstPoint) < 0;
|
|
44
|
+
const lastBelow = plane.signedDistanceToPoint(lastPoint) < 0;
|
|
45
|
+
if (this.partial) {
|
|
46
|
+
return firstBelow || lastBelow;
|
|
47
|
+
}
|
|
48
|
+
return firstBelow && lastBelow;
|
|
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 BelowPlaneFilter(new PlaneObject(transformedPlane), this.partial);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -8,17 +8,23 @@ export declare class EdgeFilterBuilder extends FilterBuilderBase<Edge> {
|
|
|
8
8
|
/**
|
|
9
9
|
* Selects edges that lie on the given plane.
|
|
10
10
|
* @param plane - The reference plane.
|
|
11
|
-
* @param
|
|
12
|
-
* @param bothDirections - When true, also matches the plane offset in the opposite direction.
|
|
11
|
+
* @param offsetOrOptions - Offset distance, or an options object with `offset`, `bothDirections`, and `partial`.
|
|
13
12
|
*/
|
|
14
|
-
onPlane(plane: PlaneLike | PlaneObjectBase,
|
|
13
|
+
onPlane(plane: PlaneLike | PlaneObjectBase, offsetOrOptions?: number | {
|
|
14
|
+
offset?: number;
|
|
15
|
+
bothDirections?: boolean;
|
|
16
|
+
partial?: boolean;
|
|
17
|
+
}): this;
|
|
15
18
|
/**
|
|
16
19
|
* Excludes edges that lie on the given plane.
|
|
17
20
|
* @param plane - The reference plane.
|
|
18
|
-
* @param
|
|
19
|
-
* @param bothDirections - When true, also excludes the plane offset in the opposite direction.
|
|
21
|
+
* @param offsetOrOptions - Offset distance, or an options object with `offset`, `bothDirections`, and `partial`.
|
|
20
22
|
*/
|
|
21
|
-
notOnPlane(plane: PlaneLike | PlaneObjectBase,
|
|
23
|
+
notOnPlane(plane: PlaneLike | PlaneObjectBase, offsetOrOptions?: number | {
|
|
24
|
+
offset?: number;
|
|
25
|
+
bothDirections?: boolean;
|
|
26
|
+
partial?: boolean;
|
|
27
|
+
}): this;
|
|
22
28
|
/**
|
|
23
29
|
* Selects edges that are parallel to the given plane.
|
|
24
30
|
* @param plane - The reference plane.
|
|
@@ -89,5 +95,33 @@ export declare class EdgeFilterBuilder extends FilterBuilderBase<Edge> {
|
|
|
89
95
|
* @param faceFilters - One or more face filter builders to match against.
|
|
90
96
|
*/
|
|
91
97
|
notBelongsToFace(...faceFilters: FilterBuilderBase<Face>[]): this;
|
|
98
|
+
/**
|
|
99
|
+
* Selects edges that geometrically intersect with edges of the given scene object.
|
|
100
|
+
* @param sceneObject - A scene object whose edges are tested for intersection.
|
|
101
|
+
*/
|
|
102
|
+
intersectsWith(sceneObject: ISceneObject): this;
|
|
103
|
+
/**
|
|
104
|
+
* Excludes edges that geometrically intersect with edges of the given scene object.
|
|
105
|
+
* @param sceneObject - A scene object whose edges are tested for intersection.
|
|
106
|
+
*/
|
|
107
|
+
notIntersectsWith(sceneObject: ISceneObject): this;
|
|
108
|
+
/**
|
|
109
|
+
* Selects edges that are entirely above the given plane (in the direction of its normal).
|
|
110
|
+
* @param plane - The reference plane.
|
|
111
|
+
* @param offsetOrOptions - Offset distance, or an options object with `offset` and `partial`.
|
|
112
|
+
*/
|
|
113
|
+
above(plane: PlaneLike | PlaneObjectBase, offsetOrOptions?: number | {
|
|
114
|
+
offset?: number;
|
|
115
|
+
partial?: boolean;
|
|
116
|
+
}): this;
|
|
117
|
+
/**
|
|
118
|
+
* Selects edges that are entirely below the given plane (opposite to its normal direction).
|
|
119
|
+
* @param plane - The reference plane.
|
|
120
|
+
* @param offsetOrOptions - Offset distance, or an options object with `offset` and `partial`.
|
|
121
|
+
*/
|
|
122
|
+
below(plane: PlaneLike | PlaneObjectBase, offsetOrOptions?: number | {
|
|
123
|
+
offset?: number;
|
|
124
|
+
partial?: boolean;
|
|
125
|
+
}): this;
|
|
92
126
|
static build(): EdgeFilterBuilder;
|
|
93
127
|
}
|
|
@@ -11,6 +11,8 @@ import { PlaneObjectBase } from "../../features/plane-renderable-base.js";
|
|
|
11
11
|
import { AtIndexFilter, NotAtIndexFilter } from "./at-index.js";
|
|
12
12
|
import { BelongsToFaceFilter, NotBelongsToFaceFilter } from "./belongs-to-face.js";
|
|
13
13
|
import { BelongsToFaceFromSceneObjectFilter, NotBelongsToFaceFromSceneObjectFilter } from "./belongs-to-object.js";
|
|
14
|
+
import { IntersectsWithFilter, NotIntersectsWithFilter } from "./intersects-with.js";
|
|
15
|
+
import { AbovePlaneFilter, BelowPlaneFilter } from "./above-below.js";
|
|
14
16
|
import { SceneObject } from "../../common/scene-object.js";
|
|
15
17
|
export class EdgeFilterBuilder extends FilterBuilderBase {
|
|
16
18
|
constructor() {
|
|
@@ -43,13 +45,14 @@ export class EdgeFilterBuilder extends FilterBuilderBase {
|
|
|
43
45
|
/**
|
|
44
46
|
* Selects edges that lie on the given plane.
|
|
45
47
|
* @param plane - The reference plane.
|
|
46
|
-
* @param
|
|
47
|
-
* @param bothDirections - When true, also matches the plane offset in the opposite direction.
|
|
48
|
+
* @param offsetOrOptions - Offset distance, or an options object with `offset`, `bothDirections`, and `partial`.
|
|
48
49
|
*/
|
|
49
|
-
onPlane(plane,
|
|
50
|
+
onPlane(plane, offsetOrOptions) {
|
|
50
51
|
if (!plane) {
|
|
51
52
|
throw new Error('Plane is required');
|
|
52
53
|
}
|
|
54
|
+
const opts = typeof offsetOrOptions === 'number' ? { offset: offsetOrOptions } : (offsetOrOptions ?? {});
|
|
55
|
+
const { offset = 0, bothDirections = false, partial = false } = opts;
|
|
53
56
|
let planeObj;
|
|
54
57
|
let planeObj2;
|
|
55
58
|
if (plane instanceof PlaneObjectBase) {
|
|
@@ -67,20 +70,21 @@ export class EdgeFilterBuilder extends FilterBuilderBase {
|
|
|
67
70
|
planeObj = new PlaneObject(normalized);
|
|
68
71
|
}
|
|
69
72
|
}
|
|
70
|
-
const filter = new OnPlaneFilter(planeObj, planeObj2);
|
|
73
|
+
const filter = new OnPlaneFilter(planeObj, planeObj2, partial);
|
|
71
74
|
this.filters.push(filter);
|
|
72
75
|
return this;
|
|
73
76
|
}
|
|
74
77
|
/**
|
|
75
78
|
* Excludes edges that lie on the given plane.
|
|
76
79
|
* @param plane - The reference plane.
|
|
77
|
-
* @param
|
|
78
|
-
* @param bothDirections - When true, also excludes the plane offset in the opposite direction.
|
|
80
|
+
* @param offsetOrOptions - Offset distance, or an options object with `offset`, `bothDirections`, and `partial`.
|
|
79
81
|
*/
|
|
80
|
-
notOnPlane(plane,
|
|
82
|
+
notOnPlane(plane, offsetOrOptions) {
|
|
81
83
|
if (!plane) {
|
|
82
84
|
throw new Error('Plane is required');
|
|
83
85
|
}
|
|
86
|
+
const opts = typeof offsetOrOptions === 'number' ? { offset: offsetOrOptions } : (offsetOrOptions ?? {});
|
|
87
|
+
const { offset = 0, bothDirections = false, partial = false } = opts;
|
|
84
88
|
let planeObj;
|
|
85
89
|
let planeObj2;
|
|
86
90
|
if (plane instanceof PlaneObjectBase) {
|
|
@@ -98,7 +102,7 @@ export class EdgeFilterBuilder extends FilterBuilderBase {
|
|
|
98
102
|
planeObj = new PlaneObject(normalized);
|
|
99
103
|
}
|
|
100
104
|
}
|
|
101
|
-
const filter = new NotOnPlaneFilter(planeObj, planeObj2);
|
|
105
|
+
const filter = new NotOnPlaneFilter(planeObj, planeObj2, partial);
|
|
102
106
|
this.filters.push(filter);
|
|
103
107
|
return this;
|
|
104
108
|
}
|
|
@@ -262,6 +266,70 @@ export class EdgeFilterBuilder extends FilterBuilderBase {
|
|
|
262
266
|
}
|
|
263
267
|
return this;
|
|
264
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Selects edges that geometrically intersect with edges of the given scene object.
|
|
271
|
+
* @param sceneObject - A scene object whose edges are tested for intersection.
|
|
272
|
+
*/
|
|
273
|
+
intersectsWith(sceneObject) {
|
|
274
|
+
const filter = new IntersectsWithFilter(sceneObject);
|
|
275
|
+
this.filters.push(filter);
|
|
276
|
+
return this;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Excludes edges that geometrically intersect with edges of the given scene object.
|
|
280
|
+
* @param sceneObject - A scene object whose edges are tested for intersection.
|
|
281
|
+
*/
|
|
282
|
+
notIntersectsWith(sceneObject) {
|
|
283
|
+
const filter = new NotIntersectsWithFilter(sceneObject);
|
|
284
|
+
this.filters.push(filter);
|
|
285
|
+
return this;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Selects edges that are entirely above the given plane (in the direction of its normal).
|
|
289
|
+
* @param plane - The reference plane.
|
|
290
|
+
* @param offsetOrOptions - Offset distance, or an options object with `offset` and `partial`.
|
|
291
|
+
*/
|
|
292
|
+
above(plane, offsetOrOptions) {
|
|
293
|
+
if (!plane) {
|
|
294
|
+
throw new Error('Plane is required');
|
|
295
|
+
}
|
|
296
|
+
const opts = typeof offsetOrOptions === 'number' ? { offset: offsetOrOptions } : (offsetOrOptions ?? {});
|
|
297
|
+
const { offset = 0, partial = false } = opts;
|
|
298
|
+
let planeObj;
|
|
299
|
+
if (plane instanceof PlaneObjectBase) {
|
|
300
|
+
planeObj = plane;
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
let normalized = normalizePlane(plane);
|
|
304
|
+
planeObj = offset ? new PlaneObject(normalized.offset(offset)) : new PlaneObject(normalized);
|
|
305
|
+
}
|
|
306
|
+
const filter = new AbovePlaneFilter(planeObj, partial);
|
|
307
|
+
this.filters.push(filter);
|
|
308
|
+
return this;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Selects edges that are entirely below the given plane (opposite to its normal direction).
|
|
312
|
+
* @param plane - The reference plane.
|
|
313
|
+
* @param offsetOrOptions - Offset distance, or an options object with `offset` and `partial`.
|
|
314
|
+
*/
|
|
315
|
+
below(plane, offsetOrOptions) {
|
|
316
|
+
if (!plane) {
|
|
317
|
+
throw new Error('Plane is required');
|
|
318
|
+
}
|
|
319
|
+
const opts = typeof offsetOrOptions === 'number' ? { offset: offsetOrOptions } : (offsetOrOptions ?? {});
|
|
320
|
+
const { offset = 0, partial = false } = opts;
|
|
321
|
+
let planeObj;
|
|
322
|
+
if (plane instanceof PlaneObjectBase) {
|
|
323
|
+
planeObj = plane;
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
let normalized = normalizePlane(plane);
|
|
327
|
+
planeObj = offset ? new PlaneObject(normalized.offset(offset)) : new PlaneObject(normalized);
|
|
328
|
+
}
|
|
329
|
+
const filter = new BelowPlaneFilter(planeObj, partial);
|
|
330
|
+
this.filters.push(filter);
|
|
331
|
+
return this;
|
|
332
|
+
}
|
|
265
333
|
static build() {
|
|
266
334
|
return new EdgeFilterBuilder();
|
|
267
335
|
}
|