Fix intersect detection using time indexing

Facepalm, obviously absolute array indexes are not going to necessarily
align vs. time over multiple feeds/history. Instead use
`np.searchsorted()` on whatever curve has the smallest support and find
the appropriate index of intersection in time so that alignment always
starts at a sensible reference.

Also adds a `debug_print: bool` input arg which can enable all the
prints when working on this.
multichartz
Tyler Goodlet 2023-01-23 13:23:46 -05:00
parent 2aa5137283
commit d0b39e8a2b
1 changed files with 94 additions and 77 deletions

View File

@ -20,7 +20,9 @@ Chart view box primitives
"""
from __future__ import annotations
from contextlib import asynccontextmanager
import math
from math import (
isinf,
)
import time
from typing import (
Optional,
@ -904,6 +906,8 @@ class ChartView(ViewBox):
def interact_graphics_cycle(
self,
*args,
debug_print: bool = False,
):
profiler = Profiler(
msg=f'ChartView.interact_graphics_cycle() for {self.name}',
@ -971,7 +975,7 @@ class ChartView(ViewBox):
],
] = {}
max_istart: float = 0
# major_in_view: np.ndarray = None
major_in_view: np.ndarray = None
for name, viz in chart._vizs.items():
@ -1061,7 +1065,7 @@ class ChartView(ViewBox):
mx_disp = disp
major_mn = ymn
major_mx = ymx
# major_in_view = in_view
major_in_view = in_view
# compute directional (up/down) y-range % swing/dispersion
y_ref = y_med
@ -1099,6 +1103,9 @@ class ChartView(ViewBox):
if (
len(start_datums) < 2
):
if not major_viz:
major_viz = viz
# print(f'ONLY ranging major: {viz.name}')
major_viz.plot.vb._set_yrange(
yrange=yrange,
@ -1125,13 +1132,6 @@ class ChartView(ViewBox):
if viz is major_viz:
ymn = y_min
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:
@ -1149,31 +1149,61 @@ class ChartView(ViewBox):
# y-range based on the major curves y-range.
# get intersection point y-values for both curves
mshm = major_viz.shm
minor_in_view_start = minor_in_view[0]
minor_i_start = minor_in_view_start['index']
minor_i_start_t = minor_in_view_start['time']
minor_i_start = minor_in_view[0]['index']
major_i_start = mshm.array['index'][0]
major_in_view_start = major_in_view[0]
major_i_start = major_in_view_start['index']
major_i_start_t = major_in_view_start['time']
abs_i_start = max(
minor_i_start,
major_i_start,
)
y_major_intersect = major_in_view_start[key]
y_minor_intersect = minor_in_view_start[key]
y_maj_intersect = mshm._array[abs_i_start][key]
y_minor_intersect = viz.shm._array[abs_i_start][key]
tdiff = (major_i_start_t - minor_i_start_t)
if debug_print:
print(
f'{major_viz.name} time diff with minor:\n'
f'maj:{major_i_start_t}\n'
'-\n'
f'min:{minor_i_start_t}\n'
f'=> {tdiff}\n'
)
# major has later timestamp adjust minor
if tdiff > 0:
y_minor_i = np.searchsorted(
minor_in_view['time'],
major_i_start_t,
)
y_minor_intersect = minor_in_view[y_minor_i][key]
# minor has later timestamp adjust major
elif tdiff < 0:
y_major_i = np.searchsorted(
major_in_view['time'],
minor_i_start_t,
)
y_major_intersect = major_in_view[y_major_i][key]
if debug_print:
print(
f'major_i_start: {major_i_start}\n'
f'major_i_start_t: {major_i_start_t}\n'
f'minor_i_start: {minor_i_start}\n'
f'minor_i_start_t: {minor_i_start_t}\n'
)
# TODO: probably write this as a compile cpython or
# numba func.
# if abs_i_start > major_i_start:
# compute directional (up/down) y-range
# % swing/dispersion starting at the reference index
# determined by the above indexing arithmetic.
y_ref = y_maj_intersect
y_ref = y_major_intersect
if not y_ref:
log.warning(
f'BAD y_maj_intersect?!: {y_maj_intersect}'
f'BAD y_major_intersect?!: {y_major_intersect}'
)
# breakpoint()
@ -1196,18 +1226,15 @@ class ChartView(ViewBox):
y_ref = y_minor_intersect
r_up_minor = (y_max - y_ref) / y_ref
# y_maj_ref = max(
# major_in_view[0][key],
# y_maj_intersect,
# )
y_maj_ref = y_maj_intersect
y_maj_ref = y_major_intersect
new_maj_ymx = y_maj_ref * (1 + r_up_minor)
new_maj_mxmn = (major_mn, new_maj_ymx)
# print(
# f'{view.name} OUT OF RANGE:\n'
# '--------------------\n'
# f'y_max:{y_max} > ymx:{ymx}\n'
# )
if debug_print:
print(
f'{view.name} OUT OF RANGE:\n'
'--------------------\n'
f'y_max:{y_max} > ymx:{ymx}\n'
)
ymx = y_max
if y_min < ymn:
@ -1215,58 +1242,46 @@ class ChartView(ViewBox):
y_ref = y_minor_intersect
r_down_minor = (y_min - y_ref) / y_ref
# y_maj_ref = min(
# major_in_view[0][key],
# y_maj_intersect,
# )
y_maj_ref = y_maj_intersect
y_maj_ref = y_major_intersect
new_maj_ymn = y_maj_ref * (1 + r_down_minor)
new_maj_mxmn = (
new_maj_ymn,
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'
# )
if debug_print:
print(
f'{view.name} OUT OF RANGE:\n'
'--------------------\n'
f'y_min:{y_min} < ymn:{ymn}\n'
)
ymn = y_min
# now scale opposite side to compensate
# y_ref = y_major_intersect
# r_down_minor = (major_ - y_ref) / y_ref
if new_maj_mxmn:
# print(
# f'RESCALE 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=new_maj_mxmn,
# # range_margin=None,
# )
if debug_print:
print(
f'RESCALE MAJOR {major_viz.name}:\n'
f'previous: {(major_mn, major_mx)}\n'
f'new: {new_maj_mxmn}\n'
)
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 debug_print:
print(
f'{view.name} APPLY group mxmn\n'
'--------------------\n'
f'y_minor_intersect: {y_minor_intersect}\n'
f'y_major_intersect: {y_major_intersect}\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 (
math.isinf(ymx)
or math.isinf(ymn)
isinf(ymx)
or isinf(ymn)
):
# breakpoint()
log.warning(
f'BAD ymx/ymn: {(ymn, ymx)}'
)
@ -1281,11 +1296,13 @@ class ChartView(ViewBox):
# 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'
# )
if debug_print:
print(
f'Scale MAJOR {major_viz.name}:\n'
f'scaled mx_disp: {mx_disp}\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),
)