No-overlays, y-ranging optimizations
When the caller passes `do_overlay_scaling=False` we skip the given chart's `Viz` iteration loop, and set the yrange immediately, then continue to the next chart (if `do_linked_charts` is set) instead of a `continue` short circuit within the viz sub-loop. Deats: - add a `_maybe_calc_yrange()` helper which makes the `yranges` provided-or-not case logic more terse (factored). - add a `do_linked_charts=False` short circuit. - drop the legacy commented swing % calcs stuff. - use the `ChartView._viz` when `do_overlay_scaling=False` thus presuming that we want to handle the viz mapped to *this* view box. - add a `._yrange` "last set yrange" tracking var which keeps record of the last ymn/ymx value set in `._set_yrange()` BEFORE doing range margins; this will be used for incremental update in the display loop.log_linearized_curve_overlays
parent
3daee0caa9
commit
c57567ab0d
|
@ -445,6 +445,7 @@ class ChartView(ViewBox):
|
||||||
# TODO: probably just assign this whenever a new `PlotItem` is
|
# TODO: probably just assign this whenever a new `PlotItem` is
|
||||||
# allocated since they're 1to1 with views..
|
# allocated since they're 1to1 with views..
|
||||||
self._viz: Viz | None = None
|
self._viz: Viz | None = None
|
||||||
|
self._yrange: tuple[float, float] | None = None
|
||||||
|
|
||||||
def start_ic(
|
def start_ic(
|
||||||
self,
|
self,
|
||||||
|
@ -483,7 +484,7 @@ class ChartView(ViewBox):
|
||||||
async def open_async_input_handler(
|
async def open_async_input_handler(
|
||||||
self,
|
self,
|
||||||
|
|
||||||
) -> 'ChartView':
|
) -> ChartView:
|
||||||
|
|
||||||
async with (
|
async with (
|
||||||
_event.open_handlers(
|
_event.open_handlers(
|
||||||
|
@ -785,7 +786,7 @@ class ChartView(ViewBox):
|
||||||
|
|
||||||
# NOTE: this value pairs (more or less) with L1 label text
|
# NOTE: this value pairs (more or less) with L1 label text
|
||||||
# height offset from from the bid/ask lines.
|
# height offset from from the bid/ask lines.
|
||||||
range_margin: float | None = 0.09,
|
range_margin: float | None = 0.1,
|
||||||
|
|
||||||
bars_range: Optional[tuple[int, int, int, int]] = None,
|
bars_range: Optional[tuple[int, int, int, int]] = None,
|
||||||
|
|
||||||
|
@ -858,6 +859,11 @@ class ChartView(ViewBox):
|
||||||
|
|
||||||
ylow, yhigh = yrange
|
ylow, yhigh = yrange
|
||||||
|
|
||||||
|
# always stash last range for diffing by
|
||||||
|
# incremental update calculations BEFORE adding
|
||||||
|
# margin.
|
||||||
|
self._yrange = ylow, yhigh
|
||||||
|
|
||||||
# view margins: stay within a % of the "true range"
|
# view margins: stay within a % of the "true range"
|
||||||
if range_margin is not None:
|
if range_margin is not None:
|
||||||
diff = yhigh - ylow
|
diff = yhigh - ylow
|
||||||
|
@ -870,10 +876,6 @@ class ChartView(ViewBox):
|
||||||
yhigh * (1 + range_margin),
|
yhigh * (1 + range_margin),
|
||||||
)
|
)
|
||||||
|
|
||||||
# XXX: this often needs to be unset
|
|
||||||
# to get different view modes to operate
|
|
||||||
# correctly!
|
|
||||||
|
|
||||||
# print(
|
# print(
|
||||||
# f'set limits {self.name}:\n'
|
# f'set limits {self.name}:\n'
|
||||||
# f'ylow: {ylow}\n'
|
# f'ylow: {ylow}\n'
|
||||||
|
@ -975,7 +977,6 @@ class ChartView(ViewBox):
|
||||||
# ms_threshold=4,
|
# ms_threshold=4,
|
||||||
)
|
)
|
||||||
|
|
||||||
chart = self._chart
|
|
||||||
linked = self.linked
|
linked = self.linked
|
||||||
if (
|
if (
|
||||||
do_linked_charts
|
do_linked_charts
|
||||||
|
@ -983,7 +984,9 @@ class ChartView(ViewBox):
|
||||||
):
|
):
|
||||||
plots = {linked.chart.name: linked.chart}
|
plots = {linked.chart.name: linked.chart}
|
||||||
plots |= linked.subplots
|
plots |= linked.subplots
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
chart = self._chart
|
||||||
plots = {chart.name: chart}
|
plots = {chart.name: chart}
|
||||||
|
|
||||||
# TODO: a faster single-loop-iterator way of doing this?
|
# TODO: a faster single-loop-iterator way of doing this?
|
||||||
|
@ -1031,42 +1034,45 @@ class ChartView(ViewBox):
|
||||||
] = {}
|
] = {}
|
||||||
major_in_view: np.ndarray = None
|
major_in_view: np.ndarray = None
|
||||||
|
|
||||||
|
# ONLY auto-yrange the viz mapped to THIS view box
|
||||||
|
if not do_overlay_scaling:
|
||||||
|
viz = self._viz
|
||||||
|
if debug_print:
|
||||||
|
print(f'ONLY ranging THIS viz: {viz.name}')
|
||||||
|
|
||||||
|
out = _maybe_calc_yrange(
|
||||||
|
viz,
|
||||||
|
yranges,
|
||||||
|
profiler,
|
||||||
|
chart_name,
|
||||||
|
)
|
||||||
|
if out is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
read_slc, yrange = out
|
||||||
|
viz.plot.vb._set_yrange(yrange=yrange)
|
||||||
|
profiler(f'{viz.name}@{chart_name} single curve yrange')
|
||||||
|
|
||||||
|
# don't iterate overlays, just move to next chart
|
||||||
|
continue
|
||||||
|
|
||||||
for name, viz in chart._vizs.items():
|
for name, viz in chart._vizs.items():
|
||||||
|
|
||||||
if debug_print:
|
if debug_print:
|
||||||
print(
|
print(
|
||||||
f'UX GRAPHICS CYCLE: {viz.name}@{chart_name}'
|
f'UX GRAPHICS CYCLE: {viz.name}@{chart_name}'
|
||||||
)
|
)
|
||||||
|
|
||||||
if not viz.render:
|
out = _maybe_calc_yrange(
|
||||||
# print(f'skipping {flow.name}')
|
viz,
|
||||||
continue
|
yranges,
|
||||||
|
profiler,
|
||||||
# pass in no array which will read and render from the last
|
chart_name,
|
||||||
# passed array (normally provided by the display loop.)
|
)
|
||||||
in_view, i_read_range, _ = viz.update_graphics()
|
|
||||||
|
|
||||||
if not in_view:
|
|
||||||
continue
|
|
||||||
|
|
||||||
profiler(f'{viz.name}@{chart_name} `Viz.update_graphics()`')
|
|
||||||
|
|
||||||
yrange = yranges.get(viz) if yranges else None
|
|
||||||
if yrange is not None:
|
|
||||||
# print(f'INPUT {viz.name} yrange: {yrange}')
|
|
||||||
read_slc = slice(*i_read_range)
|
|
||||||
|
|
||||||
else:
|
|
||||||
out = viz.maxmin(i_read_range=i_read_range)
|
|
||||||
if out is None:
|
if out is None:
|
||||||
log.warning(f'No yrange provided for {name}!?')
|
continue
|
||||||
return
|
|
||||||
(
|
|
||||||
_, # ixrng,
|
|
||||||
read_slc,
|
|
||||||
yrange
|
|
||||||
) = out
|
|
||||||
profiler(f'{viz.name}@{chart_name} `Viz.maxmin()`')
|
|
||||||
|
|
||||||
|
read_slc, yrange = out
|
||||||
pi = viz.plot
|
pi = viz.plot
|
||||||
|
|
||||||
# handle multiple graphics-objs per viewbox cases
|
# handle multiple graphics-objs per viewbox cases
|
||||||
|
@ -1091,9 +1097,6 @@ class ChartView(ViewBox):
|
||||||
ymn, ymx = yrange
|
ymn, ymx = yrange
|
||||||
# print(f'adding {viz.name} to overlay')
|
# print(f'adding {viz.name} to overlay')
|
||||||
|
|
||||||
if not do_overlay_scaling:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# determine start datum in view
|
# determine start datum in view
|
||||||
arr = viz.shm.array
|
arr = viz.shm.array
|
||||||
in_view = arr[read_slc]
|
in_view = arr[read_slc]
|
||||||
|
@ -1132,24 +1135,6 @@ class ChartView(ViewBox):
|
||||||
major_in_view = in_view
|
major_in_view = in_view
|
||||||
profiler(f'{viz.name}@{chart_name} set new major')
|
profiler(f'{viz.name}@{chart_name} set new major')
|
||||||
|
|
||||||
# compute directional (up/down) y-range % swing/dispersion
|
|
||||||
# y_ref = y_med
|
|
||||||
# up_rng = (ymx - y_ref) / y_ref
|
|
||||||
# down_rng = (ymn - y_ref) / y_ref
|
|
||||||
# mx_up_rng = max(mx_up_rng, up_rng)
|
|
||||||
# mn_down_rng = min(mn_down_rng, down_rng)
|
|
||||||
# print(
|
|
||||||
# f'{viz.name}@{chart_name} group mxmn calc\n'
|
|
||||||
# '--------------------\n'
|
|
||||||
# f'y_start: {y_start}\n'
|
|
||||||
# f'ymn: {ymn}\n'
|
|
||||||
# f'ymx: {ymx}\n'
|
|
||||||
# f'mx_disp: {mx_disp}\n'
|
|
||||||
# f'up %: {up_rng * 100}\n'
|
|
||||||
# f'down %: {down_rng * 100}\n'
|
|
||||||
# f'mx up %: {mx_up_rng * 100}\n'
|
|
||||||
# f'mn down %: {mn_down_rng * 100}\n'
|
|
||||||
# )
|
|
||||||
profiler(f'{viz.name}@{chart_name} MINOR curve scale')
|
profiler(f'{viz.name}@{chart_name} MINOR curve scale')
|
||||||
|
|
||||||
# non-overlay group case
|
# non-overlay group case
|
||||||
|
@ -1165,21 +1150,32 @@ class ChartView(ViewBox):
|
||||||
# thus a single viz/overlay) then we ONLY set the lone
|
# thus a single viz/overlay) then we ONLY set the lone
|
||||||
# chart's (viz) yrange and short circuit to the next chart
|
# chart's (viz) yrange and short circuit to the next chart
|
||||||
# in the linked charts sequence.
|
# in the linked charts sequence.
|
||||||
|
|
||||||
if (
|
if (
|
||||||
not do_overlay_scaling
|
len(overlay_table) < 2
|
||||||
or len(overlay_table) < 2
|
|
||||||
or not overlay_table
|
or not overlay_table
|
||||||
):
|
):
|
||||||
if debug_print:
|
if debug_print:
|
||||||
print(f'ONLY ranging major: {viz.name}')
|
print(f'ONLY ranging major: {viz.name}')
|
||||||
|
|
||||||
|
# we're either in `do_overlay_scaling=False` mode
|
||||||
|
# or there is only one curve so we need to pick
|
||||||
|
# that "only curve".
|
||||||
if not major_viz:
|
if not major_viz:
|
||||||
major_viz = viz
|
major_viz = viz
|
||||||
|
|
||||||
|
if yranges is not None:
|
||||||
|
yrange = yranges.get(major_viz) or yrange
|
||||||
|
|
||||||
|
assert yrange
|
||||||
|
print(f'ONLY ranging major: {viz.name}')
|
||||||
major_viz.plot.vb._set_yrange(
|
major_viz.plot.vb._set_yrange(
|
||||||
yrange=yrange,
|
yrange=yrange,
|
||||||
)
|
)
|
||||||
profiler(f'{viz.name}@{chart_name} single curve yrange')
|
profiler(f'{viz.name}@{chart_name} single curve yrange')
|
||||||
|
if not do_linked_charts:
|
||||||
|
return
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
profiler(f'<{chart_name}>.interact_graphics_cycle({name})')
|
profiler(f'<{chart_name}>.interact_graphics_cycle({name})')
|
||||||
|
@ -1192,7 +1188,6 @@ class ChartView(ViewBox):
|
||||||
y_start,
|
y_start,
|
||||||
y_min,
|
y_min,
|
||||||
y_max,
|
y_max,
|
||||||
# y_med,
|
|
||||||
read_slc,
|
read_slc,
|
||||||
minor_in_view,
|
minor_in_view,
|
||||||
)
|
)
|
||||||
|
@ -1400,3 +1395,49 @@ class ChartView(ViewBox):
|
||||||
# breakpoint()
|
# breakpoint()
|
||||||
|
|
||||||
profiler.finish()
|
profiler.finish()
|
||||||
|
|
||||||
|
|
||||||
|
def _maybe_calc_yrange(
|
||||||
|
viz: Viz,
|
||||||
|
yranges: dict[Viz, tuple[float, float]],
|
||||||
|
profiler: Profiler,
|
||||||
|
chart_name: str,
|
||||||
|
|
||||||
|
) -> tuple[slice, tuple[float, float]] | None:
|
||||||
|
|
||||||
|
if not viz.render:
|
||||||
|
return
|
||||||
|
# # print(f'skipping {flow.name}')
|
||||||
|
# continue
|
||||||
|
|
||||||
|
# pass in no array which will read and render from the last
|
||||||
|
# passed array (normally provided by the display loop.)
|
||||||
|
in_view, i_read_range, _ = viz.update_graphics()
|
||||||
|
|
||||||
|
if not in_view:
|
||||||
|
return
|
||||||
|
# continue
|
||||||
|
|
||||||
|
profiler(f'{viz.name}@{chart_name} `Viz.update_graphics()`')
|
||||||
|
|
||||||
|
# check if explicit yranges were passed in by the caller
|
||||||
|
yrange = yranges.get(viz) if yranges else None
|
||||||
|
if yrange is not None:
|
||||||
|
read_slc = slice(*i_read_range)
|
||||||
|
|
||||||
|
else:
|
||||||
|
out = viz.maxmin(i_read_range=i_read_range)
|
||||||
|
if out is None:
|
||||||
|
log.warning(f'No yrange provided for {viz.name}!?')
|
||||||
|
return
|
||||||
|
(
|
||||||
|
_, # ixrng,
|
||||||
|
read_slc,
|
||||||
|
yrange
|
||||||
|
) = out
|
||||||
|
profiler(f'{viz.name}@{chart_name} `Viz.maxmin()`')
|
||||||
|
|
||||||
|
return (
|
||||||
|
read_slc,
|
||||||
|
yrange,
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue