2222"""
2323
2424try :
25- from typing import Optional , List
25+ from typing import Optional , List , TypeVar
26+
27+ T = TypeVar ("T" )
2628except ImportError :
2729 pass
2830import displayio
2931from adafruit_display_shapes .polygon import Polygon
3032
3133
3234class _CyclicBuffer :
33- def __init__ (self , size : int ) -> None :
34- self ._buffer = [0.0 ] * size
35+ def __init__ (self , size : int , init_value : T ) -> None :
36+ self ._buffer = [init_value ] * size
3537 self ._start = 0 # between 0 and size-1
3638 self ._end = 0 # between 0 and 2*size-1
3739
38- def push (self , value : int | float ) -> None :
40+ def push (self , value : T ) -> None :
3941 """Pushes value at the end of the buffer.
4042
41- :param int|float value: value to be pushed
43+ :param T value: value to be pushed
4244
4345 """
4446
@@ -47,7 +49,7 @@ def push(self, value: int | float) -> None:
4749 self ._buffer [self ._end % len (self ._buffer )] = value
4850 self ._end += 1
4951
50- def pop (self ) -> int | float :
52+ def pop (self ) -> T :
5153 """Pop value from the start of the buffer and returns it."""
5254
5355 if self .len () == 0 :
@@ -70,7 +72,7 @@ def clear(self) -> None:
7072 self ._start = 0
7173 self ._end = 0
7274
73- def values (self ) -> List [int | float ]:
75+ def values (self ) -> List [T ]:
7476 """Returns valid data from the buffer."""
7577
7678 if self .len () == 0 :
@@ -123,10 +125,10 @@ def __init__(
123125 self ._max_items = max_items # maximum number of items in the list
124126 self ._lines = len (colors )
125127 self ._buffers = [
126- _CyclicBuffer (self ._max_items ) for i in range (self ._lines )
128+ _CyclicBuffer (self ._max_items , 0.0 ) for i in range (self ._lines )
127129 ] # values per sparkline
128130 self ._points = [
129- _CyclicBuffer (self ._max_items ) for i in range (self ._lines )
131+ _CyclicBuffer (self ._max_items , ( 0 , 0 ) ) for i in range (self ._lines )
130132 ] # _points: all points of sparkline
131133 self .dyn_xpitch = dyn_xpitch
132134 if not dyn_xpitch :
@@ -196,26 +198,6 @@ def add_values(self, values: List[float], update: bool = True) -> None:
196198 if update :
197199 self .update_line (i )
198200
199- @staticmethod
200- def _xintercept (
201- x_1 : float ,
202- y_1 : float ,
203- x_2 : float ,
204- y_2 : float ,
205- horizontal_y : float ,
206- ) -> Optional [
207- int
208- ]: # finds intercept of the line and a horizontal line at horizontalY
209- slope = (y_2 - y_1 ) / (x_2 - x_1 )
210- b = y_1 - slope * x_1
211-
212- if slope == 0 and y_1 != horizontal_y : # does not intercept horizontalY
213- return None
214- xint = (
215- horizontal_y - b
216- ) / slope # calculate the x-intercept at position y=horizontalY
217- return int (xint )
218-
219201 def _add_point (
220202 self ,
221203 line : int ,
@@ -236,7 +218,6 @@ def _draw(self) -> None:
236218 for i in range (self ._lines ):
237219 Polygon .draw (self ._bitmap , self ._points [i ].values (), i + 1 , close = False )
238220
239- # pylint: disable=too-many-locals, too-many-nested-blocks, too-many-branches
240221 def update_line (self , line : int = None ) -> None :
241222 """Update the drawing of the sparkline.
242223 param int|None line: Line to update. Set to None for updating all (default).
@@ -264,59 +245,11 @@ def update_line(self, line: int = None) -> None:
264245 self ._points [a_line ].clear () # remove all points
265246
266247 for count , value in enumerate (self ._buffers [a_line ].values ()):
267- if count == 0 :
268- self ._add_point (a_line , 0 , value )
269- else :
270- x = int (xpitch * count )
271- last_x = int (xpitch * (count - 1 ))
272- top = self .y_tops [a_line ]
273- bottom = self .y_bottoms [a_line ]
274-
275- if (bottom <= last_value <= top ) and (
276- bottom <= value <= top
277- ): # both points are in range, plot the line
278- self ._add_point (a_line , x , value )
279-
280- else : # at least one point is out of range, clip one or both ends the line
281- if ((last_value > top ) and (value > top )) or (
282- (last_value < bottom ) and (value < bottom )
283- ):
284- # both points are on the same side out of range: don't draw anything
285- pass
286- else :
287- xint_bottom = self ._xintercept (
288- last_x , last_value , x , value , bottom
289- ) # get possible new x intercept points
290- xint_top = self ._xintercept (
291- last_x , last_value , x , value , top
292- ) # on the top and bottom of range
293- if (xint_bottom is None ) or (
294- xint_top is None
295- ): # out of range doublecheck
296- pass
297- else :
298- # Initialize the adjusted values as the baseline
299- adj_x = x
300- adj_value = value
301-
302- if value > last_value : # slope is positive
303- if xint_top <= x : # top is clipped
304- adj_x = xint_top
305- adj_value = top # y
306- else : # slope is negative
307- if xint_bottom <= x : # bottom is clipped
308- adj_x = xint_bottom
309- adj_value = bottom # y
310-
311- self ._add_point (a_line , adj_x , adj_value )
312-
313- last_value = value # store value for the next iteration
248+ self ._add_point (a_line , int (xpitch * count ), value )
314249
315250 if redraw :
316251 self ._draw ()
317252
318- # pylint: enable=too-many-locals, too-many-nested-blocks, too-many-branches
319-
320253 def values_of (self , line : int ) -> List [float ]:
321254 """Returns the values displayed on the sparkline at given index."""
322255
0 commit comments