brepjs 18.113.0 → 18.115.0
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/dist/adjacencyFns-BYnyXXqt.js +247 -0
- package/dist/adjacencyFns-d8ZabkvX.cjs +294 -0
- package/dist/brepjs.cjs +31 -406
- package/dist/brepjs.js +7 -390
- package/dist/{healingFns-D00TPE9p.js → healingFns-2TJHHDg5.js} +3 -246
- package/dist/{healingFns-x1sHV-HO.cjs → healingFns-xLJ6Cp_O.cjs} +0 -291
- package/dist/index.d.ts +2 -2
- package/dist/refResolveFns-hEOEcFZ8.js +761 -0
- package/dist/refResolveFns-pRVyjy35.cjs +880 -0
- package/dist/shapeRef.cjs +21 -7
- package/dist/shapeRef.d.ts +10 -2
- package/dist/shapeRef.js +2 -2
- package/dist/topology/shapeRef/index.d.ts +1 -0
- package/dist/topology/shapeRef/refResolveFns.d.ts +53 -0
- package/dist/topology/shapeRef/shapeRefFns.d.ts +9 -7
- package/dist/topology.cjs +8 -7
- package/dist/topology.js +2 -1
- package/package.json +1 -1
- package/dist/shapeRefFns-ChMnsOAF.js +0 -248
- package/dist/shapeRefFns-CuepnFOm.cjs +0 -283
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
const require_topologyQueryFns = require("./topologyQueryFns-CoXVHuN7.cjs");
|
|
2
|
-
const require_faceFns = require("./faceFns-BV0GnTlt.cjs");
|
|
3
|
-
const require_shapeFns = require("./shapeFns-ybo0INGL.cjs");
|
|
4
|
-
const require_measureFns = require("./measureFns-DblG0Yaz.cjs");
|
|
5
|
-
//#region src/topology/shapeRef/scoring.ts
|
|
6
|
-
/**
|
|
7
|
-
* Default face scorer combining surface type, normal alignment, centroid proximity,
|
|
8
|
-
* and area similarity.
|
|
9
|
-
*
|
|
10
|
-
* Scoring breakdown:
|
|
11
|
-
* - Surface type match: +1.0 (mismatch when both defined: -Infinity)
|
|
12
|
-
* - Normal dot product: weighted contribution (rejected if < 0.707)
|
|
13
|
-
* - Centroid distance: quadratic penalty (rejected if distSq > 100)
|
|
14
|
-
* - Area ratio: penalized if |log(hintArea / faceArea)| > 1.0
|
|
15
|
-
*/
|
|
16
|
-
function defaultScorer(hint, face) {
|
|
17
|
-
let score = 0;
|
|
18
|
-
const faceType = require_faceFns.faceGeomType(face);
|
|
19
|
-
if (hint.surfaceType !== void 0) if (faceType === hint.surfaceType) score += 1;
|
|
20
|
-
else return -Infinity;
|
|
21
|
-
if (hint.normal !== void 0) {
|
|
22
|
-
const faceNormal = require_faceFns.normalAt(face);
|
|
23
|
-
const dot = hint.normal[0] * faceNormal[0] + hint.normal[1] * faceNormal[1] + hint.normal[2] * faceNormal[2];
|
|
24
|
-
if (dot < .707) return -Infinity;
|
|
25
|
-
score += dot;
|
|
26
|
-
}
|
|
27
|
-
if (hint.centroid !== void 0) {
|
|
28
|
-
const faceCentroid = require_faceFns.faceCenter(face);
|
|
29
|
-
const dx = hint.centroid[0] - faceCentroid[0];
|
|
30
|
-
const dy = hint.centroid[1] - faceCentroid[1];
|
|
31
|
-
const dz = hint.centroid[2] - faceCentroid[2];
|
|
32
|
-
const distSq = dx * dx + dy * dy + dz * dz;
|
|
33
|
-
if (distSq > 100) return -Infinity;
|
|
34
|
-
score -= distSq / 100;
|
|
35
|
-
}
|
|
36
|
-
if (hint.area !== void 0 && hint.area > 0) {
|
|
37
|
-
const areaResult = require_measureFns.measureArea(face);
|
|
38
|
-
if (areaResult.ok && areaResult.value > 0) {
|
|
39
|
-
const logRatio = Math.abs(Math.log(hint.area / areaResult.value));
|
|
40
|
-
if (logRatio > 1) score -= logRatio;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return score;
|
|
44
|
-
}
|
|
45
|
-
//#endregion
|
|
46
|
-
//#region src/topology/shapeRef/shapeRefFns.ts
|
|
47
|
-
/** Snapshot the geometric properties of a face for later matching. */
|
|
48
|
-
function captureHint(face) {
|
|
49
|
-
const surfaceType = require_faceFns.faceGeomType(face);
|
|
50
|
-
const normal = require_faceFns.normalAt(face);
|
|
51
|
-
const centroid = require_faceFns.faceCenter(face);
|
|
52
|
-
const areaResult = require_measureFns.measureArea(face);
|
|
53
|
-
return {
|
|
54
|
-
entityType: "face",
|
|
55
|
-
surfaceType,
|
|
56
|
-
normal,
|
|
57
|
-
centroid,
|
|
58
|
-
area: areaResult.ok ? areaResult.value : void 0
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
/** Threshold for dominant-axis detection (abs(component) > 0.9). */
|
|
62
|
-
var AXIS_THRESHOLD = .9;
|
|
63
|
-
/** Determine the cardinal role name for a box face from its outward normal. */
|
|
64
|
-
function boxRoleFromNormal(n) {
|
|
65
|
-
if (n[2] > AXIS_THRESHOLD) return "box:top";
|
|
66
|
-
if (n[2] < -.9) return "box:bottom";
|
|
67
|
-
if (n[1] > AXIS_THRESHOLD) return "box:back";
|
|
68
|
-
if (n[1] < -.9) return "box:front";
|
|
69
|
-
if (n[0] > AXIS_THRESHOLD) return "box:right";
|
|
70
|
-
if (n[0] < -.9) return "box:left";
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Auto-assign role names to the faces of a shape based on operation type.
|
|
74
|
-
*
|
|
75
|
-
* For 'box': uses face normals to assign cardinal names
|
|
76
|
-
* ('box:top', 'box:bottom', 'box:front', 'box:back', 'box:left', 'box:right').
|
|
77
|
-
* **Note:** Box role detection assumes axis-aligned faces (normal within 0.9 of
|
|
78
|
-
* a cardinal axis). Rotated boxes may receive fewer than 6 named roles; remaining
|
|
79
|
-
* faces fall through to sequential naming.
|
|
80
|
-
*
|
|
81
|
-
* For other types: sequential naming ('opType:face_0', 'opType:face_1', ...).
|
|
82
|
-
*
|
|
83
|
-
* @returns Map from role name to its face hash codes (one at assignment time;
|
|
84
|
-
* a role accrues more hashes only later, when `updateRoles` tracks a split).
|
|
85
|
-
*/
|
|
86
|
-
function assignRoles(shape, operationType) {
|
|
87
|
-
const faces = require_topologyQueryFns.getFaces(shape);
|
|
88
|
-
const roles = /* @__PURE__ */ new Map();
|
|
89
|
-
if (operationType === "box") {
|
|
90
|
-
for (const face of faces) {
|
|
91
|
-
const role = boxRoleFromNormal(require_faceFns.normalAt(face));
|
|
92
|
-
if (role !== void 0 && !roles.has(role)) roles.set(role, [require_shapeFns.getHashCode(face)]);
|
|
93
|
-
}
|
|
94
|
-
return roles;
|
|
95
|
-
}
|
|
96
|
-
let index = 0;
|
|
97
|
-
for (const face of faces) {
|
|
98
|
-
roles.set(`${operationType}:face_${index}`, [require_shapeFns.getHashCode(face)]);
|
|
99
|
-
index++;
|
|
100
|
-
}
|
|
101
|
-
return roles;
|
|
102
|
-
}
|
|
103
|
-
/** Create a ShapeRef from an origin ID, role name, and face. */
|
|
104
|
-
function createRef(origin, role, face) {
|
|
105
|
-
return {
|
|
106
|
-
origin,
|
|
107
|
-
role,
|
|
108
|
-
hint: captureHint(face)
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Advance a role's face hashes through one evolution: drop deleted faces,
|
|
113
|
-
* replace a modified face with *all* its successors (a 1→many split keeps every
|
|
114
|
-
* fragment), and keep unchanged faces. Deduped so a shared successor isn't
|
|
115
|
-
* doubled.
|
|
116
|
-
*/
|
|
117
|
-
function nextHashes(hashes, evolution) {
|
|
118
|
-
const successors = [];
|
|
119
|
-
for (const hash of hashes) {
|
|
120
|
-
if (evolution.deleted.has(hash)) continue;
|
|
121
|
-
const modified = evolution.modified.get(hash);
|
|
122
|
-
const targets = modified && modified.length > 0 ? modified : [hash];
|
|
123
|
-
for (const h of targets) if (!successors.includes(h)) successors.push(h);
|
|
124
|
-
}
|
|
125
|
-
return successors;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Propagate a role table through a ShapeEvolution record.
|
|
129
|
-
* Returns a new RoleTable with hashes updated according to the evolution.
|
|
130
|
-
*
|
|
131
|
-
* - Deleted faces: hash dropped (role removed once all its hashes are gone).
|
|
132
|
-
* - Modified faces: hash replaced by **all** successor hashes — so a 1→many
|
|
133
|
-
* split keeps every fragment, and `resolveRef` disambiguates among them.
|
|
134
|
-
* - Unchanged faces: hash preserved.
|
|
135
|
-
*
|
|
136
|
-
* Note: `evolution.generated` is intentionally not consumed here — on the OCCT
|
|
137
|
-
* kernels its hashes refer to an intermediate shape, not the final result, so
|
|
138
|
-
* naming generated faces produces roles that never resolve (verified: 0 live
|
|
139
|
-
* generated hashes across cut/fuse on occt-wasm). Stable names for generated
|
|
140
|
-
* geometry (fillet rounds, boolean seams) need history-fidelity work tracked
|
|
141
|
-
* separately.
|
|
142
|
-
*/
|
|
143
|
-
function updateRoles(roles, origin, evolution) {
|
|
144
|
-
const originRoles = roles.get(origin);
|
|
145
|
-
if (!originRoles) return roles;
|
|
146
|
-
const updatedOriginRoles = /* @__PURE__ */ new Map();
|
|
147
|
-
for (const [role, hashes] of originRoles) {
|
|
148
|
-
const successors = nextHashes(hashes, evolution);
|
|
149
|
-
if (successors.length > 0) updatedOriginRoles.set(role, successors);
|
|
150
|
-
}
|
|
151
|
-
const newRoles = /* @__PURE__ */ new Map();
|
|
152
|
-
for (const [key, value] of roles) newRoles.set(key, key === origin ? updatedOriginRoles : value);
|
|
153
|
-
return newRoles;
|
|
154
|
-
}
|
|
155
|
-
/** Ambiguity threshold: if two scores are within this range, it's ambiguous. */
|
|
156
|
-
var AMBIGUITY_THRESHOLD = .1;
|
|
157
|
-
/** Minimum score for geometric fallback to accept a match. */
|
|
158
|
-
var MIN_SCORE = .5;
|
|
159
|
-
/**
|
|
160
|
-
* Score `candidates` against `hint`, returning the best match, a tie within
|
|
161
|
-
* {@link AMBIGUITY_THRESHOLD}, or nothing above {@link MIN_SCORE}. Scoping the
|
|
162
|
-
* candidates to a role's tracked successors (rather than every face) is what
|
|
163
|
-
* makes split-face disambiguation reliable — the fragments compete only with
|
|
164
|
-
* each other, not with unrelated geometry.
|
|
165
|
-
*/
|
|
166
|
-
function scoreFaces(hint, candidates, scoreFn) {
|
|
167
|
-
let bestScore = -Infinity;
|
|
168
|
-
let bestFace;
|
|
169
|
-
let secondBestScore = -Infinity;
|
|
170
|
-
const scored = [];
|
|
171
|
-
for (const face of candidates) {
|
|
172
|
-
const score = scoreFn(hint, face);
|
|
173
|
-
if (score > MIN_SCORE) scored.push([face, score]);
|
|
174
|
-
if (score > bestScore) {
|
|
175
|
-
secondBestScore = bestScore;
|
|
176
|
-
bestScore = score;
|
|
177
|
-
bestFace = face;
|
|
178
|
-
} else if (score > secondBestScore) secondBestScore = score;
|
|
179
|
-
}
|
|
180
|
-
if (bestFace !== void 0 && bestScore > MIN_SCORE) {
|
|
181
|
-
if (bestScore - secondBestScore < AMBIGUITY_THRESHOLD && scored.length > 1) return {
|
|
182
|
-
kind: "ambiguous",
|
|
183
|
-
candidates: scored.filter(([, s]) => s >= bestScore - AMBIGUITY_THRESHOLD).map(([f]) => f)
|
|
184
|
-
};
|
|
185
|
-
return {
|
|
186
|
-
kind: "match",
|
|
187
|
-
face: bestFace
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
return { kind: "none" };
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Resolve a ShapeRef to a face in the current shape.
|
|
194
|
-
*
|
|
195
|
-
* Resolution strategy:
|
|
196
|
-
* 1. Exact: the role's tracked successor hashes. One survivor → exact match;
|
|
197
|
-
* several survivors (a face that split) → disambiguate among *only those*
|
|
198
|
-
* fragments; none survive → deleted.
|
|
199
|
-
* 2. Geometric fallback over the whole shape when the role isn't tracked (or a
|
|
200
|
-
* scoped score turned up nothing): best-scoring face, else ambiguous /
|
|
201
|
-
* not-found.
|
|
202
|
-
*/
|
|
203
|
-
function resolveRef(ref, roles, currentShape, scorer) {
|
|
204
|
-
const faces = require_topologyQueryFns.getFaces(currentShape);
|
|
205
|
-
const scoreFn = scorer ?? defaultScorer;
|
|
206
|
-
const targetHashes = roles.get(ref.origin)?.get(ref.role);
|
|
207
|
-
if (targetHashes !== void 0 && targetHashes.length > 0) {
|
|
208
|
-
const survivors = faces.filter((f) => targetHashes.includes(require_shapeFns.getHashCode(f)));
|
|
209
|
-
if (survivors.length === 1) {
|
|
210
|
-
const [only] = survivors;
|
|
211
|
-
if (only !== void 0) return {
|
|
212
|
-
face: only,
|
|
213
|
-
confidence: "exact"
|
|
214
|
-
};
|
|
215
|
-
} else if (survivors.length === 0) return {
|
|
216
|
-
ref,
|
|
217
|
-
reason: "deleted"
|
|
218
|
-
};
|
|
219
|
-
else {
|
|
220
|
-
const outcome = scoreFaces(ref.hint, survivors, scoreFn);
|
|
221
|
-
if (outcome.kind === "match") return {
|
|
222
|
-
face: outcome.face,
|
|
223
|
-
confidence: "geometric-fallback"
|
|
224
|
-
};
|
|
225
|
-
if (outcome.kind === "ambiguous") return {
|
|
226
|
-
ref,
|
|
227
|
-
reason: "ambiguous",
|
|
228
|
-
candidates: outcome.candidates
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
const outcome = scoreFaces(ref.hint, faces, scoreFn);
|
|
233
|
-
if (outcome.kind === "match") return {
|
|
234
|
-
face: outcome.face,
|
|
235
|
-
confidence: "geometric-fallback"
|
|
236
|
-
};
|
|
237
|
-
if (outcome.kind === "ambiguous") return {
|
|
238
|
-
ref,
|
|
239
|
-
reason: "ambiguous",
|
|
240
|
-
candidates: outcome.candidates
|
|
241
|
-
};
|
|
242
|
-
return {
|
|
243
|
-
ref,
|
|
244
|
-
reason: "not-found"
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
//#endregion
|
|
248
|
-
Object.defineProperty(exports, "assignRoles", {
|
|
249
|
-
enumerable: true,
|
|
250
|
-
get: function() {
|
|
251
|
-
return assignRoles;
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
Object.defineProperty(exports, "captureHint", {
|
|
255
|
-
enumerable: true,
|
|
256
|
-
get: function() {
|
|
257
|
-
return captureHint;
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
Object.defineProperty(exports, "createRef", {
|
|
261
|
-
enumerable: true,
|
|
262
|
-
get: function() {
|
|
263
|
-
return createRef;
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
Object.defineProperty(exports, "defaultScorer", {
|
|
267
|
-
enumerable: true,
|
|
268
|
-
get: function() {
|
|
269
|
-
return defaultScorer;
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
Object.defineProperty(exports, "resolveRef", {
|
|
273
|
-
enumerable: true,
|
|
274
|
-
get: function() {
|
|
275
|
-
return resolveRef;
|
|
276
|
-
}
|
|
277
|
-
});
|
|
278
|
-
Object.defineProperty(exports, "updateRoles", {
|
|
279
|
-
enumerable: true,
|
|
280
|
-
get: function() {
|
|
281
|
-
return updateRoles;
|
|
282
|
-
}
|
|
283
|
-
});
|