Clean up cross-curve intersect point indexing

When there are `N`-curves we need to consider the smallest
x-data-support subset when figuring out for each major-minor pair such
that the "shorter" series is always returns aligned to the longer one.

This makes the var naming more explicit with `major/minor_i_start` as
well as clarifies more stringently a bunch of other variables and
explicitly uses the `minor_y_intersect` y value in the scaling transform
calcs. Also fixes some debug prints.
multichartz
Tyler Goodlet 2023-01-21 17:31:31 -05:00
parent 0cdb065222
commit b9f3546d2f
1 changed files with 60 additions and 45 deletions

View File

@ -930,17 +930,20 @@ class ChartView(ViewBox):
for chart_name, chart in plots.items(): for chart_name, chart in plots.items():
# Viz "group" maxmins table; presumes that some path # Common `PlotItem` maxmin table; presumes that some path
# graphics (and thus their backing data sets) # graphics (and thus their backing data sets) are in the
# are in the same co-domain and thus can be sorted # same co-domain and view box (since the were added
# as one set per plot. # a separate graphics objects to a common plot) and thus can
mxmns_by_pi: dict[ # be sorted as one set per plot.
mxmns_by_common_pi: dict[
pg.PlotItem, pg.PlotItem,
tuple[float, float], tuple[float, float],
] = {} ] = {}
# collect certain flows into groups and do a common calc to # 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()`. # determine auto-ranging input for `._set_yrange()`.
# this is primarly used for our so called "log-linearized
mxmn_groups: dict[ mxmn_groups: dict[
set[Viz], set[Viz],
set[Viz, tuple[float, float]], set[Viz, tuple[float, float]],
@ -967,15 +970,15 @@ class ChartView(ViewBox):
) = out ) = out
pi = viz.plot pi = viz.plot
mxmn = mxmns_by_pi.get(pi) mxmn = mxmns_by_common_pi.get(pi)
if mxmn: if mxmn:
yrange = mxmns_by_pi[pi] = ( yrange = mxmns_by_common_pi[pi] = (
min(yrange[0], mxmn[0]), min(yrange[0], mxmn[0]),
max(yrange[1], mxmn[1]), max(yrange[1], mxmn[1]),
) )
else: else:
mxmns_by_pi[pi] = yrange mxmns_by_common_pi[pi] = yrange
if viz.is_ohlc: if viz.is_ohlc:
# print(f'adding {viz.name} to overlay') # print(f'adding {viz.name} to overlay')
@ -1019,7 +1022,7 @@ class ChartView(ViewBox):
np.ndarray, # in-view array np.ndarray, # in-view array
], ],
] = {} ] = {}
max_start: float = 0 max_istart: float = 0
major_viz: Viz = None major_viz: Viz = None
for viz_name, out in mxmn_groups.items(): for viz_name, out in mxmn_groups.items():
@ -1029,15 +1032,13 @@ class ChartView(ViewBox):
(ymn, ymx), (ymn, ymx),
) = out ) = out
x_start = ixrng[0]
max_start = max(x_start, max_start)
# determine start datum in view # determine start datum in view
viz = chart._vizs[viz_name] 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]
# row_stop = arr[read_slc.stop - 1]
max_istart = max(in_view[0]['index'], max_istart)
if viz.is_ohlc: if viz.is_ohlc:
y_med = np.median(in_view['close']) y_med = np.median(in_view['close'])
@ -1045,11 +1046,12 @@ class ChartView(ViewBox):
else: else:
y_med = np.median(in_view[viz.name]) y_med = np.median(in_view[viz.name])
y_start = row_start[viz.name] y_start = row_start[viz.name]
# y_stop = row_stop[viz.name]
print( # x_start = ixrng[0]
f'{viz.name} -> (x_start: {x_start}, y_start: {y_start}\n' # print(
) # f'{viz.name} ->\n'
# f'(x_start: {x_start}, y_start: {y_start}\n'
# )
start_datums[viz.plot.vb] = ( start_datums[viz.plot.vb] = (
viz, viz,
y_start, y_start,
@ -1080,17 +1082,14 @@ class ChartView(ViewBox):
mn_down_rng = min(mn_down_rng, down_rng) mn_down_rng = min(mn_down_rng, down_rng)
print( print(
'####################\n'
f'{viz.name}@{chart_name} group mxmn calc\n' f'{viz.name}@{chart_name} group mxmn calc\n'
'--------------------\nn' '--------------------\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'
'####################\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'
'####################\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'
) )
@ -1128,28 +1127,39 @@ class ChartView(ViewBox):
# that the intersecting y-value is used as the # that the intersecting y-value is used as the
# reference point for scaling minor curve's # reference point for scaling minor curve's
# y-range based on the major curves y-range. # y-range based on the major curves y-range.
abs_ifirst = minor_in_view[0]['index']
mshm = major_viz.shm
abs_i_start = max(
abs_ifirst,
mshm.array['index'][0],
)
# 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
minor_i_start = minor_in_view[0]['index']
major_i_start = mshm.array['index'][0],
abs_i_start = max(
minor_i_start,
major_i_start,
)
y_maj_intersect = mshm._array[abs_i_start][key] y_maj_intersect = mshm._array[abs_i_start][key]
y_min_intersect = minor_in_view[abs_i_start - abs_ifirst] y_minor_intersect = viz.shm._array[abs_i_start][key]
# TODO: probably write this as a compile cpython or # TODO: probably write this as a compile cpython or
# numba func. # numba func.
# if abs_i_start > major_i_start:
# compute directional (up/down) y-range # compute directional (up/down) y-range
# % swing/dispersion starting at the reference index # % swing/dispersion starting at the reference index
# determined by the above indexing arithmetic. # determined by the above indexing arithmetic.
y_ref = y_maj_intersect y_ref = y_maj_intersect
assert y_ref if not y_ref:
breakpoint()
r_up = (major_mx - y_ref) / y_ref r_up = (major_mx - y_ref) / y_ref
r_down = (major_mn - y_ref) / y_ref r_down = (major_mn - y_ref) / y_ref
ymn = y_start * (1 + r_down)
ymx = y_start * (1 + r_up) minor_y_start = y_minor_intersect
ymn = minor_y_start * (1 + r_down)
ymx = minor_y_start * (1 + r_up)
# XXX: handle out of view cases where minor curve # XXX: handle out of view cases where minor curve
# now is outside the range of the major curve. in # now is outside the range of the major curve. in
@ -1159,42 +1169,47 @@ 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_min_intersect[key] 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) new_maj_ymx = y_maj_intersect * (1 + r_up_minor)
new_maj_mxmn = (major_mn, new_maj_ymx) new_maj_mxmn = (major_mn, new_maj_ymx)
ymx = y_max
print( print(
f'{view.name} OUT OF RANGE:\n' f'{view.name} OUT OF RANGE:\n'
f'MAJOR is {major_viz.name}\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
if y_min < ymn: if y_min < ymn:
y_ref = y_min_intersect[key] 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) new_maj_ymn = y_maj_intersect * (1 + r_down_minor)
new_maj_mxmn = ( new_maj_mxmn = (
new_maj_ymn, new_maj_ymn,
new_maj_ymx[1] if new_maj_mxmn else major_mx new_maj_mxmn[1] if new_maj_mxmn else major_mx
)
print(
f'{view.name} OUT OF RANGE:\n'
'--------------------\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
print(
f'{view.name} OUT OF RANGE:\n'
f'MAJOR is {major_viz.name}\n'
f'y_min:{y_min} < ymn:{ymn}\n'
)
if new_maj_mxmn: if new_maj_mxmn:
major_mx, major_mn = new_maj_mxmn
major_viz.plot.vb._set_yrange( major_viz.plot.vb._set_yrange(
yrange=new_maj_mxmn, yrange=new_maj_mxmn,
# range_margin=None,
) )
print( print(
f'{view.name} APPLY group mxmn\n' f'{view.name} APPLY group mxmn\n'
f'y_start: {y_start}\n' '--------------------\n'
f'minor_y_start: {minor_y_start}\n'
f'mn_down_rng: {mn_down_rng * 100}\n' f'mn_down_rng: {mn_down_rng * 100}\n'
f'mx_up_rng: {mx_up_rng * 100}\n' f'mx_up_rng: {mx_up_rng * 100}\n'
f'scaled ymn: {ymn}\n' f'scaled ymn: {ymn}\n'