fluidcad 0.0.26 → 0.0.28
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 +45 -0
- package/lib/dist/common/scene-object.js +121 -0
- package/lib/dist/common/shape-factory.d.ts +1 -1
- package/lib/dist/common/shape-history-tracker.d.ts +35 -0
- package/lib/dist/common/shape-history-tracker.js +114 -0
- package/lib/dist/common/shape.js +7 -1
- package/lib/dist/common/shapes.d.ts +0 -1
- package/lib/dist/common/shapes.js +0 -1
- package/lib/dist/common/solid.js +5 -1
- package/lib/dist/core/extrude.d.ts +12 -13
- package/lib/dist/core/extrude.js +19 -1
- package/lib/dist/core/part.d.ts +2 -1
- package/lib/dist/core/part.js +4 -1
- package/lib/dist/core/sketch.d.ts +4 -3
- package/lib/dist/core/sketch.js +4 -1
- package/lib/dist/features/chamfer.js +12 -6
- package/lib/dist/features/extrude-base.d.ts +43 -1
- package/lib/dist/features/extrude-base.js +141 -36
- package/lib/dist/features/extrude-to-face.d.ts +1 -1
- package/lib/dist/features/extrude-to-face.js +42 -19
- package/lib/dist/features/extrude-two-distances.d.ts +1 -1
- package/lib/dist/features/extrude-two-distances.js +41 -15
- package/lib/dist/features/extrude.d.ts +1 -1
- package/lib/dist/features/extrude.js +75 -20
- package/lib/dist/features/fillet.js +3 -4
- package/lib/dist/features/fuse.js +14 -0
- package/lib/dist/features/infinite-extrude.d.ts +1 -0
- package/lib/dist/features/infinite-extrude.js +33 -4
- package/lib/dist/features/loft.js +18 -5
- package/lib/dist/features/mirror-shape.d.ts +1 -3
- package/lib/dist/features/mirror-shape.js +2 -1
- package/lib/dist/features/revolve.js +17 -4
- package/lib/dist/features/rotate.js +1 -0
- package/lib/dist/features/simple-extruder.js +5 -0
- package/lib/dist/features/sweep.js +13 -2
- package/lib/dist/features/translate.js +3 -1
- package/lib/dist/filters/face/face-filter.d.ts +12 -0
- package/lib/dist/filters/face/face-filter.js +21 -0
- package/lib/dist/filters/face/torus-filter.d.ts +19 -0
- package/lib/dist/filters/face/torus-filter.js +38 -0
- package/lib/dist/helpers/scene-helpers.d.ts +10 -2
- package/lib/dist/helpers/scene-helpers.js +278 -10
- package/lib/dist/index.d.ts +1 -0
- package/lib/dist/oc/boolean-ops.d.ts +32 -4
- package/lib/dist/oc/boolean-ops.js +122 -11
- package/lib/dist/oc/color-transfer.d.ts +37 -0
- package/lib/dist/oc/color-transfer.js +135 -0
- package/lib/dist/oc/extrude-ops.js +25 -3
- package/lib/dist/oc/face-ops.d.ts +0 -1
- package/lib/dist/oc/face-ops.js +0 -13
- package/lib/dist/oc/face-query.d.ts +2 -0
- package/lib/dist/oc/face-query.js +30 -0
- package/lib/dist/oc/fillet-ops.d.ts +5 -3
- package/lib/dist/oc/fillet-ops.js +107 -70
- package/lib/dist/oc/intersection.js +6 -3
- package/lib/dist/oc/mesh.d.ts +25 -2
- package/lib/dist/oc/mesh.js +112 -35
- package/lib/dist/oc/shape-ops.d.ts +25 -20
- package/lib/dist/oc/shape-ops.js +129 -113
- package/lib/dist/rendering/mesh-transform.js +17 -1
- package/lib/dist/rendering/render-solid.js +19 -6
- package/lib/dist/rendering/render-wire.js +2 -0
- package/lib/dist/rendering/render.d.ts +12 -2
- package/lib/dist/rendering/render.js +195 -220
- package/lib/dist/scene-manager.d.ts +2 -0
- package/lib/dist/scene-manager.js +4 -3
- package/lib/dist/tests/common/scene-object-history.test.d.ts +1 -0
- package/lib/dist/tests/common/scene-object-history.test.js +274 -0
- package/lib/dist/tests/common/shape-history-tracker.test.d.ts +1 -0
- package/lib/dist/tests/common/shape-history-tracker.test.js +110 -0
- package/lib/dist/tests/features/2d/project-regression.test.d.ts +1 -0
- package/lib/dist/tests/features/2d/project-regression.test.js +69 -0
- package/lib/dist/tests/features/2d/project-user-regression.test.d.ts +1 -0
- package/lib/dist/tests/features/2d/project-user-regression.test.js +37 -0
- package/lib/dist/tests/features/color-lineage.test.d.ts +1 -0
- package/lib/dist/tests/features/color-lineage.test.js +213 -0
- package/lib/dist/tests/features/cut-symmetric-through-all.test.d.ts +1 -0
- package/lib/dist/tests/features/cut-symmetric-through-all.test.js +32 -0
- package/lib/dist/tests/features/extrude-history.test.d.ts +1 -0
- package/lib/dist/tests/features/extrude-history.test.js +248 -0
- package/lib/dist/tests/features/extrude.test.js +71 -0
- package/lib/dist/tests/features/fillet2d.test.js +16 -1
- package/lib/dist/tests/features/peer-ops-history.test.d.ts +1 -0
- package/lib/dist/tests/features/peer-ops-history.test.js +119 -0
- package/lib/dist/tests/features/select.test.js +50 -0
- package/lib/dist/tests/features/subtract.test.js +21 -1
- package/lib/dist/tests/setup.js +3 -2
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/ui/dist/assets/{index-BeLxRMCv.js → index-BrW_x4uc.js} +37 -37
- package/ui/dist/index.html +1 -1
- package/lib/dist/common/solid-face.d.ts +0 -9
- package/lib/dist/common/solid-face.js +0 -22
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Face } from "../common/face.js";
|
|
2
2
|
import { Edge } from "../common/edge.js";
|
|
3
|
+
import { Shape } from "../common/shape.js";
|
|
3
4
|
import { SceneObject } from "../common/scene-object.js";
|
|
4
5
|
import { Extrudable } from "../helpers/types.js";
|
|
5
6
|
import { IExtrude } from "../core/interfaces.js";
|
|
@@ -8,16 +9,23 @@ import { Point2DLike } from "../math/point.js";
|
|
|
8
9
|
import { Plane } from "../math/plane.js";
|
|
9
10
|
import { FaceFilterBuilder } from "../filters/face/face-filter.js";
|
|
10
11
|
import { EdgeFilterBuilder } from "../filters/edge/edge-filter.js";
|
|
12
|
+
import { ShapeHistory } from "../common/shape-history-tracker.js";
|
|
11
13
|
export declare abstract class ExtrudeBase extends SceneObject implements IExtrude {
|
|
12
14
|
protected _extrudable: Extrudable | null;
|
|
15
|
+
protected _faceSource: SceneObject | null;
|
|
13
16
|
protected _draft?: number | [number, number];
|
|
14
17
|
protected _endOffset?: number;
|
|
15
18
|
protected _drill?: boolean;
|
|
16
19
|
protected _picking: boolean;
|
|
17
20
|
protected _pickPoints: LazyVertex[];
|
|
18
21
|
protected _thin?: [number] | [number, number];
|
|
19
|
-
constructor(
|
|
22
|
+
constructor(source?: Extrudable | SceneObject);
|
|
20
23
|
get extrudable(): Extrudable;
|
|
24
|
+
get faceSource(): SceneObject | null;
|
|
25
|
+
isFaceSourced(): boolean;
|
|
26
|
+
getSource(): SceneObject | null;
|
|
27
|
+
getSourcePlane(): Plane | null;
|
|
28
|
+
getSourceFaces(): Face[];
|
|
21
29
|
startFaces(...args: number[] | FaceFilterBuilder[]): SceneObject;
|
|
22
30
|
endFaces(...args: number[] | FaceFilterBuilder[]): SceneObject;
|
|
23
31
|
startEdges(...args: number[] | EdgeFilterBuilder[]): SceneObject;
|
|
@@ -33,6 +41,40 @@ export declare abstract class ExtrudeBase extends SceneObject implements IExtrud
|
|
|
33
41
|
internalEdges(...args: number[] | EdgeFilterBuilder[]): SceneObject;
|
|
34
42
|
capFaces(...args: number[] | FaceFilterBuilder[]): SceneObject;
|
|
35
43
|
capEdges(...args: number[] | EdgeFilterBuilder[]): SceneObject;
|
|
44
|
+
/**
|
|
45
|
+
* Read edges for a classification category, preferring the pre-computed
|
|
46
|
+
* state key (set by `classifyExtrudeEdges` during build) and falling back
|
|
47
|
+
* to deriving from the corresponding face-category state (for peer ops that
|
|
48
|
+
* haven't opted into the unified classification step yet).
|
|
49
|
+
*/
|
|
50
|
+
private getClassifiedEdges;
|
|
51
|
+
/**
|
|
52
|
+
* Remap the state-stored face category arrays through a fusion's tool-side
|
|
53
|
+
* history so each face reference points at the actual post-fusion face in
|
|
54
|
+
* the final solid. Call after `fuseWithSceneObjects` returns a `toolHistory`.
|
|
55
|
+
*/
|
|
56
|
+
protected remapClassifiedFaces(history: ShapeHistory): void;
|
|
57
|
+
/**
|
|
58
|
+
* Record every face/edge of the given shapes as additions on this operation.
|
|
59
|
+
* Used by 3D ops in the "no scene fusion" path — when the tools land
|
|
60
|
+
* unchanged in the scene, every face/edge is brand new from this op's POV.
|
|
61
|
+
*/
|
|
62
|
+
protected recordShapeFacesAndEdgesAsAdditions(shapes: Shape[]): void;
|
|
63
|
+
/**
|
|
64
|
+
* One-shot edge classification: derive start/end/side/internal/cap edges
|
|
65
|
+
* from the already-classified face arrays in state and store them as
|
|
66
|
+
* `start-edges`, `end-edges`, `side-edges`, `internal-edges`, `cap-edges`.
|
|
67
|
+
*
|
|
68
|
+
* Call this once after face classification (and after any post-fusion
|
|
69
|
+
* face remapping) so that the selection accessors can just read the
|
|
70
|
+
* pre-computed arrays instead of re-deriving on every access. Matches
|
|
71
|
+
* the classification step from the spec: "Classify the new edges and
|
|
72
|
+
* faces created by the operation".
|
|
73
|
+
*
|
|
74
|
+
* Side edges are the edges of side faces minus any edge that's also on
|
|
75
|
+
* a start/end face (those already belong to start-edges / end-edges).
|
|
76
|
+
*/
|
|
77
|
+
protected classifyExtrudeEdges(): void;
|
|
36
78
|
private buildSuffix;
|
|
37
79
|
private resolveFaces;
|
|
38
80
|
private resolveEdges;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Face } from "../common/face.js";
|
|
2
|
+
import { Edge } from "../common/edge.js";
|
|
1
3
|
import { SceneObject } from "../common/scene-object.js";
|
|
2
4
|
import { LazySelectionSceneObject } from "./lazy-scene-object.js";
|
|
3
5
|
import { normalizePoint2D } from "../helpers/normalize.js";
|
|
@@ -7,21 +9,65 @@ import { FaceFilterBuilder } from "../filters/face/face-filter.js";
|
|
|
7
9
|
import { EdgeFilterBuilder } from "../filters/edge/edge-filter.js";
|
|
8
10
|
import { ShapeFilter } from "../filters/filter.js";
|
|
9
11
|
import { EdgeOps } from "../oc/edge-ops.js";
|
|
12
|
+
import { Explorer } from "../oc/explorer.js";
|
|
13
|
+
import { getOC } from "../oc/init.js";
|
|
14
|
+
import { ShapeHistoryTracker } from "../common/shape-history-tracker.js";
|
|
15
|
+
function dedupEdgesByIsSame(edges) {
|
|
16
|
+
const result = [];
|
|
17
|
+
for (const edge of edges) {
|
|
18
|
+
if (!result.some(r => r.getShape().IsSame(edge.getShape()))) {
|
|
19
|
+
result.push(edge);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
10
24
|
export class ExtrudeBase extends SceneObject {
|
|
11
25
|
_extrudable = null;
|
|
26
|
+
_faceSource = null;
|
|
12
27
|
_draft;
|
|
13
28
|
_endOffset;
|
|
14
29
|
_drill = true;
|
|
15
30
|
_picking = false;
|
|
16
31
|
_pickPoints = [];
|
|
17
32
|
_thin;
|
|
18
|
-
constructor(
|
|
33
|
+
constructor(source) {
|
|
19
34
|
super();
|
|
20
|
-
|
|
35
|
+
if (source) {
|
|
36
|
+
if (source.isExtrudable()) {
|
|
37
|
+
this._extrudable = source;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
this._faceSource = source;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
21
43
|
}
|
|
22
44
|
get extrudable() {
|
|
23
45
|
return this._extrudable;
|
|
24
46
|
}
|
|
47
|
+
get faceSource() {
|
|
48
|
+
return this._faceSource;
|
|
49
|
+
}
|
|
50
|
+
isFaceSourced() {
|
|
51
|
+
return this._faceSource !== null;
|
|
52
|
+
}
|
|
53
|
+
getSource() {
|
|
54
|
+
return this._extrudable ?? this._faceSource;
|
|
55
|
+
}
|
|
56
|
+
getSourcePlane() {
|
|
57
|
+
if (this._extrudable) {
|
|
58
|
+
return this._extrudable.getPlane();
|
|
59
|
+
}
|
|
60
|
+
const faces = this.getSourceFaces();
|
|
61
|
+
return faces.length > 0 ? faces[0].getPlane() : null;
|
|
62
|
+
}
|
|
63
|
+
getSourceFaces() {
|
|
64
|
+
if (!this._faceSource) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
return this._faceSource.getShapes()
|
|
68
|
+
.flatMap(s => s.getSubShapes('face'))
|
|
69
|
+
.filter((f) => f instanceof Face);
|
|
70
|
+
}
|
|
25
71
|
startFaces(...args) {
|
|
26
72
|
const suffix = this.buildSuffix('start-faces', args);
|
|
27
73
|
return new LazySelectionSceneObject(`${this.generateUniqueName(suffix)}`, (parent) => {
|
|
@@ -47,19 +93,10 @@ export class ExtrudeBase extends SceneObject {
|
|
|
47
93
|
startEdges(...args) {
|
|
48
94
|
const suffix = this.buildSuffix('start-edges', args);
|
|
49
95
|
return new LazySelectionSceneObject(`${this.generateUniqueName(suffix)}`, (parent) => {
|
|
50
|
-
|
|
51
|
-
const edges = parent.getState('start-edges') || [];
|
|
52
|
-
const transform = parent.getTransform();
|
|
53
|
-
const originalEdges = transform
|
|
54
|
-
? (this.getState('start-edges') || [])
|
|
55
|
-
: null;
|
|
56
|
-
return this.resolveEdges(edges, args, transform, originalEdges);
|
|
57
|
-
}
|
|
58
|
-
const faces = parent.getState('start-faces') || [];
|
|
59
|
-
const edges = faces.flatMap(f => f.getEdges());
|
|
96
|
+
const edges = this.getClassifiedEdges(parent, 'start-edges', 'start-faces');
|
|
60
97
|
const transform = parent.getTransform();
|
|
61
98
|
const originalEdges = transform
|
|
62
|
-
?
|
|
99
|
+
? this.getClassifiedEdges(this, 'start-edges', 'start-faces')
|
|
63
100
|
: null;
|
|
64
101
|
return this.resolveEdges(edges, args, transform, originalEdges);
|
|
65
102
|
}, this);
|
|
@@ -67,19 +104,10 @@ export class ExtrudeBase extends SceneObject {
|
|
|
67
104
|
endEdges(...args) {
|
|
68
105
|
const suffix = this.buildSuffix('end-edges', args);
|
|
69
106
|
return new LazySelectionSceneObject(`${this.generateUniqueName(suffix)}`, (parent) => {
|
|
70
|
-
|
|
71
|
-
const edges = parent.getState('end-edges') || [];
|
|
72
|
-
const transform = parent.getTransform();
|
|
73
|
-
const originalEdges = transform
|
|
74
|
-
? (this.getState('end-edges') || [])
|
|
75
|
-
: null;
|
|
76
|
-
return this.resolveEdges(edges, args, transform, originalEdges);
|
|
77
|
-
}
|
|
78
|
-
const faces = parent.getState('end-faces') || [];
|
|
79
|
-
const edges = faces.flatMap(f => f.getEdges());
|
|
107
|
+
const edges = this.getClassifiedEdges(parent, 'end-edges', 'end-faces');
|
|
80
108
|
const transform = parent.getTransform();
|
|
81
109
|
const originalEdges = transform
|
|
82
|
-
?
|
|
110
|
+
? this.getClassifiedEdges(this, 'end-edges', 'end-faces')
|
|
83
111
|
: null;
|
|
84
112
|
return this.resolveEdges(edges, args, transform, originalEdges);
|
|
85
113
|
}, this);
|
|
@@ -98,13 +126,17 @@ export class ExtrudeBase extends SceneObject {
|
|
|
98
126
|
sideEdges(...args) {
|
|
99
127
|
const suffix = this.buildSuffix('side-edges', args);
|
|
100
128
|
return new LazySelectionSceneObject(`${this.generateUniqueName(suffix)}`, (parent) => {
|
|
129
|
+
const classified = parent.getState('side-edges');
|
|
130
|
+
if (classified !== undefined) {
|
|
131
|
+
return this.resolveEdges(classified, args);
|
|
132
|
+
}
|
|
133
|
+
// Fallback for peer ops that haven't called classifyExtrudeEdges: derive on the fly.
|
|
101
134
|
const sideFaces = parent.getState('side-faces') || [];
|
|
102
135
|
const startFaces = parent.getState('start-faces') || [];
|
|
103
136
|
const endFaces = parent.getState('end-faces') || [];
|
|
104
137
|
const excludedEdges = [...startFaces, ...endFaces].flatMap(f => f.getEdges());
|
|
105
|
-
const edges = sideFaces.flatMap(f => f.getEdges())
|
|
106
|
-
.filter(e => !excludedEdges.some(ex => e.getShape().IsSame(ex.getShape())))
|
|
107
|
-
.filter((e, i, arr) => arr.findIndex(o => o.getShape().IsSame(e.getShape())) === i);
|
|
138
|
+
const edges = dedupEdgesByIsSame(sideFaces.flatMap(f => f.getEdges())
|
|
139
|
+
.filter(e => !excludedEdges.some(ex => e.getShape().IsSame(ex.getShape()))));
|
|
108
140
|
return this.resolveEdges(edges, args);
|
|
109
141
|
}, this);
|
|
110
142
|
}
|
|
@@ -136,12 +168,7 @@ export class ExtrudeBase extends SceneObject {
|
|
|
136
168
|
internalEdges(...args) {
|
|
137
169
|
const suffix = this.buildSuffix('internal-edges', args);
|
|
138
170
|
return new LazySelectionSceneObject(`${this.generateUniqueName(suffix)}`, (parent) => {
|
|
139
|
-
|
|
140
|
-
const edges = parent.getState('internal-edges') || [];
|
|
141
|
-
return this.resolveEdges(edges, args);
|
|
142
|
-
}
|
|
143
|
-
const faces = parent.getState('internal-faces') || [];
|
|
144
|
-
const edges = faces.flatMap(f => f.getEdges());
|
|
171
|
+
const edges = this.getClassifiedEdges(parent, 'internal-edges', 'internal-faces');
|
|
145
172
|
return this.resolveEdges(edges, args);
|
|
146
173
|
}, this);
|
|
147
174
|
}
|
|
@@ -159,11 +186,89 @@ export class ExtrudeBase extends SceneObject {
|
|
|
159
186
|
capEdges(...args) {
|
|
160
187
|
const suffix = this.buildSuffix('cap-edges', args);
|
|
161
188
|
return new LazySelectionSceneObject(`${this.generateUniqueName(suffix)}`, (parent) => {
|
|
162
|
-
const
|
|
163
|
-
const edges = faces.flatMap(f => f.getEdges());
|
|
189
|
+
const edges = this.getClassifiedEdges(parent, 'cap-edges', 'cap-faces');
|
|
164
190
|
return this.resolveEdges(edges, args);
|
|
165
191
|
}, this);
|
|
166
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Read edges for a classification category, preferring the pre-computed
|
|
195
|
+
* state key (set by `classifyExtrudeEdges` during build) and falling back
|
|
196
|
+
* to deriving from the corresponding face-category state (for peer ops that
|
|
197
|
+
* haven't opted into the unified classification step yet).
|
|
198
|
+
*/
|
|
199
|
+
getClassifiedEdges(source, edgeKey, faceKey) {
|
|
200
|
+
const classified = source.getState(edgeKey);
|
|
201
|
+
if (classified !== undefined) {
|
|
202
|
+
return classified;
|
|
203
|
+
}
|
|
204
|
+
const faces = source.getState(faceKey) || [];
|
|
205
|
+
return dedupEdgesByIsSame(faces.flatMap(f => f.getEdges()));
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Remap the state-stored face category arrays through a fusion's tool-side
|
|
209
|
+
* history so each face reference points at the actual post-fusion face in
|
|
210
|
+
* the final solid. Call after `fuseWithSceneObjects` returns a `toolHistory`.
|
|
211
|
+
*/
|
|
212
|
+
remapClassifiedFaces(history) {
|
|
213
|
+
const keys = ['start-faces', 'end-faces', 'side-faces', 'internal-faces', 'cap-faces'];
|
|
214
|
+
for (const key of keys) {
|
|
215
|
+
const faces = this.getState(key);
|
|
216
|
+
if (faces && faces.length > 0) {
|
|
217
|
+
this.setState(key, ShapeHistoryTracker.remapFaces(faces, history));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Record every face/edge of the given shapes as additions on this operation.
|
|
223
|
+
* Used by 3D ops in the "no scene fusion" path — when the tools land
|
|
224
|
+
* unchanged in the scene, every face/edge is brand new from this op's POV.
|
|
225
|
+
*/
|
|
226
|
+
recordShapeFacesAndEdgesAsAdditions(shapes) {
|
|
227
|
+
const oc = getOC();
|
|
228
|
+
const FACE = oc.TopAbs_ShapeEnum.TopAbs_FACE;
|
|
229
|
+
const EDGE = oc.TopAbs_ShapeEnum.TopAbs_EDGE;
|
|
230
|
+
for (const shape of shapes) {
|
|
231
|
+
for (const raw of Explorer.findShapes(shape.getShape(), FACE)) {
|
|
232
|
+
this.recordAddedFace(Face.fromTopoDSFace(Explorer.toFace(raw)), this);
|
|
233
|
+
}
|
|
234
|
+
for (const raw of Explorer.findShapes(shape.getShape(), EDGE)) {
|
|
235
|
+
this.recordAddedEdge(Edge.fromTopoDSEdge(Explorer.toEdge(raw)), this);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* One-shot edge classification: derive start/end/side/internal/cap edges
|
|
241
|
+
* from the already-classified face arrays in state and store them as
|
|
242
|
+
* `start-edges`, `end-edges`, `side-edges`, `internal-edges`, `cap-edges`.
|
|
243
|
+
*
|
|
244
|
+
* Call this once after face classification (and after any post-fusion
|
|
245
|
+
* face remapping) so that the selection accessors can just read the
|
|
246
|
+
* pre-computed arrays instead of re-deriving on every access. Matches
|
|
247
|
+
* the classification step from the spec: "Classify the new edges and
|
|
248
|
+
* faces created by the operation".
|
|
249
|
+
*
|
|
250
|
+
* Side edges are the edges of side faces minus any edge that's also on
|
|
251
|
+
* a start/end face (those already belong to start-edges / end-edges).
|
|
252
|
+
*/
|
|
253
|
+
classifyExtrudeEdges() {
|
|
254
|
+
const startFaces = this.getState('start-faces') || [];
|
|
255
|
+
const endFaces = this.getState('end-faces') || [];
|
|
256
|
+
const sideFaces = this.getState('side-faces') || [];
|
|
257
|
+
const internalFaces = this.getState('internal-faces') || [];
|
|
258
|
+
const capFaces = this.getState('cap-faces') || [];
|
|
259
|
+
const startEdges = dedupEdgesByIsSame(startFaces.flatMap(f => f.getEdges()));
|
|
260
|
+
const endEdges = dedupEdgesByIsSame(endFaces.flatMap(f => f.getEdges()));
|
|
261
|
+
const excludedForSide = [...startEdges, ...endEdges];
|
|
262
|
+
const sideEdges = dedupEdgesByIsSame(sideFaces.flatMap(f => f.getEdges())
|
|
263
|
+
.filter(e => !excludedForSide.some(ex => ex.getShape().IsSame(e.getShape()))));
|
|
264
|
+
const internalEdges = dedupEdgesByIsSame(internalFaces.flatMap(f => f.getEdges()));
|
|
265
|
+
const capEdges = dedupEdgesByIsSame(capFaces.flatMap(f => f.getEdges()));
|
|
266
|
+
this.setState('start-edges', startEdges);
|
|
267
|
+
this.setState('end-edges', endEdges);
|
|
268
|
+
this.setState('side-edges', sideEdges);
|
|
269
|
+
this.setState('internal-edges', internalEdges);
|
|
270
|
+
this.setState('cap-edges', capEdges);
|
|
271
|
+
}
|
|
167
272
|
buildSuffix(prefix, args) {
|
|
168
273
|
if (args.length === 0) {
|
|
169
274
|
return prefix;
|
|
@@ -230,7 +335,7 @@ export class ExtrudeBase extends SceneObject {
|
|
|
230
335
|
return this._thin;
|
|
231
336
|
}
|
|
232
337
|
serializePickFields() {
|
|
233
|
-
const plane = this.
|
|
338
|
+
const plane = this.getSourcePlane();
|
|
234
339
|
return {
|
|
235
340
|
picking: this.isPicking() || undefined,
|
|
236
341
|
pickPoints: this.isPicking()
|
|
@@ -4,7 +4,7 @@ import { Extrudable } from "../helpers/types.js";
|
|
|
4
4
|
import { Point } from "../math/point.js";
|
|
5
5
|
export declare class ExtrudeToFace extends ExtrudeBase {
|
|
6
6
|
face: SceneObject | 'first-face' | 'last-face';
|
|
7
|
-
constructor(face: SceneObject | 'first-face' | 'last-face',
|
|
7
|
+
constructor(face: SceneObject | 'first-face' | 'last-face', source?: Extrudable | SceneObject);
|
|
8
8
|
build(context: BuildSceneObjectContext): void;
|
|
9
9
|
private createAdvancedExtrude;
|
|
10
10
|
private resizePlanarFace;
|
|
@@ -13,14 +13,14 @@ import { ThinFaceMaker } from "../oc/thin-face-maker.js";
|
|
|
13
13
|
import { Point } from "../math/point.js";
|
|
14
14
|
export class ExtrudeToFace extends ExtrudeBase {
|
|
15
15
|
face;
|
|
16
|
-
constructor(face,
|
|
17
|
-
super(
|
|
16
|
+
constructor(face, source) {
|
|
17
|
+
super(source);
|
|
18
18
|
this.face = face;
|
|
19
19
|
}
|
|
20
20
|
build(context) {
|
|
21
21
|
const allSceneObjects = context.getSceneObjects();
|
|
22
22
|
const sceneObjects = this.resolveFusionScope(allSceneObjects);
|
|
23
|
-
const plane = this.
|
|
23
|
+
const plane = this.getSourcePlane();
|
|
24
24
|
const pickedFaces = this.resolvePickedFaces(plane);
|
|
25
25
|
if (pickedFaces !== null && pickedFaces.length === 0) {
|
|
26
26
|
return;
|
|
@@ -35,7 +35,13 @@ export class ExtrudeToFace extends ExtrudeBase {
|
|
|
35
35
|
let faces;
|
|
36
36
|
let inwardEdges;
|
|
37
37
|
let outwardEdges;
|
|
38
|
-
if (this.
|
|
38
|
+
if (this.isFaceSourced()) {
|
|
39
|
+
if (this.isThin()) {
|
|
40
|
+
throw new Error("thin() is not supported with a face-sourced extrude");
|
|
41
|
+
}
|
|
42
|
+
faces = pickedFaces ?? this.getSourceFaces();
|
|
43
|
+
}
|
|
44
|
+
else if (this.isThin()) {
|
|
39
45
|
const thinResult = ThinFaceMaker.make(this.extrudable.getGeometries(), plane, this._thin[0], this._thin[1]);
|
|
40
46
|
faces = thinResult.faces;
|
|
41
47
|
inwardEdges = thinResult.inwardEdges;
|
|
@@ -79,26 +85,37 @@ export class ExtrudeToFace extends ExtrudeBase {
|
|
|
79
85
|
this.setState('side-faces', allSideFaces);
|
|
80
86
|
this.setState('internal-faces', allInternalFaces);
|
|
81
87
|
this.setState('cap-faces', allCapFaces);
|
|
82
|
-
this.
|
|
88
|
+
this.getSource()?.removeShapes(this);
|
|
83
89
|
if (this.face instanceof SceneObject) {
|
|
84
90
|
this.face.removeShapes(this);
|
|
85
91
|
}
|
|
86
92
|
if (this._operationMode === 'remove') {
|
|
87
93
|
const scope = this.resolveFusionScope(allSceneObjects);
|
|
88
|
-
cutWithSceneObjects(scope, solids, plane, 0, this);
|
|
94
|
+
cutWithSceneObjects(scope, solids, plane, 0, this, { recordHistoryFor: this });
|
|
95
|
+
this.setFinalShapes(this.getShapes());
|
|
89
96
|
return;
|
|
90
97
|
}
|
|
91
98
|
if (sceneObjects.length === 0) {
|
|
92
99
|
this.addShapes(solids);
|
|
100
|
+
this.recordShapeFacesAndEdgesAsAdditions(solids);
|
|
101
|
+
this.classifyExtrudeEdges();
|
|
102
|
+
this.setFinalShapes(this.getShapes());
|
|
93
103
|
return;
|
|
94
104
|
}
|
|
95
|
-
const fusionResult = fuseWithSceneObjects(sceneObjects, solids
|
|
105
|
+
const fusionResult = fuseWithSceneObjects(sceneObjects, solids, {
|
|
106
|
+
recordHistoryFor: this,
|
|
107
|
+
});
|
|
96
108
|
for (const modifiedShape of fusionResult.modifiedShapes) {
|
|
97
109
|
if (modifiedShape.object) {
|
|
98
110
|
modifiedShape.object.removeShape(modifiedShape.shape, this);
|
|
99
111
|
}
|
|
100
112
|
}
|
|
101
113
|
this.addShapes(fusionResult.newShapes);
|
|
114
|
+
if (fusionResult.toolHistory) {
|
|
115
|
+
this.remapClassifiedFaces(fusionResult.toolHistory);
|
|
116
|
+
}
|
|
117
|
+
this.classifyExtrudeEdges();
|
|
118
|
+
this.setFinalShapes(this.getShapes());
|
|
102
119
|
}
|
|
103
120
|
createAdvancedExtrude(sourceFace, targetFace, isPlanar, sketchPlane) {
|
|
104
121
|
const targetDistance = this.computeSignedDistanceToFace(targetFace, sketchPlane);
|
|
@@ -128,7 +145,7 @@ export class ExtrudeToFace extends ExtrudeBase {
|
|
|
128
145
|
resizePlanarFace(targetFace) {
|
|
129
146
|
const endOffset = this.getEndOffset();
|
|
130
147
|
if (endOffset) {
|
|
131
|
-
const dir = this.
|
|
148
|
+
const dir = this.getSourcePlane().normal.reverse();
|
|
132
149
|
return FaceQuery.makeInfinitePlanarFace(targetFace, endOffset, dir);
|
|
133
150
|
}
|
|
134
151
|
return FaceQuery.makeInfinitePlanarFace(targetFace);
|
|
@@ -165,7 +182,7 @@ export class ExtrudeToFace extends ExtrudeBase {
|
|
|
165
182
|
}
|
|
166
183
|
splitShapesByFace(extrusions, targetFace) {
|
|
167
184
|
const result = [];
|
|
168
|
-
const sourcePlane = this.
|
|
185
|
+
const sourcePlane = this.getSourcePlane();
|
|
169
186
|
for (const shape of extrusions) {
|
|
170
187
|
const solids = BooleanOps.splitShape(shape, targetFace);
|
|
171
188
|
if (solids.length === 1) {
|
|
@@ -221,10 +238,11 @@ export class ExtrudeToFace extends ExtrudeBase {
|
|
|
221
238
|
}
|
|
222
239
|
}
|
|
223
240
|
getFirstOrLastFace(sceneObjects, mode) {
|
|
224
|
-
const plane = this.
|
|
241
|
+
const plane = this.getSourcePlane();
|
|
242
|
+
const source = this.getSource();
|
|
225
243
|
const allFaces = [];
|
|
226
244
|
for (const obj of sceneObjects) {
|
|
227
|
-
if (obj ===
|
|
245
|
+
if (obj === source) {
|
|
228
246
|
continue;
|
|
229
247
|
}
|
|
230
248
|
for (const shape of obj.getShapes()) {
|
|
@@ -242,8 +260,9 @@ export class ExtrudeToFace extends ExtrudeBase {
|
|
|
242
260
|
}
|
|
243
261
|
getDependencies() {
|
|
244
262
|
const deps = [];
|
|
245
|
-
|
|
246
|
-
|
|
263
|
+
const source = this.getSource();
|
|
264
|
+
if (source) {
|
|
265
|
+
deps.push(source);
|
|
247
266
|
}
|
|
248
267
|
if (this.face instanceof SceneObject) {
|
|
249
268
|
deps.push(this.face);
|
|
@@ -254,10 +273,9 @@ export class ExtrudeToFace extends ExtrudeBase {
|
|
|
254
273
|
const newFace = this.face instanceof SceneObject
|
|
255
274
|
? (remap.get(this.face) || this.face)
|
|
256
275
|
: this.face;
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
return new ExtrudeToFace(newFace, extrudable).syncWith(this);
|
|
276
|
+
const source = this.getSource();
|
|
277
|
+
const remapped = source ? (remap.get(source) || source) : undefined;
|
|
278
|
+
return new ExtrudeToFace(newFace, remapped).syncWith(this);
|
|
261
279
|
}
|
|
262
280
|
compareTo(other) {
|
|
263
281
|
if (!(other instanceof ExtrudeToFace)) {
|
|
@@ -266,7 +284,12 @@ export class ExtrudeToFace extends ExtrudeBase {
|
|
|
266
284
|
if (!super.compareTo(other)) {
|
|
267
285
|
return false;
|
|
268
286
|
}
|
|
269
|
-
|
|
287
|
+
const thisSource = this.getSource();
|
|
288
|
+
const otherSource = other.getSource();
|
|
289
|
+
if (!thisSource !== !otherSource) {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
if (thisSource && otherSource && !thisSource.compareTo(otherSource)) {
|
|
270
293
|
return false;
|
|
271
294
|
}
|
|
272
295
|
if (typeof (this.face) !== typeof (other.face)) {
|
|
@@ -289,7 +312,7 @@ export class ExtrudeToFace extends ExtrudeBase {
|
|
|
289
312
|
serialize() {
|
|
290
313
|
return {
|
|
291
314
|
sheptType: 'wire',
|
|
292
|
-
extrudable: this.
|
|
315
|
+
extrudable: this.getSource()?.serialize(),
|
|
293
316
|
draft: this.getDraft(),
|
|
294
317
|
endOffset: this.getEndOffset(),
|
|
295
318
|
face: typeof (this.face) === 'string' ? this.face : 'selection',
|
|
@@ -4,7 +4,7 @@ import { Extrudable } from "../helpers/types.js";
|
|
|
4
4
|
export declare class ExtrudeTwoDistances extends ExtrudeBase {
|
|
5
5
|
distance1: number;
|
|
6
6
|
distance2: number;
|
|
7
|
-
constructor(distance1: number, distance2: number,
|
|
7
|
+
constructor(distance1: number, distance2: number, source?: Extrudable | SceneObject);
|
|
8
8
|
build(context: BuildSceneObjectContext): void;
|
|
9
9
|
getDependencies(): SceneObject[];
|
|
10
10
|
createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
|
|
@@ -9,14 +9,14 @@ import { ThinFaceMaker } from "../oc/thin-face-maker.js";
|
|
|
9
9
|
export class ExtrudeTwoDistances extends ExtrudeBase {
|
|
10
10
|
distance1;
|
|
11
11
|
distance2;
|
|
12
|
-
constructor(distance1, distance2,
|
|
13
|
-
super(
|
|
12
|
+
constructor(distance1, distance2, source) {
|
|
13
|
+
super(source);
|
|
14
14
|
this.distance1 = distance1;
|
|
15
15
|
this.distance2 = distance2;
|
|
16
16
|
}
|
|
17
17
|
build(context) {
|
|
18
18
|
const sceneObjects = this.resolveFusionScope(context.getSceneObjects());
|
|
19
|
-
const plane = this.
|
|
19
|
+
const plane = this.getSourcePlane();
|
|
20
20
|
const pickedFaces = this.resolvePickedFaces(plane);
|
|
21
21
|
if (pickedFaces !== null && pickedFaces.length === 0) {
|
|
22
22
|
return;
|
|
@@ -24,7 +24,13 @@ export class ExtrudeTwoDistances extends ExtrudeBase {
|
|
|
24
24
|
let faces;
|
|
25
25
|
let inwardEdges;
|
|
26
26
|
let outwardEdges;
|
|
27
|
-
if (this.
|
|
27
|
+
if (this.isFaceSourced()) {
|
|
28
|
+
if (this.isThin()) {
|
|
29
|
+
throw new Error("thin() is not supported with a face-sourced extrude");
|
|
30
|
+
}
|
|
31
|
+
faces = pickedFaces ?? this.getSourceFaces();
|
|
32
|
+
}
|
|
33
|
+
else if (this.isThin()) {
|
|
28
34
|
const thinResult = ThinFaceMaker.make(this.extrudable.getGeometries(), plane, this._thin[0], this._thin[1]);
|
|
29
35
|
faces = thinResult.faces;
|
|
30
36
|
inwardEdges = thinResult.inwardEdges;
|
|
@@ -43,7 +49,9 @@ export class ExtrudeTwoDistances extends ExtrudeBase {
|
|
|
43
49
|
const extrusions2 = extruder2.extrude();
|
|
44
50
|
const endFaces = extruder2.getEndFaces();
|
|
45
51
|
const all = [...extrusions1, ...extrusions2];
|
|
46
|
-
const
|
|
52
|
+
const halvesFuse = BooleanOps.fuse(all);
|
|
53
|
+
const extrusions = halvesFuse.result;
|
|
54
|
+
halvesFuse.dispose();
|
|
47
55
|
const remainingFaces = [];
|
|
48
56
|
const fusedStartFaces = [];
|
|
49
57
|
const fusedEndFaces = [];
|
|
@@ -112,17 +120,25 @@ export class ExtrudeTwoDistances extends ExtrudeBase {
|
|
|
112
120
|
this.setState('side-faces', sideFaces);
|
|
113
121
|
this.setState('internal-faces', internalFaces);
|
|
114
122
|
this.setState('cap-faces', capFaces);
|
|
115
|
-
this.
|
|
123
|
+
this.getSource()?.removeShapes(this);
|
|
116
124
|
if (this._operationMode === 'remove') {
|
|
117
125
|
const scope = this.resolveFusionScope(context.getSceneObjects());
|
|
118
|
-
cutWithSceneObjects(scope, extrusions, plane, this.distance1 + this.distance2, this
|
|
126
|
+
cutWithSceneObjects(scope, extrusions, plane, this.distance1 + this.distance2, this, {
|
|
127
|
+
recordHistoryFor: this,
|
|
128
|
+
});
|
|
129
|
+
this.setFinalShapes(this.getShapes());
|
|
119
130
|
return;
|
|
120
131
|
}
|
|
121
132
|
if (extrusions.length === 0 || sceneObjects.length === 0) {
|
|
122
133
|
this.addShapes(extrusions);
|
|
134
|
+
this.recordShapeFacesAndEdgesAsAdditions(extrusions);
|
|
135
|
+
this.classifyExtrudeEdges();
|
|
136
|
+
this.setFinalShapes(this.getShapes());
|
|
123
137
|
return;
|
|
124
138
|
}
|
|
125
|
-
const fusionResult = fuseWithSceneObjects(sceneObjects, extrusions
|
|
139
|
+
const fusionResult = fuseWithSceneObjects(sceneObjects, extrusions, {
|
|
140
|
+
recordHistoryFor: this,
|
|
141
|
+
});
|
|
126
142
|
for (const modifiedShape of fusionResult.modifiedShapes) {
|
|
127
143
|
if (!modifiedShape.object) {
|
|
128
144
|
continue;
|
|
@@ -130,15 +146,20 @@ export class ExtrudeTwoDistances extends ExtrudeBase {
|
|
|
130
146
|
modifiedShape.object.removeShape(modifiedShape.shape, this);
|
|
131
147
|
}
|
|
132
148
|
this.addShapes(fusionResult.newShapes);
|
|
149
|
+
if (fusionResult.toolHistory) {
|
|
150
|
+
this.remapClassifiedFaces(fusionResult.toolHistory);
|
|
151
|
+
}
|
|
152
|
+
this.classifyExtrudeEdges();
|
|
153
|
+
this.setFinalShapes(this.getShapes());
|
|
133
154
|
}
|
|
134
155
|
getDependencies() {
|
|
135
|
-
|
|
156
|
+
const source = this.getSource();
|
|
157
|
+
return source ? [source] : [];
|
|
136
158
|
}
|
|
137
159
|
createCopy(remap) {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return new ExtrudeTwoDistances(this.distance1, this.distance2, extrudable).syncWith(this);
|
|
160
|
+
const source = this.getSource();
|
|
161
|
+
const remapped = source ? (remap.get(source) || source) : undefined;
|
|
162
|
+
return new ExtrudeTwoDistances(this.distance1, this.distance2, remapped).syncWith(this);
|
|
142
163
|
}
|
|
143
164
|
compareTo(other) {
|
|
144
165
|
if (!(other instanceof ExtrudeTwoDistances)) {
|
|
@@ -150,7 +171,12 @@ export class ExtrudeTwoDistances extends ExtrudeBase {
|
|
|
150
171
|
if (this.distance1 !== other.distance1 || this.distance2 !== other.distance2) {
|
|
151
172
|
return false;
|
|
152
173
|
}
|
|
153
|
-
|
|
174
|
+
const thisSource = this.getSource();
|
|
175
|
+
const otherSource = other.getSource();
|
|
176
|
+
if (!thisSource !== !otherSource) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
if (thisSource && otherSource && !thisSource.compareTo(otherSource)) {
|
|
154
180
|
return false;
|
|
155
181
|
}
|
|
156
182
|
return true;
|
|
@@ -163,7 +189,7 @@ export class ExtrudeTwoDistances extends ExtrudeBase {
|
|
|
163
189
|
}
|
|
164
190
|
serialize() {
|
|
165
191
|
return {
|
|
166
|
-
extrudable: this.
|
|
192
|
+
extrudable: this.getSource()?.serialize(),
|
|
167
193
|
distance1: this.distance1,
|
|
168
194
|
distance2: this.distance2,
|
|
169
195
|
operationMode: this._operationMode !== 'add' ? this._operationMode : undefined,
|
|
@@ -3,7 +3,7 @@ import { Extrudable } from "../helpers/types.js";
|
|
|
3
3
|
import { ExtrudeBase } from "./extrude-base.js";
|
|
4
4
|
export declare class Extrude extends ExtrudeBase {
|
|
5
5
|
distance: number;
|
|
6
|
-
constructor(distance: number,
|
|
6
|
+
constructor(distance: number, source?: Extrudable | SceneObject);
|
|
7
7
|
build(context: BuildSceneObjectContext): void;
|
|
8
8
|
private buildAdd;
|
|
9
9
|
private buildSymmetric;
|