04a655c744
Add method for drawing polygons. For non-filled polygons, uses the existing line-drawing code to render arbitrary polygons using the given coords list, at the given x,y position, in the given colour. For filled polygons, arbitrary closed polygons are rendered using a fast point-in-polygon algorithm to determine where the edges of the polygon lie on each pixel row. Tests and documentation updates are also included. Signed-off-by: Mat Booth <mat.booth@gmail.com>
223 lines
4.6 KiB
Python
223 lines
4.6 KiB
Python
import sys
|
|
|
|
try:
|
|
import framebuf
|
|
from array import array
|
|
except ImportError:
|
|
print("SKIP")
|
|
raise SystemExit
|
|
|
|
|
|
# TODO: poly needs functions that aren't in dynruntime.h yet.
|
|
if not hasattr(framebuf.FrameBuffer, "poly"):
|
|
print("SKIP")
|
|
raise SystemExit
|
|
|
|
|
|
def print_buffer(buffer, width, height):
|
|
for row in range(height):
|
|
for col in range(width):
|
|
val = buffer[(row * width) + col]
|
|
sys.stdout.write(" {:02x}".format(val) if val else " ··")
|
|
sys.stdout.write("\n")
|
|
|
|
|
|
buf = bytearray(70 * 70)
|
|
|
|
w = 30
|
|
h = 25
|
|
fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8)
|
|
col = 0xFF
|
|
col_fill = 0x99
|
|
|
|
# This describes a arbitrary polygon (this happens to be a concave polygon in
|
|
# the shape of an upper-case letter 'M').
|
|
poly = array(
|
|
"h",
|
|
(
|
|
0,
|
|
20,
|
|
3,
|
|
20,
|
|
3,
|
|
10,
|
|
6,
|
|
17,
|
|
9,
|
|
10,
|
|
9,
|
|
20,
|
|
12,
|
|
20,
|
|
12,
|
|
3,
|
|
9,
|
|
3,
|
|
6,
|
|
10,
|
|
3,
|
|
3,
|
|
0,
|
|
3,
|
|
),
|
|
)
|
|
# This describes the same polygon, but the points are in reverse order
|
|
# (it shouldn't matter if the polygon has clockwise or anti-clockwise
|
|
# winding). Also defined as a bytes instead of array.
|
|
poly_reversed = bytes(
|
|
(
|
|
0,
|
|
3,
|
|
3,
|
|
3,
|
|
6,
|
|
10,
|
|
9,
|
|
3,
|
|
12,
|
|
3,
|
|
12,
|
|
20,
|
|
9,
|
|
20,
|
|
9,
|
|
10,
|
|
6,
|
|
17,
|
|
3,
|
|
10,
|
|
3,
|
|
20,
|
|
0,
|
|
20,
|
|
)
|
|
)
|
|
|
|
# Draw the line polygon (at the origin) and the reversed-order polygon (offset).
|
|
fbuf.fill(0)
|
|
fbuf.poly(0, 0, poly, col)
|
|
fbuf.poly(15, -2, poly_reversed, col)
|
|
print_buffer(buf, w, h)
|
|
print()
|
|
|
|
# Same but filled.
|
|
fbuf.fill(0)
|
|
fbuf.poly(0, 0, poly, col_fill, True)
|
|
fbuf.poly(15, -2, poly_reversed, col_fill, True)
|
|
print_buffer(buf, w, h)
|
|
print()
|
|
|
|
# Draw the fill then the outline to ensure that no fill goes outside the outline.
|
|
fbuf.fill(0)
|
|
fbuf.poly(0, 0, poly, col_fill, True)
|
|
fbuf.poly(0, 0, poly, col)
|
|
fbuf.poly(15, -2, poly, col_fill, True)
|
|
fbuf.poly(15, -2, poly, col)
|
|
print_buffer(buf, w, h)
|
|
print()
|
|
|
|
# Draw the outline then the fill to ensure the fill completely covers the outline.
|
|
fbuf.fill(0)
|
|
fbuf.poly(0, 0, poly, col)
|
|
fbuf.poly(0, 0, poly, col_fill, True)
|
|
fbuf.poly(15, -2, poly, col)
|
|
fbuf.poly(15, -2, poly, col_fill, True)
|
|
print_buffer(buf, w, h)
|
|
print()
|
|
|
|
# Draw polygons that will go out of bounds at each of the edges.
|
|
for x, y in (
|
|
(
|
|
-8,
|
|
-8,
|
|
),
|
|
(
|
|
24,
|
|
-6,
|
|
),
|
|
(
|
|
20,
|
|
12,
|
|
),
|
|
(
|
|
-2,
|
|
10,
|
|
),
|
|
):
|
|
fbuf.fill(0)
|
|
fbuf.poly(x, y, poly, col)
|
|
print_buffer(buf, w, h)
|
|
print()
|
|
fbuf.fill(0)
|
|
fbuf.poly(x, y, poly_reversed, col, True)
|
|
print_buffer(buf, w, h)
|
|
print()
|
|
|
|
# Edge cases: These two lists describe self-intersecting polygons
|
|
poly_hourglass = array("h", (0, 0, 9, 0, 0, 19, 9, 19))
|
|
poly_star = array("h", (7, 0, 3, 18, 14, 5, 0, 5, 11, 18))
|
|
|
|
# As before, fill then outline.
|
|
fbuf.fill(0)
|
|
fbuf.poly(0, 2, poly_hourglass, col_fill, True)
|
|
fbuf.poly(0, 2, poly_hourglass, col)
|
|
fbuf.poly(12, 2, poly_star, col_fill, True)
|
|
fbuf.poly(12, 2, poly_star, col)
|
|
print_buffer(buf, w, h)
|
|
print()
|
|
|
|
# Outline then fill.
|
|
fbuf.fill(0)
|
|
fbuf.poly(0, 2, poly_hourglass, col)
|
|
fbuf.poly(0, 2, poly_hourglass, col_fill, True)
|
|
fbuf.poly(12, 2, poly_star, col)
|
|
fbuf.poly(12, 2, poly_star, col_fill, True)
|
|
print_buffer(buf, w, h)
|
|
print()
|
|
|
|
# Edge cases: These are "degenerate" polygons.
|
|
poly_empty = array("h") # Will draw nothing at all.
|
|
poly_one = array("h", (20, 20)) # Will draw a single point.
|
|
poly_two = array("h", (10, 10, 5, 5)) # Will draw a single line.
|
|
poly_wrong_length = array("h", (2, 2, 4)) # Will round down to one point.
|
|
|
|
fbuf.fill(0)
|
|
fbuf.poly(0, 0, poly_empty, col)
|
|
fbuf.poly(0, 0, poly_one, col)
|
|
fbuf.poly(0, 0, poly_two, col)
|
|
fbuf.poly(0, 0, poly_wrong_length, col)
|
|
print_buffer(buf, w, h)
|
|
print()
|
|
|
|
# A shape with a horizontal overhang.
|
|
poly_overhang = array("h", (0, 0, 0, 5, 5, 5, 5, 10, 10, 10, 10, 0))
|
|
|
|
fbuf.fill(0)
|
|
fbuf.poly(0, 0, poly_overhang, col)
|
|
fbuf.poly(0, 0, poly_overhang, col_fill, True)
|
|
print_buffer(buf, w, h)
|
|
print()
|
|
|
|
fbuf.fill(0)
|
|
fbuf.poly(0, 0, poly_overhang, col_fill, True)
|
|
fbuf.poly(0, 0, poly_overhang, col)
|
|
print_buffer(buf, w, h)
|
|
print()
|
|
|
|
# Triangles
|
|
w = 70
|
|
h = 70
|
|
fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8)
|
|
t1 = array("h", [40, 0, 20, 68, 62, 40])
|
|
t2 = array("h", [40, 0, 0, 16, 20, 68])
|
|
|
|
fbuf.fill(0)
|
|
fbuf.poly(0, 0, t1, 0xFF, False)
|
|
fbuf.poly(0, 0, t2, 0xFF, False)
|
|
print_buffer(buf, w, h)
|
|
|
|
fbuf.fill(0)
|
|
fbuf.poly(0, 0, t1, 0xFF, True)
|
|
fbuf.poly(0, 0, t2, 0xFF, True)
|
|
print_buffer(buf, w, h)
|