Only set the major curve's range once (per render cycle)

Turns out this is a limitation of the `ViewBox.setYRange()` api: you
can't call it more then once and expect anything but the first call to
be applied without letting a render cycle run. As such, we wait until
the end of the log-linear scaling loop to finally apply the major curves
y-mx/mn after all minor curves have been evaluated.

This also drops all the debug prints (for now) to get a feel for latency
in production mode.
multichartz
Tyler Goodlet 2023-01-21 22:19:33 -05:00
parent 7f49792a29
commit 5da2f10ff0
1 changed files with 106 additions and 53 deletions

View File

@ -827,11 +827,11 @@ class ChartView(ViewBox):
# to get different view modes to operate # to get different view modes to operate
# correctly! # correctly!
profiler( # print(
f'set limits {self.name}:\n' # f'set limits {self.name}:\n'
f'ylow: {ylow}\n' # f'ylow: {ylow}\n'
f'yhigh: {yhigh}\n' # f'yhigh: {yhigh}\n'
) # )
self.setYRange( self.setYRange(
ylow, ylow,
yhigh, yhigh,
@ -841,6 +841,7 @@ class ChartView(ViewBox):
yMin=ylow, yMin=ylow,
yMax=yhigh, yMax=yhigh,
) )
self.update()
# LOL: yet anothercucking pg buggg.. # LOL: yet anothercucking pg buggg..
# can't use `msg=f'setYRange({ylow}, {yhigh}')` # can't use `msg=f'setYRange({ylow}, {yhigh}')`
@ -993,7 +994,9 @@ class ChartView(ViewBox):
profiler(f'<{chart_name}>.interact_graphics_cycle({name})') profiler(f'<{chart_name}>.interact_graphics_cycle({name})')
# if no overlays, set lone chart's yrange and short circuit # if no overlays, set lone chart's yrange and short circuit
if len(mxmn_groups) < 2: if (
len(mxmn_groups) < 2
):
viz.plot.vb._set_yrange( viz.plot.vb._set_yrange(
yrange=yrange, yrange=yrange,
) )
@ -1024,6 +1027,7 @@ class ChartView(ViewBox):
] = {} ] = {}
max_istart: float = 0 max_istart: float = 0
major_viz: Viz = None major_viz: Viz = None
# major_in_view: np.ndarray = None
for viz_name, out in mxmn_groups.items(): for viz_name, out in mxmn_groups.items():
( (
@ -1072,6 +1076,7 @@ class ChartView(ViewBox):
mx_disp = disp mx_disp = disp
major_mn = ymn major_mn = ymn
major_mx = ymx major_mx = ymx
# major_in_view = in_view
# compute directional (up/down) y-range % swing/dispersion # compute directional (up/down) y-range % swing/dispersion
y_ref = y_med y_ref = y_med
@ -1081,18 +1086,18 @@ class ChartView(ViewBox):
mx_up_rng = max(mx_up_rng, up_rng) mx_up_rng = max(mx_up_rng, up_rng)
mn_down_rng = min(mn_down_rng, down_rng) mn_down_rng = min(mn_down_rng, down_rng)
print( # print(
f'{viz.name}@{chart_name} group mxmn calc\n' # f'{viz.name}@{chart_name} group mxmn calc\n'
'--------------------\n' # '--------------------\n'
f'y_start: {y_start}\n' # f'y_start: {y_start}\n'
f'ymn: {ymn}\n' # f'ymn: {ymn}\n'
f'ymx: {ymx}\n' # f'ymx: {ymx}\n'
f'mx_disp: {mx_disp}\n' # f'mx_disp: {mx_disp}\n'
f'up %: {up_rng * 100}\n' # f'up %: {up_rng * 100}\n'
f'down %: {down_rng * 100}\n' # f'down %: {down_rng * 100}\n'
f'mx up %: {mx_up_rng * 100}\n' # f'mx up %: {mx_up_rng * 100}\n'
f'mn down %: {mn_down_rng * 100}\n' # f'mn down %: {mn_down_rng * 100}\n'
) # )
for ( for (
view, view,
@ -1113,6 +1118,14 @@ class ChartView(ViewBox):
if viz is major_viz: if viz is major_viz:
ymn = y_min ymn = y_min
ymx = y_max ymx = y_max
# print(
# f'{view.name} MAJOR mxmn\n'
# '--------------------\n'
# f'scaled ymn: {ymn}\n'
# f'scaled ymx: {ymx}\n'
# f'scaled mx_disp: {mx_disp}\n'
# )
continue
else: else:
key = 'open' if viz.is_ohlc else viz.name key = 'open' if viz.is_ohlc else viz.name
@ -1129,11 +1142,11 @@ class ChartView(ViewBox):
# y-range based on the major curves y-range. # y-range based on the major curves y-range.
# get intersection point y-values for both curves # get intersection point y-values for both curves
# abs_i_start = max_istart
mshm = major_viz.shm mshm = major_viz.shm
minor_i_start = minor_in_view[0]['index'] minor_i_start = minor_in_view[0]['index']
major_i_start = mshm.array['index'][0], major_i_start = mshm.array['index'][0]
abs_i_start = max( abs_i_start = max(
minor_i_start, minor_i_start,
major_i_start, major_i_start,
@ -1169,53 +1182,75 @@ class ChartView(ViewBox):
# is side (up/down) specific. # is side (up/down) specific.
new_maj_mxmn: None | tuple[float, float] = None new_maj_mxmn: None | tuple[float, float] = None
if y_max > ymx: if y_max > ymx:
y_ref = y_minor_intersect y_ref = y_minor_intersect
r_up_minor = (y_max - y_ref) / y_ref r_up_minor = (y_max - y_ref) / y_ref
new_maj_ymx = y_maj_intersect * (1 + r_up_minor)
# y_maj_ref = max(
# major_in_view[0][key],
# y_maj_intersect,
# )
y_maj_ref = y_maj_intersect
new_maj_ymx = y_maj_ref * (1 + r_up_minor)
new_maj_mxmn = (major_mn, new_maj_ymx) new_maj_mxmn = (major_mn, new_maj_ymx)
print( # print(
f'{view.name} OUT OF RANGE:\n' # f'{view.name} OUT OF RANGE:\n'
'--------------------\n' # '--------------------\n'
f'y_max:{y_max} > ymx:{ymx}\n' # f'y_max:{y_max} > ymx:{ymx}\n'
f'RESCALE MAJOR {major_viz.name}:\n' # )
f'{new_maj_mxmn}\n'
)
ymx = y_max ymx = y_max
if y_min < ymn: if y_min < ymn:
y_ref = y_minor_intersect y_ref = y_minor_intersect
r_down_minor = (y_min - y_ref) / y_ref r_down_minor = (y_min - y_ref) / y_ref
new_maj_ymn = y_maj_intersect * (1 + r_down_minor)
# y_maj_ref = min(
# major_in_view[0][key],
# y_maj_intersect,
# )
y_maj_ref = y_maj_intersect
new_maj_ymn = y_maj_ref * (1 + r_down_minor)
new_maj_mxmn = ( new_maj_mxmn = (
new_maj_ymn, new_maj_ymn,
new_maj_mxmn[1] if new_maj_mxmn else major_mx new_maj_mxmn[1] if new_maj_mxmn else major_mx
) )
print( # print(
f'{view.name} OUT OF RANGE:\n' # f'{view.name} OUT OF RANGE:\n'
'--------------------\n' # '--------------------\n'
f'y_min:{y_min} < ymn:{ymn}\n' # f'y_min:{y_min} < ymn:{ymn}\n'
f'RESCALE MAJOR {major_viz.name}:\n' # )
f'{new_maj_mxmn}\n'
)
ymn = y_min ymn = y_min
if new_maj_mxmn: # now scale opposite side to compensate
major_mx, major_mn = new_maj_mxmn # y_ref = y_major_intersect
major_viz.plot.vb._set_yrange( # r_down_minor = (major_ - y_ref) / y_ref
yrange=new_maj_mxmn,
# range_margin=None,
)
print( if new_maj_mxmn:
f'{view.name} APPLY group mxmn\n' # print(
'--------------------\n' # f'RESCALE MAJOR {major_viz.name}:\n'
f'minor_y_start: {minor_y_start}\n' # f'previous: {(major_mn, major_mx)}\n'
f'mn_down_rng: {mn_down_rng * 100}\n' # f'new: {new_maj_mxmn}\n'
f'mx_up_rng: {mx_up_rng * 100}\n' # )
f'scaled ymn: {ymn}\n' # major_viz.plot.vb._set_yrange(
f'scaled ymx: {ymx}\n' # yrange=new_maj_mxmn,
f'scaled mx_disp: {mx_disp}\n' # # range_margin=None,
) # )
major_mn, major_mx = new_maj_mxmn
# vrs = major_viz.plot.vb.viewRange()
# if vrs[1][0] > new_maj_mxmn[0]:
# breakpoint()
# print(
# f'{view.name} APPLY group mxmn\n'
# '--------------------\n'
# f'minor_y_start: {minor_y_start}\n'
# f'mn_down_rng: {mn_down_rng * 100}\n'
# f'mx_up_rng: {mx_up_rng * 100}\n'
# f'scaled ymn: {ymn}\n'
# f'scaled ymx: {ymx}\n'
# f'scaled mx_disp: {mx_disp}\n'
# )
if ( if (
math.isinf(ymx) math.isinf(ymx)
@ -1227,4 +1262,22 @@ class ChartView(ViewBox):
yrange=(ymn, ymx), yrange=(ymn, ymx),
) )
# NOTE XXX: we have to set the major curve's range once (and
# only once) here since we're doing this entire routine
# inside of a single render cycle (and apparently calling
# `ViewBox.setYRange()` multiple times within one only takes
# the first call as serious...) XD
# print(
# f'Scale MAJOR {major_viz.name}:\n'
# f'previous: {(major_mn, major_mx)}\n'
# f'new: {new_maj_mxmn}\n'
# )
major_viz.plot.vb._set_yrange(
yrange=(major_mn, major_mx),
)
# major_mx, major_mn = new_maj_mxmn
# vrs = major_viz.plot.vb.viewRange()
# if vrs[1][0] > major_mn:
# breakpoint()
profiler.finish() profiler.finish()