Skip to content

add support for arcs #67

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)