Skip to content

Commit

Permalink
Add various functions to convert polar geometry to cartesian geometry (
Browse files Browse the repository at this point in the history
  • Loading branch information
corranwebster authored Oct 21, 2024
1 parent 58ec39e commit 7e877f4
Show file tree
Hide file tree
Showing 12 changed files with 3,068 additions and 16 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Beautiful Micropython Graphics

<img src="./pico-tempe-shapes.png" width="480" alt="A Raspberry Pi Pico with a variety of shapes drawn on a screen" /> <img src=".//docs/source/user_guide/shapes.png" width="160" alt="A variety of shapes drawn in an image" />

<img src="./pico-tempe-polar.png" width="480" alt="A Raspberry Pi Pico with a variety of polar plots on the screen" /> <img src=".//docs/source/user_guide/polar.png" width="160" alt="A variety of polar plots drawn in an image" />

Tempe is a pure-Micropython graphics system designed to be make using the
full capabilities of display devices more accessible, particularly on
memory-constrained microcontrollers. The aim is to allow data scientists,
Expand Down
Binary file added docs/source/user_guide/polar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 7 additions & 10 deletions examples/lines_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,13 @@
# 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,
)
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),
Expand Down
189 changes: 189 additions & 0 deletions examples/polar_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import asyncio
from array import array
import framebuf
import random
import math

from tempe.colors import grey_e, grey_3
from tempe.colormaps.viridis import viridis
from tempe.data_view import Repeat, Range, Interpolated
from tempe.geometry import ColumnGeometry, Extend, RowGeometry
from tempe.polar_geometry import polar_rects, polar_point_arrays, polar_points, polar_r_lines
from tempe.surface import Surface
from tempe.shapes import Rectangles, Polygons, Circles, Lines
from tempe.markers import Markers
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 a coxcomb plot
circle_grid = Circles(
ColumnGeometry([
Repeat(64),
Repeat(64),
Range(10, 70, 10),
]),
Repeat(grey_e),
fill=False,
clip=(0, 0, 320, 240),
)
surface.add_shape('UNDERLAY', circle_grid)
ray_grid = Lines(
polar_r_lines(64, 64, ColumnGeometry([
Repeat(0),
Range(0, 360, 60),
Repeat(60),
])),
Repeat(grey_e),
clip=(0, 0, 320, 240),
)
surface.add_shape('UNDERLAY', ray_grid)
coxcomb = Polygons(
polar_rects(64, 64, ColumnGeometry([
Repeat(0),
Range(0, 360, 60),
[30, 15, 45, 5, 45, 60],
Repeat(60)
])),
Interpolated(viridis, 6),
clip=(0, 0, 320, 240),
)
surface.add_shape('DRAWING', coxcomb)

# draw a radar plot
circle_grid = Circles(
ColumnGeometry([
Repeat(196),
Repeat(64),
Range(10, 70, 10),
]),
Repeat(grey_e),
fill=False,
clip=(0, 0, 320, 240),
)
surface.add_shape('UNDERLAY', circle_grid)
ray_grid = Lines(
polar_r_lines(196, 64, ColumnGeometry([
Repeat(0),
Range(0, 360, 60),
Repeat(60),
])),
Repeat(grey_e),
clip=(0, 0, 320, 240),
)
surface.add_shape('UNDERLAY', ray_grid)
radar = Polygons(
polar_point_arrays(196, 64,
RowGeometry.from_lists([
[30, 0, 15, 60, 45, 120, 5, 180, 45, 240, 60, 300],
[12, 0, 18, 60, 43, 120, 35, 180, 25, 240, 20, 300],
]),
),
Interpolated(viridis, 6),
fill=False,
clip=(0, 0, 320, 240),
)
surface.add_shape('DRAWING', radar)
markers = Markers(
Extend([
polar_points(196, 64,
ColumnGeometry([
[30, 15, 45, 5, 45, 60],
Range(0, 360, 60),
]),
),
ColumnGeometry([Repeat(24)]),
]),
Repeat(grey_3),
[f" {x}" for x in [6, 3, 9, 1, 9, 12]],
clip=(0, 0, 320, 240),
)
surface.add_shape('OVERLAY', markers)

circle_bar = Polygons(
polar_rects(128, 240-64, ColumnGeometry([
Range(24, 60, 12),
Repeat(-90),
Repeat(8),
[240, 320, 100],
]), decimation=6),
Interpolated(viridis, 3),
clip=(0, 0, 320, 240),
)
surface.add_shape('DRAWING', circle_bar)
circle_bar_ends = Circles(
Extend([
polar_points(128, 240-64, ColumnGeometry([
Range(28, 60, 12),
[240-90, 320-90, 100-90],
])),
ColumnGeometry([Repeat(4)]),
]),
Interpolated(viridis, 3),
fill=True,
clip=(0, 0, 320, 240),
)
surface.add_shape('DRAWING', circle_bar_ends)

values = [6, 3, 9, 1, 9, 12]
cumsum = [sum(values[:i]) for i in range(len(values) + 1)]
angles = [int(values * 360 / cumsum[-1]) for values in cumsum]
deltas = [angles[i+1] - angles[i] for i in range(len(values))]

donut = Polygons(
polar_rects(256, 240-64, ColumnGeometry([
Repeat(30),
angles[:-1],
Repeat(24),
deltas,
]), decimation=10),
Interpolated(viridis, 6),
fill=True,
clip=(0, 0, 320, 240),
)
surface.add_shape('DRAWING', donut)


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('polar.rgb565', (320, 240))
# refresh the display
with display:
display.clear()
surface.refresh(display, working_buffer)
Binary file added pico-tempe-polar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 30 additions & 4 deletions src/tempe/geometry.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from .data_view import DataView

from array import array
from math import pi, sin, cos

from .data_view import DataView

POINT = "point"
CIRCLE = "circle"
Expand Down Expand Up @@ -43,7 +45,11 @@ def __iter__(self):
yield array("h", coords)

def __len__(self):
return max(len(coord) for coord in self.geometry)
lengths = [len(coord) for coord in self.geometry if len(coord) is not None]
if lengths:
return max(lengths)
else:
return None


class StripGeometry(Geometry):
Expand All @@ -65,9 +71,29 @@ def __iter__(self):
start = 0
buf = array("h", bytearray(2 * size))
for i in range(len(self)):
start += self.step * self.n_coords
buf[:] = self.geometry[start : start + size]
start += self.step * self.n_coords
yield buf

def __len__(self):
return ((len(self.geometry) // self.n_coords) - self.n_vertices) // self.step + 1


class Extend(Geometry):

def __iter__(self):
for coords in zip(*self.geometry):
buf = array('h', bytearray(2*sum(len(coord) for coord in coords)))
i = 0
for coord in coords:
buf[i:i + len(coord)] = coord
i += len(coord)
yield buf

def __len__(self):
return ((len(self.geometry) // self.n_coords) - self.n_vertices) // self.step
lengths = [len(coord) for coord in self.geometry if len(coord) is not None]
if lengths:
return max(lengths)
else:
return None

6 changes: 4 additions & 2 deletions src/tempe/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ class WidePolyLines(ColoredGeometry):
def draw(self, buffer, x=0, y=0):
vertices = array('h', bytearray(16))
for geometry, color in self:
lines, w = geometry
w = geometry[-1]
lines = geometry[:-1]
for i in range(0, len(lines) - 2, 2):
x0 = lines[i]
y0 = lines[i+1]
Expand All @@ -84,7 +85,8 @@ def _bounds(self):
max_y = -0x7fff
min_y = 0x7fff
for geometry in self.geometry:
lines, w = geometry
w = geometry[-1]
lines = geometry[:-1]
for i in range(0, len(lines), 2):
max_x = max(max_x, lines[i] + w)
min_x = min(min_x, lines[i] - w)
Expand Down
1 change: 1 addition & 0 deletions src/tempe/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
["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/polar.py", "github:unital/tempe/src/tempe/polar.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"],
Expand Down
Loading

0 comments on commit 7e877f4

Please sign in to comment.