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.
Animazione
Animazione

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)
sin e cos
sin e cos
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)
Pentagono
Pentagono

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
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

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)
Esagono
Esagono
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 seguente

Python: 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)
Animazione
Animazione
Tags:
Arduino12 IoT12 Python12 Raspberry PI8
Potrebbe interessarti anche: