@ifc-lite/clash 1.8.0 → 1.9.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/README.md +18 -0
- package/dist/adapters/ifcx.d.ts.map +1 -1
- package/dist/adapters/ifcx.js +22 -36
- package/dist/adapters/ifcx.js.map +1 -1
- package/dist/adapters/shared.d.ts +61 -0
- package/dist/adapters/shared.d.ts.map +1 -0
- package/dist/adapters/shared.js +143 -0
- package/dist/adapters/shared.js.map +1 -0
- package/dist/adapters/step.d.ts +62 -3
- package/dist/adapters/step.d.ts.map +1 -1
- package/dist/adapters/step.js +180 -89
- package/dist/adapters/step.js.map +1 -1
- package/dist/contact/index.d.ts +14 -0
- package/dist/contact/index.d.ts.map +1 -1
- package/dist/contact/index.js +14 -0
- package/dist/contact/index.js.map +1 -1
- package/dist/contact/min-distance.d.ts +87 -0
- package/dist/contact/min-distance.d.ts.map +1 -0
- package/dist/contact/min-distance.js +255 -0
- package/dist/contact/min-distance.js.map +1 -0
- package/dist/contact/shared-faces.js +10 -1
- package/dist/contact/shared-faces.js.map +1 -1
- package/dist/engine-ts/broad.js +20 -2
- package/dist/engine-ts/broad.js.map +1 -1
- package/dist/engine-ts/tri-mesh.d.ts +16 -12
- package/dist/engine-ts/tri-mesh.d.ts.map +1 -1
- package/dist/engine-ts/tri-mesh.js +16 -12
- package/dist/engine-ts/tri-mesh.js.map +1 -1
- package/dist/selectors.d.ts.map +1 -1
- package/dist/selectors.js +20 -4
- package/dist/selectors.js.map +1 -1
- package/package.json +8 -8
package/dist/adapters/step.js
CHANGED
|
@@ -1,68 +1,10 @@
|
|
|
1
1
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
-
/**
|
|
5
|
-
* STEP / IFC2x3 / IFC4 source adapter: turn an `IfcDataStore` plus its meshes
|
|
6
|
-
* into representation-agnostic `ClashElement`s, and precompute the
|
|
7
|
-
* void/host/assembly pair exclusions from IFC relationships.
|
|
8
|
-
*
|
|
9
|
-
* This module is the only part of the package that depends on
|
|
10
|
-
* `@ifc-lite/parser` / `@ifc-lite/query`; it is reached via the
|
|
11
|
-
* `@ifc-lite/clash/step` subpath so the core stays version-neutral.
|
|
12
|
-
*/
|
|
13
|
-
import { getInheritanceChainAcrossSchemas, isIfcTypeLikeEntity, } from '@ifc-lite/parser';
|
|
14
4
|
import { EntityNode } from '@ifc-lite/query';
|
|
15
5
|
import { makeExclusionSet, qualifiedKey } from '../exclude.js';
|
|
16
6
|
import { fromPositions } from '../math/aabb.js';
|
|
17
|
-
|
|
18
|
-
* Types that are never physical clash candidates: voids, virtual/reference
|
|
19
|
-
* geometry, and non-product material associations. Including them produced
|
|
20
|
-
* phantom clashes (IfcVirtualElement, IfcOpeningElement, even
|
|
21
|
-
* IfcMaterialConstituent) that no clash rule referenced - they are dropped from
|
|
22
|
-
* the candidate set entirely, so "detect all" and per-rule runs only ever
|
|
23
|
-
* consider real building elements. (#1464)
|
|
24
|
-
*
|
|
25
|
-
* Spatial containers are handled separately by {@link isSpatialContainerTag},
|
|
26
|
-
* which derives them from the schema rather than from a list.
|
|
27
|
-
*/
|
|
28
|
-
const NON_CLASHABLE_TAGS = new Set([
|
|
29
|
-
'IfcOpeningElement',
|
|
30
|
-
'IfcOpeningStandardCase',
|
|
31
|
-
'IfcVirtualElement',
|
|
32
|
-
'IfcGrid',
|
|
33
|
-
'IfcGridAxis',
|
|
34
|
-
'IfcAnnotation',
|
|
35
|
-
'IfcMaterial',
|
|
36
|
-
'IfcMaterialConstituent',
|
|
37
|
-
'IfcMaterialLayer',
|
|
38
|
-
]);
|
|
39
|
-
/** Memoizes the schema walk below; IFC type names are a bounded vocabulary. */
|
|
40
|
-
const spatialContainerByTag = new Map();
|
|
41
|
-
/**
|
|
42
|
-
* True for spatial *containers* - the entities whose geometry describes an
|
|
43
|
-
* extent that, by construction, encloses the elements assigned to it. A storey
|
|
44
|
-
* against the slab it contains is not a coordination problem, and IFC4.3
|
|
45
|
-
* infrastructure exports routinely give IfcBuildingStorey / IfcRoad / IfcBridge
|
|
46
|
-
* tessellated bodies, so every contained element clashed with its own
|
|
47
|
-
* container. (follow-up to #1464)
|
|
48
|
-
*
|
|
49
|
-
* Derived from the schema, not enumerated: `getInheritanceChainAcrossSchemas`
|
|
50
|
-
* walks the bundled IFC2X3 + IFC4 + IFC4X3 union, so the IFC4.3 facility leaves
|
|
51
|
-
* (IfcRoad, IfcBridge, IfcFacilityPart, ...) resolve even though the parser's
|
|
52
|
-
* own codegen pin is IFC4_ADD2_TC1 and would return an empty chain for them.
|
|
53
|
-
* Both supertypes are checked because IFC2X3 has no `IfcSpatialElement` -
|
|
54
|
-
* `IfcSpatialStructureElement` descends straight from `IfcProduct` there.
|
|
55
|
-
* This subsumes the IfcSpace / IfcSpatialZone entries that #1464 listed by hand.
|
|
56
|
-
*/
|
|
57
|
-
function isSpatialContainerTag(tag) {
|
|
58
|
-
const cached = spatialContainerByTag.get(tag);
|
|
59
|
-
if (cached !== undefined)
|
|
60
|
-
return cached;
|
|
61
|
-
const chain = getInheritanceChainAcrossSchemas(tag);
|
|
62
|
-
const spatial = chain.some((a) => a === 'IfcSpatialElement' || a === 'IfcSpatialStructureElement');
|
|
63
|
-
spatialContainerByTag.set(tag, spatial);
|
|
64
|
-
return spatial;
|
|
65
|
-
}
|
|
7
|
+
import { isNonClashableTag, mergeMeshes } from './shared.js';
|
|
66
8
|
/**
|
|
67
9
|
* Lift local-frame vertices into the model's world frame: `world = origin + local`
|
|
68
10
|
* (the inverse of the renderer's per-element local frame). Returns a fresh f32
|
|
@@ -77,14 +19,91 @@ function worldFramePositions(local, o) {
|
|
|
77
19
|
}
|
|
78
20
|
return out;
|
|
79
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* The durable key for an element that has NO stored GlobalId — a malformed /
|
|
24
|
+
* fallback-only IFC root, or every element of a GLB-sourced model, whose store
|
|
25
|
+
* carries geometry and no IFC entities at all.
|
|
26
|
+
*
|
|
27
|
+
* ## Why the model id is in it
|
|
28
|
+
*
|
|
29
|
+
* `key` is the element identity that `clashReviewKey` (`../review.ts`) and the
|
|
30
|
+
* viewer's `elementPairExclusion` (`apps/viewer/src/lib/clash/exclusions.ts`)
|
|
31
|
+
* are BOTH keyed on, and both drop `model` on purpose — in the viewer that is a
|
|
32
|
+
* per-load `crypto.randomUUID()`, so folding it in would make every saved
|
|
33
|
+
* review and every saved rule go inert on the next reload. A GlobalId is
|
|
34
|
+
* globally unique, so that works. An express id is only unique WITHIN a model:
|
|
35
|
+
* every file is numbered from 1, so a bare `expressid:2` names an element in
|
|
36
|
+
* every model of a federation at once, and a review status or an exclusion set
|
|
37
|
+
* on one model's element would silently cover another model's element.
|
|
38
|
+
*
|
|
39
|
+
* ## Why it is encoded
|
|
40
|
+
*
|
|
41
|
+
* `clashReviewKey` composes `rule`, `a.key` and `b.key` with a SPACE, on the
|
|
42
|
+
* stated ground that a space never occurs inside an IfcGUID. A model id is not
|
|
43
|
+
* an IfcGUID — the CLI passes `basename(filePath)` — so it can. Percent-
|
|
44
|
+
* encoding is injective (distinct model ids stay distinct keys) and emits no
|
|
45
|
+
* space, so the separator assumption keeps holding.
|
|
46
|
+
*
|
|
47
|
+
* The `expressid:` prefix is unchanged, and this is a pure fallback: an element
|
|
48
|
+
* WITH a GlobalId is keyed on it exactly as before.
|
|
49
|
+
*/
|
|
50
|
+
function syntheticKey(modelId, expressId) {
|
|
51
|
+
return `expressid:${encodeURIComponent(modelId)}:${expressId}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Whether this store carries ANY GlobalId at all — i.e. whether a total miss
|
|
55
|
+
* says something about the ids we used, or only about the file.
|
|
56
|
+
*
|
|
57
|
+
* Called at most once per `elementsFromStep`, and only on the already-degraded
|
|
58
|
+
* path, so the scan is off every normal run. It short-circuits on the first
|
|
59
|
+
* hit, which is the answer for any real IFC.
|
|
60
|
+
*/
|
|
61
|
+
function storeHasAnyGlobalId(store) {
|
|
62
|
+
const { entities } = store;
|
|
63
|
+
for (let i = 0; i < entities.count; i += 1) {
|
|
64
|
+
if (entities.getGlobalId(entities.expressId[i]))
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
80
69
|
export function elementsFromStep(options) {
|
|
81
|
-
const { store, meshes, modelId, federation, worldTransform, buildExclusions = true } = options;
|
|
70
|
+
const { store, meshes, modelId, federation, worldTransform, buildExclusions = true, meshIdOffset = 0, } = options;
|
|
82
71
|
const elements = [];
|
|
72
|
+
// One expressId can back MULTIPLE elements: a GPU-instanced entity is fed
|
|
73
|
+
// in as one `MeshData` per occurrence, all sharing one `expressId` but
|
|
74
|
+
// holding distinct world-space positions and a distinct `occurrenceKey`
|
|
75
|
+
// (see `MeshData.occurrenceKey` doc, `packages/geometry/src/types.ts`).
|
|
76
|
+
// `byExpressId` fans relationship exclusions (below, `buildStepExclusions`)
|
|
77
|
+
// out across every occurrence at each side of a relationship, so a host's
|
|
78
|
+
// void/assembly exclusions cover every physical placement of the
|
|
79
|
+
// filler/sibling, not just whichever occurrence happened to be built last
|
|
80
|
+
// (same remedy as #1405 / #2865).
|
|
83
81
|
const byExpressId = new Map();
|
|
82
|
+
/** Elements whose GlobalId lookup came back empty — see the check below. */
|
|
83
|
+
let missingGlobalIds = 0;
|
|
84
|
+
// Pass 1: group every mesh by its OWNING OCCURRENCE — `occurrenceKey` when
|
|
85
|
+
// present (a GPU-instanced entity's individual placement), else the bare
|
|
86
|
+
// expressId (every other mesh: at most one MeshData per expressId, so the
|
|
87
|
+
// expressId alone already identifies the occurrence) — filtering
|
|
88
|
+
// non-clashable tags and empty geometry up front, and folding each mesh's
|
|
89
|
+
// own origin into world-frame positions as it is collected. An entity with
|
|
90
|
+
// several mesh representations of the SAME occurrence (Body + Axis, several
|
|
91
|
+
// sub-meshes, ...) must become exactly ONE `ClashElement`, never one per
|
|
92
|
+
// mesh — see `./shared.ts`'s `mergeMeshes` doc for why, and `ifcx.ts` for
|
|
93
|
+
// the sibling adapter this mirrors. Grouping by the bare expressId alone
|
|
94
|
+
// (dropping `occurrenceKey`) would instead coalesce DISTINCT physical
|
|
95
|
+
// occurrences that merely share one expressId into one element, merging
|
|
96
|
+
// their geometry (and bounds) across unrelated world-space locations.
|
|
97
|
+
const groups = new Map();
|
|
98
|
+
const groupMeta = new Map();
|
|
84
99
|
for (const mesh of meshes) {
|
|
85
100
|
if (!mesh.positions || mesh.positions.length === 0)
|
|
86
101
|
continue;
|
|
87
|
-
|
|
102
|
+
// STORE-LOCAL id. Everything below that touches `store` — the entity table,
|
|
103
|
+
// `EntityNode`, the relationship graph in `buildStepExclusions` — must use
|
|
104
|
+
// this, never `mesh.expressId`, which the host may already have shifted
|
|
105
|
+
// into the global id space (see `meshIdOffset`).
|
|
106
|
+
const expressId = mesh.expressId - meshIdOffset;
|
|
88
107
|
const node = new EntityNode(store, expressId);
|
|
89
108
|
// Drop non-physical / non-product geometry up front so it never becomes a
|
|
90
109
|
// clash candidate (no rule should have to exclude IfcSpace by hand). (#1464)
|
|
@@ -104,9 +123,7 @@ export function elementsFromStep(options) {
|
|
|
104
123
|
// (which always shows classes 1 and 2) is a catalogue, not a place clash
|
|
105
124
|
// runs from. Clashing origin-stacked templates against each other would be
|
|
106
125
|
// pure noise, so excluding class 1 is the intended reading, not collateral.
|
|
107
|
-
if (
|
|
108
|
-
isSpatialContainerTag(tag) ||
|
|
109
|
-
isIfcTypeLikeEntity(tag.toUpperCase()))
|
|
126
|
+
if (isNonClashableTag(tag))
|
|
110
127
|
continue;
|
|
111
128
|
// The wasm geometry path stores positions in the element's LOCAL frame
|
|
112
129
|
// (world = origin + position; see `MeshData.origin`). Clash works in world
|
|
@@ -119,32 +136,100 @@ export function elementsFromStep(options) {
|
|
|
119
136
|
const positions = o && (o[0] !== 0 || o[1] !== 0 || o[2] !== 0)
|
|
120
137
|
? worldFramePositions(mesh.positions, o)
|
|
121
138
|
: mesh.positions;
|
|
139
|
+
const occurrenceId = mesh.occurrenceKey ?? String(expressId);
|
|
140
|
+
const group = groups.get(occurrenceId);
|
|
141
|
+
if (group) {
|
|
142
|
+
group.push({ positions, indices: mesh.indices });
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
groups.set(occurrenceId, [{ positions, indices: mesh.indices }]);
|
|
146
|
+
groupMeta.set(occurrenceId, { tag, expressId, occurrenceKey: mesh.occurrenceKey });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Pass 2: one ClashElement per OCCURRENCE, geometry merged across every
|
|
150
|
+
// mesh of that same occurrence that survived the filter above.
|
|
151
|
+
for (const [occurrenceId, meshGroup] of groups) {
|
|
152
|
+
const { tag, expressId, occurrenceKey } = groupMeta.get(occurrenceId);
|
|
153
|
+
const node = new EntityNode(store, expressId);
|
|
154
|
+
const merged = mergeMeshes(meshGroup);
|
|
122
155
|
// Read stored (table-backed) values directly. `node.globalId` / `node.name`
|
|
123
156
|
// fall back to `extractEntityAttributesOnDemand` when the table value is
|
|
124
157
|
// empty (common: Name is optional, globalId is empty for fallback-only /
|
|
125
|
-
// malformed roots) — and with a fresh node per
|
|
126
|
-
// once per element inside this loop (AGENTS.md hot-loop ban). The
|
|
127
|
-
// getters never trigger on-demand extraction. `node.type`
|
|
128
|
-
// `node.storey()` (relationship-only) are table-backed
|
|
158
|
+
// malformed roots) — and with a fresh node per entity that fallback would
|
|
159
|
+
// fire once per element inside this loop (AGENTS.md hot-loop ban). The
|
|
160
|
+
// table getters never trigger on-demand extraction. `node.type`
|
|
161
|
+
// (getTypeName) and `node.storey()` (relationship-only) are table-backed
|
|
162
|
+
// and stay.
|
|
129
163
|
const storedGlobalId = store.entities.getGlobalId(expressId);
|
|
130
164
|
const storedName = store.entities.getName(expressId);
|
|
131
|
-
// Fall back to a
|
|
132
|
-
// malformed IFC roots
|
|
133
|
-
|
|
165
|
+
// Fall back to a MODEL-SCOPED synthetic key rather than dropping geometry:
|
|
166
|
+
// malformed IFC roots, and whole GLB-sourced models, still participate in
|
|
167
|
+
// clashes. See {@link syntheticKey} for why the model id belongs in it.
|
|
168
|
+
const baseKey = storedGlobalId || syntheticKey(modelId, expressId);
|
|
169
|
+
// A GPU-instanced occurrence carries `mesh.occurrenceKey`; fold it into the
|
|
170
|
+
// identity so distinct physical occurrences of one expressId don't collapse
|
|
171
|
+
// onto one review/exclusion key. Flat meshes are unaffected (occurrenceKey
|
|
172
|
+
// absent, one bucket per expressId as before).
|
|
173
|
+
const key = occurrenceKey ? `${baseKey}:${occurrenceKey}` : baseKey;
|
|
174
|
+
if (!storedGlobalId)
|
|
175
|
+
missingGlobalIds += 1;
|
|
134
176
|
const element = {
|
|
135
177
|
key,
|
|
178
|
+
// `expressId` is local here, so the offset is applied exactly ONCE and
|
|
179
|
+
// the result is the id the renderer/selection channel already uses for
|
|
180
|
+
// this mesh — i.e. `mesh.expressId` again, whenever `meshIdOffset` and
|
|
181
|
+
// the federation agree (they are both read off the same `model.idOffset`
|
|
182
|
+
// in the viewer). See the federated round-trip test in `step.test.ts`.
|
|
183
|
+
// Deliberately shared across every occurrence of one expressId — the
|
|
184
|
+
// renderer/selection channel addresses instanced occurrences by their
|
|
185
|
+
// shared source entity id, not a per-occurrence one.
|
|
136
186
|
ref: federation ? federation.toGlobalId(modelId, expressId) : expressId,
|
|
137
187
|
model: modelId,
|
|
138
188
|
tag,
|
|
139
189
|
name: storedName || undefined,
|
|
140
190
|
storey: node.storey()?.name || undefined,
|
|
141
|
-
bounds: fromPositions(positions, worldTransform),
|
|
142
|
-
positions,
|
|
143
|
-
indices:
|
|
191
|
+
bounds: fromPositions(merged.positions, worldTransform),
|
|
192
|
+
positions: merged.positions,
|
|
193
|
+
indices: merged.indices,
|
|
144
194
|
transform: worldTransform,
|
|
145
195
|
};
|
|
146
196
|
elements.push(element);
|
|
147
|
-
byExpressId.
|
|
197
|
+
const bucket = byExpressId.get(expressId);
|
|
198
|
+
if (bucket)
|
|
199
|
+
bucket.push(element);
|
|
200
|
+
else
|
|
201
|
+
byExpressId.set(expressId, [element]);
|
|
202
|
+
}
|
|
203
|
+
// A wrong `meshIdOffset` — above all a FORGOTTEN one — leaves ids that are
|
|
204
|
+
// positive, plausible and simply address the wrong rows, so nothing about an
|
|
205
|
+
// individual id gives it away. Its signature is the SHAPE of the damage:
|
|
206
|
+
// EVERY GlobalId lookup misses, never just some. A real file can carry the
|
|
207
|
+
// occasional fallback-only root with no GlobalId, but "not one element in
|
|
208
|
+
// this model has one" is a host wiring bug, and it is silent otherwise —
|
|
209
|
+
// `key` degrades to the synthetic fallback, `buildStepExclusions` below finds
|
|
210
|
+
// no relationships, and the caller gets a plausible-looking result set with
|
|
211
|
+
// the void/host/assembly exclusions quietly disabled.
|
|
212
|
+
//
|
|
213
|
+
// The `storeHasAnyGlobalId` guard is what keeps that from crying wolf. A
|
|
214
|
+
// total miss means "the ids we used are wrong" only if there was something to
|
|
215
|
+
// hit: a GLB-sourced model has an ENTITY-LESS store
|
|
216
|
+
// (`createMinimalGlbDataStore` -> `createSyntheticDataStore` in the viewer),
|
|
217
|
+
// so EVERY element missing is its normal, correct state, and warning on it
|
|
218
|
+
// would fire on a correct configuration on every run — which teaches people
|
|
219
|
+
// to ignore the one message that reports the real defect. The same goes for a
|
|
220
|
+
// file whose roots carry no GlobalId at all. When the store does hold
|
|
221
|
+
// GlobalIds and not one of our ids reached them, the ids are the problem.
|
|
222
|
+
//
|
|
223
|
+
// One `if` outside the loop, and the scan behind it runs only when the whole
|
|
224
|
+
// model already missed: no per-element cost.
|
|
225
|
+
if (elements.length > 0 &&
|
|
226
|
+
missingGlobalIds === elements.length &&
|
|
227
|
+
storeHasAnyGlobalId(store)) {
|
|
228
|
+
console.warn(`[clash/step] every element in model "${modelId}" (${elements.length}) resolved to an ` +
|
|
229
|
+
`empty GlobalId. This usually means \`meshIdOffset\` (used: ${meshIdOffset}) does not ` +
|
|
230
|
+
'match the shift the host applied to `mesh.expressId`, so the store is being addressed ' +
|
|
231
|
+
'with ids it does not contain — element keys, names and the void/host exclusions are ' +
|
|
232
|
+
'all degraded.');
|
|
148
233
|
}
|
|
149
234
|
const exclusions = buildExclusions
|
|
150
235
|
? buildStepExclusions(store, byExpressId)
|
|
@@ -162,19 +247,28 @@ export function elementsFromStep(options) {
|
|
|
162
247
|
*/
|
|
163
248
|
export function buildStepExclusions(store, byExpressId) {
|
|
164
249
|
const pairs = [];
|
|
165
|
-
for (const [expressId,
|
|
250
|
+
for (const [expressId, elementsAtId] of byExpressId) {
|
|
166
251
|
const node = new EntityNode(store, expressId);
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
252
|
+
// A relationship is stated between EXPRESS ids, not occurrences: fan it
|
|
253
|
+
// out across every occurrence bucketed at each side (usually one element
|
|
254
|
+
// each; more than one only for a GPU-instanced expressId), so a host's
|
|
255
|
+
// void/assembly exclusions cover every physical placement of the
|
|
256
|
+
// filler/sibling, not just whichever occurrence happened to be built last.
|
|
257
|
+
const pairAll = (otherId) => {
|
|
258
|
+
const others = byExpressId.get(otherId);
|
|
259
|
+
if (!others)
|
|
260
|
+
return;
|
|
261
|
+
for (const a of elementsAtId) {
|
|
262
|
+
const ek = qualifiedKey(a.model, a.key);
|
|
263
|
+
for (const b of others) {
|
|
264
|
+
pairs.push([ek, qualifiedKey(b.model, b.key)]);
|
|
265
|
+
}
|
|
172
266
|
}
|
|
267
|
+
};
|
|
268
|
+
for (const opening of node.voids()) {
|
|
269
|
+
pairAll(opening.expressId);
|
|
173
270
|
for (const filler of opening.filledBy()) {
|
|
174
|
-
|
|
175
|
-
if (fillerElement) {
|
|
176
|
-
pairs.push([ek, qualifiedKey(fillerElement.model, fillerElement.key)]);
|
|
177
|
-
}
|
|
271
|
+
pairAll(filler.expressId);
|
|
178
272
|
}
|
|
179
273
|
}
|
|
180
274
|
const parent = node.decomposedBy();
|
|
@@ -182,10 +276,7 @@ export function buildStepExclusions(store, byExpressId) {
|
|
|
182
276
|
for (const sibling of parent.decomposes()) {
|
|
183
277
|
if (sibling.expressId === expressId)
|
|
184
278
|
continue;
|
|
185
|
-
|
|
186
|
-
if (siblingElement) {
|
|
187
|
-
pairs.push([ek, qualifiedKey(siblingElement.model, siblingElement.key)]);
|
|
188
|
-
}
|
|
279
|
+
pairAll(sibling.expressId);
|
|
189
280
|
}
|
|
190
281
|
}
|
|
191
282
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"step.js","sourceRoot":"","sources":["../../src/adapters/step.ts"],"names":[],"mappings":"AAAA;;+DAE+D;
|
|
1
|
+
{"version":3,"file":"step.js","sourceRoot":"","sources":["../../src/adapters/step.ts"],"names":[],"mappings":"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"}
|
package/dist/contact/index.d.ts
CHANGED
|
@@ -20,6 +20,20 @@ export type { Mesh, Vec3, AABB } from './types.js';
|
|
|
20
20
|
export type { SharedFaceCluster, SharedFaceOptions } from './shared-faces.js';
|
|
21
21
|
export type { TrianglePair, NarrowPhaseOptions } from './narrow-phase.js';
|
|
22
22
|
export { narrowPhase, clusterSharedFaces };
|
|
23
|
+
/**
|
|
24
|
+
* Minimum-distance query, exposed here rather than left package-private
|
|
25
|
+
* because every consumer that wants it lives outside this package. The
|
|
26
|
+
* viewer's measure tool needs the exact surface-to-surface distance, and
|
|
27
|
+
* before this it could not reach any of the machinery: `triTriDistance` sits
|
|
28
|
+
* under `math/`, which has no export subpath, so a deep import is hard-blocked
|
|
29
|
+
* by `ERR_PACKAGE_PATH_NOT_EXPORTED`.
|
|
30
|
+
*
|
|
31
|
+
* `buildMeshBvh` / `queryMeshCross` come with it: a caller measuring one
|
|
32
|
+
* element against several should build each tree once rather than pay for it
|
|
33
|
+
* per query.
|
|
34
|
+
*/
|
|
35
|
+
export { minDistanceBetweenMeshes, minDistanceBetweenBvhs, type MeshDistance, type MinDistanceOptions, } from './min-distance.js';
|
|
36
|
+
export { buildMeshBvh, queryMeshCross, type MeshBvh } from './mesh-bvh.js';
|
|
23
37
|
export interface ContactOptions extends NarrowPhaseOptions, SharedFaceOptions {
|
|
24
38
|
}
|
|
25
39
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contact/index.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,kBAAkB,EAClB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,IAAI,EAAQ,MAAM,YAAY,CAAC;AAE7C,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC9E,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;AAE3C,MAAM,WAAW,cAAe,SAAQ,kBAAkB,EAAE,iBAAiB;CAAG;AAEhF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,GAAE,cAAmB,GAAG,iBAAiB,EAAE,CAIhG"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contact/index.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,kBAAkB,EAClB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,IAAI,EAAQ,MAAM,YAAY,CAAC;AAE7C,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC9E,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAE3E,MAAM,WAAW,cAAe,SAAQ,kBAAkB,EAAE,iBAAiB;CAAG;AAEhF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,GAAE,cAAmB,GAAG,iBAAiB,EAAE,CAIhG"}
|
package/dist/contact/index.js
CHANGED
|
@@ -19,6 +19,20 @@
|
|
|
19
19
|
import { narrowPhase } from './narrow-phase.js';
|
|
20
20
|
import { clusterSharedFaces, } from './shared-faces.js';
|
|
21
21
|
export { narrowPhase, clusterSharedFaces };
|
|
22
|
+
/**
|
|
23
|
+
* Minimum-distance query, exposed here rather than left package-private
|
|
24
|
+
* because every consumer that wants it lives outside this package. The
|
|
25
|
+
* viewer's measure tool needs the exact surface-to-surface distance, and
|
|
26
|
+
* before this it could not reach any of the machinery: `triTriDistance` sits
|
|
27
|
+
* under `math/`, which has no export subpath, so a deep import is hard-blocked
|
|
28
|
+
* by `ERR_PACKAGE_PATH_NOT_EXPORTED`.
|
|
29
|
+
*
|
|
30
|
+
* `buildMeshBvh` / `queryMeshCross` come with it: a caller measuring one
|
|
31
|
+
* element against several should build each tree once rather than pay for it
|
|
32
|
+
* per query.
|
|
33
|
+
*/
|
|
34
|
+
export { minDistanceBetweenMeshes, minDistanceBetweenBvhs, } from './min-distance.js';
|
|
35
|
+
export { buildMeshBvh, queryMeshCross } from './mesh-bvh.js';
|
|
22
36
|
/**
|
|
23
37
|
* Compute the contact interface(s) between two world-space meshes: the real
|
|
24
38
|
* shared-face polygon (surface), intersection line, or point — not an AABB.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contact/index.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,kBAAkB,GAGnB,MAAM,mBAAmB,CAAC;AAM3B,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contact/index.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,kBAAkB,GAGnB,MAAM,mBAAmB,CAAC;AAM3B,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,OAAO,EACL,wBAAwB,EACxB,sBAAsB,GAGvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,cAAc,EAAgB,MAAM,eAAe,CAAC;AAI3E;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,CAAO,EAAE,CAAO,EAAE,OAAuB,EAAE;IACzE,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IACtC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,OAAO,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimum distance between two world-space meshes, with the witness points.
|
|
3
|
+
*
|
|
4
|
+
* Why this is a new traversal rather than a new predicate: the exact
|
|
5
|
+
* triangle-to-triangle minimum distance already exists as `triTriDistance`
|
|
6
|
+
* (`../math/triangle-distance.ts`), and a second copy of that math would be one
|
|
7
|
+
* more thing to keep in step. What did NOT exist anywhere in this package is a
|
|
8
|
+
* traversal that can find the CLOSEST pair.
|
|
9
|
+
*
|
|
10
|
+
* Every other query here is an OVERLAP predicate: `Bvh.queryAABB`,
|
|
11
|
+
* `Bvh.queryPairs` and `queryMeshCross` all prune on AABB intersection, so for
|
|
12
|
+
* two disjoint meshes they return an empty candidate set and there is nothing
|
|
13
|
+
* left to measure. Inflating a clash `clearance` does not substitute either:
|
|
14
|
+
* `testPair` only ever considers triangle pairs already within its margin, so
|
|
15
|
+
* the answer is capped by the margin you guessed rather than found.
|
|
16
|
+
*
|
|
17
|
+
* So this is branch-and-bound over the two BVHs: descend the node pair whose
|
|
18
|
+
* AABB-to-AABB LOWER bound is smallest, and prune any pair whose lower bound is
|
|
19
|
+
* already >= the best real distance found so far. The bound is exact for
|
|
20
|
+
* axis-aligned boxes and never exceeds the true triangle distance, which is
|
|
21
|
+
* what makes the pruning safe: a pruned subtree cannot contain a closer pair.
|
|
22
|
+
*/
|
|
23
|
+
import { type MeshBvh } from './mesh-bvh.js';
|
|
24
|
+
import type { BvhNode } from './bvh.js';
|
|
25
|
+
import type { Mesh, Vec3 } from './types.js';
|
|
26
|
+
export interface MeshDistance {
|
|
27
|
+
/** Minimum distance between the two surfaces. 0 when they touch or overlap. */
|
|
28
|
+
readonly distance: number;
|
|
29
|
+
/** Witness point on mesh A. */
|
|
30
|
+
readonly pointA: Vec3;
|
|
31
|
+
/** Witness point on mesh B. */
|
|
32
|
+
readonly pointB: Vec3;
|
|
33
|
+
/** Triangle indices the witness points lie on. */
|
|
34
|
+
readonly triangleA: number;
|
|
35
|
+
readonly triangleB: number;
|
|
36
|
+
}
|
|
37
|
+
export interface MinDistanceOptions {
|
|
38
|
+
/** Triangles per BVH leaf. Default 8, matching `buildMeshBvh`. */
|
|
39
|
+
readonly leafSize?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Stop as soon as a distance at or below this is found. Use 0 to stop on
|
|
42
|
+
* first contact when only "do they touch" matters; leave unset for the exact
|
|
43
|
+
* distance.
|
|
44
|
+
*/
|
|
45
|
+
readonly earlyExitAtOrBelow?: number;
|
|
46
|
+
}
|
|
47
|
+
/** A node pair waiting to be explored, with its lower bound on any pair beneath it. */
|
|
48
|
+
export interface PairEntry {
|
|
49
|
+
na: BvhNode;
|
|
50
|
+
nb: BvhNode;
|
|
51
|
+
lowerSq: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Binary min-heap over `lowerSq`. Exported for its own tests but deliberately
|
|
55
|
+
* NOT re-exported from `contact/index.ts`, so it stays out of the package's
|
|
56
|
+
* public surface: it is an implementation detail of the traversal, and a
|
|
57
|
+
* hand-rolled sift is precisely the kind of code that needs a direct contract
|
|
58
|
+
* test rather than being covered incidentally.
|
|
59
|
+
*
|
|
60
|
+
* Note that a broken heap order does NOT break the RESULT — the traversal
|
|
61
|
+
* still visits every unpruned pair and still finds the true minimum, just
|
|
62
|
+
* slower. That is why the distance tests cannot catch an inverted comparison
|
|
63
|
+
* here, and why this class is tested on its own terms.
|
|
64
|
+
*
|
|
65
|
+
* Small and local on purpose: this is the only
|
|
66
|
+
* priority queue in the package, and pulling in a dependency (or a generic
|
|
67
|
+
* implementation) for one traversal would be more surface than the ~25 lines
|
|
68
|
+
* it replaces.
|
|
69
|
+
*/
|
|
70
|
+
export declare class PairHeap {
|
|
71
|
+
private readonly items;
|
|
72
|
+
push(entry: PairEntry): void;
|
|
73
|
+
pop(): PairEntry | undefined;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Exact minimum distance between two meshes, or `null` when either has no
|
|
77
|
+
* triangles (there is no distance to a mesh that is not there — deliberately
|
|
78
|
+
* distinct from returning 0, which would read as "they touch").
|
|
79
|
+
*/
|
|
80
|
+
export declare function minDistanceBetweenMeshes(a: Mesh, b: Mesh, opts?: MinDistanceOptions): MeshDistance | null;
|
|
81
|
+
/**
|
|
82
|
+
* Same, for callers that already hold the BVHs. Worth having separately: the
|
|
83
|
+
* BVH is the expensive part, and a measure tool asking about one element
|
|
84
|
+
* against several others should build each mesh's tree once.
|
|
85
|
+
*/
|
|
86
|
+
export declare function minDistanceBetweenBvhs(a: MeshBvh, b: MeshBvh, opts?: MinDistanceOptions): MeshDistance | null;
|
|
87
|
+
//# sourceMappingURL=min-distance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"min-distance.d.ts","sourceRoot":"","sources":["../../src/contact/min-distance.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAI3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,KAAK,EAAQ,IAAI,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+BAA+B;IAC/B,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,+BAA+B;IAC/B,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,kDAAkD;IAClD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC;AAmBD,uFAAuF;AACvF,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IAEzC,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAY5B,GAAG,IAAI,SAAS,GAAG,SAAS;CAoB7B;AAUD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,CAAC,EAAE,IAAI,EACP,CAAC,EAAE,IAAI,EACP,IAAI,GAAE,kBAAuB,GAC5B,YAAY,GAAG,IAAI,CAIrB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,CAAC,EAAE,OAAO,EACV,CAAC,EAAE,OAAO,EACV,IAAI,GAAE,kBAAuB,GAC5B,YAAY,GAAG,IAAI,CAsIrB"}
|