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
|
# track downsampled state
|
||||||
self._in_ds = r._in_ds
|
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 (
|
return (
|
||||||
True,
|
True,
|
||||||
(ivl, ivr),
|
(ivl, ivr),
|
||||||
graphics,
|
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(
|
def reset_graphics(
|
||||||
self,
|
self,
|
||||||
|
|
||||||
|
|
@ -1070,6 +1141,14 @@ class Viz(Struct):
|
||||||
self.update_graphics(force_redraw=True)
|
self.update_graphics(force_redraw=True)
|
||||||
self._mxmn_cache_enabled = 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(
|
def draw_last(
|
||||||
self,
|
self,
|
||||||
array_key: str | None = None,
|
array_key: str | None = None,
|
||||||
|
|
|
||||||
|
|
@ -211,9 +211,9 @@ async def increment_history_view(
|
||||||
):
|
):
|
||||||
hist_chart: ChartPlotWidget = ds.hist_chart
|
hist_chart: ChartPlotWidget = ds.hist_chart
|
||||||
hist_viz: Viz = ds.hist_viz
|
hist_viz: Viz = ds.hist_viz
|
||||||
# viz: Viz = ds.viz
|
viz: Viz = ds.viz
|
||||||
assert 'hist' in hist_viz.shm.token['shm_name']
|
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
|
# TODO: seems this is more reliable at keeping the slow
|
||||||
# chart incremented in view more correctly?
|
# chart incremented in view more correctly?
|
||||||
|
|
@ -250,27 +250,27 @@ async def increment_history_view(
|
||||||
# - samplerd could emit the actual update range via
|
# - samplerd could emit the actual update range via
|
||||||
# tuple and then we only enter the below block if that
|
# tuple and then we only enter the below block if that
|
||||||
# range is detected as in-view?
|
# range is detected as in-view?
|
||||||
# match msg:
|
match msg:
|
||||||
# case {
|
case {
|
||||||
# 'backfilling': (viz_name, timeframe),
|
'backfilling': (viz_name, timeframe),
|
||||||
# } if (
|
} if (
|
||||||
# viz_name == name
|
viz_name == name
|
||||||
# ):
|
):
|
||||||
# log.warning(
|
log.warning(
|
||||||
# f'Forcing HARD REDRAW:\n'
|
f'Forcing HARD REDRAW:\n'
|
||||||
# f'name: {name}\n'
|
f'name: {name}\n'
|
||||||
# f'timeframe: {timeframe}\n'
|
f'timeframe: {timeframe}\n'
|
||||||
# )
|
)
|
||||||
# # TODO: only allow this when the data is IN VIEW!
|
# TODO: only allow this when the data is IN VIEW!
|
||||||
# # also, we probably can do this more efficiently
|
# also, we probably can do this more efficiently
|
||||||
# # / smarter by only redrawing the portion of the
|
# / smarter by only redrawing the portion of the
|
||||||
# # path necessary?
|
# path necessary?
|
||||||
# {
|
{
|
||||||
# 60: hist_viz,
|
60: hist_viz,
|
||||||
# 1: viz,
|
1: viz,
|
||||||
# }[timeframe].update_graphics(
|
}[timeframe].update_graphics(
|
||||||
# force_redraw=True
|
force_redraw=True
|
||||||
# )
|
)
|
||||||
|
|
||||||
# check if slow chart needs an x-domain shift and/or
|
# check if slow chart needs an x-domain shift and/or
|
||||||
# y-range resize.
|
# y-range resize.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue