fluidcad 0.0.27 → 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.
Files changed (54) hide show
  1. package/lib/dist/common/scene-object.d.ts +45 -0
  2. package/lib/dist/common/scene-object.js +121 -0
  3. package/lib/dist/common/shape-factory.d.ts +1 -1
  4. package/lib/dist/common/shape-history-tracker.d.ts +35 -0
  5. package/lib/dist/common/shape-history-tracker.js +114 -0
  6. package/lib/dist/common/shape.js +7 -1
  7. package/lib/dist/common/solid.js +5 -1
  8. package/lib/dist/features/chamfer.js +12 -6
  9. package/lib/dist/features/extrude-base.d.ts +36 -0
  10. package/lib/dist/features/extrude-base.js +105 -33
  11. package/lib/dist/features/extrude-to-face.js +13 -2
  12. package/lib/dist/features/extrude-two-distances.js +18 -3
  13. package/lib/dist/features/extrude.js +29 -6
  14. package/lib/dist/features/fillet.js +3 -4
  15. package/lib/dist/features/fuse.js +14 -0
  16. package/lib/dist/features/infinite-extrude.d.ts +1 -0
  17. package/lib/dist/features/infinite-extrude.js +33 -4
  18. package/lib/dist/features/loft.js +18 -5
  19. package/lib/dist/features/revolve.js +13 -2
  20. package/lib/dist/features/sweep.js +13 -2
  21. package/lib/dist/helpers/scene-helpers.d.ts +7 -1
  22. package/lib/dist/helpers/scene-helpers.js +274 -9
  23. package/lib/dist/oc/boolean-ops.d.ts +29 -3
  24. package/lib/dist/oc/boolean-ops.js +107 -9
  25. package/lib/dist/oc/color-transfer.d.ts +37 -0
  26. package/lib/dist/oc/color-transfer.js +135 -0
  27. package/lib/dist/oc/extrude-ops.js +25 -3
  28. package/lib/dist/oc/fillet-ops.d.ts +5 -3
  29. package/lib/dist/oc/fillet-ops.js +23 -4
  30. package/lib/dist/oc/intersection.js +6 -3
  31. package/lib/dist/oc/mesh.js +1 -1
  32. package/lib/dist/oc/shape-ops.d.ts +25 -0
  33. package/lib/dist/oc/shape-ops.js +131 -12
  34. package/lib/dist/tests/common/scene-object-history.test.d.ts +1 -0
  35. package/lib/dist/tests/common/scene-object-history.test.js +274 -0
  36. package/lib/dist/tests/common/shape-history-tracker.test.d.ts +1 -0
  37. package/lib/dist/tests/common/shape-history-tracker.test.js +110 -0
  38. package/lib/dist/tests/features/2d/project-regression.test.d.ts +1 -0
  39. package/lib/dist/tests/features/2d/project-regression.test.js +69 -0
  40. package/lib/dist/tests/features/2d/project-user-regression.test.d.ts +1 -0
  41. package/lib/dist/tests/features/2d/project-user-regression.test.js +37 -0
  42. package/lib/dist/tests/features/color-lineage.test.d.ts +1 -0
  43. package/lib/dist/tests/features/color-lineage.test.js +213 -0
  44. package/lib/dist/tests/features/cut-symmetric-through-all.test.d.ts +1 -0
  45. package/lib/dist/tests/features/cut-symmetric-through-all.test.js +32 -0
  46. package/lib/dist/tests/features/extrude-history.test.d.ts +1 -0
  47. package/lib/dist/tests/features/extrude-history.test.js +248 -0
  48. package/lib/dist/tests/features/peer-ops-history.test.d.ts +1 -0
  49. package/lib/dist/tests/features/peer-ops-history.test.js +119 -0
  50. package/lib/dist/tests/features/subtract.test.js +21 -1
  51. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  52. package/package.json +3 -3
  53. package/ui/dist/assets/{index-55iqIwnj.js → index-BrW_x4uc.js} +1 -1
  54. package/ui/dist/index.html +1 -1
@@ -1,4 +1,6 @@
1
1
  import { Shape, ShapeFilter } from "./shape.js";
2
+ import { Face } from "./face.js";
3
+ import { Edge } from "./edge.js";
2
4
  import { Matrix4 } from "../math/matrix4.js";
3
5
  import { ISceneObject } from "../core/interfaces.js";
4
6
  import { FusionScope, OperationMode } from "../features/extrude-options.js";
@@ -8,6 +10,19 @@ export type SourceLocation = {
8
10
  line: number;
9
11
  column: number;
10
12
  };
13
+ export type AdditionRecord<T> = {
14
+ shape: T;
15
+ addedBy: SceneObject;
16
+ };
17
+ export type RemovalRecord<T> = {
18
+ shape: T;
19
+ removedBy: SceneObject;
20
+ };
21
+ export type ModificationRecord<T> = {
22
+ sources: T[];
23
+ results: T[];
24
+ modifiedBy: SceneObject;
25
+ };
11
26
  export interface Comparable<T> {
12
27
  compareTo(other: T): boolean;
13
28
  }
@@ -74,11 +89,41 @@ export declare abstract class SceneObject implements Comparable<SceneObject>, Se
74
89
  private set addedShapes(value);
75
90
  private get removedShapes();
76
91
  private set removedShapes(value);
92
+ private get addedFaces();
93
+ private set addedFaces(value);
94
+ private get modifiedFaces();
95
+ private set modifiedFaces(value);
96
+ private get removedFaces();
97
+ private set removedFaces(value);
98
+ private get addedEdges();
99
+ private set addedEdges(value);
100
+ private get modifiedEdges();
101
+ private set modifiedEdges(value);
102
+ private get removedEdges();
103
+ private set removedEdges(value);
104
+ private get finalShapes();
105
+ private set finalShapes(value);
77
106
  getUniqueType(): string;
78
107
  addShape(shape: Shape): void;
79
108
  addShapes(shapes: Shape[]): void;
80
109
  removeShape(shape: Shape, removedBy: SceneObject): void;
81
110
  removeShapes(removedBy: SceneObject, force?: boolean): void;
111
+ recordAddedFace(face: Face, addedBy: SceneObject): void;
112
+ recordAddedEdge(edge: Edge, addedBy: SceneObject): void;
113
+ recordModifiedFaces(sources: Face[], results: Face[], modifiedBy: SceneObject): void;
114
+ recordModifiedEdges(sources: Edge[], results: Edge[], modifiedBy: SceneObject): void;
115
+ recordRemovedFace(face: Face, removedBy: SceneObject): void;
116
+ recordRemovedEdge(edge: Edge, removedBy: SceneObject): void;
117
+ private ownsFace;
118
+ private ownsEdge;
119
+ getAddedFaces(scope?: Set<SceneObject>): Face[];
120
+ getModifiedFaces(scope?: Set<SceneObject>): ModificationRecord<Face>[];
121
+ getRemovedFaces(scope?: Set<SceneObject>): Face[];
122
+ getAddedEdges(scope?: Set<SceneObject>): Edge[];
123
+ getModifiedEdges(scope?: Set<SceneObject>): ModificationRecord<Edge>[];
124
+ getRemovedEdges(scope?: Set<SceneObject>): Edge[];
125
+ setFinalShapes(shapes: Shape[]): void;
126
+ getFinalShapes(): Shape[];
82
127
  getOwnShapes(filter?: ShapeFilter, scope?: Set<SceneObject>): Shape[];
83
128
  getChildShapes(filter?: ShapeFilter, type?: ShapeType): Shape[];
84
129
  getShapes(filter?: ShapeFilter, type?: ShapeType): Shape[];
@@ -21,6 +21,13 @@ export class SceneObject {
21
21
  this.state = new Map();
22
22
  this.state.set('addedShapes', []);
23
23
  this.state.set('removedShapes', []);
24
+ this.state.set('addedFaces', []);
25
+ this.state.set('modifiedFaces', []);
26
+ this.state.set('removedFaces', []);
27
+ this.state.set('addedEdges', []);
28
+ this.state.set('modifiedEdges', []);
29
+ this.state.set('removedEdges', []);
30
+ this.state.set('finalShapes', []);
24
31
  this._id = randomUUID().toString();
25
32
  }
