From 7f83f5938f2c09cc5c12de491ea8bc5df53d5b7a Mon Sep 17 00:00:00 2001 From: foamyguy Date: Thu, 14 Nov 2024 11:20:50 -0600 Subject: [PATCH] allow dynamic arc. update pylint --- .pre-commit-config.yaml | 2 +- .pylintrc | 5 +- adafruit_display_shapes/arc.py | 137 ++++++++++++++++++--- examples/display_shapes_arc_dynamic.py | 65 ++++++++++ examples/display_shapes_sparkline_ticks.py | 3 +- 5 files changed, 189 insertions(+), 23 deletions(-) create mode 100644 examples/display_shapes_arc_dynamic.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 70ade69..374676d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/pycqa/pylint - rev: v2.17.4 + rev: v3.3.1 hooks: - id: pylint name: pylint (library code) diff --git a/.pylintrc b/.pylintrc index f945e92..4c51e24 100644 --- a/.pylintrc +++ b/.pylintrc @@ -55,7 +55,7 @@ confidence= # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" # disable=import-error,raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,deprecated-str-translate-call -disable=raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,import-error,pointless-string-statement,unspecified-encoding +disable=raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,import-error,pointless-string-statement,unspecified-encoding,too-many-arguments,too-many-positional-arguments # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option @@ -360,9 +360,6 @@ valid-metaclass-classmethod-first-arg=mcs [DESIGN] -# Maximum number of arguments for function / method -max-args=5 - # Maximum number of attributes for a class (see R0902). # max-attributes=7 max-attributes=11 diff --git a/adafruit_display_shapes/arc.py b/adafruit_display_shapes/arc.py index fe790f7..e1f7158 100644 --- a/adafruit_display_shapes/arc.py +++ b/adafruit_display_shapes/arc.py @@ -80,28 +80,133 @@ def __init__( **kwargs, ) -> None: super().__init__(*args, **kwargs) + # shift direction by angle/2 - direction = direction - angle / 2 + self._direction = direction - angle / 2 + self._radius = radius + self._angle = angle + self._segments = segments + self._outline = outline + self._fill = fill + self._arc_width = arc_width + self.palette = None + self.vector_polygon = None + self.outline_polygon = None + + self._init_arc() + + def _init_arc(self): # create outer points points = [] - for i in range(segments + 1): - alpha = (i * angle / segments + direction) / 180 * math.pi - x0 = int(radius * math.cos(alpha)) - y0 = -int(radius * math.sin(alpha)) + for i in range(self._segments + 1): + alpha = (i * self._angle / self._segments + self._direction) / 180 * math.pi + x0 = int(self._radius * math.cos(alpha)) + y0 = -int(self._radius * math.sin(alpha)) points.append((x0, y0)) # create inner points - if arc_width > 1: - for i in range(segments, -1, -1): - alpha = (i * angle / segments + direction) / 180 * math.pi - x0 = int((radius - arc_width) * math.cos(alpha)) - y0 = -int((radius - arc_width) * math.sin(alpha)) + if self._arc_width > 1: + for i in range(self._segments, -1, -1): + alpha = ( + (i * self._angle / self._segments + self._direction) / 180 * math.pi + ) + x0 = int((self._radius - self._arc_width) * math.cos(alpha)) + y0 = -int((self._radius - self._arc_width) * math.sin(alpha)) points.append((x0, y0)) # create polygon(s) and add to ourselves - if arc_width > 1 and HAVE_VECTORIO and fill is not None: - palette = displayio.Palette(1) - palette[0] = fill - self.append(vectorio.Polygon(pixel_shader=palette, points=points, x=0, y=0)) - if outline is not None: - self.append(Polygon(points, outline=outline, colors=1, close=arc_width > 1)) + if self._arc_width > 1 and HAVE_VECTORIO and self._fill is not None: + if self.palette is None: + self.palette = displayio.Palette(1) + self.palette[0] = self._fill + if self.vector_polygon is None: + self.vector_polygon = vectorio.Polygon( + pixel_shader=self.palette, points=points, x=0, y=0 + ) + self.append(self.vector_polygon) + else: + self.vector_polygon.points = points + + if self._outline is not None: + if self.outline_polygon is None: + self.outline_polygon = Polygon( + points, outline=self._outline, colors=1, close=self._arc_width > 1 + ) + else: + self.remove(self.outline_polygon) + self.outline_polygon = Polygon( + points, outline=self._outline, colors=1, close=self._arc_width > 1 + ) + self.append(self.outline_polygon) + + @property + def direction(self): + """Which direction the arc is pointing""" + return self._direction + + @direction.setter + def direction(self, value): + self._direction = value + self._direction = value - self.angle / 2 + self._init_arc() + + @property + def radius(self): + """Radius of the arc""" + return self._radius + + @radius.setter + def radius(self, value): + self._radius = value + self._init_arc() + + @property + def angle(self): + """How wide the curve of the arc is in degrees""" + return self._angle + + @angle.setter + def angle(self, value): + self._angle = value + self._init_arc() + + @property + def segments(self): + """Number of segments of the arc, more segments make smoother + rounded parts but use more time and memory""" + return self._segments + + @segments.setter + def segments(self, value): + self._segments = value + self._init_arc() + + @property + def outline(self): + """The outline color. None for no outline""" + return self._outline + + @outline.setter + def outline(self, value): + self._outline = value + self._init_arc() + + @property + def fill(self): + """The fill color. None for no fill""" + return self._fill + + @fill.setter + def fill(self, value): + self._fill = value + self._init_arc() + + @property + def arc_width(self): + """The thickness of the arc in pixels""" + return self._arc_width + + @arc_width.setter + def arc_width(self, value): + self._arc_width = value + self._init_arc() diff --git a/examples/display_shapes_arc_dynamic.py b/examples/display_shapes_arc_dynamic.py new file mode 100644 index 0000000..a94b2fd --- /dev/null +++ b/examples/display_shapes_arc_dynamic.py @@ -0,0 +1,65 @@ +# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries +# SPDX-License-Identifier: MIT +""" +Illustrates how to dynamically update arcs over time. +""" +import time +import board + +import displayio +from adafruit_display_shapes.arc import Arc +from adafruit_display_shapes.circle import Circle + +# use built in display (PyPortal, PyGamer, PyBadge, CLUE, etc.) +# see guide for setting up external displays (TFT / OLED breakouts, RGB matrices, etc.) +# https://learn.adafruit.com/circuitpython-display-support-using-displayio/display-and-display-bus +display = board.DISPLAY + +w2 = int(display.width / 2) +h2 = int(display.height / 2) + +WHITE = 0xFFFFFF +RED = 0xFF0000 +GREEN = 0x00FF00 +BLUE = 0x0000FF + +# Make the display context +group = displayio.Group() +display.root_group = group + +# little circle in the center of all arcs +circle = Circle(w2, h2, 5, fill=0x00FF00) +group.append(circle) + +# red arc , 10 pixels wide +arc1 = Arc( + x=w2, + y=h2, + radius=min(display.width, display.height) / 4, + angle=90, + direction=90, + segments=20, + arc_width=10, + fill=RED, +) +group.append(arc1) + +# blue arc (or pie) +arc2 = Arc( + x=w2, + y=h2, + radius=min(display.width, display.height) / 6, + angle=90, + direction=0, + segments=10, + arc_width=min(display.width, display.height) / 6 - 5, + outline=BLUE, +) +group.append(arc2) + +while True: + for i in range(360 // 40 + 1): + arc1.angle = i * 40 + arc2.direction = i * 40 + time.sleep(0.05) + print(len(arc2)) diff --git a/examples/display_shapes_sparkline_ticks.py b/examples/display_shapes_sparkline_ticks.py index a2bb533..d31719f 100755 --- a/examples/display_shapes_sparkline_ticks.py +++ b/examples/display_shapes_sparkline_ticks.py @@ -165,8 +165,7 @@ x_start = sparkline1.x - 5 x_end = sparkline1.x y_both = int(round(sparkline1.y + (i * (chart_height) / (total_ticks)))) - if y_both > sparkline1.y + chart_height - 1: - y_both = sparkline1.y + chart_height - 1 + y_both = min(y_both, sparkline1.y + chart_height - 1) my_group.append(Line(x_start, y_both, x_end, y_both, color=line_color))