diff --git a/README.rst b/README.rst index 7001540..5686e18 100644 --- a/README.rst +++ b/README.rst @@ -63,6 +63,7 @@ Usage Example import displayio from adafruit_display_shapes.rect import Rect from adafruit_display_shapes.circle import Circle + from adafruit_display_shapes.arc import Arc from adafruit_display_shapes.roundrect import RoundRect splash = displayio.Group() @@ -84,6 +85,9 @@ Usage Example circle = Circle(100, 100, 20, fill=0x00FF00, outline=0xFF00FF) splash.append(circle) + arc = Arc(x=100, y=100, radius=25, angle=45, direction=90, segments=10, outline=0x00FF00) + splash.append(arc) + rect2 = Rect(50, 100, 61, 81, outline=0x0, stroke=3) splash.append(rect2) diff --git a/adafruit_display_shapes/arc.py b/adafruit_display_shapes/arc.py new file mode 100644 index 0000000..fe790f7 --- /dev/null +++ b/adafruit_display_shapes/arc.py @@ -0,0 +1,107 @@ +# SPDX-FileCopyrightText: 2023 Bernhard Bablok +# +# SPDX-License-Identifier: MIT + +""" +`arc` +================================================================================ + +Various common shapes for use with displayio - Arc shape! + + +* Author(s): Bernhard Bablok + +Implementation Notes +-------------------- + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + +""" + +try: + from typing import Optional +except ImportError: + pass + +import math +import displayio +from adafruit_display_shapes.polygon import Polygon + +try: + import vectorio + + HAVE_VECTORIO = True +except ImportError: + HAVE_VECTORIO = False + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Display_Shapes.git" + + +class Arc(displayio.Group): + # pylint: disable=too-few-public-methods, invalid-name + """An arc. Technically, an arc is a Group with one or two polygons. + + An arc is defined by a radius, an angle (in degrees) and a direction (also in + degrees). The latter is the direction of the midpoint of the arc. + + The direction-parameter uses the layout of polar-coordinates, i.e. zero points + to the right, 90 to the top, 180 to the left and 270 to the bottom. + + The Arc-class creates the arc as a polygon. The number of segments define + how round the arc is. There is a memory-tradeoff if the segment-number is + large. + + :param float radius: The (outer) radius of the arc. + :param float angle: The angle of the arc in degrees. + :param float direction: The direction of the middle-point of the arc in degrees (0) + :param int segments: The number of segments of the arc. + :param arc_width int: (Optional) The width of the arc. This creates an inner arc as well. + :param int|None outline: The outline of the arc. Can be a hex value for a color or + ``None`` for no outline. + :param int|None fill: The fill-color of the arc. Can be a hex value for a color or + ``None`` for no filling. Ignored if port does not support vectorio. + """ + + def __init__( + # pylint: disable=too-many-arguments, too-many-locals + self, + radius: float, + angle: float, + direction: float, + segments: int, + *args, + arc_width: Optional[int] = 1, + outline: Optional[int] = None, + fill: Optional[int] = None, + **kwargs, + ) -> None: + super().__init__(*args, **kwargs) + # shift direction by angle/2 + direction = direction - angle / 2 + # 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)) + 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)) + 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)) diff --git a/docs/api.rst b/docs/api.rst index 3ddc2d0..5abe902 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -24,3 +24,6 @@ .. automodule:: adafruit_display_shapes.sparkline :members: + +.. automodule:: adafruit_display_shapes.arc + :members: diff --git a/docs/examples.rst b/docs/examples.rst index 50e01b1..643da63 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -51,3 +51,12 @@ Example showing the features of the new Circle setter .. literalinclude:: ../examples/display_shapes_circle_animation.py :caption: examples/display_shapes_circle_animation.py :linenos: + +Arc Simple Test +--------------- + +Example demonstrating various arcs. + +.. literalinclude:: ../examples/display_shapes_arc.py + :caption: examples/display_shapes_arc.py + :linenos: diff --git a/examples/display_shapes_arc.py b/examples/display_shapes_arc.py new file mode 100644 index 0000000..5139a77 --- /dev/null +++ b/examples/display_shapes_arc.py @@ -0,0 +1,73 @@ +# SPDX-FileCopyrightText: 2023 Bernhard Bablok +# SPDX-License-Identifier: MIT + +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=0xFF0000, outline=0xFF0000) +group.append(circle) + +# red arc with white outline, 10 pixels wide +arc1 = Arc( + x=w2, + y=h2, + radius=min(display.width, display.height) / 4, + angle=90, + direction=90, + segments=10, + arc_width=10, + outline=WHITE, + fill=RED, +) +group.append(arc1) + +# green arc (single line) +arc2 = Arc( + x=w2, + y=h2, + radius=min(display.width, display.height) / 4 + 5, + angle=180, + direction=90, + segments=20, + arc_width=1, + outline=GREEN, +) +group.append(arc2) + +# blue arc (or pie) +arc3 = Arc( + x=w2, + y=h2, + radius=min(display.width, display.height) / 4, + angle=90, + direction=-90, + segments=10, + arc_width=min(display.width, display.height) / 4 - 5, + outline=BLUE, +) +group.append(arc3) + +while True: + time.sleep(0.1)