Re-implement `.default_view()` on `Viz`
Since we don't really need it defined on the "chart widget" move it to a viz method and rework it to hell: - always discard the invalid view l > r case. - use the graphic's UPPX to determine UI-to-scene coordinate scaling for the L1-label collision detection, if there is no L1 just offset by a few (index step scaled) datums; this allows us to drop the 2x x-range calls as was hacked previous. - handle no-data-in-view cases explicitly and error if we get any ostensibly impossible cases. - expect caller to trigger a graphics cycle if needed. Further support this includes a rework a slew of other important details: - add `Viz.index_step`, an idempotent computed, index (presumably uniform) step value which is needed for variable sample rate graphics displayed on an epoch (second) time index. - rework `Viz.datums_range()` to pass view x-endpoints as first and last elements in return `tuple`; tighten up snap-to-data edge case logic using `max()`/`min()` calls and better internal var naming. - adjust all calls to `slice_from_time()` to not expect an "abs" slice. - drop all `.yrange` resetting since we can just have the `Renderer` do it when necessary.epoch_index_backup
							parent
							
								
									3a0cbe518e
								
							
						
					
					
						commit
						5976d68bb2
					
				| 
						 | 
					@ -25,6 +25,7 @@ incremental update.
 | 
				
			||||||
from __future__ import annotations
 | 
					from __future__ import annotations
 | 
				
			||||||
from typing import (
 | 
					from typing import (
 | 
				
			||||||
    Optional,
 | 
					    Optional,
 | 
				
			||||||
 | 
					    TYPE_CHECKING,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import msgspec
 | 
					import msgspec
 | 
				
			||||||
| 
						 | 
					@ -49,7 +50,6 @@ from ..data._pathops import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from ._ohlc import (
 | 
					from ._ohlc import (
 | 
				
			||||||
    BarItems,
 | 
					    BarItems,
 | 
				
			||||||
    # bar_from_ohlc_row,
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from ._curve import (
 | 
					from ._curve import (
 | 
				
			||||||
    Curve,
 | 
					    Curve,
 | 
				
			||||||
| 
						 | 
					@ -63,6 +63,11 @@ from .._profile import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if TYPE_CHECKING:
 | 
				
			||||||
 | 
					    from ._interaction import ChartView
 | 
				
			||||||
 | 
					    from ._chart import ChartPlotWidget
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
log = get_logger(__name__)
 | 
					log = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -231,11 +236,13 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
    is_ohlc: bool = False
 | 
					    is_ohlc: bool = False
 | 
				
			||||||
    render: bool = True  # toggle for display loop
 | 
					    render: bool = True  # toggle for display loop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # _index_field: str = 'index'
 | 
				
			||||||
    _index_field: str = 'time'
 | 
					    _index_field: str = 'time'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # downsampling state
 | 
					    # downsampling state
 | 
				
			||||||
    _last_uppx: float = 0
 | 
					    _last_uppx: float = 0
 | 
				
			||||||
    _in_ds: bool = False
 | 
					    _in_ds: bool = False
 | 
				
			||||||
 | 
					    _index_step: float | None = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # map from uppx -> (downsampled data, incremental graphics)
 | 
					    # map from uppx -> (downsampled data, incremental graphics)
 | 
				
			||||||
    _src_r: Optional[Renderer] = None
 | 
					    _src_r: Optional[Renderer] = None
 | 
				
			||||||
| 
						 | 
					@ -244,12 +251,6 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
        tuple[Renderer, pg.GraphicsItem],
 | 
					        tuple[Renderer, pg.GraphicsItem],
 | 
				
			||||||
    ] = (None, None)
 | 
					    ] = (None, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # TODO: hackery to be able to set a shm later
 | 
					 | 
				
			||||||
    # but whilst also allowing this type to hashable,
 | 
					 | 
				
			||||||
    # likely will require serializable token that is used to attach
 | 
					 | 
				
			||||||
    # to the underlying shm ref after startup?
 | 
					 | 
				
			||||||
    # _shm: Optional[ShmArray] = None  # currently, may be filled in "later"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # cache of y-range values per x-range input.
 | 
					    # cache of y-range values per x-range input.
 | 
				
			||||||
    _mxmns: dict[tuple[int, int], tuple[float, float]] = {}
 | 
					    _mxmns: dict[tuple[int, int], tuple[float, float]] = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -261,6 +262,17 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
    def index_field(self) -> str:
 | 
					    def index_field(self) -> str:
 | 
				
			||||||
        return self._index_field
 | 
					        return self._index_field
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def index_step(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        reset: bool = False,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ) -> float:
 | 
				
			||||||
 | 
					        if self._index_step is None:
 | 
				
			||||||
 | 
					            index = self.shm.array[self.index_field]
 | 
				
			||||||
 | 
					            self._index_step = index[-1] - index[-2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self._index_step
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def maxmin(
 | 
					    def maxmin(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        lbar: int,
 | 
					        lbar: int,
 | 
				
			||||||
| 
						 | 
					@ -275,23 +287,35 @@ 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 = (lbar, rbar)
 | 
					        rkey = (round(lbar), round(rbar))
 | 
				
			||||||
        cached_result = self._mxmns.get(rkey)
 | 
					        cached_result = self._mxmns.get(rkey)
 | 
				
			||||||
 | 
					        do_print = 'btc' in self.name
 | 
				
			||||||
        if cached_result:
 | 
					        if cached_result:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # if do_print:
 | 
				
			||||||
 | 
					            #     print(
 | 
				
			||||||
 | 
					            #         f'{self.name} CACHED maxmin\n'
 | 
				
			||||||
 | 
					            #         f'{rkey} -> {cached_result}'
 | 
				
			||||||
 | 
					            #     )
 | 
				
			||||||
            return cached_result
 | 
					            return cached_result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        shm = self.shm
 | 
					        shm = self.shm
 | 
				
			||||||
        if shm is None:
 | 
					        if shm is None:
 | 
				
			||||||
 | 
					            breakpoint()
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        arr = shm.array
 | 
					        arr = shm.array
 | 
				
			||||||
 | 
					        # times = arr['time']
 | 
				
			||||||
 | 
					        # step = round(times[-1] - times[-2])
 | 
				
			||||||
 | 
					        # if (
 | 
				
			||||||
 | 
					        #     do_print
 | 
				
			||||||
 | 
					        #     and step == 60
 | 
				
			||||||
 | 
					        # ):
 | 
				
			||||||
 | 
					        #     breakpoint()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # get relative slice indexes into array
 | 
					        # get relative slice indexes into array
 | 
				
			||||||
        if self.index_field == 'time':
 | 
					        if self.index_field == 'time':
 | 
				
			||||||
            (
 | 
					            read_slc = slice_from_time(
 | 
				
			||||||
                abs_slc,
 | 
					 | 
				
			||||||
                read_slc,
 | 
					 | 
				
			||||||
            ) = slice_from_time(
 | 
					 | 
				
			||||||
                arr,
 | 
					                arr,
 | 
				
			||||||
                start_t=lbar,
 | 
					                start_t=lbar,
 | 
				
			||||||
                stop_t=rbar,
 | 
					                stop_t=rbar,
 | 
				
			||||||
| 
						 | 
					@ -306,11 +330,17 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
            ]
 | 
					            ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not slice_view.size:
 | 
					        if not slice_view.size:
 | 
				
			||||||
 | 
					            log.warning(f'{self.name} no maxmin in view?')
 | 
				
			||||||
 | 
					            # breakpoint()
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        elif self.yrange:
 | 
					        elif self.yrange:
 | 
				
			||||||
            mxmn = self.yrange
 | 
					            mxmn = self.yrange
 | 
				
			||||||
            # print(f'{self.name} M4 maxmin: {mxmn}')
 | 
					            if do_print:
 | 
				
			||||||
 | 
					                print(
 | 
				
			||||||
 | 
					                    f'{self.name} M4 maxmin:\n'
 | 
				
			||||||
 | 
					                    f'{rkey} -> {mxmn}'
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            if self.is_ohlc:
 | 
					            if self.is_ohlc:
 | 
				
			||||||
| 
						 | 
					@ -323,7 +353,11 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
                yhigh = np.max(view)
 | 
					                yhigh = np.max(view)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            mxmn = ylow, yhigh
 | 
					            mxmn = ylow, yhigh
 | 
				
			||||||
            # print(f'{self.name} MANUAL maxmin: {mxmn}')
 | 
					            if do_print:
 | 
				
			||||||
 | 
					                print(
 | 
				
			||||||
 | 
					                    f'{self.name} MANUAL ohlc={self.is_ohlc} maxmin:\n'
 | 
				
			||||||
 | 
					                    f'{rkey} -> {mxmn}'
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # cache result for input range
 | 
					        # cache result for input range
 | 
				
			||||||
        assert mxmn
 | 
					        assert mxmn
 | 
				
			||||||
| 
						 | 
					@ -333,8 +367,7 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def view_range(self) -> tuple[int, int]:
 | 
					    def view_range(self) -> tuple[int, int]:
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        Return the indexes in view for the associated
 | 
					        Return the start and stop x-indexes for the managed ``ViewBox``.
 | 
				
			||||||
        plot displaying this viz's data.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        vr = self.plot.viewRect()
 | 
					        vr = self.plot.viewRect()
 | 
				
			||||||
| 
						 | 
					@ -345,15 +378,18 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def bars_range(self) -> tuple[int, int, int, int]:
 | 
					    def bars_range(self) -> tuple[int, int, int, int]:
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        Return a range tuple for the bars present in view.
 | 
					        Return a range tuple for the left-view, left-datum, right-datum
 | 
				
			||||||
 | 
					        and right-view x-indices.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        start, l, datum_start, datum_stop, r, stop = self.datums_range()
 | 
					        l, start, datum_start, datum_stop, stop, r = self.datums_range()
 | 
				
			||||||
        return l, datum_start, datum_stop, r
 | 
					        return l, datum_start, datum_stop, r
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def datums_range(
 | 
					    def datums_range(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
 | 
					        view_range: None | tuple[float, float] = None,
 | 
				
			||||||
        index_field: str | None = None,
 | 
					        index_field: str | None = None,
 | 
				
			||||||
 | 
					        array: None | np.ndarray = None,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ) -> tuple[
 | 
					    ) -> tuple[
 | 
				
			||||||
        int, int, int, int, int, int
 | 
					        int, int, int, int, int, int
 | 
				
			||||||
| 
						 | 
					@ -362,39 +398,54 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
        Return a range tuple for the datums present in view.
 | 
					        Return a range tuple for the datums present in view.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        l, r = self.view_range()
 | 
					        l, r = view_range or self.view_range()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        index_field: str = index_field or self.index_field
 | 
					        index_field: str = index_field or self.index_field
 | 
				
			||||||
        if index_field == 'index':
 | 
					        if index_field == 'index':
 | 
				
			||||||
            l, r = round(l), round(r)
 | 
					            l, r = round(l), round(r)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if array is None:
 | 
				
			||||||
            array = self.shm.array
 | 
					            array = self.shm.array
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        index = array[index_field]
 | 
					        index = array[index_field]
 | 
				
			||||||
        start = index[0]
 | 
					        first = round(index[0])
 | 
				
			||||||
        stop = index[-1]
 | 
					        last = round(index[-1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # first and last datums in view determined by
 | 
				
			||||||
 | 
					        # l / r view range.
 | 
				
			||||||
 | 
					        leftmost = round(l)
 | 
				
			||||||
 | 
					        rightmost = round(r)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # invalid view state
 | 
				
			||||||
        if (
 | 
					        if (
 | 
				
			||||||
            l < 0
 | 
					            r < l
 | 
				
			||||||
            or r < l
 | 
					            or l < 0
 | 
				
			||||||
            or l < start
 | 
					            or r < 0
 | 
				
			||||||
 | 
					            or (l > last and r > last)
 | 
				
			||||||
        ):
 | 
					        ):
 | 
				
			||||||
            datum_start = start
 | 
					            leftmost = first
 | 
				
			||||||
            datum_stop = stop
 | 
					            rightmost = last
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            datum_start = max(l, start)
 | 
					            rightmost = max(
 | 
				
			||||||
            datum_stop = r
 | 
					                min(last, rightmost),
 | 
				
			||||||
            if l < stop:
 | 
					                first,
 | 
				
			||||||
                datum_stop = min(r, stop)
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert datum_start < datum_stop
 | 
					            leftmost = min(
 | 
				
			||||||
 | 
					                max(first, leftmost),
 | 
				
			||||||
 | 
					                last,
 | 
				
			||||||
 | 
					                rightmost - 1,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assert leftmost < rightmost
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            start,
 | 
					 | 
				
			||||||
            l,  # left x-in-view
 | 
					            l,  # left x-in-view
 | 
				
			||||||
            datum_start,
 | 
					            first,  # first datum
 | 
				
			||||||
            datum_stop,
 | 
					            leftmost,
 | 
				
			||||||
 | 
					            rightmost,
 | 
				
			||||||
 | 
					            last,  # last_datum
 | 
				
			||||||
            r,  # right-x-in-view
 | 
					            r,  # right-x-in-view
 | 
				
			||||||
            stop,
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def read(
 | 
					    def read(
 | 
				
			||||||
| 
						 | 
					@ -415,6 +466,7 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        index_field: str = index_field or self.index_field
 | 
					        index_field: str = index_field or self.index_field
 | 
				
			||||||
 | 
					        vr = l, r = self.view_range()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # readable data
 | 
					        # readable data
 | 
				
			||||||
        array = self.shm.array
 | 
					        array = self.shm.array
 | 
				
			||||||
| 
						 | 
					@ -423,13 +475,17 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
            profiler('self.shm.array READ')
 | 
					            profiler('self.shm.array READ')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        (
 | 
					        (
 | 
				
			||||||
            ifirst,
 | 
					 | 
				
			||||||
            l,
 | 
					            l,
 | 
				
			||||||
 | 
					            ifirst,
 | 
				
			||||||
            lbar,
 | 
					            lbar,
 | 
				
			||||||
            rbar,
 | 
					            rbar,
 | 
				
			||||||
            r,
 | 
					 | 
				
			||||||
            ilast,
 | 
					            ilast,
 | 
				
			||||||
        ) = self.datums_range(index_field=index_field)
 | 
					            r,
 | 
				
			||||||
 | 
					        ) = self.datums_range(
 | 
				
			||||||
 | 
					            view_range=vr,
 | 
				
			||||||
 | 
					            index_field=index_field,
 | 
				
			||||||
 | 
					            array=array,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        # if rbar < lbar:
 | 
					        # if rbar < lbar:
 | 
				
			||||||
        #     breakpoint()
 | 
					        #     breakpoint()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -440,26 +496,22 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # TODO: support time slicing
 | 
					        # TODO: support time slicing
 | 
				
			||||||
        if index_field == 'time':
 | 
					        if index_field == 'time':
 | 
				
			||||||
            (
 | 
					            read_slc = slice_from_time(
 | 
				
			||||||
                abs_slc,
 | 
					 | 
				
			||||||
                read_slc,
 | 
					 | 
				
			||||||
            ) = slice_from_time(
 | 
					 | 
				
			||||||
                array,
 | 
					                array,
 | 
				
			||||||
                start_t=lbar,
 | 
					                start_t=lbar,
 | 
				
			||||||
                stop_t=rbar,
 | 
					                stop_t=rbar,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            in_view = array[read_slc]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # diff = rbar - lbar
 | 
					            # TODO: maybe we should return this from the slicer call
 | 
				
			||||||
            # if (
 | 
					            # above?
 | 
				
			||||||
            #     'btc' in self.name
 | 
					            in_view = array[read_slc]
 | 
				
			||||||
            #     and 'hist' not in self.shm.token
 | 
					            if in_view.size:
 | 
				
			||||||
            # ):
 | 
					                abs_indx = in_view['index']
 | 
				
			||||||
            #     print(
 | 
					                abs_slc = slice(
 | 
				
			||||||
            #         f'{self.name}: len(iv) = {len(in_view)}\n'
 | 
					                    int(abs_indx[0]),
 | 
				
			||||||
            #         f'start/stop: {lbar},{rbar}\n',
 | 
					                    int(abs_indx[-1]),
 | 
				
			||||||
            #         f'diff: {diff}\n',
 | 
					                )
 | 
				
			||||||
            #     )
 | 
					
 | 
				
			||||||
            if profiler:
 | 
					            if profiler:
 | 
				
			||||||
                profiler(
 | 
					                profiler(
 | 
				
			||||||
                    '`slice_from_time('
 | 
					                    '`slice_from_time('
 | 
				
			||||||
| 
						 | 
					@ -635,8 +687,6 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
            should_redraw = True
 | 
					            should_redraw = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            showing_src_data = True
 | 
					            showing_src_data = True
 | 
				
			||||||
            # reset yrange to be computed from source data
 | 
					 | 
				
			||||||
            self.yrange = None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # MAIN RENDER LOGIC:
 | 
					        # MAIN RENDER LOGIC:
 | 
				
			||||||
        # - determine in view data and redraw on range change
 | 
					        # - determine in view data and redraw on range change
 | 
				
			||||||
| 
						 | 
					@ -662,10 +712,6 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            **rkwargs,
 | 
					            **rkwargs,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        if showing_src_data:
 | 
					 | 
				
			||||||
            # print(f"{self.name} SHOWING SOURCE")
 | 
					 | 
				
			||||||
            # reset yrange to be computed from source data
 | 
					 | 
				
			||||||
            self.yrange = None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not out:
 | 
					        if not out:
 | 
				
			||||||
            log.warning(f'{self.name} failed to render!?')
 | 
					            log.warning(f'{self.name} failed to render!?')
 | 
				
			||||||
| 
						 | 
					@ -678,7 +724,6 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # XXX: SUPER UGGGHHH... without this we get stale cache
 | 
					        # XXX: SUPER UGGGHHH... without this we get stale cache
 | 
				
			||||||
        # graphics that don't update until you downsampler again..
 | 
					        # graphics that don't update until you downsampler again..
 | 
				
			||||||
        # reset = False
 | 
					 | 
				
			||||||
        # if reset:
 | 
					        # if reset:
 | 
				
			||||||
        #     with graphics.reset_cache():
 | 
					        #     with graphics.reset_cache():
 | 
				
			||||||
        #         # assign output paths to graphicis obj
 | 
					        #         # assign output paths to graphicis obj
 | 
				
			||||||
| 
						 | 
					@ -798,6 +843,129 @@ class Viz(msgspec.Struct):  # , frozen=True):
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        ).length()
 | 
					        ).length()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def default_view(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        bars_from_y: int = int(616 * 3/8),
 | 
				
			||||||
 | 
					        y_offset: int = 0,
 | 
				
			||||||
 | 
					        do_ds: bool = True,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ) -> None:
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Set the plot's viewbox to a "default" startup setting where
 | 
				
			||||||
 | 
					        we try to show the underlying data range sanely.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        shm: ShmArray = self.shm
 | 
				
			||||||
 | 
					        array: np.ndarray = shm.array
 | 
				
			||||||
 | 
					        view: ChartView = self.plot.vb
 | 
				
			||||||
 | 
					        (
 | 
				
			||||||
 | 
					            vl,
 | 
				
			||||||
 | 
					            first_datum,
 | 
				
			||||||
 | 
					            datum_start,
 | 
				
			||||||
 | 
					            datum_stop,
 | 
				
			||||||
 | 
					            last_datum,
 | 
				
			||||||
 | 
					            vr,
 | 
				
			||||||
 | 
					        ) = self.datums_range(array=array)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # invalid case: view is not ordered correctly
 | 
				
			||||||
 | 
					        # return and expect caller to sort it out.
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					            vl > vr
 | 
				
			||||||
 | 
					        ):
 | 
				
			||||||
 | 
					            log.warning(
 | 
				
			||||||
 | 
					                'Skipping `.default_view()` viewbox not initialized..\n'
 | 
				
			||||||
 | 
					                f'l -> r: {vl} -> {vr}\n'
 | 
				
			||||||
 | 
					                f'datum_start -> datum_stop: {datum_start} -> {datum_stop}\n'
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        chartw: ChartPlotWidget = self.plot.getViewWidget()
 | 
				
			||||||
 | 
					        index_field = self.index_field
 | 
				
			||||||
 | 
					        step = self.index_step()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if index_field == 'time':
 | 
				
			||||||
 | 
					            # transform l -> r view range values into
 | 
				
			||||||
 | 
					            # data index domain to determine how view
 | 
				
			||||||
 | 
					            # should be reset to better match data.
 | 
				
			||||||
 | 
					            read_slc = slice_from_time(
 | 
				
			||||||
 | 
					                array,
 | 
				
			||||||
 | 
					                start_t=vl,
 | 
				
			||||||
 | 
					                stop_t=vr,
 | 
				
			||||||
 | 
					                step=step,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        index_iv = array[index_field][read_slc]
 | 
				
			||||||
 | 
					        uppx: float = self.graphics.x_uppx() or 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # l->r distance in scene units, no larger then data span
 | 
				
			||||||
 | 
					        data_diff = last_datum - first_datum
 | 
				
			||||||
 | 
					        rl_diff = min(vr - vl, data_diff)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # orient by offset from the y-axis including
 | 
				
			||||||
 | 
					        # space to compensate for the L1 labels.
 | 
				
			||||||
 | 
					        if not y_offset:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # we get the L1 spread label "length" in view coords and
 | 
				
			||||||
 | 
					            # make sure it doesn't colide with the right-most datum in
 | 
				
			||||||
 | 
					            # view.
 | 
				
			||||||
 | 
					            _, l1_len = chartw.pre_l1_xs()
 | 
				
			||||||
 | 
					            offset = l1_len/(uppx*step)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # if no L1 label is present just offset by a few datums
 | 
				
			||||||
 | 
					            # from the y-axis.
 | 
				
			||||||
 | 
					            if chartw._max_l1_line_len == 0:
 | 
				
			||||||
 | 
					                offset += 3*step
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # no data is in view so check for the only 2 sane cases:
 | 
				
			||||||
 | 
					        # - entire view is LEFT of data
 | 
				
			||||||
 | 
					        # - entire view is RIGHT of data
 | 
				
			||||||
 | 
					        if index_iv.size == 0:
 | 
				
			||||||
 | 
					            log.warning(f'No data in view for {vl} -> {vr}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # 2 cases either the view is to the left or right of the
 | 
				
			||||||
 | 
					            #   data set.
 | 
				
			||||||
 | 
					            if (
 | 
				
			||||||
 | 
					                vl <= first_datum
 | 
				
			||||||
 | 
					                and vr <= first_datum
 | 
				
			||||||
 | 
					            ):
 | 
				
			||||||
 | 
					                l_reset = first_datum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            elif (
 | 
				
			||||||
 | 
					                vl >= last_datum
 | 
				
			||||||
 | 
					                and vr >= last_datum
 | 
				
			||||||
 | 
					            ):
 | 
				
			||||||
 | 
					                l_reset = r_reset - rl_diff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                raise RuntimeError(f'Unknown view state {vl} -> {vr}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            # maintain the l->r view distance
 | 
				
			||||||
 | 
					            l_reset = r_reset - rl_diff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # remove any custom user yrange setttings
 | 
				
			||||||
 | 
					        if chartw._static_yrange == 'axis':
 | 
				
			||||||
 | 
					            chartw._static_yrange = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        view.setXRange(
 | 
				
			||||||
 | 
					            min=l_reset,
 | 
				
			||||||
 | 
					            max=r_reset,
 | 
				
			||||||
 | 
					            padding=0,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if do_ds:
 | 
				
			||||||
 | 
					            view.maybe_downsample_graphics()
 | 
				
			||||||
 | 
					            view._set_yrange()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # caller should do this!
 | 
				
			||||||
 | 
					            # self.linked.graphics_cycle()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Renderer(msgspec.Struct):
 | 
					class Renderer(msgspec.Struct):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -959,6 +1127,8 @@ class Renderer(msgspec.Struct):
 | 
				
			||||||
        fast_path = self.fast_path
 | 
					        fast_path = self.fast_path
 | 
				
			||||||
        reset = False
 | 
					        reset = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.viz.yrange = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # redraw the entire source data if we have either of:
 | 
					        # redraw the entire source data if we have either of:
 | 
				
			||||||
        # - no prior path graphic rendered or,
 | 
					        # - no prior path graphic rendered or,
 | 
				
			||||||
        # - we always intend to re-render the data only in view
 | 
					        # - we always intend to re-render the data only in view
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue