Animare un poligono su Raspberry PI Pico Display
In questa demo in MicroPython mostro come far ruotare un poligono sullo schermo del Raspberry PI Pico usando la funzione per disegnare le linee creata precedentemente.
Queste funzioni servono per ricavare la coordinata X e Y del segmento da ruotare in funzione dell'angolo di rotazione espresso in radianti, rispettivamente:
Come prima cosa definisco la funzione di animazione che incrementa l'angolo di rotazione ogni volta che viene richiamata
Poi definisco la funzione update_display che si occuperà di disegnare i segmenti che compongono il poligono sul display
Infine gestisco il ciclo infinito di animazione introducendo una piccola pausa ad ogni ciclo (utime.sleep(0.01))
Il pulsante A incrementa il numero di lati, mentre il B li riduce.
La pressione dei pulsanti modificano la variabile globale, global segment, tra un valore minimo di 3 segmenti (triangolo) ad un massimo di 20 (praticamente un cerchio).
Anche il ciclo di animazione va modificato gestendo la pressione dei pulsanti
Matematica
Far ruotare un oggetto sullo schermo richiede l'uso di un po' di trigonometria di base come le funzioni seno e coseno.Queste funzioni servono per ricavare la coordinata X e Y del segmento da ruotare in funzione dell'angolo di rotazione espresso in radianti, rispettivamente:
- x = math.cos(angle)
- y = math.sin(angle)
Le funzioni sin e cos ritornano un valore in virgola mobile (float) compreso tra 0 e 1 che va poi moltiplicato per la lunghezza del segmento da ruotare (raggio)
Animazione
Il codice MicroPython per creare un poligono e ruotarlo è il seguente.Come prima cosa definisco la funzione di animazione che incrementa l'angolo di rotazione ogni volta che viene richiamata
Python: Animazione rotazione
angle_offset = -math.pi / 2
angle_inc = math.pi / 40
# aggiorno l'angolo di rotazione (animazione)
def update_position():
global angle_offset
angle_offset += angle_inc
if angle_offset >= math.pi * 2:
angle_offset = float(angle_offset - math.pi * 2)
Poi definisco la funzione update_display che si occuperà di disegnare i segmenti che compongono il poligono sul display
Python: Disegno poligono
# disegno poligono
segment = 5 # numero di lati del poligono
rad_deg = 180 / math.pi # conversione radianti in gradi
pi360 = math.pi * 2.0 # angolo giro
def update_display():
display.set_pen(0, 0, 0)
display.clear()
cx = width / 2
cy = height / 2
radius = cy - 10
angle_inc = pi360 / segment
angle = angle_offset
x0 = 0
y0 = 0
xp = 0
yp = 0
# visualizzo l'angolo corrente
display.set_pen(0, 80, 0)
display.text(str(angle * rad_deg), 5, 118, 230, 2)
for i in range(segment):
x = int(cx + math.cos(angle) * radius)
y = int(cy + math.sin(angle) * radius)
if i == 0:
x0 = x
y0 = y
display.set_pen(128, 128, 128)
line(cx, cy, x, y)
else:
display.set_pen(255, 0, 0)
line(xp, yp, x, y)
angle += angle_inc
# salvo le coordinate attuali
xp = x
yp = y
line(xp, yp, x0, y0)
# testo sgart.it
display.set_pen(128, 0, 0)
display.text("SGART.IT", 150, 5, 170, 2)
display.update()
Infine gestisco il ciclo infinito di animazione introducendo una piccola pausa ad ogni ciclo (utime.sleep(0.01))
Python: Ciclo di animazione
while True:
display.set_led(0, 0, 0)
update_position()
update_display()
# breve pausa
utime.sleep(0.01)
Modifica dei lati
Per completare la demo, aggiungo la gestione dei pulsanti A e B, in modo modifichino il numero di lati di cui è composto il poligono.Il pulsante A incrementa il numero di lati, mentre il B li riduce.
Python: Gestione incremento/decremento angolo
def incSegment():
global segment
if segment < 20:
segment += 1
display.set_led(0, 80, 0)
return True
return False
def decSegment():
global segment
if segment > 3:
segment -= 1
display.set_led(0, 80, 0)
return True
return False
Anche il ciclo di animazione va modificato gestendo la pressione dei pulsanti
Python: Gestione pressione pulsanti
while True:
display.set_led(0, 0, 0)
# gestione pulsanti
if display.is_pressed(display.BUTTON_A):
incSegment()
elif display.is_pressed(display.BUTTON_B):
decSegment()
update_position()
update_display()
# breve pausa
utime.sleep(0.01)
La velocità dell'animazione è data dal tempo di attesa nel ciclo, utime.sleep(0.01), e l'incremento dell'angolo ad ogni ciclo, variabile angle_inc.
Codice completo
Il codice completo dell'animazione completo della funzione line e il seguentePython: Animazione poligoni
import picodisplay as display
import math
import utime
width = display.get_width()
height = display.get_height()
print("screen: " + str(width) + " x " + str(height) + " px")
buffer = bytearray(width * height * 2) # 2-bytes per pixel (RGB565)
display.init(buffer)
display.set_backlight(1.0)
#---------------------------------------------
def line(x1, y1, x2, y2):
if x1 == x2 and y1 == y2:
# punto
display.pixel(x1, y1)
else:
ox = x1
oy = y1
w = x2 - x1
h = y2 - y1
greaterDx = math.copysign(w, 1) > math.copysign(h, 1)
# calcolo inclinazione linea
m = 1.0
if w != 0:
m = h / w
# disegno linea
if greaterDx:
if x1 > x2:
ox = x2
oy = y2
w = math.copysign(w, 1)
for x in range(w + 1):
nx = ox + x
ny = oy
if h != 0:
ny = oy + (m * x)
display.pixel(int(nx), int(ny))
else:
if y1 > y2:
ox = x2
oy = y2
h = math.copysign(h, 1)
for y in range(h + 1):
nx = ox
if w != 0:
nx = ox + (y / m)
ny = oy + y
display.pixel(int(nx), int(ny))
#---------------------------------------------
def incSegment():
global segment
if segment < 20:
segment += 1
display.set_led(0, 80, 0)
return True
return False
def decSegment():
global segment
if segment > 3:
segment -= 1
display.set_led(0, 80, 0)
return True
return False
#---------------------------------------------
angle_offset = -math.pi / 2
angle_inc = math.pi / 40
# aggiorno l'angolo di rotazione (animazione)
def update_position():
global angle_offset
angle_offset += angle_inc
if angle_offset >= math.pi * 2:
angle_offset = float(angle_offset - math.pi * 2)
#---------------------------------------------
# disegno poligono
segment = 5 # numero di lati del poligono
rad_deg = 180 / math.pi # conversione radianti in gradi
pi360 = math.pi * 2.0 # angolo giro
def update_display():
display.set_pen(0, 0, 0)
display.clear()
cx = width / 2
cy = height / 2
radius = cy - 10
angle_inc = pi360 / segment
angle = angle_offset
x0 = 0
y0 = 0
xp = 0
yp = 0
# visualizzo l'angolo corrente
display.set_pen(0, 80, 0)
display.text(str(angle * rad_deg), 5, 118, 230, 2)
for i in range(segment):
x = int(cx + math.cos(angle) * radius)
y = int(cy + math.sin(angle) * radius)
if i == 0:
x0 = x
y0 = y
display.set_pen(128, 128, 128)
line(cx, cy, x, y)
else:
display.set_pen(255, 0, 0)
line(xp, yp, x, y)
angle += angle_inc
# salvo le coordinate attuali
xp = x
yp = y
line(xp, yp, x0, y0)
# testo sgart.it
display.set_pen(128, 0, 0)
display.text("SGART.IT", 150, 5, 170, 2)
display.update()
#---------------------------------------------
while True:
display.set_led(0, 0, 0)
# gestione pulsanti
if display.is_pressed(display.BUTTON_A):
incSegment()
elif display.is_pressed(display.BUTTON_B):
decSegment()
update_position()
update_display()
# breve pausa
utime.sleep(0.01)