Skip to content
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

Add thick line support #16

Merged
merged 5 commits into from
Oct 21, 2024
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
106 changes: 106 additions & 0 deletions examples/lines_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import asyncio
from array import array
import framebuf
import random
import math

from tempe.bitmaps import Bitmaps, ColoredBitmaps
from tempe.colormaps.viridis import viridis
from tempe.colormaps.viridis import viridis
from tempe.data_view import Repeat, Range, Interpolated
from tempe.geometry import RowGeometry, ColumnGeometry
from tempe.surface import Surface
from tempe.shapes import Rectangles
from tempe.lines import WideLines, WidePolyLines
from tempe.display import FileDisplay

random.seed(0)

surface = Surface()

# a buffer one quarter the size of the screen
working_buffer = array('H', bytearray(2*320*61))

# fill the background with white pixels
background = Rectangles([(0, 0, 320, 240)], [0xffff])
surface.add_shape('BACKGROUND', background)


# draw some lines
lines = WideLines(
ColumnGeometry([
Range(8, 96, 8), Repeat(20), Range(8, 184, 16), Repeat(100),
Range(1, 11)
]),
Interpolated(viridis, 10),
clip=(0, 0, 160, 120)
)
surface.add_shape('DRAWING', lines)


# draw some lines in the opposite direction
lines = WideLines(
ColumnGeometry([
Range(8, 96, 8) + 160, Repeat(100), Range(8, 184, 16) + 160, Repeat(20),
Range(1, 11)
]),
Interpolated(viridis, 10),
clip=(160, 0, 160, 120),
round=False,
)
surface.add_shape('DRAWING', lines)

# draw some polylines
polylines = WidePolyLines(
RowGeometry([
(
array('h', [
10 + 30 * i, 125,
10 + 30 * i, 225,
15 + 30 * i, 225,
25 + 30 * i, 165,
10 + 30 * i, 125,
]),
i+1,
)
for i in range(10)
]),
Interpolated(viridis, 10),
clip=(0, 120, 320, 120)
)
surface.add_shape('DRAWING', polylines)

def main(surface, working_buffer):


async def init_display():
from devices.st7789 import ST7789
from machine import Pin, SPI

spi = SPI(0, baudrate=62_500_000, phase=1, polarity=1, sck=Pin(18, Pin.OUT), mosi=Pin(19, Pin.OUT), miso=Pin(16, Pin.OUT))
backlight = Pin(20, Pin.OUT)
display = ST7789(spi, cs_pin=Pin(17, Pin.OUT, value=1), dc_pin=Pin(16, Pin.OUT))
backlight(1)
await display.init()
return display

# set up the display object
display = asyncio.run(init_display())

# refresh the display
display.clear()
surface.refresh(display, working_buffer)

if __name__ == '__main__':

# if we have an actual screen, use it
main(surface, working_buffer)

elif __name__ != '__test__':

# set up the display object
display = FileDisplay('lines.rgb565', (320, 240))
# refresh the display
with display:
display.clear()
surface.refresh(display, working_buffer)
34 changes: 34 additions & 0 deletions src/tempe/_speedups.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import micropython


@micropython.viper
def line_points(x0: int, y0: int, x1: int, y1: int, w: int, d: int, vertices: ptr16):
dx: int = x1 - x0
dy: int = 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

130 changes: 130 additions & 0 deletions src/tempe/lines.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
from array import array
from math import sqrt
import micropython

from .shapes import ColoredGeometry


class WideLines(ColoredGeometry):
"""Render multiple colored line segments with variable width.

Geometry should produce x0, y0, x1, y1, width arrays.
"""

def __init__(self, geometry, colors, *, round=True, surface=None, clip=None):
super().__init__(geometry, colors, surface=surface, clip=clip)
self.round = round

def draw(self, buffer, x=0, y=0):
vertices = array('h', bytearray(16))
should_round = self.round
for geometry, color in self:
x0 = geometry[0]
y0 = geometry[1]
x1 = geometry[2]
y1 = geometry[3]
w = geometry[4]
if w < 2:
buffer.line(x0 - x, y0 - y, x1 - x, y1 - y, color)
else:
d = 2 * int(sqrt((x1 - x0)**2 + (y1 - y0)**2))
line_points(x0, y0, x1, y1, w, d, vertices)
buffer.poly(-x, -y, vertices, color, True)
if should_round:
r = w // 2
buffer.ellipse(x0 - x, y0 - y, r, r, color, True)
buffer.ellipse(x1 - x, y1 - y, r, r, color, True)

def _bounds(self):
max_x = -0x7fff
min_x = 0x7fff
max_y = -0x7fff
min_y = 0x7fff
for geometry in self.geometry:
max_x = max(max_x, geometry[0] + geometry[4], geometry[2] + geometry[4])
min_x = min(min_x, geometry[0] - geometry[4], geometry[2] - geometry[4])
max_y = max(max_y, geometry[1] + geometry[4], geometry[3] + geometry[4])
min_y = min(min_y, geometry[1] - geometry[4], geometry[3] - geometry[4])

return (min_x, min_y, max_x - min_x, max_y - min_y)



class WidePolyLines(ColoredGeometry):
"""Render multiple colored polylines with variable width.

Geometry should produce array of [x0, y0, x1, y1, ...] and width.
"""

def draw(self, buffer, x=0, y=0):
vertices = array('h', bytearray(16))
for geometry, color in self:
lines, w = geometry
for i in range(0, len(lines) - 2, 2):
x0 = lines[i]
y0 = lines[i+1]
x1 = lines[i+2]
y1 = lines[i+3]
if w < 2:
buffer.line(x0 - x, y0 - y, x1 - x, y1 - y, color)
else:
d = 2 * int(sqrt((x1 - x0)**2 + (y1 - y0)**2))
line_points(x0, y0, x1, y1, w, d, vertices)
buffer.poly(-x, -y, vertices, color, True)
if w >= 2:
r = w // 2
for i in range(0, len(lines), 2):
x0 = lines[i]
y0 = lines[i+1]
buffer.ellipse(x0 - x, y0 - y, r, r, color, True)

def _bounds(self):
max_x = -0x7fff
min_x = 0x7fff
max_y = -0x7fff
min_y = 0x7fff
for geometry in self.geometry:
lines, w = geometry
for i in range(0, len(lines), 2):
max_x = max(max_x, lines[i] + w)
min_x = min(min_x, lines[i] - w)
max_y = max(max_y, lines[i+1] + w)
min_y = min(min_y, lines[i+1] - w)

return (min_x, min_y, max_x - min_x, max_y - min_y)


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

try:
from ._speedups import line_points
except SyntaxError:
pass
13 changes: 13 additions & 0 deletions src/tempe/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
{
"urls": [
["tempe/__init__.py", "github:unital/tempe/src/tempe/__init__.py"],
["tempe/_speedups.py", "github:unital/tempe/src/tempe/_speedups.py"],
["tempe/_data_view_math.py", "github:unital/tempe/src/tempe/_data_view_math.py"],
["tempe/bitmaps.py", "github:unital/tempe/src/tempe/bitmaps.py"],
["tempe/data_view.py", "github:unital/tempe/src/tempe/data_view.py"],
["tempe/display.py", "github:unital/tempe/src/tempe/display.py"],
["tempe/geometry.py", "github:unital/tempe/src/tempe/geometry.py"],
["tempe/lines.py", "github:unital/tempe/src/tempe/lines.py"],
["tempe/markers.py", "github:unital/tempe/src/tempe/markers.py"],
["tempe/raster.py", "github:unital/tempe/src/tempe/raster.py"],
["tempe/shapes.py", "github:unital/tempe/src/tempe/shapes.py"],
["tempe/surface.py", "github:unital/tempe/src/tempe/surface.py"],
["tempe/text.py", "github:unital/tempe/src/tempe/text.py"],
["tempe/util.py", "github:unital/tempe/src/tempe/util.py"]
],
"version": "0.1"
}
Loading