Skip to content

Commit 0e9fd85

Browse files
committed
Added Triangle Shape
1 parent 3a746be commit 0e9fd85

File tree

2 files changed

+222
-0
lines changed

2 files changed

+222
-0
lines changed

adafruit_display_shapes/triangle.py

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2019 Limor Fried for Adafruit Industries
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
"""
23+
`triangle`
24+
================================================================================
25+
26+
Various common shapes for use with displayio - Triangle shape!
27+
28+
29+
* Author(s): Melissa LeBlanc-Williams
30+
31+
Implementation Notes
32+
--------------------
33+
34+
**Software and Dependencies:**
35+
36+
* Adafruit CircuitPython firmware for the supported boards:
37+
https://github.com/adafruit/circuitpython/releases
38+
39+
"""
40+
41+
import displayio
42+
43+
__version__ = "0.0.0-auto.0"
44+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Display_Shapes.git"
45+
46+
47+
class Triangle(displayio.TileGrid):
48+
# pylint: disable=too-many-arguments,invalid-name
49+
"""A triangle.
50+
51+
:param x0: The x-position of the first vertex.
52+
:param y0: The y-position of the first vertex.
53+
:param x1: The x-position of the second vertex.
54+
:param y1: The y-position of the second vertex.
55+
:param x2: The x-position of the third vertex.
56+
:param y2: The y-position of the third vertex.
57+
:param fill: The color to fill the triangle. Can be a hex value for a color or
58+
``None`` for transparent.
59+
:param outline: The outline of the triangle. Can be a hex value for a color or
60+
``None`` for no outline.
61+
"""
62+
def __init__(self, x0, y0, x1, y1, x2, y2, *, fill=None, outline=None):
63+
# Sort coordinates by Y order (y2 >= y1 >= y0)
64+
if y0 > y1:
65+
y0, y1 = y1, y0
66+
x0, x1 = x1, x0
67+
68+
if y1 > y2:
69+
y1, y2 = y2, y1
70+
x1, x2 = x2, x1
71+
72+
if y0 > y1:
73+
y0, y1 = y1, y0
74+
x0, x1 = x1, x0
75+
76+
smallest_x = x0
77+
largest_x = x0
78+
79+
# Find the largest and smallest X values to figure out width for bitmap
80+
if x1 > largest_x:
81+
largest_x = x1
82+
83+
if x1 < smallest_x:
84+
smallest_x = x1
85+
86+
if x2 > largest_x:
87+
largest_x = x2
88+
89+
if x2 < smallest_x:
90+
smallest_x = x2
91+
92+
height = y2 - y0 + 1
93+
width = largest_x - smallest_x + 1
94+
95+
self._palette = displayio.Palette(3)
96+
self._palette.make_transparent(0)
97+
self._bitmap = displayio.Bitmap(width, height, 3)
98+
99+
if fill is not None:
100+
self._helper(x0 - smallest_x, 0, x1 - smallest_x, y1 - y0, x2 - smallest_x, y2 - y0)
101+
self._palette[2] = fill
102+
else:
103+
self._palette.make_transparent(2)
104+
105+
if outline is not None:
106+
print("outline")
107+
self._palette[1] = outline
108+
self._line(x0 - smallest_x, 0, x1 - smallest_x, y1 - y0, 1)
109+
self._line(x1 - smallest_x, y1 - y0, x2 - smallest_x, y2 - y0, 1)
110+
self._line(x2 - smallest_x, y2 - y0, x0 - smallest_x, 0, 1)
111+
112+
super().__init__(self._bitmap, pixel_shader=self._palette, x=smallest_x, y=y0)
113+
114+
# pylint: disable=invalid-name, too-many-locals, too-many-branches
115+
def _helper(self, x0, y0, x1, y1, x2, y2):
116+
if y0 == y2: # Handle awkward all-on-same-line case as its own thing
117+
a = x0
118+
b = x0
119+
if x1 < a:
120+
a = x1
121+
elif x1 > b:
122+
b = x1
123+
124+
if x2 < a:
125+
a = x2
126+
elif x2 > b:
127+
b = x2
128+
self._line(a, y0, b, y0, 2)
129+
return
130+
131+
if y1 == y2:
132+
last = y1 # Include y1 scanline
133+
else:
134+
last = y1 - 1 # Skip it
135+
136+
# Upper Triangle
137+
for y in range(y0, last + 1):
138+
a = round(x0 + (x1 - x0) * (y - y0) / (y1 - y0))
139+
b = round(x0 + (x2 - x0) * (y - y0) / (y2 - y0))
140+
if a > b:
141+
a, b = b, a
142+
self._line(a, y, b, y, 2)
143+
# Lower Triangle
144+
for y in range(last + 1, y2 + 1):
145+
a = round(x1 + (x2 - x1) * (y - y1) / (y2 - y1))
146+
b = round(x0 + (x2 - x0) * (y - y0) / (y2 - y0))
147+
148+
if a > b:
149+
a, b = b, a
150+
self._line(a, y, b, y, 2)
151+
152+
def _line(self, x0, y0, x1, y1, color):
153+
if x0 == x1:
154+
if y0 > y1:
155+
y0, y1 = y1, y0
156+
for _h in range(y0, y1):
157+
self._bitmap[x0, _h] = color
158+
elif y0 == y1:
159+
if x0 > x1:
160+
x0, x1 = x1, x0
161+
for _w in range(x0, x1):
162+
self._bitmap[_w, y0] = color
163+
else:
164+
steep = abs(y1 - y0) > abs(x1 - x0)
165+
if steep:
166+
x0, y0 = y0, x0
167+
x1, y1 = y1, x1
168+
169+
if x0 > x1:
170+
x0, x1 = x1, x0
171+
y0, y1 = y1, y0
172+
173+
dx = x1 - x0
174+
dy = abs(y1 - y0)
175+
176+
err = dx / 2
177+
178+
if y0 < y1:
179+
ystep = 1
180+
else:
181+
ystep = -1
182+
183+
for x in range(x0, x1):
184+
if steep:
185+
self._bitmap[y0, x] = color
186+
else:
187+
self._bitmap[x, y0] = color
188+
err -= dy
189+
if err < 0:
190+
y0 += ystep
191+
err += dx
192+
# pylint: enable=invalid-name, too-many-locals, too-many-branches
193+
194+
@property
195+
def fill(self):
196+
"""The fill of the triangle. Can be a hex value for a color or
197+
``None`` for transparent."""
198+
return self._palette[2]
199+
200+
@fill.setter
201+
def fill(self, color):
202+
if color is None:
203+
self._palette.make_transparent(2)
204+
else:
205+
self._palette[2] = color
206+
207+
@property
208+
def outline(self):
209+
"""The outline of the triangle. Can be a hex value for a color or
210+
``None`` for no outline."""
211+
return self._palette[1]
212+
213+
@outline.setter
214+
def outline(self, color):
215+
if color is None:
216+
self._palette.make_transparent(1)
217+
else:
218+
self._palette[1] = color

examples/display_shapes_simpletest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from adafruit_display_shapes.rect import Rect
44
from adafruit_display_shapes.circle import Circle
55
from adafruit_display_shapes.roundrect import RoundRect
6+
from adafruit_display_shapes.triangle import Triangle
67

78
# Make the display context
89
splash = displayio.Group(max_size=10)
@@ -17,6 +18,9 @@
1718
splash.append(bg_sprite)
1819
##########################################################################
1920

21+
triangle = Triangle(170, 50, 120, 140, 210, 160, fill=0x00FF00, outline=0xFF00FF)
22+
splash.append(triangle)
23+
2024
rect = Rect(80, 20, 41, 41, fill=0x0)
2125
splash.append(rect)
2226

0 commit comments

Comments
 (0)