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
parent
48493e50b0
commit
90b817eb69
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in New Issue