Created
July 21, 2022 23:49
-
-
Save todbot/5cf76ca7344e9196326014989172fee1 to your computer and use it in GitHub Desktop.
Demo rotation of a vectorio polygon
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
# vectorio_rotate_example.py | |
# 21 Jul 2022 - @todbot / Tod Kurt | |
# Demo rotation of a vectorio polygon | |
import time | |
import board | |
import displayio | |
import vectorio | |
import math | |
#import ulab.numpy as np | |
display = board.DISPLAY # assume we have built-in display | |
# 6-pointed star created with Figma & PathToPoints: https://todbot.github.io/PathToPoints/ | |
path0_w = 43.301 | |
path0_h = 50.000 | |
path0 = [(22.001,1.003),(22.398,2.963),(22.795,4.924),(23.192,6.880),(23.590,8.848),(23.985,10.794),(24.383,12.759),(24.779,14.715),(25.176,16.674),(25.574,18.637),(26.816,19.147),(28.706,18.513),(30.594,17.880),(32.492,17.243),(34.399,16.604),(36.292,15.968),(38.183,15.334),(40.093,14.694),(41.983,14.060),(43.472,13.658),(41.972,14.983),(40.475,16.305),(38.978,17.628),(37.475,18.955),(35.975,20.280),(34.479,21.602),(32.980,22.926),(31.482,24.249),(29.987,25.570),(30.518,26.899),(32.014,28.220),(33.517,29.549),(35.012,30.869),(36.513,32.195),(38.017,33.524),(39.510,34.842),(41.006,36.164),(42.505,37.488),(43.208,38.352),(41.310,37.715),(39.408,37.077),(37.515,36.442),(35.617,35.805),(33.719,35.168),(31.823,34.532),(29.924,33.895),(28.035,33.262),(26.124,32.621),(25.434,34.057),(25.036,36.019),(24.640,37.972),(24.241,39.942),(23.845,41.897),(23.447,43.859),(23.050,45.821),(22.653,47.777),(22.255,49.743),(21.858,50.297),(21.463,48.349),(21.063,46.378),(20.667,44.424),(20.268,42.452),(19.873,40.504),(19.473,38.529),(19.078,36.579),(18.680,34.618),(18.282,32.655),(16.517,33.076),(14.606,33.718),(12.721,34.350),(10.814,34.990),(8.933,35.621),(7.028,36.260),(5.135,36.895),(3.245,37.529),(1.337,38.169),(1.071,37.863),(2.560,36.547),(4.067,35.216),(5.558,33.899),(7.062,32.570),(8.560,31.247),(10.061,29.921),(11.551,28.605),(13.052,27.279),(14.449,25.955),(12.945,24.627),(11.447,23.303),(9.945,21.976),(8.448,20.654),(6.948,19.329),(5.458,18.013),(3.957,16.687),(2.453,15.358),(0.957,14.037),(1.483,13.880),(3.376,14.515),(5.264,15.149),(7.168,15.787),(9.064,16.423),(10.969,17.062),(12.855,17.695),(14.756,18.333),(16.646,18.967),(18.312,19.199),(18.710,17.236),(19.106,15.283),(19.504,13.317),(19.900,11.361),(20.298,9.400),(20.694,7.446),(21.092,5.479),(21.490,3.515),(21.888,1.554),] | |
shape0_w = 35 # desired screen size of path | |
shape0_h = shape0_w * (path0_h/path0_w) # maintain aspect ratio | |
shape0_color = 0xff00ff # preferred color | |
def rotate_points(pts, a): | |
"""Rotate a list of points pts by angle a around origin""" | |
sa, ca = math.sin(a), math.cos(a) # do this computation only once | |
return [ (p[0]*ca - p[1]*sa, p[1]*ca + p[0]*sa) for p in pts ] # p[0]=x, p[1]=y | |
def recenter_points(pts, cx, cy): | |
"""Center points around new origin c""" | |
return [(p[0] - cx, p[1] - cy) for p in pts] | |
def rescale_points(pts, orig_w, orig_h, new_w, new_h): | |
"""Rescale point path to new w,h""" | |
return [ (new_w * p[0] / orig_w, new_h * p[1] / orig_h ) for p in pts] | |
def int_points(pts): | |
"""Convert array of flooat points to int, for vectorio""" | |
return [(int(p[0]),int(p[1])) for p in pts] | |
# rescale to desired size | |
path0 = rescale_points(path0, path0_w, path0_h, shape0_w, shape0_h) | |
# recenter points around midpoint of path so rotation works as expected | |
path0 = recenter_points(path0, cx=shape0_w/2, cy=shape0_h/2) | |
maingroup = displayio.Group() | |
display.show(maingroup) # put main group on display, everything goes in maingroup | |
# create vectorio shape, put in a group, put that on maingroup | |
pal = displayio.Palette(1) | |
pal[0] = shape0_color | |
shape0 = vectorio.Polygon(pixel_shader=pal, points=int_points(path0)) | |
shapeg = displayio.Group() | |
shapeg.append(shape0) | |
maingroup.append(shapeg) | |
x, y, theta = 80, 80, 0 # starting position for our shape | |
xvel, yvel, theta_vel = 1.8, 1.0, 0.04 # starting velocities | |
last_debug_time = 0 | |
while True: | |
elapsed_time = time.monotonic() | |
# rotate about shape origin, based off original float points | |
shape0.points = int_points( rotate_points(path0, theta) ) | |
elapsed_time = time.monotonic() - elapsed_time | |
if time.monotonic() - last_debug_time > 0.5: | |
last_debug_time = time.monotonic() | |
print("elapsed millis %d" % (elapsed_time * 1000)) | |
# update position | |
x, y, theta = x + xvel, y + yvel, theta + theta_vel | |
shapeg.x = int(x) | |
shapeg.y = int(y) | |
# bounce on screen edge hit | |
if x < shape0_w//2 or x > (display.width - shape0_w//2): | |
xvel = -xvel | |
if y < shape0_h//2 or y > (display.height - shape0_h//2): | |
yvel = -yvel | |
time.sleep(0.05) | |
Updated for ulab.numpy
https://gist.github.com/tlyu/15ca0bda137ebac79af69fff03e7874f
Summary (on a Clue):
np_rotate_points
1msnp_rotate_points
plusnp_int_points
total 21msrotate_points
9msrotate_points
plusint_points
total 16ms
Creating lists of tuples is expensive! The ulab.numpy
times seem to have a fairly high variance. Maybe it's some garbage collection stuff?
Thank you so much for this! For some reason I had convinced myself that np.dot()
didn't return the correct data structure when given a list of points.
But wow I wish vectorio
took floats so we could avoid the int conversion. Maybe I will make a PR for that.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example video:
vectorio_rotate_example.mp4