22
22
"""
23
23
24
24
try :
25
- from typing import Optional , List
25
+ from typing import Optional , List , TypeVar
26
+
27
+ T = TypeVar ("T" )
26
28
except ImportError :
27
29
pass
28
30
import displayio
29
31
from adafruit_display_shapes .polygon import Polygon
30
32
31
33
32
34
class _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
35
37
self ._start = 0 # between 0 and size-1
36
38
self ._end = 0 # between 0 and 2*size-1
37
39
38
- def push (self , value : int | float ) -> None :
40
+ def push (self , value : T ) -> None :
39
41
"""Pushes value at the end of the buffer.
40
42
41
- :param int|float value: value to be pushed
43
+ :param T value: value to be pushed
42
44
43
45
"""
44
46
@@ -47,7 +49,7 @@ def push(self, value: int | float) -> None:
47
49
self ._buffer [self ._end % len (self ._buffer )] = value
48
50
self ._end += 1
49
51
50
- def pop (self ) -> int | float :
52
+ def pop (self ) -> T :
51
53
"""Pop value from the start of the buffer and returns it."""
52
54
53
55
if self .len () == 0 :
@@ -70,7 +72,7 @@ def clear(self) -> None:
70
72
self ._start = 0
71
73
self ._end = 0
72
74
73
- def values (self ) -> List [int | float ]:
75
+ def values (self ) -> List [T ]:
74
76
"""Returns valid data from the buffer."""
75
77
76
78
if self .len () == 0 :
@@ -123,10 +125,10 @@ def __init__(
123
125
self ._max_items = max_items # maximum number of items in the list
124
126
self ._lines = len (colors )
125
127
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 )
127
129
] # values per sparkline
128
130
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 )
130
132
] # _points: all points of sparkline
131
133
self .dyn_xpitch = dyn_xpitch
132
134
if not dyn_xpitch :
@@ -196,26 +198,6 @@ def add_values(self, values: List[float], update: bool = True) -> None:
196
198
if update :
197
199
self .update_line (i )
198
200
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
-
219
201
def _add_point (
220
202
self ,
221
203
line : int ,
@@ -236,7 +218,6 @@ def _draw(self) -> None:
236
218
for i in range (self ._lines ):
237
219
Polygon .draw (self ._bitmap , self ._points [i ].values (), i + 1 , close = False )
238
220
239
- # pylint: disable=too-many-locals, too-many-nested-blocks, too-many-branches
240
221
def update_line (self , line : int = None ) -> None :
241
222
"""Update the drawing of the sparkline.
242
223
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:
264
245
self ._points [a_line ].clear () # remove all points
265
246
266
247
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 )
314
249
315
250
if redraw :
316
251
self ._draw ()
317
252
318
- # pylint: enable=too-many-locals, too-many-nested-blocks, too-many-branches
319
-
320
253
def values_of (self , line : int ) -> List [float ]:
321
254
"""Returns the values displayed on the sparkline at given index."""
322
255
0 commit comments