Further fixes `Viz.default_view()` and `.index_step()`

Use proper uppx scaling when either of scaling the data to the x-domain
index-range or when the uppx is < 1 (now that we support it) such that
both the fast and slow chart always appropriately scale and offset to
the y-axis with the last datum graphic just adjacent to the order line
arrow markers.

Further this fixes the `.index_step()` calc to use the "earliest" 16
values to compute the expected sample step diff since the last set often
contained gaps due to start up race conditions and generated
unexpected/incorrect output.

Further this drops the `.curve_width_pxs()` method and replaces it with
`.px_width()`, taken from the graphics object API and instead returns
the pixel account for the whole view width instead of the
x-domain-data-range within the view.
multichartz
Tyler Goodlet 2022-12-28 01:30:34 -05:00
parent 0deee65318
commit d7b9c4044e
1 changed files with 66 additions and 39 deletions

View File

@ -286,10 +286,14 @@ class Viz(msgspec.Struct): # , frozen=True):
) -> float:
if self._index_step is None:
index = self.shm.array[self.index_field]
self._index_step = max(
np.diff(index[-16:]).max(),
1,
)
isample = index[:16]
mxdiff = np.diff(isample).max()
self._index_step = max(mxdiff, 1)
if (
mxdiff < 1
or 1 < mxdiff < 60
):
breakpoint()
return self._index_step
@ -298,6 +302,8 @@ class Viz(msgspec.Struct): # , frozen=True):
lbar: int,
rbar: int,
use_caching: bool = True,
) -> Optional[tuple[float, float]]:
'''
Compute the cached max and min y-range values for a given
@ -308,15 +314,17 @@ class Viz(msgspec.Struct): # , frozen=True):
# TODO: hash the slice instead maybe?
# https://stackoverflow.com/a/29980872
rkey = (round(lbar), round(rbar))
cached_result = self._mxmns.get(rkey)
do_print = False # (self.index_step() == 60)
if cached_result:
if do_print:
print(
f'{self.name} CACHED maxmin\n'
f'{rkey} -> {cached_result}'
)
return cached_result
do_print: bool = False
if use_caching:
cached_result = self._mxmns.get(rkey)
if cached_result:
if do_print:
print(
f'{self.name} CACHED maxmin\n'
f'{rkey} -> {cached_result}'
)
return cached_result
shm = self.shm
if shm is None:
@ -332,14 +340,15 @@ class Viz(msgspec.Struct): # , frozen=True):
stop_t=rbar,
step=self.index_step(),
)
slice_view = arr[read_slc]
else:
ifirst = arr[0]['index']
slice_view = arr[
lbar - ifirst:
read_slc = slice(
lbar - ifirst,
(rbar - ifirst) + 1
]
)
slice_view = arr[read_slc]
if not slice_view.size:
log.warning(f'{self.name} no maxmin in view?')
@ -366,14 +375,13 @@ class Viz(msgspec.Struct): # , frozen=True):
mxmn = ylow, yhigh
if (
do_print
# and self.index_step() > 1
):
s = 3
print(
f'{self.name} MANUAL ohlc={self.is_ohlc} maxmin:\n'
f'{rkey} -> {mxmn}\n'
f'read_slc: {read_slc}\n'
f'abs_slc: {slice_view["index"]}\n'
# f'abs_slc: {slice_view["index"]}\n'
f'first {s}:\n{slice_view[:s]}\n'
f'last {s}:\n{slice_view[-s:]}\n'
)
@ -610,7 +618,7 @@ class Viz(msgspec.Struct): # , frozen=True):
return graphics
should_redraw: bool = False
ds_allowed: bool = True
ds_allowed: bool = True # guard for m4 activation
# TODO: probably specialize ``Renderer`` types instead of
# these logic checks?
@ -624,7 +632,7 @@ class Viz(msgspec.Struct): # , frozen=True):
graphics,
r,
should_redraw,
in_line,
ds_allowed, # in line mode?
) = render_baritems(
self,
graphics,
@ -632,7 +640,6 @@ class Viz(msgspec.Struct): # , frozen=True):
profiler,
**kwargs,
)
ds_allowed = in_line
elif not r:
if isinstance(graphics, StepCurve):
@ -807,7 +814,7 @@ class Viz(msgspec.Struct): # , frozen=True):
# XXX: pretty sure we don't need this?
# if isinstance(g, Curve):
# with dsg.reset_cache():
uppx = self._last_uppx
uppx = round(self._last_uppx)
y = y[-uppx:]
ymn, ymx = y.min(), y.max()
# print(f'drawing uppx={uppx} mxmn line: {ymn}, {ymx}')
@ -829,19 +836,6 @@ class Viz(msgspec.Struct): # , frozen=True):
# print(f'updating NOT DS curve {self.name}')
g.update()
def curve_width_pxs(self) -> float:
'''
Return the width of the current datums in view in pixel units.
'''
_, lbar, rbar, _ = self.bars_range()
return self.view.mapViewToDevice(
QLineF(
lbar, 0,
rbar, 0
)
).length()
def default_view(
self,
bars_from_y: int = int(616 * 3/8),
@ -900,19 +894,33 @@ class Viz(msgspec.Struct): # , frozen=True):
# l->r distance in scene units, no larger then data span
data_diff = last_datum - first_datum
rl_diff = min(vr - vl, data_diff)
rl_diff = vr - vl
rescale_to_data: bool = False
# new_uppx: float = 1
if rl_diff > data_diff:
rescale_to_data = True
rl_diff = data_diff
new_uppx: float = data_diff / self.px_width()
# orient by offset from the y-axis including
# space to compensate for the L1 labels.
if not y_offset:
_, offset = chartw.pre_l1_xs()
_, l1_offset = chartw.pre_l1_xs()
offset = l1_offset
if (
rescale_to_data
):
offset = (offset / uppx) * new_uppx
else:
offset = (y_offset * step) + uppx*step
# align right side of view to the rightmost datum + the selected
# offset from above.
r_reset = last_datum + offset
r_reset = (self.graphics.x_last() or last_datum) + offset
# no data is in view so check for the only 2 sane cases:
# - entire view is LEFT of data
@ -1054,3 +1062,22 @@ class Viz(msgspec.Struct): # , frozen=True):
do_rt_update,
should_tread,
)
def px_width(self) -> float:
'''
Return the width of the view box containing
this graphic in pixel units.
'''
vb = self.plot.vb
if not vb:
return 0
vl, vr = self.view_range()
return vb.mapViewToDevice(
QLineF(
vl, 0,
vr, 0,
)
).length()