Skip to content

Commit ebcd9e6

Browse files
author
mmiscool
committed
Boolean opperations optimizations
1 parent 3a8cc5c commit ebcd9e6

File tree

3 files changed

+45
-37
lines changed

3 files changed

+45
-37
lines changed

src/BREP/SolidMethods/authoring.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ export function _getPointIndex(p) {
2323
throw new Error(`Invalid point coordinates: (${x}, ${y}, ${z}) - must be finite numbers`);
2424
}
2525

26+
// Some reconstruction paths intentionally leave this map empty and rebuild lazily.
27+
if ((this._vertKeyToIndex?.size || 0) === 0 && Array.isArray(this._vertProperties) && this._vertProperties.length > 0) {
28+
this._vertKeyToIndex = new Map();
29+
for (let i = 0; i < this._vertProperties.length; i += 3) {
30+
const vx = this._vertProperties[i + 0];
31+
const vy = this._vertProperties[i + 1];
32+
const vz = this._vertProperties[i + 2];
33+
this._vertKeyToIndex.set(`${vx},${vy},${vz}`, (i / 3) | 0);
34+
}
35+
}
36+
2637
const k = this._key(p);
2738
const found = this._vertKeyToIndex.get(k);
2839
if (found !== undefined) return found;

src/BREP/SolidMethods/booleanOps.js

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,40 @@ export function _combineIdMaps(other) {
1212
return merged;
1313
}
1414

15+
function _invertFaceNameMap(nameToId) {
16+
const idToName = new Map();
17+
if (!nameToId || typeof nameToId.entries !== 'function') return idToName;
18+
for (const [name, id] of nameToId.entries()) {
19+
idToName.set(id, name);
20+
}
21+
return idToName;
22+
}
23+
1524
function _collapseFaceIdsByName(solid) {
1625
if (!solid || !solid._faceNameToID || !solid._idToFaceName || !Array.isArray(solid._triIDs)) return false;
1726
const nameToId = solid._faceNameToID;
27+
const idToName = solid._idToFaceName;
28+
const triIDs = solid._triIDs;
29+
const canonicalById = new Map();
1830
let changed = false;
1931

20-
for (let i = 0; i < solid._triIDs.length; i++) {
21-
const id = solid._triIDs[i];
22-
const name = solid._idToFaceName.get(id);
23-
if (!name) continue;
24-
const canonical = nameToId.get(name);
25-
if (canonical !== undefined && canonical !== id) {
26-
solid._triIDs[i] = canonical;
32+
for (let i = 0; i < triIDs.length; i++) {
33+
const id = triIDs[i];
34+
let canonical = canonicalById.get(id);
35+
if (canonical === undefined) {
36+
const name = idToName.get(id);
37+
canonical = (name !== undefined) ? (nameToId.get(name) ?? id) : id;
38+
canonicalById.set(id, canonical);
39+
}
40+
if (canonical !== id) {
41+
triIDs[i] = canonical;
2742
changed = true;
2843
}
2944
}
3045

3146
if (!changed) return false;
3247

33-
solid._idToFaceName = new Map(
34-
[...solid._faceNameToID.entries()].map(([name, id]) => [id, name]),
35-
);
48+
solid._idToFaceName = _invertFaceNameMap(solid._faceNameToID);
3649
solid._faceIndex = null;
3750
solid._dirty = true;
3851
try { if (solid._manifold && typeof solid._manifold.delete === 'function') solid._manifold.delete(); } catch { }
@@ -54,7 +67,6 @@ export function union(other) {
5467
try { out._auxEdges = [...(this._auxEdges || []), ...(other?._auxEdges || [])]; } catch { }
5568
try { out._faceMetadata = this._combineFaceMetadata(other); } catch { }
5669
try { out._edgeMetadata = this._combineEdgeMetadata(other); } catch { }
57-
_collapseFaceIdsByName(out);
5870
return out;
5971
}
6072

@@ -68,7 +80,6 @@ export function subtract(other) {
6880
try { out._auxEdges = [...(this._auxEdges || []), ...(other?._auxEdges || [])]; } catch { }
6981
try { out._faceMetadata = this._combineFaceMetadata(other); } catch { }
7082
try { out._edgeMetadata = this._combineEdgeMetadata(other); } catch { }
71-
_collapseFaceIdsByName(out);
7283

7384
return out;
7485
}
@@ -82,7 +93,6 @@ export function intersect(other) {
8293
try { out._auxEdges = [...(this._auxEdges || []), ...(other?._auxEdges || [])]; } catch { }
8394
try { out._faceMetadata = this._combineFaceMetadata(other); } catch { }
8495
try { out._edgeMetadata = this._combineEdgeMetadata(other); } catch { }
85-
_collapseFaceIdsByName(out);
8696
return out;
8797
}
8898

@@ -99,7 +109,6 @@ export function difference(other) {
99109
try { out._auxEdges = [...(this._auxEdges || []), ...(other?._auxEdges || [])]; } catch { }
100110
try { out._faceMetadata = this._combineFaceMetadata(other); } catch { }
101111
try { out._edgeMetadata = this._combineEdgeMetadata(other); } catch { }
102-
_collapseFaceIdsByName(out);
103112
return out;
104113
}
105114

@@ -112,7 +121,6 @@ export function setTolerance(tolerance) {
112121
try { out._auxEdges = Array.isArray(this._auxEdges) ? this._auxEdges.slice() : []; } catch { }
113122
try { out._faceMetadata = new Map(this._faceMetadata); } catch { }
114123
try { out._edgeMetadata = new Map(this._edgeMetadata); } catch { }
115-
_collapseFaceIdsByName(out);
116124
return out;
117125
}
118126
export function simplify(tolerance = undefined, updateInPlace = false) {
@@ -133,14 +141,8 @@ export function simplify(tolerance = undefined, updateInPlace = false) {
133141
this._triVerts = Array.from(meshOut.triVerts);
134142
this._triIDs = Solid._expandTriIDsFromMesh(meshOut);
135143

136-
// Rebuild vertex key map
144+
// Defer rebuilding key map until authoring methods need it.
137145
this._vertKeyToIndex = new Map();
138-
for (let i = 0; i < this._vertProperties.length; i += 3) {
139-
const x = this._vertProperties[i + 0];
140-
const y = this._vertProperties[i + 1];
141-
const z = this._vertProperties[i + 2];
142-
this._vertKeyToIndex.set(`${x},${y},${z}`, (i / 3) | 0);
143-
}
144146

145147
// Keep existing face name map; best-effort completion for any new IDs
146148
const completeMap = new Map(this._idToFaceName);
@@ -160,9 +162,10 @@ export function simplify(tolerance = undefined, updateInPlace = false) {
160162
}
161163
} catch { /* ignore */ }
162164
this._idToFaceName = completeMap;
163-
this._faceNameToID = new Map(
164-
[...this._idToFaceName.entries()].map(([id, name]) => [name, id]),
165-
);
165+
this._faceNameToID = new Map();
166+
for (const [id, name] of this._idToFaceName.entries()) {
167+
this._faceNameToID.set(name, id);
168+
}
166169

167170
// Replace cached manifold and reset caches
168171
try { if (this._manifold && this._manifold !== outM && typeof this._manifold.delete === 'function') this._manifold.delete(); } catch { }
@@ -209,13 +212,8 @@ export function _fromManifold(manifoldObj, idToFaceName) {
209212
solid._vertProperties = Array.from(mesh.vertProperties);
210213
solid._triVerts = Array.from(mesh.triVerts);
211214
solid._triIDs = Solid._expandTriIDsFromMesh(mesh);
212-
213-
for (let i = 0; i < mesh.vertProperties.length; i += 3) {
214-
const x = mesh.vertProperties[i + 0];
215-
const y = mesh.vertProperties[i + 1];
216-
const z = mesh.vertProperties[i + 2];
217-
solid._vertKeyToIndex.set(`${x},${y},${z}`, i / 3);
218-
}
215+
// Avoid O(vertexCount) string allocations here; authoring methods lazily rebuild this map.
216+
solid._vertKeyToIndex = new Map();
219217

220218
const completeMap = new Map(idToFaceName);
221219
try {
@@ -235,9 +233,10 @@ export function _fromManifold(manifoldObj, idToFaceName) {
235233
} catch (_) { /* best-effort completion */ }
236234

237235
solid._idToFaceName = new Map(completeMap);
238-
solid._faceNameToID = new Map(
239-
[...solid._idToFaceName.entries()].map(([id, name]) => [name, id]),
240-
);
236+
solid._faceNameToID = new Map();
237+
for (const [id, name] of solid._idToFaceName.entries()) {
238+
solid._faceNameToID.set(name, id);
239+
}
241240

242241
solid._manifold = manifoldObj;
243242
solid._dirty = false;

src/BREP/applyBooleanOperation.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,6 @@ export async function applyBooleanOperation(partHistory, baseSolid, booleanParam
531531
};
532532

533533
const addResult = (solid, target) => {
534-
solid.visualize();
535534
const inheritedName = target?.name || target?.uuid || null;
536535
const finalName = inheritedName || (featureID ? `${featureID}_${++idx}` : solid.name || 'RESULT');
537536
try { solid.name = finalName; } catch (_) { }
@@ -681,7 +680,6 @@ export async function applyBooleanOperation(partHistory, baseSolid, booleanParam
681680
}
682681
}
683682
}
684-
result.visualize();
685683
debugLog('Boolean successful', {
686684
result: __booleanDebugSummarizeSolid(result),
687685
removedCount: tools.length + (baseSolid ? 1 : 0),

0 commit comments

Comments
 (0)