Skip to content

Commit fe8779f

Browse files
committed
Move checking if line fits on bitmap to polygon class
1 parent b2aec7f commit fe8779f

File tree

2 files changed

+30
-83
lines changed

2 files changed

+30
-83
lines changed

adafruit_display_shapes/multisparkline.py

Lines changed: 12 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,25 @@
2222
"""
2323

2424
try:
25-
from typing import Optional, List
25+
from typing import Optional, List, TypeVar
26+
27+
T = TypeVar("T")
2628
except ImportError:
2729
pass
2830
import displayio
2931
from adafruit_display_shapes.polygon import Polygon
3032

3133

3234
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
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

adafruit_display_shapes/polygon.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ def _line(
112112

113113
# pylint: enable=too-many-arguments
114114

115+
@staticmethod
116+
def _safe_draw(
117+
bitmap: displayio.Bitmap,
118+
p: Tuple[int, int],
119+
color: int,
120+
) -> None:
121+
(x, y) = p
122+
if 0 <= x < bitmap.width and 0 <= y < bitmap.height:
123+
bitmap[x, y] = color
124+
115125
# pylint: disable=too-many-branches, too-many-locals
116126
@staticmethod
117127
def _line_on(
@@ -122,16 +132,20 @@ def _line_on(
122132
) -> None:
123133
(x_0, y_0) = p_0
124134
(x_1, y_1) = p_1
135+
136+
def pt_on(x, y):
137+
Polygon._safe_draw(bitmap, (x, y), color)
138+
125139
if x_0 == x_1:
126140
if y_0 > y_1:
127141
y_0, y_1 = y_1, y_0
128142
for _h in range(y_0, y_1 + 1):
129-
bitmap[x_0, _h] = color
143+
pt_on(x_0, _h)
130144
elif y_0 == y_1:
131145
if x_0 > x_1:
132146
x_0, x_1 = x_1, x_0
133147
for _w in range(x_0, x_1 + 1):
134-
bitmap[_w, y_0] = color
148+
pt_on(_w, y_0)
135149
else:
136150
steep = abs(y_1 - y_0) > abs(x_1 - x_0)
137151
if steep:
@@ -154,9 +168,9 @@ def _line_on(
154168

155169
for x in range(x_0, x_1 + 1):
156170
if steep:
157-
bitmap[y_0, x] = color
171+
pt_on(y_0, x)
158172
else:
159-
bitmap[x, y_0] = color
173+
pt_on(x, y_0)
160174
err -= d_y
161175
if err < 0:
162176
y_0 += ystep

0 commit comments

Comments
 (0)