26
33
  get id() {
@@ -208,6 +215,48 @@ export class SceneObject {
208
215
  set removedShapes(shapes) {
209
216
  this.state.set('removedShapes', shapes);
210
217
  }
218
+ get addedFaces() {
219
+ return this.state.get('addedFaces');
220
+ }
221
+ set addedFaces(records) {
222
+ this.state.set('addedFaces', records);
223
+ }
224
+ get modifiedFaces() {
225
+ return this.state.get('modifiedFaces');
226
+ }
227
+ set modifiedFaces(records) {
228
+ this.state.set('modifiedFaces', records);
229
+ }
230
+ get removedFaces() {
231
+ return this.state.get('removedFaces');
232
+ }
233
+ set removedFaces(records) {
234
+ this.state.set('removedFaces', records);
235
+ }
236
+ get addedEdges() {
237
+ return this.state.get('addedEdges');
238
+ }
239
+ set addedEdges(records) {
240
+ this.state.set('addedEdges', records);
241
+ }
242
+ get modifiedEdges() {
243
+ return this.state.get('modifiedEdges');
244
+ }
245
+ set modifiedEdges(records) {
246
+ this.state.set('modifiedEdges', records);
247
+ }
248
+ get removedEdges() {
249
+ return this.state.get('removedEdges');
250
+ }
251
+ set removedEdges(records) {
252
+ this.state.set('removedEdges', records);
253
+ }
254
+ get finalShapes() {
255
+ return this.state.get('finalShapes');
256
+ }
257
+ set finalShapes(shapes) {
258
+ this.state.set('finalShapes', shapes);
259
+ }
211
260
  getUniqueType() {
212
261
  return this.getType();
213
262
  }
@@ -251,6 +300,78 @@ export class SceneObject {
251
300
  this.removeShape(shape, removedBy);
252
301
  }
253
302
  }
303
+ recordAddedFace(face, addedBy) {
304
+ this.addedFaces.push({ shape: face, addedBy });
305
+ }
306
+ recordAddedEdge(edge, addedBy) {
307
+ this.addedEdges.push({ shape: edge, addedBy });
308
+ }
309
+ recordModifiedFaces(sources, results, modifiedBy) {
310
+ this.modifiedFaces.push({ sources, results, modifiedBy });
311
+ }
312
+ recordModifiedEdges(sources, results, modifiedBy) {
313
+ this.modifiedEdges.push({ sources, results, modifiedBy });
314
+ }
315
+ recordRemovedFace(face, removedBy) {
316
+ if (this.isContainer()) {
317
+ for (const child of this.children) {
318
+ if (child.ownsFace(face)) {
319
+ child.recordRemovedFace(face, removedBy);
320
+ }
321
+ }
322
+ return;
323
+ }
324
+ this.removedFaces.push({ shape: face, removedBy });
325
+ }
326
+ recordRemovedEdge(edge, removedBy) {
327
+ if (this.isContainer()) {
328
+ for (const child of this.children) {
329
+ if (child.ownsEdge(edge)) {
330
+ child.recordRemovedEdge(edge, removedBy);
331
+ }
332
+ }
333
+ return;
334
+ }
335
+ this.removedEdges.push({ shape: edge, removedBy });
336
+ }
337
+ ownsFace(face) {
338
+ return this.addedFaces.some(r => r.shape === face);
339
+ }
340
+ ownsEdge(edge) {
341
+ return this.addedEdges.some(r => r.shape === edge);
342
+ }
343
+ getAddedFaces(scope) {
344
+ return this.addedFaces
345
+ .filter(r => !scope || scope.has(r.addedBy))
346
+ .map(r => r.shape);
347
+ }
348
+ getModifiedFaces(scope) {
349
+ return this.modifiedFaces.filter(r => !scope || scope.has(r.modifiedBy));
350
+ }
351
+ getRemovedFaces(scope) {
352
+ return this.removedFaces
353
+ .filter(r => !scope || scope.has(r.removedBy))
354
+ .map(r => r.shape);
355
+ }
356
+ getAddedEdges(scope) {
357
+ return this.addedEdges
358
+ .filter(r => !scope || scope.has(r.addedBy))
359
+ .map(r => r.shape);
360
+ }
361
+ getModifiedEdges(scope) {
362
+ return this.modifiedEdges.filter(r => !scope || scope.has(r.modifiedBy));
363
+ }
364
+ getRemovedEdges(scope) {
365
+ return this.removedEdges
366
+ .filter(r => !scope || scope.has(r.removedBy))
367
+ .map(r => r.shape);
368
+ }
369
+ setFinalShapes(shapes) {
370
+ this.finalShapes = shapes;
371
+ }
372
+ getFinalShapes() {
373
+ return this.finalShapes;
374
+ }
254
375
  getOwnShapes(filter, scope) {
255
376
  filter = {
256
377
  excludeMeta: filter?.excludeMeta ?? true,
@@ -4,5 +4,5 @@ import { Wire } from "./wire.js";
4
4
  import { Face } from "./face.js";
5
5
  import { Edge } from "./edge.js";
6
6
  export declare class ShapeFactory {
7
- static fromShape(shape: TopoDS_Shape): Wire | Edge | Face | Solid;
7
+ static fromShape(shape: TopoDS_Shape): Solid | Face | Edge | Wire;
8
8
  }
@@ -0,0 +1,35 @@
1
+ import type { BRepBuilderAPI_MakeShape } from "occjs-wrapper";
2
+ import { Face } from "./face.js";
3
+ import { Edge } from "./edge.js";
4
+ import { Shape } from "./shape.js";
5
+ export type ShapeHistoryRecord<T> = {
6
+ sources: T[];
7
+ results: T[];
8
+ };
9
+ export type ShapeHistory = {
10
+ addedFaces: Face[];
11
+ modifiedFaces: ShapeHistoryRecord<Face>[];
12
+ generatedFaces: ShapeHistoryRecord<Face>[];
13
+ removedFaces: Face[];
14
+ addedEdges: Edge[];
15
+ modifiedEdges: ShapeHistoryRecord<Edge>[];
16
+ generatedEdges: ShapeHistoryRecord<Edge>[];
17
+ removedEdges: Edge[];
18
+ };
19
+ export declare class ShapeHistoryTracker {
20
+ /**
21
+ * Remap a list of pre-operation faces through a `ShapeHistory`'s
22
+ * modifications. For each input face:
23
+ * - If it appears as a source in `modifiedFaces`, emit the corresponding
24
+ * result faces (1:N).
25
+ * - Otherwise, pass it through unchanged (it survived the operation with
26
+ * the same TShape pointer).
27
+ *
28
+ * Use this to keep classification arrays (start/end/side/…) valid after a
29
+ * fusion has modified some of their faces.
30
+ */
31
+ static remapFaces(faces: Face[], history: ShapeHistory): Face[];
32
+ static remapEdges(edges: Edge[], history: ShapeHistory): Edge[];
33
+ static collect(maker: BRepBuilderAPI_MakeShape, inputs: Shape[]): ShapeHistory;
34
+ private static collectForType;
35
+ }
@@ -0,0 +1,114 @@
1
+ import { getOC } from "../oc/init.js";
2
+ import { Explorer } from "../oc/explorer.js";
3
+ import { ShapeOps } from "../oc/shape-ops.js";
4
+ import { Face } from "./face.js";
5
+ import { Edge } from "./edge.js";
6
+ export class ShapeHistoryTracker {
7
+ /**
8
+ * Remap a list of pre-operation faces through a `ShapeHistory`'s
9
+ * modifications. For each input face:
10
+ * - If it appears as a source in `modifiedFaces`, emit the corresponding
11
+ * result faces (1:N).
12
+ * - Otherwise, pass it through unchanged (it survived the operation with
13
+ * the same TShape pointer).
14
+ *
15
+ * Use this to keep classification arrays (start/end/side/…) valid after a
16
+ * fusion has modified some of their faces.
17
+ */
18
+ static remapFaces(faces, history) {
19
+ const result = [];
20
+ for (const face of faces) {
21
+ const record = history.modifiedFaces.find(m => m.sources.some(s => s.getShape().IsSame(face.getShape())));
22
+ if (record) {
23
+ result.push(...record.results);
24
+ }
25
+ else {
26
+ result.push(face);
27
+ }
28
+ }
29
+ return result;
30
+ }
31
+ static remapEdges(edges, history) {
32
+ const result = [];
33
+ for (const edge of edges) {
34
+ const record = history.modifiedEdges.find(m => m.sources.some(s => s.getShape().IsSame(edge.getShape())));
35
+ if (record) {
36
+ result.push(...record.results);
37
+ }
38
+ else {
39
+ result.push(edge);
40
+ }
41
+ }
42
+ return result;
43
+ }
44
+ static collect(maker, inputs) {
45
+ const oc = getOC();
46
+ const FACE = oc.TopAbs_ShapeEnum.TopAbs_FACE;
47
+ const EDGE = oc.TopAbs_ShapeEnum.TopAbs_EDGE;
48
+ const output = maker.Shape();
49
+ const outputFaces = Explorer.findShapes(output, FACE);
50
+ const outputEdges = Explorer.findShapes(output, EDGE);
51
+ const faces = ShapeHistoryTracker.collectForType(maker, inputs, FACE, outputFaces, (raw) => Face.fromTopoDSFace(Explorer.toFace(raw)));
52
+ const edges = ShapeHistoryTracker.collectForType(maker, inputs, EDGE, outputEdges, (raw) => Edge.fromTopoDSEdge(Explorer.toEdge(raw)));
53
+ return {
54
+ addedFaces: faces.added,
55
+ modifiedFaces: faces.modified,
56
+ generatedFaces: faces.generated,
57
+ removedFaces: faces.removed,
58
+ addedEdges: edges.added,
59
+ modifiedEdges: edges.modified,
60
+ generatedEdges: edges.generated,
61
+ removedEdges: edges.removed,
62
+ };
63
+ }
64
+ static collectForType(maker, inputs, type, outputRaws, wrap) {
65
+ const oc = getOC();
66
+ const modified = [];
67
+ const generated = [];
68
+ const removed = [];
69
+ // Collect every output raw that was either Modified or Generated from an input,
70
+ // so we can later classify the remaining outputs as pure additions.
71
+ const claimed = new oc.TopTools_MapOfShape();
72
+ const isOfType = (raw) => raw.ShapeType() === type;
73
+ for (const input of inputs) {
74
+ const inputRaws = Explorer.findShapes(input.getShape(), type);
75
+ for (const inputRaw of inputRaws) {
76
+ const modifiedRaws = ShapeOps.shapeListToArray(maker.Modified(inputRaw)).filter(isOfType);
77
+ const generatedRaws = ShapeOps.shapeListToArray(maker.Generated(inputRaw)).filter(isOfType);
78
+ const isDeleted = maker.IsDeleted(inputRaw);
79
+ if (modifiedRaws.length > 0) {
80
+ modified.push({
81
+ sources: [wrap(inputRaw)],
82
+ results: modifiedRaws.map(wrap),
83
+ });
84
+ for (const r of modifiedRaws) {
85
+ claimed.Add(r);
86
+ }
87
+ }
88
+ if (generatedRaws.length > 0) {
89
+ generated.push({
90
+ sources: [wrap(inputRaw)],
91
+ results: generatedRaws.map(wrap),
92
+ });
93
+ for (const r of generatedRaws) {
94
+ claimed.Add(r);
95
+ }
96
+ }
97
+ // An input that is deleted with no successor is a removal. If it was
98
+ // Modified into something, the modification record already captures
99
+ // its fate — don't double-count as removed.
100
+ if (isDeleted && modifiedRaws.length === 0 && generatedRaws.length === 0) {
101
+ removed.push(wrap(inputRaw));
102
+ }
103
+ }
104
+ }
105
+ const added = [];
106
+ for (const raw of outputRaws) {
107
+ if (!claimed.Contains(raw)) {
108
+ added.push(wrap(raw));
109
+ }
110
+ }
111
+ claimed.delete();
112
+ return { added, modified, generated, removed };
113
+ }
114
+ }
@@ -87,7 +87,13 @@ export class Shape {
87
87
  if (this.isVertex()) {
88
88
  throw new Error("Cannot set color on vertex shape");
89
89
  }
90
- this.colorMap.push({ shape: face, color });
90
+ const existing = this.colorMap.findIndex(c => c.shape.IsSame(face));
91
+ if (existing >= 0) {
92
+ this.colorMap[existing] = { shape: face, color };
93
+ }
94
+ else {
95
+ this.colorMap.push({ shape: face, color });
96
+ }
91
97
  }
92
98
  getColor(face) {
93
99
  const entry = this.colorMap.find(c => c.shape.IsSame(face));
@@ -60,7 +60,11 @@ export class Solid extends Shape {
60
60
  return null;
61
61
  }
62
62
  copy() {
63
- return new Solid(this.getShape());
63
+ const copied = new Solid(this.getShape());
64
+ for (const entry of this.colorMap) {
65
+ copied.colorMap.push({ shape: entry.shape, color: entry.color });
66
+ }
67
+ return copied;
64
68
  }
65
69
  static fromTopoDSSolid(solid) {
66
70
  return new Solid(solid);
@@ -2,6 +2,7 @@ import { SceneObject } from "../common/scene-object.js";
2
2
  import { FilletOps } from "../oc/fillet-ops.js";
3
3
  import { Explorer } from "../oc/explorer.js";
4
4
  import { ShapeOps } from "../oc/shape-ops.js";
5
+ import { ColorTransfer } from "../oc/color-transfer.js";
5
6
  export class Chamfer extends SceneObject {
6
7
  distance;
7
8
  distance2;
@@ -62,9 +63,9 @@ export class Chamfer extends SceneObject {
62
63
  }
63
64
  edges = edges.filter(e => !targetEdges.includes(e));
64
65
  try {
65
- let newShape;
66
+ let preCleanSolids;
66
67
  if (!this.distance2) {
67
- newShape = FilletOps.makeChamfer(solid, targetEdges, this.distance);
68
+ preCleanSolids = FilletOps.makeChamfer(solid, targetEdges, this.distance);
68
69
  }
69
70
  else {
70
71
  const faces = solid.getFaces();
@@ -78,13 +79,18 @@ export class Chamfer extends SceneObject {
78
79
  }
79
80
  commonFaces.push(firstCommonFace);
80
81
  }
81
- newShape = FilletOps.makeChamferTwoDistances(solid, targetEdges, this.distance, this.distance2, commonFaces, this.isAngle);
82
+ preCleanSolids = FilletOps.makeChamferTwoDistances(solid, targetEdges, this.distance, this.distance2, commonFaces, this.isAngle);
82
83
  }
83
84
  const obj = shapeObjectMap.get(shape);
84
85
  obj.removeShape(shape, this);
85
- const subShapes = Explorer.findSolidsWrapped(ShapeOps.cleanShape(newShape));
86
- for (const subShape of subShapes) {
87
- newShapes.push(subShape);
86
+ // Clean each chamfer result and chain colors through the cleanup's
87
+ // UnifySameDomain history so any merged faces keep their colors.
88
+ for (const preClean of preCleanSolids) {
89
+ const cleanup = ShapeOps.cleanShapeWithLineage(preClean);
90
+ ColorTransfer.applyThroughCleanup(preClean, cleanup);
91
+ const cleaned = cleanup.shape;
92
+ cleanup.dispose();
93
+ newShapes.push(cleaned);
88
94
  }
89
95
  }
90
96
  catch {
@@ -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,6 +9,7 @@ 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;
13
15
  protected _faceSource: SceneObject | null;
@@ -39,6 +41,40 @@ export declare abstract class ExtrudeBase extends SceneObject implements IExtrud
39
41
  internalEdges(...args: number[] | EdgeFilterBuilder[]): SceneObject;
40
42
  capFaces(...args: number[] | FaceFilterBuilder[]): SceneObject;
41
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;
42
78
  private buildSuffix;
43
79
  private resolveFaces;
44
80
  private resolveEdges;