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
parent
2aa5137283
commit
d0b39e8a2b
|
@ -20,7 +20,9 @@ Chart view box primitives
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
import math
|
from math import (
|
||||||
|
isinf,
|
||||||
|
)
|
||||||
import time
|
import time
|
||||||
from typing import (
|
from typing import (
|
||||||
Optional,
|
Optional,
|
||||||
|
@ -904,6 +906,8 @@ class ChartView(ViewBox):
|
||||||
|
|
||||||
def interact_graphics_cycle(
|
def interact_graphics_cycle(
|
||||||
self,
|
self,
|
||||||
|
*args,
|
||||||
|
debug_print: bool = False,
|
||||||
):
|
):
|
||||||
profiler = Profiler(
|
profiler = Profiler(
|
||||||
msg=f'ChartView.interact_graphics_cycle() for {self.name}',
|
msg=f'ChartView.interact_graphics_cycle() for {self.name}',
|
||||||
|
@ -971,7 +975,7 @@ class ChartView(ViewBox):
|
||||||
],
|
],
|
||||||
] = {}
|
] = {}
|
||||||
max_istart: float = 0
|
max_istart: float = 0
|
||||||
# major_in_view: np.ndarray = None
|
major_in_view: np.ndarray = None
|
||||||
|
|
||||||
for name, viz in chart._vizs.items():
|
for name, viz in chart._vizs.items():
|
||||||
|
|
||||||
|
@ -1061,7 +1065,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
|
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
|
||||||
|
@ -1099,6 +1103,9 @@ class ChartView(ViewBox):
|
||||||
if (
|
if (
|
||||||
len(start_datums) < 2
|
len(start_datums) < 2
|
||||||
):
|
):
|
||||||
|
if not major_viz:
|
||||||
|
major_viz = viz
|
||||||
|
|
||||||
# print(f'ONLY ranging major: {viz.name}')
|
# print(f'ONLY ranging major: {viz.name}')
|
||||||
major_viz.plot.vb._set_yrange(
|
major_viz.plot.vb._set_yrange(
|
||||||
yrange=yrange,
|
yrange=yrange,
|
||||||
|
@ -1125,13 +1132,6 @@ 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
|
continue
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -1149,31 +1149,61 @@ 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
|
||||||
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_in_view_start = major_in_view[0]
|
||||||
major_i_start = mshm.array['index'][0]
|
major_i_start = major_in_view_start['index']
|
||||||
|
major_i_start_t = major_in_view_start['time']
|
||||||
|
|
||||||
abs_i_start = max(
|
y_major_intersect = major_in_view_start[key]
|
||||||
minor_i_start,
|
y_minor_intersect = minor_in_view_start[key]
|
||||||
major_i_start,
|
|
||||||
|
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'
|
||||||
)
|
)
|
||||||
|
|
||||||
y_maj_intersect = mshm._array[abs_i_start][key]
|
# major has later timestamp adjust minor
|
||||||
y_minor_intersect = viz.shm._array[abs_i_start][key]
|
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
|
# 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_major_intersect
|
||||||
if not y_ref:
|
if not y_ref:
|
||||||
log.warning(
|
log.warning(
|
||||||
f'BAD y_maj_intersect?!: {y_maj_intersect}'
|
f'BAD y_major_intersect?!: {y_major_intersect}'
|
||||||
)
|
)
|
||||||
# breakpoint()
|
# breakpoint()
|
||||||
|
|
||||||
|
@ -1196,18 +1226,15 @@ class ChartView(ViewBox):
|
||||||
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
|
||||||
|
|
||||||
# y_maj_ref = max(
|
y_maj_ref = y_major_intersect
|
||||||
# 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_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(
|
if debug_print:
|
||||||
# f'{view.name} OUT OF RANGE:\n'
|
print(
|
||||||
# '--------------------\n'
|
f'{view.name} OUT OF RANGE:\n'
|
||||||
# f'y_max:{y_max} > ymx:{ymx}\n'
|
'--------------------\n'
|
||||||
# )
|
f'y_max:{y_max} > ymx:{ymx}\n'
|
||||||
|
)
|
||||||
ymx = y_max
|
ymx = y_max
|
||||||
|
|
||||||
if y_min < ymn:
|
if y_min < ymn:
|
||||||
|
@ -1215,58 +1242,46 @@ class ChartView(ViewBox):
|
||||||
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
|
||||||
|
|
||||||
# y_maj_ref = min(
|
y_maj_ref = y_major_intersect
|
||||||
# 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_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(
|
if debug_print:
|
||||||
# f'{view.name} OUT OF RANGE:\n'
|
print(
|
||||||
# '--------------------\n'
|
f'{view.name} OUT OF RANGE:\n'
|
||||||
# f'y_min:{y_min} < ymn:{ymn}\n'
|
'--------------------\n'
|
||||||
# )
|
f'y_min:{y_min} < ymn:{ymn}\n'
|
||||||
|
)
|
||||||
ymn = y_min
|
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:
|
if new_maj_mxmn:
|
||||||
# print(
|
if debug_print:
|
||||||
# f'RESCALE MAJOR {major_viz.name}:\n'
|
print(
|
||||||
# f'previous: {(major_mn, major_mx)}\n'
|
f'RESCALE MAJOR {major_viz.name}:\n'
|
||||||
# f'new: {new_maj_mxmn}\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,
|
|
||||||
# )
|
|
||||||
major_mn, major_mx = new_maj_mxmn
|
major_mn, major_mx = new_maj_mxmn
|
||||||
# vrs = major_viz.plot.vb.viewRange()
|
|
||||||
# if vrs[1][0] > new_maj_mxmn[0]:
|
|
||||||
# breakpoint()
|
|
||||||
|
|
||||||
# print(
|
if debug_print:
|
||||||
# f'{view.name} APPLY group mxmn\n'
|
print(
|
||||||
# '--------------------\n'
|
f'{view.name} APPLY group mxmn\n'
|
||||||
# f'minor_y_start: {minor_y_start}\n'
|
'--------------------\n'
|
||||||
# f'mn_down_rng: {mn_down_rng * 100}\n'
|
f'y_minor_intersect: {y_minor_intersect}\n'
|
||||||
# f'mx_up_rng: {mx_up_rng * 100}\n'
|
f'y_major_intersect: {y_major_intersect}\n'
|
||||||
# f'scaled ymn: {ymn}\n'
|
f'mn_down_rng: {mn_down_rng * 100}\n'
|
||||||
# f'scaled ymx: {ymx}\n'
|
f'mx_up_rng: {mx_up_rng * 100}\n'
|
||||||
# f'scaled mx_disp: {mx_disp}\n'
|
f'scaled ymn: {ymn}\n'
|
||||||
# )
|
f'scaled ymx: {ymx}\n'
|
||||||
|
f'scaled mx_disp: {mx_disp}\n'
|
||||||
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
math.isinf(ymx)
|
isinf(ymx)
|
||||||
or math.isinf(ymn)
|
or isinf(ymn)
|
||||||
):
|
):
|
||||||
# breakpoint()
|
|
||||||
log.warning(
|
log.warning(
|
||||||
f'BAD ymx/ymn: {(ymn, ymx)}'
|
f'BAD ymx/ymn: {(ymn, ymx)}'
|
||||||
)
|
)
|
||||||
|
@ -1281,11 +1296,13 @@ class ChartView(ViewBox):
|
||||||
# inside of a single render cycle (and apparently calling
|
# inside of a single render cycle (and apparently calling
|
||||||
# `ViewBox.setYRange()` multiple times within one only takes
|
# `ViewBox.setYRange()` multiple times within one only takes
|
||||||
# the first call as serious...) XD
|
# the first call as serious...) XD
|
||||||
# print(
|
if debug_print:
|
||||||
# f'Scale MAJOR {major_viz.name}:\n'
|
print(
|
||||||
# f'previous: {(major_mn, major_mx)}\n'
|
f'Scale MAJOR {major_viz.name}:\n'
|
||||||
# f'new: {new_maj_mxmn}\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(
|
major_viz.plot.vb._set_yrange(
|
||||||
yrange=(major_mn, major_mx),
|
yrange=(major_mn, major_mx),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue