1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Bounds on walks over file-supplied entity references, plus the
//! large-coordinate threshold every crate gates the RTC re-base on
//! ([`LARGE_COORD_THRESHOLD_METERS`]).
//!
//! A cap bounds one path's LENGTH only. A walk that can revisit also needs a
//! cycle guard, and one that fans out over a DAG also needs a work budget; the
//! three are not interchangeable and choosing wrongly fails silently in both
//! directions. The rule for picking, and the scope rule for the visited set,
//! are in AGENTS.md under "Bounding walks over file-supplied references" —
//! there rather than here, because whoever needs it is adding a NEW walk
//! somewhere else and will not open this file.
/// Maximum `IfcMappedItem` → `IfcRepresentationMap` → `MappedRepresentation.Items`
/// nesting any walk in this workspace will follow.
///
/// Shared because three crates walk the SAME chain and their bounds must agree:
/// `ifc_lite_processing::element`, `ifc_lite_geometry::router::processing` and
/// `ifc-lite-wasm`'s styling colour resolver.
///
/// Disagreement fails SILENTLY, which is why it is worth making structural. A
/// mid-review revision of #2864 held 16 against the router's 32: an element
/// whose chain was 17 to 32 links long would have rendered its geometry and
/// quietly lost its authored colour. That was caught before merge and never
/// shipped — the point is that nothing except a reviewer's attention was
/// stopping it.
///
/// `ifc_lite_processing::symbolic::item_walk::MAX_ITEM_DEPTH` also walks this
/// chain and asserts equality with this constant, but deliberately keeps its
/// own name: it charges `depth + 1` for `IfcGeometricSet` elements and
/// `IfcCompositeCurve` segments as well, so an equal number does not mean equal
/// reach. It is never more permissive than this one.
///
/// A walk over this chain also needs a cycle guard; the cap alone is not
/// sufficient (see the module docs above for which kind).
pub const MAX_MAPPED_ITEM_DEPTH: u32 = 32;
/// Maximum `IfcLocalPlacement.PlacementRelTo` chain any walk in this workspace
/// will follow. A chain longer than this composes only its first
/// `MAX_PLACEMENT_DEPTH + 1` placements and the rest is dropped — silently, on
/// every site that uses it.
///
/// Shared because two walks follow the SAME attribute of the SAME entity and
/// their bounds must agree: `ifc_lite_geometry::router::transforms` (the mesh
/// path) and `ifc_lite_geometry::profile_extractor` (the 2D drawing path).
/// They disagreed — 32 against 100 — and because both exceed-branches return
/// the IDENTITY rather than an error, an element on a 33-to-101-link chain was
/// drawn in two different places by the two paths with nothing reported. #2873
///
/// ## Why 100 and not 32
///
/// The 32 carried the rationale "keep low for WASM — each frame uses ~2KB+ of
/// stack". The wasm bundles are linked with `-zstack-size=8388608`
/// (`.cargo/config.toml` and both extra bundles in `scripts/build-wasm.sh`), so
/// the budget is 8 MiB and the cap's own worst case is ~0.1% of it; native
/// hosts give the geometry pool 256 MiB (`rust/ffi`, `rust/python`). Measured
/// rather than assumed: see the frame figures in #2873. `PlacementRelTo` is a
/// single reference, so this walk has fan-out 1 and costs O(depth) — the
/// breadth blow-up that makes a depth cap the wrong instrument elsewhere (see
/// AGENTS.md) does not apply here.
///
/// The two candidates are not symmetric. Equalising UP lets the mesh path
/// compose chains it currently flattens; equalising DOWN would make the 2D path
/// start flattening chains it composes correctly today, silently. Across the
/// 111 fixtures in `tests/models` that contain placements the deepest chain is
/// 7 links, so neither value binds on any file in the corpus and the choice is
/// decided entirely by which failure is worse on a file that does exceed it.
///
/// This bounds one path's LENGTH only. A walk over a chain that can revisit
/// also needs a cycle guard; see the module docs above.
pub const MAX_PLACEMENT_DEPTH: usize = 100;
/// The large-coordinate threshold (metres): a world coordinate whose magnitude
/// exceeds this needs RTC re-basing before it is cast to f32, or the model
/// renders with vertex jitter.
///
/// Shared because every crate that decides "is this model far from the
/// origin" must draw the line in the same place: a copy that drifts fails
/// silently (one side re-bases, the other does not, and the two frames differ
/// by the whole offset). It lives in `ifc_lite_core` because core's own
/// placement-bounds scan needs it and cannot import from above;
/// `ifc_lite_geometry` re-exports it under the same name.
///
/// Prefer [`coord_is_large`] to comparing against this directly, so the
/// comparison (strictly greater, any axis, absolute value) also has one home.
///
/// ## Why 1 km and not 10 km (#4934)
///
/// The line is precision-derived, not distance-derived: it is the point past
/// which the f32 store the mesh path casts into loses more than a BIM
/// tolerance. f32 has a 24-bit mantissa, so its ULP near `x` is
/// `2^(exponent(x) - 23)`; at 1 km that is ~0.061 mm, and at the old 10 km
/// line it is ~0.5-1 mm — comparable to the flush-face tolerances authoring
/// tools actually hit, so two faces meant to be coincident round to
/// *different* f32 lattice nodes and z-fight (#4934's report: 6.3 km survey
/// coordinates, ~0.26 mm ULP, visible speckle over every join). 1 km keeps
/// worst-case quantization at a fraction of a millimetre while matching the
/// existing `NEAR_ORIGIN` band in `rust/geometry/src/router/rtc_offset.rs`,
/// so a model just inside the line and the detector's own "already near the
/// origin" judgment agree.
pub const LARGE_COORD_THRESHOLD_METERS: f64 = 1000.0;
/// True when any component of `point` (metres) lies strictly beyond
/// [`LARGE_COORD_THRESHOLD_METERS`] from the origin.
/// What an RTC detector concluded about a model's coordinates. The anchor and
/// the decision travel together because the decision is not a property of the
/// anchor: the placement-bounds scan decides on the bbox CORNERS and answers
/// with the bbox CENTRE, which can be inside the threshold (8.5 km centre, 15 km
/// corner) or even the origin, while the coordinates still need re-basing.