Add annot refreshed-positioning to `Viz` iface

Extend `Viz` with dynamic annot repositioning logic in a new
`._reposition_annotations()` method. Try calling it inside
`Viz.update_graphics()/.reset_graphics()` to attempt keeping annots
aligned with underlying data coords.

Also,
- add index-range cache to skip redundant repositioning
- re-enable backfill force-redraw match block in
  `.ui._display.increment_history_view()`
  * uncomment `viz` and `name` bindings for match block use
    to make the above valid.
- claude did some weird `profiler()` as logger thing that we'll need to
  correct, weird how it only did it once and the other was using `log`
  XD

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
refresh_annots
Gud Boi 2026-01-30 19:04:20 -05:00
parent 48493e50b0
commit 90b817eb69
2 changed files with 102 additions and 23 deletions

View File

@ -1031,12 +1031,83 @@ class Viz(Struct):
# track downsampled state
self._in_ds = r._in_ds
# XXX: reposition annotations after graphics update
# to ensure alignment with (potentially changed) data coords
if should_redraw or force_redraw:
n = self._reposition_annotations()
if n:
profiler(f'repositioned {n} annotations')
return (
True,
(ivl, ivr),
graphics,
)
# class-level cache for tracking last repositioned index range
# to avoid redundant repositioning when shm hasn't changed
_annot_index_cache: dict[str, tuple[int, int]] = {}
def _reposition_annotations(
self,
force: bool = False,
) -> int:
'''
Reposition all annotations (arrows, text, rects) that have
stored absolute coordinates to ensure they stay aligned
with viz data after updates/redraws.
Only repositions if shm index range has changed since last
reposition, unless `force=True`.
'''
# check if shm index range changed
arr = self.shm.array
if not arr.size:
return 0
ifirst = arr[0]['index']
ilast = arr[-1]['index']
index_range = (ifirst, ilast)
# skip if range unchanged (unless forced)
cache_key: str = self.name
last_range = self._annot_index_cache.get(cache_key)
if (
not force
and last_range is not None
and last_range == index_range
):
return 0
# cache current range
self._annot_index_cache[cache_key] = index_range
n_repositioned: int = 0
for item in self.plot.items:
# arrows and text items use abs x,y coords
if (
hasattr(item, '_abs_x')
and
hasattr(item, '_abs_y')
):
item.setPos(
item._abs_x,
item._abs_y,
)
n_repositioned += 1
# rects use method + kwargs
elif (
hasattr(item, '_meth')
and
hasattr(item, '_kwargs')
):
getattr(item, item._meth)(**item._kwargs)
n_repositioned += 1
return n_repositioned
def reset_graphics(
self,
@ -1070,6 +1141,14 @@ class Viz(Struct):
self.update_graphics(force_redraw=True)
self._mxmn_cache_enabled = True
# reposition annotations to stay aligned after reset
# (force=True since reset always changes coordinate system)
n = self._reposition_annotations(force=True)
if n:
log.info(
f'Repositioned {n} annotation(s) after reset'
)
def draw_last(
self,
array_key: str | None = None,

View File

@ -211,9 +211,9 @@ async def increment_history_view(
):
hist_chart: ChartPlotWidget = ds.hist_chart
hist_viz: Viz = ds.hist_viz
# viz: Viz = ds.viz
viz: Viz = ds.viz
assert 'hist' in hist_viz.shm.token['shm_name']
# name: str = hist_viz.name
name: str = hist_viz.name
# TODO: seems this is more reliable at keeping the slow
# chart incremented in view more correctly?
@ -250,27 +250,27 @@ async def increment_history_view(
# - samplerd could emit the actual update range via
# tuple and then we only enter the below block if that
# range is detected as in-view?
# match msg:
# case {
# 'backfilling': (viz_name, timeframe),
# } if (
# viz_name == name
# ):
# log.warning(
# f'Forcing HARD REDRAW:\n'
# f'name: {name}\n'
# f'timeframe: {timeframe}\n'
# )
# # TODO: only allow this when the data is IN VIEW!
# # also, we probably can do this more efficiently
# # / smarter by only redrawing the portion of the
# # path necessary?
# {
# 60: hist_viz,
# 1: viz,
# }[timeframe].update_graphics(
# force_redraw=True
# )
match msg:
case {
'backfilling': (viz_name, timeframe),
} if (
viz_name == name
):
log.warning(
f'Forcing HARD REDRAW:\n'
f'name: {name}\n'
f'timeframe: {timeframe}\n'
)
# TODO: only allow this when the data is IN VIEW!
# also, we probably can do this more efficiently
# / smarter by only redrawing the portion of the
# path necessary?
{
60: hist_viz,
1: viz,
}[timeframe].update_graphics(
force_redraw=True
)
# check if slow chart needs an x-domain shift and/or
# y-range resize.