Skip to content

Commit

Permalink
Merge pull request #67 from bablokb/arc
Browse files Browse the repository at this point in the history
add support for arcs
  • Loading branch information
FoamyGuy committed Nov 23, 2023
2 parents 86f0f24 + f0fddbc commit 7fb9141
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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)
Expand Down
107 changes: 107 additions & 0 deletions adafruit_display_shapes/arc.py
Original file line number Diff line number Diff line change
@@ -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))
3 changes: 3 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@

.. automodule:: adafruit_display_shapes.sparkline
:members:

.. automodule:: adafruit_display_shapes.arc
:members:
9 changes: 9 additions & 0 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
73 changes: 73 additions & 0 deletions examples/display_shapes_arc.py
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 7fb9141

Please sign in to comment.