Use `Viz.draw_last()` inside `.update_graphics()`

In an effort to ensure uniform and uppx-optimized last datum graphics
updates call this method directly instead of the equivalent graphics
object thus ensuring we only update the last pixel column according with
the appropriate max/min computed from the last uppx's worth of data.

Fixes / improvements to enable `.draw_last()` usage include,
- change `Viz._render_table` -> `._alt_r: tuple[Renderer, pg.GraphicsItem] | None`
  which holds an alternative (usually downsampled) render and graphics
  obj.
- extend the `.draw_last()` signature to include:
  - `last_read` to allow passing in the already read data from
    `.update_graphics()`, if it isn't passed then a manual read is done
    internally.
  - `reset_cache: bool` which is passed through to the graphics obj.
- use the new `Formatter.flat_index_ratio: float` when indexing into xy
  1d data to compute the max/min for that px column.

Other,
- drop `bars_range` input from `maxmin()` since it's unused.
multichartz
Tyler Goodlet 2023-01-17 17:13:29 -05:00
parent b762cf0456
commit 65434e2e67
1 changed files with 70 additions and 47 deletions

View File

@ -132,9 +132,9 @@ def render_baritems(
# baseline "line" downsampled OHLC curve that should # baseline "line" downsampled OHLC curve that should
# kick on only when we reach a certain uppx threshold. # kick on only when we reach a certain uppx threshold.
self._render_table = (ds_curve_r, curve) self._alt_r = (ds_curve_r, curve)
ds_r, curve = self._render_table ds_r, curve = self._alt_r
# print( # print(
# f'r: {r.fmtr.xy_slice}\n' # f'r: {r.fmtr.xy_slice}\n'
@ -270,11 +270,11 @@ class Viz(msgspec.Struct): # , frozen=True):
_index_step: float | None = None _index_step: float | None = None
# map from uppx -> (downsampled data, incremental graphics) # map from uppx -> (downsampled data, incremental graphics)
_src_r: Optional[Renderer] = None _src_r: Renderer | None = None
_render_table: dict[ _alt_r: tuple[
Optional[int], Renderer,
tuple[Renderer, pg.GraphicsItem], pg.GraphicsItem
] = (None, None) ] | None = None
# cache of y-range values per x-range input. # cache of y-range values per x-range input.
_mxmns: dict[ _mxmns: dict[
@ -329,11 +329,6 @@ class Viz(msgspec.Struct): # , frozen=True):
def maxmin( def maxmin(
self, self,
# TODO: drop this right?
bars_range: Optional[tuple[
int, int, int, int, int, int
]] = None,
x_range: slice | tuple[int, int] | None = None, x_range: slice | tuple[int, int] | None = None,
use_caching: bool = True, use_caching: bool = True,
@ -366,11 +361,7 @@ class Viz(msgspec.Struct): # , frozen=True):
rbar, rbar,
_, _,
r, r,
) = ( ) = self.datums_range()
# TODO: drop this yah?
bars_range
or self.datums_range()
)
profiler(f'{self.name} got bars range') profiler(f'{self.name} got bars range')
x_range = lbar, rbar x_range = lbar, rbar
@ -816,18 +807,29 @@ class Viz(msgspec.Struct): # , frozen=True):
with graphics.reset_cache(): with graphics.reset_cache():
graphics.path = r.path graphics.path = r.path
graphics.fast_path = r.fast_path graphics.fast_path = r.fast_path
self.draw_last(
array_key=array_key,
last_read=read,
reset_cache=reset_cache,
)
else: else:
# assign output paths to graphicis obj # assign output paths to graphicis obj
graphics.path = r.path graphics.path = r.path
graphics.fast_path = r.fast_path graphics.fast_path = r.fast_path
graphics.draw_last_datum( self.draw_last(
path, array_key=array_key,
src_array, last_read=read,
reset_cache, reset_cache=reset_cache,
array_key, )
index_field=self.index_field, # graphics.draw_last_datum(
) # path,
# src_array,
# reset_cache,
# array_key,
# index_field=self.index_field,
# )
graphics.update() graphics.update()
profiler('.update()') profiler('.update()')
@ -845,7 +847,9 @@ class Viz(msgspec.Struct): # , frozen=True):
def draw_last( def draw_last(
self, self,
array_key: Optional[str] = None, array_key: str | None = None,
last_read: tuple | None = None,
reset_cache: bool = False,
only_last_uppx: bool = False, only_last_uppx: bool = False,
) -> None: ) -> None:
@ -854,17 +858,11 @@ class Viz(msgspec.Struct): # , frozen=True):
( (
xfirst, xlast, src_array, xfirst, xlast, src_array,
ivl, ivr, in_view, ivl, ivr, in_view,
) = self.read() ) = last_read or self.read()
g = self.graphics
array_key = array_key or self.name array_key = array_key or self.name
x, y = g.draw_last_datum(
g.path, gfx = self.graphics
src_array,
False, # never reset path
array_key,
self.index_field,
)
# the renderer is downsampling we choose # the renderer is downsampling we choose
# to always try and update a single (interpolating) # to always try and update a single (interpolating)
@ -874,19 +872,28 @@ class Viz(msgspec.Struct): # , frozen=True):
# worth of data since that's all the screen # worth of data since that's all the screen
# can represent on the last column where # can represent on the last column where
# the most recent datum is being drawn. # the most recent datum is being drawn.
uppx = ceil(self._last_uppx)
if ( if (
self._in_ds (self._in_ds or only_last_uppx)
or only_last_uppx and uppx > 0
): ):
dsg = self.ds_graphics or self.graphics alt_renderer = self._alt_r
if alt_renderer:
renderer, gfx = alt_renderer
fmtr = renderer.fmtr
x = fmtr.x_1d
y = fmtr.y_1d
else:
renderer = self._src_r
fmtr = renderer.fmtr
x = fmtr.x_1d
y = fmtr.y_1d
if alt_renderer:
uppx *= fmtr.flat_index_ratio
# XXX: pretty sure we don't need this?
# if isinstance(g, Curve):
# with dsg.reset_cache():
uppx = round(self._last_uppx)
y = y[-uppx:] y = y[-uppx:]
ymn, ymx = y.min(), y.max() ymn, ymx = y.min(), y.max()
# print(f'drawing uppx={uppx} mxmn line: {ymn}, {ymx}')
try: try:
iuppx = x[-uppx] iuppx = x[-uppx]
except IndexError: except IndexError:
@ -894,16 +901,32 @@ class Viz(msgspec.Struct): # , frozen=True):
# datum index. # datum index.
iuppx = x[0] iuppx = x[0]
dsg._last_line = QLineF( gfx._last_line = QLineF(
iuppx, ymn, iuppx, ymn,
x[-1], ymx, x[-1], ymx,
) )
# print(f'updating DS curve {self.name}') # if self.is_ohlc:
dsg.update() # times = self.shm.array['time']
# time_step = times[-1] - times[-2]
# # if 'hist' in self.shm.token['shm_name']
# # if self.index_step() == 1:
# # breakpoint()
# print(
# f'updating DS curve {self.name}@{time_step}s\n'
# f'drawing uppx={uppx} mxmn line: {ymn}, {ymx}'
# )
else: else:
x, y = gfx.draw_last_datum(
gfx.path,
src_array,
reset_cache, # never reset path
array_key,
self.index_field,
)
# print(f'updating NOT DS curve {self.name}') # print(f'updating NOT DS curve {self.name}')
g.update()
gfx.update()
def default_view( def default_view(
self, self,
@ -1029,7 +1052,7 @@ class Viz(msgspec.Struct): # , frozen=True):
) )
if do_ds: if do_ds:
# view.interaction_graphics_update_cycle() # view.interaction_graphics_cycle()
view.maybe_downsample_graphics() view.maybe_downsample_graphics()
view._set_yrange() view._set_yrange()