diff --git a/adafruit_display_shapes/line.py b/adafruit_display_shapes/line.py new file mode 100755 index 0000000..d138aff --- /dev/null +++ b/adafruit_display_shapes/line.py @@ -0,0 +1,57 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Limor Fried for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`line` +================================================================================ + +Various common shapes for use with displayio - Line shape! + + +* Author(s): Melissa LeBlanc-Williams + +Implementation Notes +-------------------- + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + +""" + +from adafruit_display_shapes.polygon import Polygon + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Display_Shapes.git" + +class Line(Polygon): + # pylint: disable=too-many-arguments,invalid-name + """A line. + + :param x0: The x-position of the first vertex. + :param y0: The y-position of the first vertex. + :param x1: The x-position of the second vertex. + :param y1: The y-position of the second vertex. + :param color: The color of the line. + """ + def __init__(self, x0, y0, x1, y1, color): + super().__init__([(x0, y0), (x1, y1)], outline=color) diff --git a/adafruit_display_shapes/polygon.py b/adafruit_display_shapes/polygon.py new file mode 100755 index 0000000..d11df3e --- /dev/null +++ b/adafruit_display_shapes/polygon.py @@ -0,0 +1,146 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Limor Fried for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`polygon` +================================================================================ + +Various common shapes for use with displayio - Polygon shape! + + +* Author(s): Melissa LeBlanc-Williams + +Implementation Notes +-------------------- + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + +""" + +import displayio + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Display_Shapes.git" + + +class Polygon(displayio.TileGrid): + # pylint: disable=too-many-arguments,invalid-name + """A polygon. + + :param points: A list of (x, y) tuples of the points + :param fill: The color to fill the polygon. Can be a hex value for a color or + ``None`` for transparent. + :param outline: The outline of the polygon. Can be a hex value for a color or + ``None`` for no outline. + """ + def __init__(self, points, *, outline=None): + xs = [] + ys = [] + + for point in points: + xs.append(point[0]) + ys.append(point[1]) + + x_offset = min(xs) + y_offset = min(ys) + + # Find the largest and smallest X values to figure out width for bitmap + width = max(xs) - min(xs) + 1 + height = max(ys) - min(ys) + 1 + + self._palette = displayio.Palette(3) + self._palette.make_transparent(0) + self._bitmap = displayio.Bitmap(width, height, 3) + + if outline is not None: + # print("outline") + self.outline = outline + for index, _ in enumerate(points): + point_a = points[index] + if index == len(points) - 1: + point_b = points[0] + else: + point_b = points[index + 1] + self._line(point_a[0] - x_offset, point_a[1] - y_offset, + point_b[0] - x_offset, point_b[1] - y_offset, 1) + + super().__init__(self._bitmap, pixel_shader=self._palette, x=x_offset, y=y_offset) + + # pylint: disable=invalid-name, too-many-locals, too-many-branches + def _line(self, x0, y0, x1, y1, color): + if x0 == x1: + if y0 > y1: + y0, y1 = y1, y0 + for _h in range(y0, y1): + self._bitmap[x0, _h] = color + elif y0 == y1: + if x0 > x1: + x0, x1 = x1, x0 + for _w in range(x0, x1): + self._bitmap[_w, y0] = color + else: + steep = abs(y1 - y0) > abs(x1 - x0) + if steep: + x0, y0 = y0, x0 + x1, y1 = y1, x1 + + if x0 > x1: + x0, x1 = x1, x0 + y0, y1 = y1, y0 + + dx = x1 - x0 + dy = abs(y1 - y0) + + err = dx / 2 + + if y0 < y1: + ystep = 1 + else: + ystep = -1 + + for x in range(x0, x1): + if steep: + self._bitmap[y0, x] = color + else: + self._bitmap[x, y0] = color + err -= dy + if err < 0: + y0 += ystep + err += dx + # pylint: enable=invalid-name, too-many-locals, too-many-branches + + @property + def outline(self): + """The outline of the polygon. Can be a hex value for a color or + ``None`` for no outline.""" + return self._palette[1] + + @outline.setter + def outline(self, color): + if color is None: + self._palette[1] = 0 + self._palette.make_transparent(1) + else: + self._palette[1] = color + self._palette.make_opaque(1) diff --git a/adafruit_display_shapes/triangle.py b/adafruit_display_shapes/triangle.py index 6d1a623..1036a8e 100755 --- a/adafruit_display_shapes/triangle.py +++ b/adafruit_display_shapes/triangle.py @@ -38,13 +38,13 @@ """ -import displayio +from adafruit_display_shapes.polygon import Polygon __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Display_Shapes.git" -class Triangle(displayio.TileGrid): +class Triangle(Polygon): # pylint: disable=too-many-arguments,invalid-name """A triangle. @@ -59,6 +59,7 @@ class Triangle(displayio.TileGrid): :param outline: The outline of the triangle. Can be a hex value for a color or ``None`` for no outline. """ + # pylint: disable=too-many-locals def __init__(self, x0, y0, x1, y1, x2, y2, *, fill=None, outline=None): # Sort coordinates by Y order (y2 >= y1 >= y0) if y0 > y1: @@ -75,29 +76,29 @@ def __init__(self, x0, y0, x1, y1, x2, y2, *, fill=None, outline=None): # Find the largest and smallest X values to figure out width for bitmap xs = [x0, x1, x2] - width = max(xs) - min(xs) + 1 - height = y2 - y0 + 1 + points = [(x0, y0), (x1, y1), (x2, y2)] - self._palette = displayio.Palette(3) - self._palette.make_transparent(0) - self._bitmap = displayio.Bitmap(width, height, 3) + # Initialize the bitmap and palette + super().__init__(points) if fill is not None: self._draw_filled(x0 - min(xs), 0, x1 - min(xs), y1 - y0, x2 - min(xs), y2 - y0) - self._palette[2] = fill + self.fill = fill else: - self._palette.make_transparent(2) + self.fill = None if outline is not None: - # print("outline") - self._palette[1] = outline - self._line(x0 - min(xs), 0, x1 - min(xs), y1 - y0, 1) - self._line(x1 - min(xs), y1 - y0, x2 - min(xs), y2 - y0, 1) - self._line(x2 - min(xs), y2 - y0, x0 - min(xs), 0, 1) - - super().__init__(self._bitmap, pixel_shader=self._palette, x=min(xs), y=y0) + self.outline = outline + for index, _ in enumerate(points): + point_a = points[index] + if index == len(points) - 1: + point_b = points[0] + else: + point_b = points[index + 1] + self._line(point_a[0] - min(xs), point_a[1] - y0, + point_b[0] - min(xs), point_b[1] - y0, 1) - # pylint: disable=invalid-name, too-many-locals, too-many-branches + # pylint: disable=invalid-name, too-many-branches def _draw_filled(self, x0, y0, x1, y1, x2, y2): if y0 == y2: # Handle awkward all-on-same-line case as its own thing a = x0 @@ -134,47 +135,6 @@ def _draw_filled(self, x0, y0, x1, y1, x2, y2): if a > b: a, b = b, a self._line(a, y, b, y, 2) - - def _line(self, x0, y0, x1, y1, color): - if x0 == x1: - if y0 > y1: - y0, y1 = y1, y0 - for _h in range(y0, y1): - self._bitmap[x0, _h] = color - elif y0 == y1: - if x0 > x1: - x0, x1 = x1, x0 - for _w in range(x0, x1): - self._bitmap[_w, y0] = color - else: - steep = abs(y1 - y0) > abs(x1 - x0) - if steep: - x0, y0 = y0, x0 - x1, y1 = y1, x1 - - if x0 > x1: - x0, x1 = x1, x0 - y0, y1 = y1, y0 - - dx = x1 - x0 - dy = abs(y1 - y0) - - err = dx / 2 - - if y0 < y1: - ystep = 1 - else: - ystep = -1 - - for x in range(x0, x1): - if steep: - self._bitmap[y0, x] = color - else: - self._bitmap[x, y0] = color - err -= dy - if err < 0: - y0 += ystep - err += dx # pylint: enable=invalid-name, too-many-locals, too-many-branches @property @@ -191,18 +151,3 @@ def fill(self, color): else: self._palette[2] = color self._palette.make_opaque(2) - - @property - def outline(self): - """The outline of the triangle. Can be a hex value for a color or - ``None`` for no outline.""" - return self._palette[1] - - @outline.setter - def outline(self, color): - if color is None: - self._palette[1] = 0 - self._palette.make_transparent(1) - else: - self._palette[1] = color - self._palette.make_opaque(1) diff --git a/examples/display_shapes_simpletest.py b/examples/display_shapes_simpletest.py index 064ea76..35eeef6 100644 --- a/examples/display_shapes_simpletest.py +++ b/examples/display_shapes_simpletest.py @@ -4,9 +4,11 @@ from adafruit_display_shapes.circle import Circle from adafruit_display_shapes.roundrect import RoundRect from adafruit_display_shapes.triangle import Triangle +from adafruit_display_shapes.line import Line +from adafruit_display_shapes.polygon import Polygon # Make the display context -splash = displayio.Group(max_size=10) +splash = displayio.Group(max_size=20) board.DISPLAY.show(splash) # Make a background color fill @@ -18,6 +20,16 @@ splash.append(bg_sprite) ########################################################################## +splash.append(Line(220, 130, 270, 210, 0xFF0000)) +splash.append(Line(270, 210, 220, 210, 0xFF0000)) +splash.append(Line(220, 210, 270, 130, 0xFF0000)) +splash.append(Line(270, 130, 220, 130, 0xFF0000)) + +#Draw a blue star +polygon = Polygon([(255, 40), (262, 62), (285, 62), (265, 76), (275, 100), + (255, 84), (235, 100), (245, 76), (225, 62), (248, 62)], outline=0x0000FF) +splash.append(polygon) + triangle = Triangle(170, 50, 120, 140, 210, 160, fill=0x00FF00, outline=0xFF00FF) splash.append(triangle)