Skip to content

fix(render): refresh trace render when a trace's attributes change#69

Merged
dustenhubbard merged 1 commit into
mainfrom
fix/trace-color-stale-render
Jul 18, 2026
Merged

fix(render): refresh trace render when a trace's attributes change#69
dustenhubbard merged 1 commit into
mainfrom
fix/trace-color-stale-render

Conversation

@dustenhubbard

Copy link
Copy Markdown
Owner

Fixes the beta-reported bug where changing a trace's color updated the selection highlight but left the drawn line in the old color.

Root cause (inherited from upstream, 2023): the incremental field render reuses cached trace objects reconciled only via added_traces/removed_traces; editTraceAttributes copy-replaces the trace, and a table-refresh clearTracking() in between leaves the cache holding the stale object. Highlight draws from the live selection set, hence the mismatch.

Fix: self-healing validation in the incremental branch — cached on-screen traces are checked against ground truth (identity in their contour); any staleness triggers a rebuild. Covers all copy-replace attribute edits (color, name, tags, fill). Selection-only refreshes stay on the fast path (perf-asserted: zero rebuilds).

Deterministic headless pixel repro: 4 new tests fail unpatched, pass patched; full suite green.

🤖 Generated with Claude Code

The incremental field render (TraceLayer.generateTraceLayer with
window_moved=False, used when generate_image=False) redraws from a cached
traces_in_view list of trace OBJECTS, reconciled only by section.added_traces
/ removed_traces. Section.editTraceAttributes - the single funnel for
color/name/tags/fill edits from the trace dialog, trace list, object list,
paste-attributes and bulk/import recolors - REPLACES the trace with a fresh
copy rather than mutating in place. A table refresh calls
section.clearTracking(), emptying the add/remove lists, after which an
incremental render still holds the old, replaced object and draws its stale
color. The selection highlight, drawn from the live selected_traces set,
tracked the new color - producing the reported mismatch (highlight new, line
old). Bug is inherited from upstream, not a fork regression.

Guard the incremental branch: if any cached on-screen trace is no longer a
live member of its contour, fall back to a full tracesAsList() rebuild so
current attributes are always drawn. Only the on-screen subset is checked, so
a selection-only refresh stays on the fast path (no full rebuild), preserving
the large-series render optimization.

Adds tests/test_trace_color_stale_render.py covering two color-change paths
(editTraceAttributes and object-list-style bulk recolor), the exact
line/highlight mismatch, a sibling name-change, and a perf guard asserting
selection-only refreshes never rebuild.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dustenhubbard
dustenhubbard enabled auto-merge (squash) July 18, 2026 02:06
@dustenhubbard
dustenhubbard merged commit de482da into main Jul 18, 2026
1 check passed
@dustenhubbard
dustenhubbard deleted the fix/trace-color-stale-render branch July 18, 2026 02:07
dustenhubbard added a commit that referenced this pull request Jul 18, 2026
Covers the trace-color render fix (#69), Windows undo UTF-8 fix and
checkbox/CI hardening (#70), upstream interop escaping (#71), and the
zarr conversion CPU bound (#72). Moves the zarr note from the beta-3
section (already shipped) into the new beta-4 section.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
dustenhubbard added a commit that referenced this pull request Jul 19, 2026
Objects imported before the autoseg color features baked their colors in at
import time, so they never pick up the current colorblind-safe default (or a
custom) palette. Add "Reapply autoseg colors..." to the object-list / object
context menu to push the CURRENT palette + seed back onto selected objects.

- palette.py: AUTOSEG_TRACE_PREFIX (single source of truth for the
  "autoseg_<id>" import naming), label_id_from_name (recovers the label id from
  an unmodified import name), and palette_color_for_name (reproduces the exact
  import color for a parseable name; falls back to a stable, PYTHONHASHSEED-
  independent zlib.crc32 hash of the name otherwise). conversions.py now builds
  import names from the shared prefix so the two stay in sync.

- series.reapplyAutosegColors: resolves the palette/seed and per-object color
  once, then rewrites colors through the normal per-section editTraceAttributes
  path over only the sections the objects appear on -- one undoable operation,
  correct list/field refresh (reuses the #69 stale-render funnel), no per-object
  full-series scans.

- GUI action goes through the existing object_function wrapper, so locked
  objects are blocked exactly as any other bulk attribute edit, and a confirm
  dialog warns before existing colors are discarded (undo restores them).

Selection is the contract: it composes with the regex/group/invert filters, so
"select all autoseg_* then reapply" works without guessing a name pattern.

Tests: id-parse + hash-fallback determinism, custom palette/seed respected,
recolor matches a fresh import, distinct ids stay distinct, single undo
restores every prior color across sections, zero-selection no-op.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
dustenhubbard added a commit that referenced this pull request Jul 23, 2026
The incremental field render (TraceLayer.generateTraceLayer with
window_moved=False, used when generate_image=False) redraws from a cached
traces_in_view list of trace OBJECTS, reconciled only by section.added_traces
/ removed_traces. Section.editTraceAttributes - the single funnel for
color/name/tags/fill edits from the trace dialog, trace list, object list,
paste-attributes and bulk/import recolors - REPLACES the trace with a fresh
copy rather than mutating in place. A table refresh calls
section.clearTracking(), emptying the add/remove lists, after which an
incremental render still holds the old, replaced object and draws its stale
color. The selection highlight, drawn from the live selected_traces set,
tracked the new color - producing the reported mismatch (highlight new, line
old). Bug is inherited from upstream, not a fork regression.

Guard the incremental branch: if any cached on-screen trace is no longer a
live member of its contour, fall back to a full tracesAsList() rebuild so
current attributes are always drawn. Only the on-screen subset is checked, so
a selection-only refresh stays on the fast path (no full rebuild), preserving
the large-series render optimization.

Adds tests/test_trace_color_stale_render.py covering two color-change paths
(editTraceAttributes and object-list-style bulk recolor), the exact
line/highlight mismatch, a sibling name-change, and a perf guard asserting
selection-only refreshes never rebuild.
dustenhubbard added a commit that referenced this pull request Jul 23, 2026
Covers the trace-color render fix (#69), Windows undo UTF-8 fix and
checkbox/CI hardening (#70), upstream interop escaping (#71), and the
zarr conversion CPU bound (#72). Moves the zarr note from the beta-3
section (already shipped) into the new beta-4 section.
dustenhubbard added a commit that referenced this pull request Jul 23, 2026
Objects imported before the autoseg color features baked their colors in at
import time, so they never pick up the current colorblind-safe default (or a
custom) palette. Add "Reapply autoseg colors..." to the object-list / object
context menu to push the CURRENT palette + seed back onto selected objects.

- palette.py: AUTOSEG_TRACE_PREFIX (single source of truth for the
  "autoseg_<id>" import naming), label_id_from_name (recovers the label id from
  an unmodified import name), and palette_color_for_name (reproduces the exact
  import color for a parseable name; falls back to a stable, PYTHONHASHSEED-
  independent zlib.crc32 hash of the name otherwise). conversions.py now builds
  import names from the shared prefix so the two stay in sync.

- series.reapplyAutosegColors: resolves the palette/seed and per-object color
  once, then rewrites colors through the normal per-section editTraceAttributes
  path over only the sections the objects appear on -- one undoable operation,
  correct list/field refresh (reuses the #69 stale-render funnel), no per-object
  full-series scans.

- GUI action goes through the existing object_function wrapper, so locked
  objects are blocked exactly as any other bulk attribute edit, and a confirm
  dialog warns before existing colors are discarded (undo restores them).

Selection is the contract: it composes with the regex/group/invert filters, so
"select all autoseg_* then reapply" works without guessing a name pattern.

Tests: id-parse + hash-fallback determinism, custom palette/seed respected,
recolor matches a fresh import, distinct ids stay distinct, single undo
restores every prior color across sections, zero-selection no-op.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant