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.
epoch_indexing_and_dataviz_layer
Tyler Goodlet 2022-12-28 01:30:34 -05:00
parent fc17187ff4
commit 459cbfdbad
1 changed files with 66 additions and 39 deletions

View File

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