-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a polar plot example and some minor corrections to other examples. (
#20)
- Loading branch information
1 parent
e61d8a3
commit 13c261e
Showing
4 changed files
with
217 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
from array import array | ||
import gc | ||
from math import sqrt, log | ||
|
||
from tempe import colors | ||
from tempe.colormaps.viridis import viridis | ||
from tempe.data_view import Range, Repeat | ||
from tempe.geometry import ColumnGeometry, RowGeometry, PointsToLines | ||
from tempe.markers import Marker | ||
from tempe.polar_geometry import polar_points, polar_r_lines | ||
from tempe.surface import Surface | ||
|
||
|
||
surface = Surface() | ||
|
||
# a buffer one third the size of the screen | ||
working_buffer = array('H', bytearray(2*320*81)) | ||
|
||
|
||
# fill the background with off-white pixels | ||
surface.rects('BACKGROUND', [(0, 0, 320, 240)], [colors.grey_1]) | ||
|
||
class LinearScale: | ||
"""Object that maps data to screen values linearly.""" | ||
|
||
def __init__(self, low_data, low_screen, high_data, high_screen): | ||
self.low_data = low_data | ||
self.low_screen = low_screen | ||
self.high_data = high_data | ||
self.high_screen = high_screen | ||
data_range = high_data - low_data | ||
screen_range = high_screen - low_screen | ||
self.scale = screen_range / data_range | ||
|
||
def scale_values(self, data): | ||
"""Scale data values to screen values.""" | ||
screen = array('h', bytearray(2*len(data))) | ||
low_data = self.low_data | ||
low_screen = self.low_screen | ||
scale = self.scale | ||
for i, value in enumerate(data): | ||
screen[i] = int(low_screen + scale * (value - low_data)) | ||
return screen | ||
|
||
class ColorScale: | ||
"""Object that maps data to color values.""" | ||
|
||
def __init__(self, colormap, low_data, low_screen, high_data, high_screen): | ||
self.colormap = colormap | ||
self.low_data = low_data | ||
self.low_screen = low_screen | ||
self.high_data = high_data | ||
self.high_screen = high_screen | ||
data_range = high_data - low_data | ||
screen_range = high_screen - low_screen | ||
self.scale = screen_range / data_range | ||
|
||
def scale_values(self, data): | ||
"""Scale data values to screen values.""" | ||
screen = array('h', bytearray(2*len(data))) | ||
low_data = self.low_data | ||
low_screen = self.low_screen | ||
scale = self.scale | ||
colormap = self.colormap | ||
max_color = len(colormap) - 1 | ||
for i, value in enumerate(data): | ||
screen[i] = colormap[max( | ||
min( | ||
int(low_screen + scale * (value - low_data)), | ||
max_color, | ||
), | ||
0, | ||
)] | ||
return screen | ||
|
||
gc.collect() | ||
from data.environmental import timestamps, air_quality | ||
|
||
# Plot screen bounds | ||
cx = 160 | ||
cy = 120 | ||
max_r = 100 | ||
|
||
# Map the data to polar coordinates | ||
air_quality_scale = LinearScale(0, 0, 150, max_r) | ||
time_scale = LinearScale(1729551600, -90, 1729638000, 270) | ||
color_scale = ColorScale(viridis, 1729500000, 0, 1729500000+48*60*60, 255) | ||
|
||
thetas = time_scale.scale_values(timestamps) | ||
line_colors = color_scale.scale_values(timestamps) | ||
quality_rs = air_quality_scale.scale_values(air_quality) | ||
|
||
# Create polar line geometry for the data points | ||
quality_lines = PointsToLines(polar_points(cx, cy, ColumnGeometry([quality_rs, thetas]))) | ||
gc.collect() | ||
|
||
# draw the plot | ||
surface.lines( | ||
"DRAWING", | ||
quality_lines, | ||
line_colors, | ||
clip=(cx - max_r, cy - max_r, 2*max_r+1, 2*max_r+1), | ||
) | ||
|
||
quality_label_values = [50, 100, 150] | ||
quality_label_rs = air_quality_scale.scale_values(quality_label_values) | ||
time_label_values = [1729551600 + i*3600 for i in range(6, 24, 6)] | ||
time_label_strings = ["9:00", "12:00", "18:00"] | ||
time_label_thetas = time_scale.scale_values(time_label_values) | ||
time_label_rs = [max_r + 8, max_r + 8, max_r + 40] | ||
|
||
surface.circles( | ||
"UNDERLAY", | ||
ColumnGeometry([ | ||
Repeat(cx), | ||
Repeat(cy), | ||
quality_label_rs, | ||
]), | ||
Repeat(colors.grey_3), | ||
fill=False, | ||
clip=(cx - max_r, cy - max_r, 2*max_r+1, 2*max_r+1), | ||
) | ||
surface.lines( | ||
"UNDERLAY", | ||
polar_r_lines(cx, cy, ColumnGeometry([ | ||
Repeat(air_quality_scale.scale_values([50])[0]), | ||
time_scale.scale_values([1729551600 + 3600*i for i in range(24)]), | ||
Repeat(int(air_quality_scale.scale * 100)), | ||
])), | ||
Repeat(colors.grey_3), | ||
clip=(cx - max_r, cy - max_r, 2*max_r+1, 2*max_r+1), | ||
) | ||
|
||
quality_label_geometry = polar_points(cx, cy, ColumnGeometry([quality_label_rs, Repeat(270)])) | ||
surface.points( | ||
"OVERLAY", | ||
quality_label_geometry, | ||
Repeat(colors.grey_6), | ||
[f" {value:d} ppb" for value in quality_label_values], | ||
clip=(cx, cy - max_r - 8, 64, 100), | ||
) | ||
time_label_geometry = polar_points( | ||
cx, | ||
cy, | ||
ColumnGeometry([time_label_rs, time_label_thetas]), | ||
) | ||
surface.points( | ||
"OVERLAY", | ||
time_label_geometry, | ||
Repeat(colors.grey_6), | ||
time_label_strings, | ||
clip=(0, 0, 320, 240), | ||
) | ||
|
||
# Plot title and additional information | ||
from tempe.fonts import roboto16bold, roboto16 | ||
from tempe.font import TempeFont | ||
surface.text( | ||
'DRAWING', | ||
[[4, 0]], | ||
[colors.grey_7], | ||
["Air Quality (ppb)"], | ||
font=TempeFont(roboto16bold), | ||
) | ||
surface.text( | ||
'DRAWING', | ||
[[4, 20]], | ||
[colors.grey_6], | ||
["20/8/24--\n22/8/24"], | ||
font=TempeFont(roboto16), | ||
) | ||
|
||
def main(surface, working_buffer): | ||
import asyncio | ||
|
||
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() | ||
import time | ||
start = time.ticks_us() | ||
surface.refresh(display, working_buffer) | ||
print(time.ticks_diff(time.ticks_us(), start)) | ||
|
||
|
||
if __name__ == '__main__': | ||
|
||
# # if we have an actual screen, use it | ||
# main(surface, working_buffer) | ||
|
||
# elif __name__ != '__test__': | ||
from tempe.display import FileDisplay | ||
|
||
# set up the display object | ||
display = FileDisplay('polar_plot.rgb565', (320, 240)) | ||
# refresh the display | ||
with display: | ||
display.clear() | ||
surface.refresh(display, working_buffer) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters