The parts of a per-geometry refinement wrapper that are not per-geometry.
Points, Lines, GSplats and Mesh each have a lod-refinement.ts wrapping the
generic loop in ./refinement.ts. All four were 83–91% identical after name
normalisation — 965 lines carrying one copy each of the admission gate, the
abort/rollback/backoff/toast catch block, the residency bookkeeping and the
four loop-progress callbacks. Only the load-and-commit body genuinely differs,
and it differs in two shapes: Points and Mesh commit the loaded data
directly, while Lines and GSplats have an async process step in between.
So this module deliberately does NOT abstract over that body. Parameterising
the commit shape would trade four readable flows for one function with a
strategy argument, and the flow is the part a reader comes here to follow.
What it extracts is only what was character-for-character identical modulo
the geometry's own name:
admitRefinementCandidate — the hasMoreLODs / exhausted-backoff /
live-visibility / residency-admission gate, whose four parts must agree with the four
exclusions in makeRefinementProgressCallbacks or the loop re-offers
a declined loader every frame while holding the update lock.
handleRefinementError — abort-versus-failure classification, pass
rollback, backoff counting, and the user-facing toast on exhaustion.
makeRefinementProgressCallbacks — getLoaderProgress,
anyHasMoreLODs, onNoProgress and onError, which differed only in the
geometry label inside their log lines.
Keeping these in one place is also what the project's cross-geometry symmetry
rule asks for: a fix to one geometry's refinement becomes a fix to all four,
rather than three follow-ups that may or may not get written.
The parts of a per-geometry refinement wrapper that are not per-geometry.
Points, Lines, GSplats and Mesh each have a
lod-refinement.tswrapping the generic loop in./refinement.ts. All four were 83–91% identical after name normalisation — 965 lines carrying one copy each of the admission gate, the abort/rollback/backoff/toast catch block, the residency bookkeeping and the four loop-progress callbacks. Only the load-and-commit body genuinely differs, and it differs in two shapes: Points and Mesh commit the loaded data directly, while Lines and GSplats have an async process step in between.So this module deliberately does NOT abstract over that body. Parameterising the commit shape would trade four readable flows for one function with a strategy argument, and the flow is the part a reader comes here to follow. What it extracts is only what was character-for-character identical modulo the geometry's own name:
hasMoreLODs/ exhausted-backoff / live-visibility / residency-admission gate, whose four parts must agree with the four exclusions in makeRefinementProgressCallbacks or the loop re-offers a declined loader every frame while holding the update lock.finallymeasurement.getLoaderProgress,anyHasMoreLODs,onNoProgressandonError, which differed only in the geometry label inside their log lines.Keeping these in one place is also what the project's cross-geometry symmetry rule asks for: a fix to one geometry's refinement becomes a fix to all four, rather than three follow-ups that may or may not get written.