feat(core): version fields and cache derived contour geometry#15
Merged
Conversation
Contour geometry was still built on the thread that asked for a figure, so a colour or line-width edit re-ran marching squares over the whole grid, and the level policy, the field statistics it depended on, and the geometry itself were resolved together with no way to reuse any of them. Split derived geometry into three stages. A field provider owns the grid and its cheap summary; the main thread resolves a `ContourSpec` into absolute levels; a worker turns a neutral `ScalarGrid2D` plus those levels into segments and receives no spec, summary or estimate. Style is applied when the figure is assembled, so it never enters a cache key and a restyle reuses cached geometry. `FieldVersion` is a session-only monotonic token owned by `ComputeService`. It covers everything that changes marching-squares input — values, shape, bounds, sampling — and nothing that does not, and it is neither persisted nor part of undo. Estimates and geometry are addressed by content, so a stale worker result cannot overwrite newer state and no traversal-based invalidation is needed. Both caches are bounded and never evict a key whose work is still in flight. Noise and background estimates are computed on demand by `EstimateField` and memoized per estimator selection, so a field pays only for the estimator it actually uses. A zero scale is a valid, cacheable measurement rather than a failure, which stops a flat field from re-queueing an identically failing job on every rebuild; an estimator that genuinely cannot run stays an error that reaches the status line. Field capabilities now derive from a cheap representation query — shape and per-axis sampling — so a descriptor lookup no longer materializes the whole grid, and `regular` still comes from the actual sampling rather than a domain guess. An explicit contour threshold above the field's peak is obeyed literally and reported with both the threshold and the peak, rather than silently drawing nothing or being rewritten to a value that happens to produce output.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Separates a 2D contour into the three things it actually is — the field data, the
level policy resolved against it, and the geometry a worker builds — and gives
each a lifetime of its own. Contour geometry is no longer built on the thread
that asks for a figure: the main thread resolves a
ContourSpecinto absolutelevels, a worker turns a neutral scalar grid plus those levels into segments, and
style is applied only when the figure is assembled.
The practical effect is that changing a contour colour or line width reuses
cached geometry instead of re-running marching squares over the whole grid, and
that a field's noise or background estimate is measured once and shared rather
than recomputed per plot.
What changed
Three-stage derived geometry
resolves
ContourSpec+ summary + estimate intoResolvedContourLevels. Theworker receives only
ScalarGrid2D+ those levels — no spec, no summary, noestimate crosses the thread boundary.
ContourStyleis applied during figure assembly, so it is not part of anycache key. Two series that resolve to the same levels on the same field version
share one geometry regardless of how they are styled.
Runtime field versions
FieldVersionis a session-only monotonic token owned byComputeService. Itadvances for everything that changes marching-squares input — values, shape,
bounds, sampling — and for nothing that does not. It is neither serialized nor
part of undo; persisted provenance lives in the field catalog instead.
VersionedFieldRef. A worker result whose field has since advanced isdiscarded rather than installed, so a setting changed mid-flight cannot be
overwritten by the result it superseded, and stale entries need no
traversal-based invalidation.
cap, estimates and summaries by entry count — and neither evicts a key whose
work is still in flight.
On-demand typed estimates
EstimateFieldcomputes noise and background estimates on demand, memoized per(field version, kind, estimator selection), so a field that only uses a noiseσ never pays for a background plane fit. A new encoding freezes the estimator
identity it was created with, so reopening a project cannot silently change
levels because the algorithm was upgraded.
ideal synthetic field previously re-queued an identically failing job on every
rebuild and stayed blank; it now resolves once. An estimator that genuinely
cannot run remains an error that reaches the status line.
Capability derivation
per-axis sampling — so a descriptor lookup no longer materializes the whole
grid.
regularstill comes from the field's actual sampling, and an explicitlysampled grid still does not qualify for contour or heatmap. A test asserts the
cheap query and the materialized payload can never disagree.
Blank plots explain themselves
than rewritten to a value that happens to produce output, and the resulting
empty half is reported with both the threshold and the peak — so a mistyped
value is visible instead of leaving a blank plot with no cause to find.
Compatibility
Pre-release: no backward compatibility is kept for older project files. The
project schema version is unchanged.
FieldVersionand the derived caches areruntime state and never appear in a saved project.
Out of scope
DOSY and ILT analysis maps keep their existing generation-based staleness model.
They are independent analysis results rather than field-derived geometry, and
migrating them belongs with giving them a field identity of their own. A unified
parameter catalog and the contour parameter editing UI are likewise not part of
this change.
Testing
cargo pr-checkpasses: formatting, the source-size limit, dependency policy,Clippy with warnings denied, and the test suite on both the reference and
DataFusion backends.
New tests cover style edits reusing cached geometry, equivalent levels sharing a
geometry key, a new field version missing old entries and rejecting stale worker
results, estimates coexisting per estimator selection, capability derivation
following actual sampling across every dataset variant, colored rasters carrying
an import version but no scalar summary or contour path, LRU eviction under both
budgets including an oversized entry and re-queueing after eviction, a degenerate
scale estimate resolving once instead of re-queueing, and an unreachable
threshold reporting both numbers.
Two probes back the claims that are otherwise untestable: a thread-local counter
asserts no marching squares runs on the calling thread, with a positive control
that fails if the instrumentation is ever dropped, and a payload counter asserts
a warm geometry cache allocates no full-grid buffer.