Use filled rect for current step

A `QRectF` is easier to make and draw (i think?) so use that and fill it
on volume events for decent sleek real-time look. Adjust the step array
generator to allow for an endpoints flag. Comment and/or clean out all
the old path filling calls that gave us perf issues..
fast_step_curve
Tyler Goodlet 2021-09-20 13:38:12 -04:00
parent 0876d2f4fe
commit 5bf8e6a90e
1 changed files with 42 additions and 62 deletions

View File

@ -18,7 +18,7 @@
Fast, smooth, sexy curves. Fast, smooth, sexy curves.
""" """
from typing import Tuple from typing import Optional
import numpy as np import numpy as np
import pyqtgraph as pg import pyqtgraph as pg
@ -37,6 +37,7 @@ from ._style import hcolor
def step_path_arrays_from_1d( def step_path_arrays_from_1d(
x: np.ndarray, x: np.ndarray,
y: np.ndarray, y: np.ndarray,
include_endpoints: bool = False,
) -> (np.ndarray, np.ndarray): ) -> (np.ndarray, np.ndarray):
'''Generate a "step mode" curve aligned with OHLC style bars '''Generate a "step mode" curve aligned with OHLC style bars
@ -63,46 +64,24 @@ def step_path_arrays_from_1d(
# we create a 1d with 2 extra indexes to # we create a 1d with 2 extra indexes to
# hold the start and (current) end value for the steps # hold the start and (current) end value for the steps
# on either end # on either end
y2 = np.empty((len(y), 2), dtype=y.dtype)
y2[:] = y[:, np.newaxis]
y_out = np.empty( y_out = np.empty(
2*len(y) + 2, 2*len(y) + 2,
dtype=y.dtype dtype=y.dtype
) )
y2 = np.empty((len(y), 2), dtype=y.dtype)
y2[:] = y[:, np.newaxis]
# flatten and set 0 endpoints # flatten and set 0 endpoints
y_out[1:-1] = y2.reshape(y2.size) y_out[1:-1] = y2.reshape(y2.size)
y_out[0] = 0 y_out[0] = 0
y_out[-1] = 0 y_out[-1] = 0
return x_out, y_out if not include_endpoints:
return x_out[:-1], y_out[:-1]
else:
def step_lines_from_point( return x_out, y_out
index: float,
level: float,
) -> Tuple[QLineF]:
# TODO: maybe consider using `QGraphicsLineItem` ??
# gives us a ``.boundingRect()`` on the objects which may make
# computing the composite bounding rect of the last bars + the
# history path faster since it's done in C++:
# https://doc.qt.io/qt-5/qgraphicslineitem.html
# index = x[0]
# level = y[0]
# (x0 - 0.5, 0) -> (x0 - 0.5, y0)
left = QLineF(index - 0.5, 0, index - 0.5, level)
# (x0 - 0.5, y0) -> (x1 + 0.5, y1)
top = QLineF(index - 0.5, level, index + 0.5, level)
# (x1 + 0.5, y1 -> (x1 + 0.5, 0)
right = QLineF(index + 0.5, level, index + 0.5, 0)
return [left, top, right]
# TODO: got a feeling that dropping this inheritance gets us even more speedups # TODO: got a feeling that dropping this inheritance gets us even more speedups
@ -112,7 +91,11 @@ class FastAppendCurve(pg.PlotCurveItem):
self, self,
*args, *args,
step_mode: bool = False, step_mode: bool = False,
color: str = 'default_lightest',
fill_color: Optional[str] = None,
**kwargs **kwargs
) -> None: ) -> None:
# TODO: we can probably just dispense with the parent since # TODO: we can probably just dispense with the parent since
@ -120,13 +103,12 @@ class FastAppendCurve(pg.PlotCurveItem):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._last_line: QLineF = None self._last_line: QLineF = None
self._xrange: Tuple[int, int] = self.dataBounds(ax=0) self._xrange: tuple[int, int] = self.dataBounds(ax=0)
self._step_mode: bool = step_mode self._step_mode: bool = step_mode
self._fill = False self._fill = False
color = hcolor('davies') self.setBrush(hcolor(fill_color or color))
self.setBrush(color) self.setPen(hcolor(color))
self.setPen(color)
# TODO: one question still remaining is if this makes trasform # TODO: one question still remaining is if this makes trasform
# interactions slower (such as zooming) and if so maybe if/when # interactions slower (such as zooming) and if so maybe if/when
@ -168,8 +150,8 @@ class FastAppendCurve(pg.PlotCurveItem):
) )
profiler('generate fresh path') profiler('generate fresh path')
if self._step_mode: # if self._step_mode:
self.path.closeSubpath() # self.path.closeSubpath()
# TODO: get this working - right now it's giving heck on vwap... # TODO: get this working - right now it's giving heck on vwap...
# if prepend_length: # if prepend_length:
@ -187,13 +169,14 @@ class FastAppendCurve(pg.PlotCurveItem):
# # self.path.moveTo(new_x[0], new_y[0]) # # self.path.moveTo(new_x[0], new_y[0])
# self.path.connectPath(old_path) # self.path.connectPath(old_path)
if append_length: elif append_length:
if self._step_mode: if self._step_mode:
new_x = x[-append_length - 2:-1] new_x, new_y = step_path_arrays_from_1d(
new_y = y[-append_length - 2:-1] x[-append_length - 2:-1],
new_x, new_y = step_path_arrays_from_1d(new_x, new_y) y[-append_length - 2:-1],
# new_x = new_x[3:] )
# new_y = new_y[3:] new_x = new_x[1:]
new_y = new_y[1:]
else: else:
# print(f"append_length: {append_length}") # print(f"append_length: {append_length}")
@ -205,28 +188,26 @@ class FastAppendCurve(pg.PlotCurveItem):
new_x, new_x,
new_y, new_y,
connect='all', connect='all',
finiteCheck=False, # finiteCheck=False,
) )
path = self.path
if self._step_mode: if self._step_mode:
if self._fill: if self._fill:
path = self.path # XXX: super slow set "union" op
# self.path = self.path.united(append_path).simplified() self.path = self.path.united(append_path).simplified()
path.addPath(append_path.simplified())
# path.connectPath(append_path.simplified()) # path.addPath(append_path)
path.closeSubpath() # path.closeSubpath()
# path.simplified()
else: else:
self.path.connectPath(append_path.simplified()) # path.addPath(append_path)
self.path.connectPath(append_path)
else: else:
# print(f"append_path br: {append_path.boundingRect()}") # print(f"append_path br: {append_path.boundingRect()}")
# self.path.moveTo(new_x[0], new_y[0]) # self.path.moveTo(new_x[0], new_y[0])
# self.path.connectPath(append_path) # self.path.connectPath(append_path)
self.path.connectPath(append_path.simplified()) path.connectPath(append_path)
# if self._step_mode:
# self.path.closeSubpath()
# self.fill_path.connectPath(
# XXX: pretty annoying but, without this there's little # XXX: pretty annoying but, without this there's little
# artefacts on the append updates to the curve... # artefacts on the append updates to the curve...
@ -243,8 +224,7 @@ class FastAppendCurve(pg.PlotCurveItem):
self._xrange = x[0], x[-1] self._xrange = x[0], x[-1]
if self._step_mode: if self._step_mode:
# TODO: use a ``QRectF`` and ``QPainterPath.addRect()`` self._last_step_rect = QRectF(x[-1] - 0.5, 0, x[-1] + 0.5, y[-1])
self._last_step_lines = step_lines_from_point(x[-1], y[-1])
else: else:
self._last_line = QLineF(x[-2], y[-2], x[-1], y[-1]) self._last_line = QLineF(x[-2], y[-2], x[-1], y[-1])
@ -301,16 +281,16 @@ class FastAppendCurve(pg.PlotCurveItem):
if self._step_mode: if self._step_mode:
p.drawLines(*tuple(filter(bool, self._last_step_lines))) brush = self.opts['brush']
# p.drawLines(*tuple(filter(bool, self._last_step_lines)))
# p.drawRect(self._last_step_rect)
p.fillRect(self._last_step_rect, brush)
p.drawPath(self.path) p.drawPath(self.path)
# fill_path = QtGui.QPainterPath(self.path)
# if self._fill:
# self.path.closeSubpath()
if self._fill: if self._fill:
print('FILLED') print('FILLED')
p.fillPath(self.path, self.opts['brush']) p.fillPath(self.path, brush)
profiler('.drawPath()') profiler('.drawPath()')