Skip to content

Commit

Permalink
Add sub-shape clipping
Browse files Browse the repository at this point in the history
This adds clipping for the basic geometries.
  • Loading branch information
corranwebster committed Nov 6, 2024
1 parent 45044f3 commit 9bfdbcb
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 17 deletions.
46 changes: 46 additions & 0 deletions src/tempe/_speedups.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,49 @@ def line_points(x0: int, y0: int, x1: int, y1: int, w: int, d: int, vertices: pt
vertices[5] = y1 - my
vertices[6] = x0 - mx
vertices[7] = y0 - my


@micropython.viper
def bisect16(lst: ptr16, val: int, k: int) -> int:
n: int = 0
k -= 1
while True:
m: int = (n + k) >> 1
p: int = m << 1
v: int = lst[p]
if v == val:
r = lst[p + 1]
return r
elif n >= m and k <= m:
return 0
elif v < val:
n = m + 1
else:
k = m - 1

@micropython.viper
def intersect_poly_rect(polygon: ptr16, n: int, x: int, y: int, w: int, h: int) -> bool:
min_x: int = 0x7fff
max_x: int = -0x8000
min_y: int = 0x7fff
max_y: int = -0x8000
x1: int = x + w
y1: int = y + h
for i in range(0, n, 2):
px = polygon[i]
py = polygon[i+1]
if px < min_x:
min_x = px
if px > max_x:
max_x = px
if py < min_y:
min_y = py
if py > max_y:
max_y = py

return (
x < max_x
and min_x < x1
and y < max_y
and min_y < y1
)
67 changes: 50 additions & 17 deletions src/tempe/shapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""Shape classes which efficiently draw primitives."""

import asyncio
from .util import intersect_poly_rect

#: Transparent color when blitting bitmaps.
BLIT_KEY_RGB565 = const(0b0000000000100000)
Expand Down Expand Up @@ -83,13 +84,19 @@ class Lines(ColoredGeometry):
Geometry should produce x0, y0, x1, y1 arrays.
"""

def draw(self, buffer, x=0, y=0):
def draw_raster(self, raster):
buffer = raster.fbuf
x = raster.x
y = raster.y
w = raster.w
h = raster.h
for geometry, color in self:
x0 = geometry[0] - x
y0 = geometry[1] - y
x1 = geometry[2] - x
y1 = geometry[3] - y
buffer.line(x0, y0, x1, y1, color)
if intersect_poly_rect(geometry, 4, x, y, w, h):
x0 = geometry[0] - x
y0 = geometry[1] - y
x1 = geometry[2] - x
y1 = geometry[3] - y
buffer.line(x0, y0, x1, y1, color)

def _get_bounds(self):
max_x = -0x7FFF
Expand Down Expand Up @@ -165,14 +172,20 @@ class PolyLines(ColoredGeometry):
Geometry should produce x0, y0, x1, y1 arrays.
"""

def draw(self, buffer, x=0, y=0):
def draw_raster(self, raster):
buffer = raster.fbuf
x = raster.x
y = raster.y
w = raster.w
h = raster.h
for geometry, color in self:
for i in range(0, len(geometry) - 2, 2):
x0 = geometry[i] - x
y0 = geometry[i + 1] - y
x1 = geometry[i + 2] - x
y1 = geometry[i + 3] - y
buffer.line(x0, y0, x1, y1, color)
if intersect_poly_rect(geometry, len(geometry), x, y, w, h):
for i in range(0, len(geometry) - 2, 2):
x0 = geometry[i] - x
y0 = geometry[i + 1] - y
x1 = geometry[i + 2] - x
y1 = geometry[i + 3] - y
buffer.line(x0, y0, x1, y1, color)

def _get_bounds(self):
max_x = -0x7FFF
Expand All @@ -195,9 +208,15 @@ class Polygons(FillableGeometry):
Geometry should produce vertex buffers.
"""

def draw(self, buffer, x=0, y=0):
def draw_raster(self, raster):
buffer = raster.fbuf
x = raster.x
y = raster.y
w = raster.w
h = raster.h
for polygon, color in self:
buffer.poly(-x, -y, polygon, color, self.fill)
if intersect_poly_rect(polygon, len(polygon), x, y, w, h):
buffer.poly(-x, -y, polygon, color, self.fill)

def _get_bounds(self):
max_x = -0x7FFF
Expand Down Expand Up @@ -254,11 +273,18 @@ class Circles(FillableGeometry):
Geometry should produce cx, cy, r arrays.
"""

def draw(self, buffer, x=0, y=0):
def draw_raster(self, raster):
buffer = raster.fbuf
x = raster.x
y = raster.y
w = raster.w
h = raster.h
for geometry, color in self:
px = geometry[0] - x
py = geometry[1] - y
r = geometry[2]
if px + r < 0 or px - r > w or py + r < 0 or py - r> h:
continue
if r == 0:
# Avoid https://github.com/micropython/micropython/issues/16053
continue
Expand All @@ -284,12 +310,19 @@ class Ellipses(FillableGeometry):
Geometry should produce cx, cy, rx, ry arrays.
"""

def draw(self, buffer, x=0, y=0):
def draw_raster(self, raster):
buffer = raster.fbuf
x = raster.x
y = raster.y
w = raster.w
h = raster.h
for geometry, color in self:
px = geometry[0] - x
py = geometry[1] - y
rx = geometry[2]
ry = geometry[3]
if px + rx < 0 or px - rx > w or py + ry < 0 or py - ry > h:
continue
if rx == 0 and ry == 0:
# Avoid https://github.com/micropython/micropython/issues/16053
continue
Expand Down
64 changes: 64 additions & 0 deletions src/tempe/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,67 @@ def contains(rect_1, rect_2):
and rect_1[1] >= rect_2[1]
and rect_1[1] + rect_1[3] <= rect_2[1] + rect_2[3]
)


def line_points(x0, y0, x1, y1, w, d, vertices):
dx = x1 - x0
dy = y1 - y0

# stuff to handle inter division always round down, when we really
# want away from 0
if dx == 0:
mx = -((w + 1) // 2)
else:
if dy > 0:
mx = -w * dy // d
else:
mx = -(w * dy // d)
if dy == 0:
my = (w + 1) // 2
else:
if dx > 0:
my = -(-w * dx // d)
else:
my = w * dx // d

vertices[0] = x0 + mx
vertices[1] = y0 + my
vertices[2] = x1 + mx
vertices[3] = y1 + my
vertices[4] = x1 - mx
vertices[5] = y1 - my
vertices[6] = x0 - mx
vertices[7] = y0 - my


def bisect16(lst, val, n):
while True:
m = (len(lst) & ~7) >> 1
l = lst[m:]
v = l[0] | (l[1] << 8)
if v == val:
l = lst[m + 2 :]
return l[0] | (l[1] << 8)
if not m:
return 0
lst = lst[m:] if v < val else lst[:m]

def intersect_poly_rect(polygon, n, x, y, w, h):
polygon = list(polygon)
max_x = max(polygon[::2])
min_x = min(polygon[::2])
max_y = max(polygon[1::2])
min_y = min(polygon[1::2])
return (
x < max_x
and min_x < x + w
and y < max_y
and min_y < y + h
)


# replace with faster viper versions where available
try:
from ._speedups import bisect16, line_points, intersect_poly_rect
except SyntaxError:
pass

0 comments on commit 9bfdbcb

Please sign in to comment.