Add graphics incr-updated "formatter" subsys
After trying to hack epoch indexed time series and failing miserably, decided to properly factor out all formatting routines into a common subsystem API: ``IncrementalFormatter`` which provides the interface for incrementally updating and tracking pre-path-graphics formatted data. Previously this functionality was mangled into our `Renderer` (which also does the work of `QPath` generation and update) but splitting it out also preps for being able to do graphics-buffer downsampling and caching on a remote host B) The ``IncrementalFormatter`` (parent type) has the default behaviour of tracking a single field-array on some source `ShmArray`, updating a flattened `numpy.ndarray` in-mem allocation, and providing a default 1d conversion for pre-downsampling and path generation. Changed out of `Renderer`, - `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to more explicitly named formatter methods. - all `.x/y_data` nd array management and update - "last view range" tracking - `.last_read`, `.diff()` - now calls `IncrementalFormatter.format_to_1d()` inside `.render()` The new API gets, - `.diff()`, `.last_read` - all view range diff tracking through `.track_inview_range()`. - better nd format array names: `.x/y_nd`, `xy_nd_start/stop`. - `.format_to_1d()` which renders pre-path formatted arrays ready for both m4 sampling and path gen. - better explicit overloadable formatting method names: * `.allocate_xy()` -> `.allocate_xy_nd()` * `.update_xy()` -> `.incr_update_xy_nd()` * `.format_xy()` -> `.format_xy_nd_to_1d()` Finally this implements per-graphics-type formatters which define each set up related formatting routines: - `OHLCBarsFmtr`: std multi-line style bars - `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data - `StepCurveFmtr`: handles vlm style curvesepoch_index_backup
parent
cbd4119101
commit
366df3307f
|
@ -25,8 +25,6 @@ incremental update.
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from typing import (
|
from typing import (
|
||||||
Optional,
|
Optional,
|
||||||
Callable,
|
|
||||||
Union,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
import msgspec
|
import msgspec
|
||||||
|
@ -43,21 +41,10 @@ from .._profile import (
|
||||||
# ms_slower_then,
|
# ms_slower_then,
|
||||||
)
|
)
|
||||||
from ._pathops import (
|
from ._pathops import (
|
||||||
by_index_and_key,
|
IncrementalFormatter,
|
||||||
|
OHLCBarsFmtr, # Plain OHLC renderer
|
||||||
# Plain OHLC renderer
|
OHLCBarsAsCurveFmtr, # OHLC converted to line
|
||||||
gen_ohlc_qpath,
|
StepCurveFmtr, # "step" curve (like for vlm)
|
||||||
|
|
||||||
# OHLC -> line renderer
|
|
||||||
ohlc_to_line,
|
|
||||||
update_ohlc_to_line,
|
|
||||||
ohlc_flat_to_xy,
|
|
||||||
|
|
||||||
# step curve renderer
|
|
||||||
to_step_format,
|
|
||||||
update_step_xy,
|
|
||||||
step_to_xy,
|
|
||||||
|
|
||||||
xy_downsample,
|
xy_downsample,
|
||||||
)
|
)
|
||||||
from ._ohlc import (
|
from ._ohlc import (
|
||||||
|
@ -76,16 +63,6 @@ from .._profile import Profiler
|
||||||
log = get_logger(__name__)
|
log = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# class FlowsTable(msgspec.Struct):
|
|
||||||
# '''
|
|
||||||
# Data-AGGRegate: high level API onto multiple (categorized)
|
|
||||||
# ``Flow``s with high level processing routines for
|
|
||||||
# multi-graphics computations and display.
|
|
||||||
|
|
||||||
# '''
|
|
||||||
# flows: dict[str, np.ndarray] = {}
|
|
||||||
|
|
||||||
|
|
||||||
def render_baritems(
|
def render_baritems(
|
||||||
flow: Flow,
|
flow: Flow,
|
||||||
graphics: BarItems,
|
graphics: BarItems,
|
||||||
|
@ -117,21 +94,24 @@ def render_baritems(
|
||||||
r = self._src_r
|
r = self._src_r
|
||||||
if not r:
|
if not r:
|
||||||
show_bars = True
|
show_bars = True
|
||||||
|
|
||||||
# OHLC bars path renderer
|
# OHLC bars path renderer
|
||||||
r = self._src_r = Renderer(
|
r = self._src_r = Renderer(
|
||||||
flow=self,
|
flow=self,
|
||||||
format_xy=gen_ohlc_qpath,
|
fmtr=OHLCBarsFmtr(
|
||||||
last_read=read,
|
shm=flow.shm,
|
||||||
|
flow=flow,
|
||||||
|
_last_read=read,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
ds_curve_r = Renderer(
|
ds_curve_r = Renderer(
|
||||||
flow=self,
|
flow=self,
|
||||||
last_read=read,
|
fmtr=OHLCBarsAsCurveFmtr(
|
||||||
|
shm=flow.shm,
|
||||||
# incr update routines
|
flow=flow,
|
||||||
allocate_xy=ohlc_to_line,
|
_last_read=read,
|
||||||
update_xy=update_ohlc_to_line,
|
),
|
||||||
format_xy=ohlc_flat_to_xy,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
curve = FlattenedOHLC(
|
curve = FlattenedOHLC(
|
||||||
|
@ -228,7 +208,7 @@ class Flow(msgspec.Struct): # , frozen=True):
|
||||||
'''
|
'''
|
||||||
name: str
|
name: str
|
||||||
plot: pg.PlotItem
|
plot: pg.PlotItem
|
||||||
graphics: Union[Curve, BarItems]
|
graphics: Curve | BarItems
|
||||||
_shm: ShmArray
|
_shm: ShmArray
|
||||||
yrange: tuple[float, float] = None
|
yrange: tuple[float, float] = None
|
||||||
|
|
||||||
|
@ -237,7 +217,6 @@ class Flow(msgspec.Struct): # , frozen=True):
|
||||||
# normally this is just a plain line.
|
# normally this is just a plain line.
|
||||||
ds_graphics: Optional[Curve] = None
|
ds_graphics: Optional[Curve] = None
|
||||||
|
|
||||||
|
|
||||||
is_ohlc: bool = False
|
is_ohlc: bool = False
|
||||||
render: bool = True # toggle for display loop
|
render: bool = True # toggle for display loop
|
||||||
|
|
||||||
|
@ -445,9 +424,14 @@ class Flow(msgspec.Struct): # , frozen=True):
|
||||||
|
|
||||||
slice_to_head: int = -1
|
slice_to_head: int = -1
|
||||||
should_redraw: bool = False
|
should_redraw: bool = False
|
||||||
|
should_line: bool = False
|
||||||
rkwargs = {}
|
rkwargs = {}
|
||||||
|
|
||||||
should_line = False
|
# TODO: probably specialize ``Renderer`` types instead of
|
||||||
|
# these logic checks?
|
||||||
|
# - put these blocks into a `.load_renderer()` meth?
|
||||||
|
# - consider a OHLCRenderer, StepCurveRenderer, Renderer?
|
||||||
|
r = self._src_r
|
||||||
if isinstance(graphics, BarItems):
|
if isinstance(graphics, BarItems):
|
||||||
# XXX: special case where we change out graphics
|
# XXX: special case where we change out graphics
|
||||||
# to a line after a certain uppx threshold.
|
# to a line after a certain uppx threshold.
|
||||||
|
@ -467,16 +451,36 @@ class Flow(msgspec.Struct): # , frozen=True):
|
||||||
should_redraw = changed_to_line or not should_line
|
should_redraw = changed_to_line or not should_line
|
||||||
self._in_ds = should_line
|
self._in_ds = should_line
|
||||||
|
|
||||||
else:
|
elif not r:
|
||||||
r = self._src_r
|
if isinstance(graphics, StepCurve):
|
||||||
if not r:
|
|
||||||
# just using for ``.diff()`` atm..
|
|
||||||
r = self._src_r = Renderer(
|
r = self._src_r = Renderer(
|
||||||
flow=self,
|
flow=self,
|
||||||
# TODO: rename this to something with ohlc
|
fmtr=StepCurveFmtr(
|
||||||
last_read=read,
|
shm=self.shm,
|
||||||
|
flow=self,
|
||||||
|
_last_read=read,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# TODO: append logic inside ``.render()`` isn't
|
||||||
|
# correct yet for step curves.. remove this to see it.
|
||||||
|
should_redraw = True
|
||||||
|
slice_to_head = -2
|
||||||
|
|
||||||
|
else:
|
||||||
|
r = self._src_r
|
||||||
|
if not r:
|
||||||
|
# just using for ``.diff()`` atm..
|
||||||
|
r = self._src_r = Renderer(
|
||||||
|
flow=self,
|
||||||
|
fmtr=IncrementalFormatter(
|
||||||
|
shm=self.shm,
|
||||||
|
flow=self,
|
||||||
|
_last_read=read,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# ``Curve`` derivative case(s):
|
# ``Curve`` derivative case(s):
|
||||||
array_key = array_key or self.name
|
array_key = array_key or self.name
|
||||||
# print(array_key)
|
# print(array_key)
|
||||||
|
@ -486,19 +490,6 @@ class Flow(msgspec.Struct): # , frozen=True):
|
||||||
should_ds: bool = r._in_ds
|
should_ds: bool = r._in_ds
|
||||||
showing_src_data: bool = not r._in_ds
|
showing_src_data: bool = not r._in_ds
|
||||||
|
|
||||||
# step_mode = getattr(graphics, '_step_mode', False)
|
|
||||||
step_mode = isinstance(graphics, StepCurve)
|
|
||||||
if step_mode:
|
|
||||||
|
|
||||||
r.allocate_xy = to_step_format
|
|
||||||
r.update_xy = update_step_xy
|
|
||||||
r.format_xy = step_to_xy
|
|
||||||
|
|
||||||
# TODO: append logic inside ``.render()`` isn't
|
|
||||||
# correct yet for step curves.. remove this to see it.
|
|
||||||
should_redraw = True
|
|
||||||
slice_to_head = -2
|
|
||||||
|
|
||||||
# downsampling incremental state checking
|
# downsampling incremental state checking
|
||||||
# check for and set std m4 downsample conditions
|
# check for and set std m4 downsample conditions
|
||||||
uppx = graphics.x_uppx()
|
uppx = graphics.x_uppx()
|
||||||
|
@ -680,34 +671,7 @@ class Flow(msgspec.Struct): # , frozen=True):
|
||||||
class Renderer(msgspec.Struct):
|
class Renderer(msgspec.Struct):
|
||||||
|
|
||||||
flow: Flow
|
flow: Flow
|
||||||
# last array view read
|
fmtr: IncrementalFormatter
|
||||||
last_read: Optional[tuple] = None
|
|
||||||
|
|
||||||
# default just returns index, and named array from data
|
|
||||||
format_xy: Callable[
|
|
||||||
[np.ndarray, str],
|
|
||||||
tuple[np.ndarray]
|
|
||||||
] = by_index_and_key
|
|
||||||
|
|
||||||
# optional pre-graphics xy formatted data which
|
|
||||||
# is incrementally updated in sync with the source data.
|
|
||||||
allocate_xy: Optional[Callable[
|
|
||||||
[int, slice],
|
|
||||||
tuple[np.ndarray, np.nd.array]
|
|
||||||
]] = None
|
|
||||||
|
|
||||||
update_xy: Optional[Callable[
|
|
||||||
[int, slice], None]
|
|
||||||
] = None
|
|
||||||
|
|
||||||
x_data: Optional[np.ndarray] = None
|
|
||||||
y_data: Optional[np.ndarray] = None
|
|
||||||
|
|
||||||
# indexes which slice into the above arrays (which are allocated
|
|
||||||
# based on source data shm input size) and allow retrieving
|
|
||||||
# incrementally updated data.
|
|
||||||
_xy_first: int = 0
|
|
||||||
_xy_last: int = 0
|
|
||||||
|
|
||||||
# output graphics rendering, the main object
|
# output graphics rendering, the main object
|
||||||
# processed in ``QGraphicsObject.paint()``
|
# processed in ``QGraphicsObject.paint()``
|
||||||
|
@ -729,58 +693,11 @@ class Renderer(msgspec.Struct):
|
||||||
_last_uppx: float = 0
|
_last_uppx: float = 0
|
||||||
_in_ds: bool = False
|
_in_ds: bool = False
|
||||||
|
|
||||||
# incremental update state(s)
|
|
||||||
_last_vr: Optional[tuple[float, float]] = None
|
|
||||||
_last_ivr: Optional[tuple[float, float]] = None
|
|
||||||
|
|
||||||
def diff(
|
|
||||||
self,
|
|
||||||
new_read: tuple[np.ndarray],
|
|
||||||
|
|
||||||
) -> tuple[
|
|
||||||
np.ndarray,
|
|
||||||
np.ndarray,
|
|
||||||
]:
|
|
||||||
(
|
|
||||||
last_xfirst,
|
|
||||||
last_xlast,
|
|
||||||
last_array,
|
|
||||||
last_ivl,
|
|
||||||
last_ivr,
|
|
||||||
last_in_view,
|
|
||||||
) = self.last_read
|
|
||||||
|
|
||||||
# TODO: can the renderer just call ``Flow.read()`` directly?
|
|
||||||
# unpack latest source data read
|
|
||||||
(
|
|
||||||
xfirst,
|
|
||||||
xlast,
|
|
||||||
array,
|
|
||||||
ivl,
|
|
||||||
ivr,
|
|
||||||
in_view,
|
|
||||||
) = new_read
|
|
||||||
|
|
||||||
# compute the length diffs between the first/last index entry in
|
|
||||||
# the input data and the last indexes we have on record from the
|
|
||||||
# last time we updated the curve index.
|
|
||||||
prepend_length = int(last_xfirst - xfirst)
|
|
||||||
append_length = int(xlast - last_xlast)
|
|
||||||
|
|
||||||
# blah blah blah
|
|
||||||
# do diffing for prepend, append and last entry
|
|
||||||
return (
|
|
||||||
slice(xfirst, last_xfirst),
|
|
||||||
prepend_length,
|
|
||||||
append_length,
|
|
||||||
slice(last_xlast, xlast),
|
|
||||||
)
|
|
||||||
|
|
||||||
def draw_path(
|
def draw_path(
|
||||||
self,
|
self,
|
||||||
x: np.ndarray,
|
x: np.ndarray,
|
||||||
y: np.ndarray,
|
y: np.ndarray,
|
||||||
connect: Union[str, np.ndarray] = 'all',
|
connect: str | np.ndarray = 'all',
|
||||||
path: Optional[QPainterPath] = None,
|
path: Optional[QPainterPath] = None,
|
||||||
redraw: bool = False,
|
redraw: bool = False,
|
||||||
|
|
||||||
|
@ -858,166 +775,50 @@ class Renderer(msgspec.Struct):
|
||||||
'''
|
'''
|
||||||
# TODO: can the renderer just call ``Flow.read()`` directly?
|
# TODO: can the renderer just call ``Flow.read()`` directly?
|
||||||
# unpack latest source data read
|
# unpack latest source data read
|
||||||
|
fmtr = self.fmtr
|
||||||
|
|
||||||
(
|
(
|
||||||
xfirst,
|
_,
|
||||||
xlast,
|
_,
|
||||||
array,
|
array,
|
||||||
ivl,
|
ivl,
|
||||||
ivr,
|
ivr,
|
||||||
in_view,
|
in_view,
|
||||||
) = new_read
|
) = new_read
|
||||||
|
|
||||||
(
|
|
||||||
pre_slice,
|
|
||||||
prepend_length,
|
|
||||||
append_length,
|
|
||||||
post_slice,
|
|
||||||
) = self.diff(new_read)
|
|
||||||
|
|
||||||
if self.update_xy:
|
|
||||||
|
|
||||||
shm = self.flow.shm
|
|
||||||
|
|
||||||
if self.y_data is None:
|
|
||||||
# we first need to allocate xy data arrays
|
|
||||||
# from the source data.
|
|
||||||
assert self.allocate_xy
|
|
||||||
self.x_data, self.y_data = self.allocate_xy(
|
|
||||||
shm,
|
|
||||||
array_key,
|
|
||||||
)
|
|
||||||
self._xy_first = shm._first.value
|
|
||||||
self._xy_last = shm._last.value
|
|
||||||
profiler('allocated xy history')
|
|
||||||
|
|
||||||
if prepend_length:
|
|
||||||
y_prepend = shm._array[pre_slice]
|
|
||||||
|
|
||||||
if read_from_key:
|
|
||||||
y_prepend = y_prepend[array_key]
|
|
||||||
|
|
||||||
xy_data, xy_slice = self.update_xy(
|
|
||||||
shm,
|
|
||||||
array_key,
|
|
||||||
|
|
||||||
# this is the pre-sliced, "normally expected"
|
|
||||||
# new data that an updater would normally be
|
|
||||||
# expected to process, however in some cases (like
|
|
||||||
# step curves) the updater routine may want to do
|
|
||||||
# the source history-data reading itself, so we pass
|
|
||||||
# both here.
|
|
||||||
y_prepend,
|
|
||||||
|
|
||||||
pre_slice,
|
|
||||||
prepend_length,
|
|
||||||
self._xy_first,
|
|
||||||
self._xy_last,
|
|
||||||
is_append=False,
|
|
||||||
)
|
|
||||||
self.y_data[xy_slice] = xy_data
|
|
||||||
self._xy_first = shm._first.value
|
|
||||||
profiler('prepended xy history: {prepend_length}')
|
|
||||||
|
|
||||||
if append_length:
|
|
||||||
y_append = shm._array[post_slice]
|
|
||||||
|
|
||||||
if read_from_key:
|
|
||||||
y_append = y_append[array_key]
|
|
||||||
|
|
||||||
xy_data, xy_slice = self.update_xy(
|
|
||||||
shm,
|
|
||||||
array_key,
|
|
||||||
|
|
||||||
y_append,
|
|
||||||
post_slice,
|
|
||||||
append_length,
|
|
||||||
|
|
||||||
self._xy_first,
|
|
||||||
self._xy_last,
|
|
||||||
is_append=True,
|
|
||||||
)
|
|
||||||
# self.y_data[post_slice] = xy_data
|
|
||||||
# self.y_data[xy_slice or post_slice] = xy_data
|
|
||||||
self.y_data[xy_slice] = xy_data
|
|
||||||
self._xy_last = shm._last.value
|
|
||||||
profiler('appened xy history: {append_length}')
|
|
||||||
|
|
||||||
if use_vr:
|
|
||||||
array = in_view
|
|
||||||
# else:
|
|
||||||
# ivl, ivr = xfirst, xlast
|
|
||||||
|
|
||||||
hist = array[:slice_to_head]
|
|
||||||
|
|
||||||
# xy-path data transform: convert source data to a format
|
# xy-path data transform: convert source data to a format
|
||||||
# able to be passed to a `QPainterPath` rendering routine.
|
# able to be passed to a `QPainterPath` rendering routine.
|
||||||
if not len(hist):
|
fmt_out = fmtr.format_to_1d(
|
||||||
|
new_read,
|
||||||
|
array_key,
|
||||||
|
profiler,
|
||||||
|
|
||||||
|
slice_to_head=slice_to_head,
|
||||||
|
read_src_from_key=read_from_key,
|
||||||
|
slice_to_inview=use_vr,
|
||||||
|
)
|
||||||
|
|
||||||
|
# no history in view case
|
||||||
|
if not fmt_out:
|
||||||
# XXX: this might be why the profiler only has exits?
|
# XXX: this might be why the profiler only has exits?
|
||||||
return
|
return
|
||||||
|
|
||||||
x_out, y_out, connect = self.format_xy(
|
(
|
||||||
self,
|
x_1d,
|
||||||
# TODO: hist here should be the pre-sliced
|
y_1d,
|
||||||
# x/y_data in the case where allocate_xy is
|
connect,
|
||||||
# defined?
|
prepend_length,
|
||||||
hist,
|
append_length,
|
||||||
array_key,
|
view_changed,
|
||||||
(ivl, ivr),
|
|
||||||
)
|
|
||||||
|
|
||||||
profiler('sliced input arrays')
|
) = fmt_out
|
||||||
|
|
||||||
if (
|
|
||||||
use_vr
|
|
||||||
):
|
|
||||||
# if a view range is passed, plan to draw the
|
|
||||||
# source ouput that's "in view" of the chart.
|
|
||||||
view_range = (ivl, ivr)
|
|
||||||
# print(f'{self._name} vr: {view_range}')
|
|
||||||
|
|
||||||
profiler(f'view range slice {view_range}')
|
|
||||||
|
|
||||||
vl, vr = view_range
|
|
||||||
|
|
||||||
zoom_or_append = False
|
|
||||||
last_vr = self._last_vr
|
|
||||||
last_ivr = self._last_ivr or vl, vr
|
|
||||||
|
|
||||||
# incremental in-view data update.
|
|
||||||
if last_vr:
|
|
||||||
# relative slice indices
|
|
||||||
lvl, lvr = last_vr
|
|
||||||
# abs slice indices
|
|
||||||
al, ar = last_ivr
|
|
||||||
|
|
||||||
# left_change = abs(x_iv[0] - al) >= 1
|
|
||||||
# right_change = abs(x_iv[-1] - ar) >= 1
|
|
||||||
|
|
||||||
if (
|
|
||||||
# likely a zoom view change
|
|
||||||
(vr - lvr) > 2 or vl < lvl
|
|
||||||
# append / prepend update
|
|
||||||
# we had an append update where the view range
|
|
||||||
# didn't change but the data-viewed (shifted)
|
|
||||||
# underneath, so we need to redraw.
|
|
||||||
# or left_change and right_change and last_vr == view_range
|
|
||||||
|
|
||||||
# not (left_change and right_change) and ivr
|
|
||||||
# (
|
|
||||||
# or abs(x_iv[ivr] - livr) > 1
|
|
||||||
):
|
|
||||||
zoom_or_append = True
|
|
||||||
|
|
||||||
self._last_vr = view_range
|
|
||||||
if len(x_out):
|
|
||||||
self._last_ivr = x_out[0], x_out[slice_to_head]
|
|
||||||
|
|
||||||
# redraw conditions
|
# redraw conditions
|
||||||
if (
|
if (
|
||||||
prepend_length > 0
|
prepend_length > 0
|
||||||
or new_sample_rate
|
or new_sample_rate
|
||||||
or append_length > 0
|
or append_length > 0
|
||||||
or zoom_or_append
|
or view_changed
|
||||||
):
|
):
|
||||||
should_redraw = True
|
should_redraw = True
|
||||||
|
|
||||||
|
@ -1039,9 +840,9 @@ class Renderer(msgspec.Struct):
|
||||||
|
|
||||||
elif should_ds and uppx > 1:
|
elif should_ds and uppx > 1:
|
||||||
|
|
||||||
x_out, y_out, ymn, ymx = xy_downsample(
|
x_1d, y_1d, ymn, ymx = xy_downsample(
|
||||||
x_out,
|
x_1d,
|
||||||
y_out,
|
y_1d,
|
||||||
uppx,
|
uppx,
|
||||||
)
|
)
|
||||||
self.flow.yrange = ymn, ymx
|
self.flow.yrange = ymn, ymx
|
||||||
|
@ -1052,8 +853,8 @@ class Renderer(msgspec.Struct):
|
||||||
self._in_ds = True
|
self._in_ds = True
|
||||||
|
|
||||||
path = self.draw_path(
|
path = self.draw_path(
|
||||||
x=x_out,
|
x=x_1d,
|
||||||
y=y_out,
|
y=y_1d,
|
||||||
connect=connect,
|
connect=connect,
|
||||||
path=path,
|
path=path,
|
||||||
redraw=True,
|
redraw=True,
|
||||||
|
@ -1088,8 +889,8 @@ class Renderer(msgspec.Struct):
|
||||||
and not should_redraw
|
and not should_redraw
|
||||||
):
|
):
|
||||||
print(f'{array_key} append len: {append_length}')
|
print(f'{array_key} append len: {append_length}')
|
||||||
new_x = x_out[-append_length - 2:] # slice_to_head]
|
new_x = x_1d[-append_length - 2:] # slice_to_head]
|
||||||
new_y = y_out[-append_length - 2:] # slice_to_head]
|
new_y = y_1d[-append_length - 2:] # slice_to_head]
|
||||||
profiler('sliced append path')
|
profiler('sliced append path')
|
||||||
|
|
||||||
profiler(
|
profiler(
|
||||||
|
@ -1137,10 +938,4 @@ class Renderer(msgspec.Struct):
|
||||||
self.path = path
|
self.path = path
|
||||||
self.fast_path = fast_path
|
self.fast_path = fast_path
|
||||||
|
|
||||||
# TODO: eventually maybe we can implement some kind of
|
|
||||||
# transform on the ``QPainterPath`` that will more or less
|
|
||||||
# detect the diff in "elements" terms?
|
|
||||||
# update diff state since we've now rendered paths.
|
|
||||||
self.last_read = new_read
|
|
||||||
|
|
||||||
return self.path, array, reset
|
return self.path, array, reset
|
||||||
|
|
1036
piker/ui/_pathops.py
1036
piker/ui/_pathops.py
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue