Luxar Viewer API Documentation - v2026.9.22
    Preparing search index...

    Function gsplatsContinuousDimTolerance

    • GSplats hidden CONTINUOUS-dimension tolerance: a float-safety epsilon (max(1e-3 × step, T × 1e-5), where T is the node's truncation radius — so max(1e-3 × step, 2.75e-5) at the default T), NOT a query reach.

      The reach is already baked into the data. compute_chunk_bounds_gsplats expands each of a chunk's AABB dims by sqrt(covariance[d,d]) × coverage_sigma with coverage_sigma set to the node's own truncation_radius, and the hidden-dim cutoff on the read side is that same radius: the projection kernel (wasm/typescript/gsplats-processing.ts::project_gsplats_nd_to_3d and its Rust twin) attenuates by (e^{-m²/2} − c)/(1 − c) with c = e^{-T²/2}, clamped at 0, which is exactly 0 at m = T = truncation_radius. (The shader's uTruncate/uTruncateSq discard is the DISPLAYED-dim cutoff and never sees a hidden dim.) So a chunk that fails a zero-tolerance test contains only splats the projection would attenuate to nothing. This value is a chunk-FETCH reach only: per-splat hidden-dim visibility comes from that projection math, never from this array — see data-processor-gsplats.ts.

      compute_chunk_bounds_gsplats applies the σ expansion to every dim NOT in its slice_dims argument, and gives the ones that ARE the fixed _BARRIER_BOUND_EPS pad plus any encoder coordinate round-trip slack. So "bounds already carry truncation_radius · σ" is a statement about the dims OUTSIDE the write side's barrier set, and this arm is only the right rule for exactly those dims — a barrier dim has tight bounds and wants the (far wider) quarter-cell reach instead. Which dims those are is settled by isBarrierDim, from the set the writer publishes; see its docstring for the resolution rule, the legacy discrete fallback and the write-side misclassification it does NOT fix.

      One consequence worth naming here: the #1183 change to this function REMOVED A MASK for that write-side risk on a store with no published set. The previous step × 3.0 rule (a bare 3.0 when dimension metadata was absent) was wide enough to cover a mis-detected barrier dim's tight bounds, so the mis-detection stayed invisible; a 1e-3 × step epsilon does not.

      Two reasons, and the formula has exactly one term for each:

      1. GSPLATS_CONTINUOUS_EPS_STEP_FRACTION × step — a continuous dim along which the splats have zero variance (a stacked axis declared continuous rather than discrete) gets no σ expansion, so its stored bound is the axis value plus any encoder coordinate round-trip slack. The dominant perturbation is that the bound is stored as float32 (chunk_bounds is dtype=np.float32) while the query position is a float64 — ≈1.9e-7 of disagreement at a coordinate of 5.3. The start + k × step arithmetic drift in the query position is real but secondary (≈9e-16 there). At tolerance 0 membership would be an exact float comparison and could silently select nothing.
      2. regularizedHiddenBand — the read side regularizes a degenerate pivot, so such a splat still renders out to T × 1e-5 in that dim's units (≈2.75e-5 at the default T). Term 1 alone falls below that whenever step < T × 1e-2 (a physical-unit axis — 10 ms frames at step = 0.01 — gives 1e-5 against a 2.75e-5 band), so splats the kernel renders at up to ~60% of full brightness (the attenuation at the 1e-5 window edge, i.e. 1 σ of the regularized pivot) would not be fetched.

      Two regimes, with a single crossover at step = T × 1e-2 (2.75e-2 at the default T):

      • step ≥ T × 1e-2 → term 1, 1e-3 × step (the _BARRIER_BOUND_EPS mirror).
      • step < T × 1e-2 → the regularization band, ABSOLUTE in the dim's units.

      Below the crossover the epsilon is therefore many CELLS wide — ≈27.5 cells at step = 1e-6, ≈27500 at step = 1e-9 — and that is deliberate, not an oversight. It is what the renderer actually shows: the regularization floor behind term 2 is an absolute variance backstop, so the rendered band does not shrink when the declared step does, and a cap at some fraction of a cell would hide renderable content from the query. (A cap was tried and removed in review: at step = 1e-6 a 0.25 × step ceiling gives 2.5e-7 while the renderer shows content out to 2.75e-5, so a splat ~10 nav steps away renders at ~60% brightness with its chunk never fetched — a NARROWING even versus the old step × 3 rule. The quarter-cell rationale belongs to the DISCRETE arm, where over-reach bleeds a neighbouring CATEGORY; a continuous axis has no categories, so a wide window there is a bandwidth question only. This file already hands mesh a FULL cell for a continuous dim, so a sub-cell ceiling is not a rule of the file.)

      The ordering property that holds at EVERY step: the epsilon is never smaller than the band a degenerate hidden dim can render in, so the query can never miss renderable content on such an axis. Below the crossover the absolute cost is bounded by T × 1e-5 in the dimension's own units — negligible as a distance, however many cells it spans. That bounds the DISTANCE, not the fraction of the node fetched: when the dim's REAL σ is micro-scale too (step = σ = 1e-9, a metre-declared axis carrying nanometre structure) the window the data needs is T · σ = 2.75e-9, so the epsilon over-fetches by ~1e4 and can pull the whole node. That is the one regime where this is worse than the old step × 3, and the fix there is the σ-plumbing named below, not a different constant.

      The real long-term fix is not a tolerance formula: make the kernel's regularization floor SCALE-AWARE for a degenerate hidden dim (anchored to the dimension's own step or extent, so the rendered band shrinks with the axis), or plumb the splats' actual σ per hidden dim into the query so the window is measured rather than bounded. Until then the tolerance cannot be both narrow and complete, and this errs on complete.

      KNOWN LIMITS. The first two are "your axis is mis-declared", not "widen this epsilon" (widening it reintroduces the over-fetch it replaces); the third is "do not author that node":

      • Coordinates far from the origin. No longer the write side's doing: since #1655 io/_ordering/gsplats.py::compute_chunk_bounds_gsplats accumulates chunk_centers ± extents in float64 and narrows it to the float32 store with OUTWARD rounding (_store_outward_f32_array), exactly as the points builder does — so an expansion smaller than half a float32 ULP is widened to a full ULP instead of being discarded, and a small σ_d on a large coordinate no longer behaves like zero variance. (It used to, and a store written before that fix still carries the tight bounds: with T = 2.75, any σ_d ≲ (1.1–2.2)e-8 × |coord| rounded away entirely.) What remains is the stored COORDINATE, not the pad — a dim with genuinely zero variance still gets a zero-width bound (the outward step has nothing to widen), sitting on a float32 value while the query position is a float64 start + k × step, and the float32 spacing at |coord| is |coord| × 1.2e-7. So a dim whose coordinates sit more than ~8000 steps from the origin can still fall outside term 1's window. Re-origin the axis, or declare it discrete.
      • A second CONTINUOUS-hidden dim with very large σ. The regularization floor is RELATIVE — sqrt(maxDiag × CHOLESKY_RELATIVE_EPSILON), i.e. σ_max × 1e-6 — whenever the hidden block is not all-zero. That block is the marginal over continuousHiddenDims ONLY (computeMarginalCholesky is called with exactly that array, and classifyHiddenDims keeps a scene-discrete dim out of it), so a scene-discrete hidden dim's σ can never raise this floor however large it is — the other dim has to be continuous-hidden too. A node with a degenerate dim and another such dim of large σ therefore renders a band of T × σ_max × 1e-6, which exceeds this epsilon once σ_max > max(1e-3 × step / (T × 1e-6), 10) in that dim's units. Both branches in closed form: where term 1 dominates it is 1000 × step / T (≈363.6 × step at the default T = 2.75, ≈166.7 × step at T = 6 — it SHRINKS as the radius grows, because the epsilon it is compared against grows), and in the band regime it is (T × 1e-5) / (T × 1e-6) = 10, independent of T entirely. (The two agree exactly at the crossover step = T × 1e-2 for any T: there 1000 × step / T = 10.) Not covered here on purpose: covering it would make the fetch window depend on an unrelated axis's extent.
      • An extreme but VALID truncation_radius fetches the whole node. The band term is T × 1e-5 with no ceiling, so a node stamping T = 1e18 gets a window of ~1e13 in that dim's units — the debug query log prints it as ∞ (formatTolerance) and effectively every chunk matches. This is not a hole in the guard above: 1e18 is finite, clampTruncationRadius passes it (its upper bound is sqrt(float32.max) ≈ 1.84e19, rendering/materials/gsplat/math.ts) and so does the WRITE side (MAX_TRUNCATION_RADIUS_FLOAT32, the same value, in packages/luxar/src/luxar/validation/types.py::validate_truncation_radius), so it is a legal authored value on both sides of the contract. Capping the band here would be the wrong fix: the MATERIAL draws that band too, so a cap re-creates exactly the fetch-narrower-than-render under-fetch #1655 item 3 removed. The window is following the renderer faithfully; the remedy is not to author a radius three orders of magnitude past anything physical (the default is GSPLAT_DEFAULT_TRUNCATION_RADIUS = 2.75, and the lower bound MIN_TRUNCATION_RADIUS ≈ 2.44e-4 lives beside the upper one).

      A third limit was closed rather than documented: a node with a much larger truncation_radius than the default used to be scored against the DEFAULT band (this function saw only a DimensionInfo), leaving the window (2.75e-5, T×1e-5] uncovered for a node stamping a bigger T. truncationRadius now carries the node's own value, so the band term scales with it.

      Parameters

      • dimInfo: DimensionInfo | undefined

        Metadata for the dimension (only step is read).

      • OptionaltruncationRadius: number

        The node's truncation_radius, already through clampTruncationRadius (see ToleranceOptions.truncationRadius); omitted ⇒ GSPLAT_DEFAULT_TRUNCATION_RADIUS.

      Returns number

      The epsilon in that dimension's units.