Factor curve-dispersion sorting into primary loop

We can determine the major curve (in view) in the first pass of all
`Viz`s so drop the 2nd loop and thus the `mxmn_groups: dict`. Also
simplifies logic for the case of only one (the major) curve in view.
log_linearized_curve_overlays
Tyler Goodlet 2023-01-22 16:07:52 -05:00
parent 896259d9e4
commit 776ffd2b1c
1 changed files with 129 additions and 138 deletions

View File

@ -908,7 +908,6 @@ class ChartView(ViewBox):
profiler = Profiler( profiler = Profiler(
msg=f'ChartView.interact_graphics_cycle() for {self.name}', msg=f'ChartView.interact_graphics_cycle() for {self.name}',
# disabled=not pg_profile_enabled(), # disabled=not pg_profile_enabled(),
# ms_threshold=ms_slower_then, # ms_threshold=ms_slower_then,
disabled=True, disabled=True,
@ -941,94 +940,24 @@ class ChartView(ViewBox):
tuple[float, float], tuple[float, float],
] = {} ] = {}
# collect certain flows have grapics objects **in seperate
# plots/viewboxes** into groups and do a common calc to
# determine auto-ranging input for `._set_yrange()`.
# this is primarly used for our so called "log-linearized
mxmn_groups: dict[
set[Viz],
set[Viz, tuple[float, float]],
] = {}
for name, viz in chart._vizs.items():
if not viz.render:
# 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:
continue
profiler(f'{viz.name}@{chart_name} `Viz.update_graphics()`')
out = viz.maxmin(i_read_range=i_read_range)
if out is None:
log.warning(f'No yrange provided for {name}!?')
return
(
ixrng,
_,
yrange
) = out
pi = viz.plot
mxmn = mxmns_by_common_pi.get(pi)
if mxmn:
yrange = mxmns_by_common_pi[pi] = (
min(yrange[0], mxmn[0]),
max(yrange[1], mxmn[1]),
)
else:
mxmns_by_common_pi[pi] = yrange
# TODO: a better predicate here, likely something
# to do with overlays and their settings..
if (
viz.is_ohlc
):
# print(f'adding {viz.name} to overlay')
mxmn_groups[viz.name] = out
else:
pi.vb._set_yrange(yrange=yrange)
profiler(
f'{viz.name}@{chart_name} `Viz.plot.vb._set_yrange()`'
)
profiler(f'<{chart_name}>.interact_graphics_cycle({name})')
# if no overlays, set lone chart's yrange and short circuit
if (
len(mxmn_groups) < 2
):
print(f'ONLY ranging major: {viz.name}')
for viz_name, out in mxmn_groups.items():
(
ixrng,
read_slc,
yrange,
) = out
# determine start datum in view
viz = chart._vizs[viz_name]
viz.plot.vb._set_yrange(
yrange=yrange,
)
return
# proportional group auto-scaling per overlay set. # proportional group auto-scaling per overlay set.
# -> loop through overlays on each multi-chart widget # -> loop through overlays on each multi-chart widget
# and scale all y-ranges based on autoscale config. # and scale all y-ranges based on autoscale config.
# -> for any "group" overlay we want to dispersion normalize # -> for any "group" overlay we want to dispersion normalize
# and scale minor charts onto the major chart: the chart # and scale minor charts onto the major chart: the chart
# with the most dispersion in the set. # with the most dispersion in the set.
major_viz: Viz = None
major_mx: float = 0 major_mx: float = 0
major_mn: float = float('inf') major_mn: float = float('inf')
mx_up_rng: float = 0 mx_up_rng: float = 0
mn_down_rng: float = 0 mn_down_rng: float = 0
mx_disp: float = 0 mx_disp: float = 0
# collect certain flows have grapics objects **in seperate
# plots/viewboxes** into groups and do a common calc to
# determine auto-ranging input for `._set_yrange()`.
# this is primarly used for our so called "log-linearized
# multi-plot" overlay technique.
start_datums: dict[ start_datums: dict[
ViewBox, ViewBox,
tuple[ tuple[
@ -1042,18 +971,58 @@ class ChartView(ViewBox):
], ],
] = {} ] = {}
max_istart: float = 0 max_istart: float = 0
major_viz: Viz = None
# major_in_view: np.ndarray = None # major_in_view: np.ndarray = None
for viz_name, out in mxmn_groups.items(): for name, viz in chart._vizs.items():
if not viz.render:
# 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:
continue
profiler(f'{viz.name}@{chart_name} `Viz.update_graphics()`')
out = viz.maxmin(i_read_range=i_read_range)
if out is None:
log.warning(f'No yrange provided for {name}!?')
return
( (
ixrng, ixrng,
read_slc, read_slc,
(ymn, ymx), yrange
) = out ) = out
pi = viz.plot
# handle multiple graphics-objs per viewbox cases
mxmn = mxmns_by_common_pi.get(pi)
if mxmn:
yrange = mxmns_by_common_pi[pi] = (
min(yrange[0], mxmn[0]),
max(yrange[1], mxmn[1]),
)
else:
mxmns_by_common_pi[pi] = yrange
# handle overlay log-linearized group scaling cases
# TODO: a better predicate here, likely something
# to do with overlays and their settings..
if (
viz.is_ohlc
):
ymn, ymx = yrange
# print(f'adding {viz.name} to overlay')
# mxmn_groups[viz.name] = out
# viz = chart._vizs[viz_name]
# determine start datum in view # determine start datum in view
viz = chart._vizs[viz_name]
arr = viz.shm.array arr = viz.shm.array
in_view = arr[read_slc] in_view = arr[read_slc]
row_start = arr[read_slc.start - 1] row_start = arr[read_slc.start - 1]
@ -1115,6 +1084,28 @@ class ChartView(ViewBox):
# f'mn down %: {mn_down_rng * 100}\n' # f'mn down %: {mn_down_rng * 100}\n'
# ) # )
# non-overlay group case
else:
pi.vb._set_yrange(yrange=yrange)
profiler(
f'{viz.name}@{chart_name} `Viz.plot.vb._set_yrange()`'
)
profiler(f'<{chart_name}>.interact_graphics_cycle({name})')
if not start_datums:
return
# if no overlays, set lone chart's yrange and short circuit
if (
len(start_datums) < 2
):
# print(f'ONLY ranging major: {viz.name}')
major_viz.plot.vb._set_yrange(
yrange=yrange,
)
return
# conduct "log-linearized multi-plot" scalings for all groups
for ( for (
view, view,
( (