Saltar al contenido

Cómo dibujar lentamente una línea en Python

Solución:

Usar un sueño no es una buena idea en este tipo de situación, ya que ralentiza todo el hilo (que es el programa completo en un modelo de un solo hilo).

Es mejor mantener algún tipo de información de estado sobre la línea y, basándose en tiempos en tiempo real (por ejemplo: milisegundos transcurridos), progrese el “crecimiento” de la línea, segundo a segundo.

Esto significa que la línea debe dividirse en segmentos y el segmento de línea más pequeño es un solo píxel. El uso del algoritmo de línea de punto medio es una forma eficaz de determinar todos los píxeles que se encuentran en una línea. Una vez que se han determinado todas las “partes de la línea”, es posible simplemente actualizar el punto final de la línea en función del tiempo transcurrido.

Aquí hay un código que escribí anteriormente que, dado un par de puntos, devuelve una lista de píxeles.

midpoint.py:

def __plotLineLow( x0,y0, x1,y1 ):
    points = []
    dx = x1 - x0
    dy = y1 - y0
    yi = 1
    if dy < 0:
        yi = -1
        dy = -dy
    D = 2*dy - dx
    y = y0

    for x in range( x0, x1 ):
        points.append( (x,y) )
        if D > 0:
           y = y + yi
           D = D - 2*dx
        D = D + 2*dy
    return points

def __plotLineHigh( x0,y0, x1,y1 ):
    points = []
    dx = x1 - x0
    dy = y1 - y0
    xi = 1
    if dx < 0:
        xi = -1
        dx = -dx
    D = 2*dx - dy
    x = x0

    for y in range( y0, y1 ):
        points.append( (x,y) )
        if D > 0:
            x = x + xi
            D = D - 2*dy
        D = D + 2*dx
    return points

def linePoints( pointA, pointB ):
    """ Generate a list of integer points on the line pointA -> pointB """
    x0, y0 = pointA
    x1, y1 = pointB
    points = []
    if ( abs(y1 - y0) < abs(x1 - x0) ):
        if ( x0 > x1 ):
            points += __plotLineLow( x1, y1, x0, y0 )
        else:
            points += __plotLineLow( x0, y0, x1, y1 )
    else:
        if ( y0 > y1 ):
            points += __plotLineHigh( x1, y1, x0, y0 )
        else:
            points += __plotLineHigh( x0, y0, x1, y1 )

    return points


if __name__ == "__main__":
    #midPoint( (597, 337), (553, 337) )
    print( str( linePoints( (135, 295), (135, 304) ) ) )

Y algún código de demostración que implementa un SlowLine clase.

import pygame
import random
import time
import sys

from midpoint import linePoints  # Midpoint line algorithm

# Window size
WINDOW_WIDTH      = 400
WINDOW_HEIGHT     = 400

SKY_BLUE = ( 30,  30,  30)
SKY_RED  = (200, 212,  14)

# Global millisecond count since start
NOW_MS = 0

class SlowLine():
    def __init__( self, pixels_per_second, x0,y0, x1,y1, colour=SKY_RED ):
        self.points       = linePoints( ( x0, y0 ), ( x1, y1 ) )
        self.pixel_count  = len( self.points )
        self.speed        = pixels_per_second
        self.start_point  = self.points[0]     # start with a single-pixel line
        self.end_point    = self.points[0]
        self.pixel_cursor = 0                  # The current end-pixel
        self.last_update  = 0                  # Last time we updated
        self.colour       = colour
        self.fully_drawn  = False

    def update(self):
        global NOW_MS

        if ( self.fully_drawn == True ):
            # nothing to do
            pass
        else:
            # How many milliseconds since the last update() call?
            if ( self.last_update == 0 ):
                self.last_update = NOW_MS
                time_delta = 0
            else:
                time_delta = NOW_MS - self.last_update
                self.last_udpate = NOW_MS

            # New pixels to add => speed * time
            new_pixel_count = time_delta * self.speed / 1000   # this may loose precision with very small speeds

            if ( new_pixel_count + self.pixel_cursor > self.pixel_count ):
                # We're out of pixels
                self.end_point  = self.points[-1]   
                self.full_drawn = True
            else:
                # Grow the line by <new_pixel_count> pixels
                self.pixel_cursor += new_pixel_count
                self.end_point     = self.points[ int( self.pixel_cursor ) ]

    def draw( self, screen ):
        pygame.draw.line( screen, self.colour, self.start_point, self.end_point )




### MAIN
pygame.init()
SURFACE = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
WINDOW  = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), SURFACE )
pygame.display.set_caption("Slow Line Movement")


# Create some random lines
lines = []
for i in range( 20 ):
    rand_speed = random.randint( 1, 50 )
    rand_x0    = random.randint( 0, WINDOW_WIDTH )
    rand_y0    = random.randint( 0, WINDOW_HEIGHT )
    rand_x1    = random.randint( 0, WINDOW_WIDTH )
    rand_y1    = random.randint( 0, WINDOW_HEIGHT )
    lines.append( SlowLine( rand_speed, rand_x0, rand_y0, rand_x1, rand_y1 ) )


# Main event loop
clock = pygame.time.Clock()
done = False
while not done:
    NOW_MS = pygame.time.get_ticks()

    # Update the line lengths
    for l in lines:
        l.update()

    # Handle user-input
    for event in pygame.event.get():
        if ( event.type == pygame.QUIT ):
            done = True

    # Movement keys
    keys = pygame.key.get_pressed()
    if ( keys[pygame.K_UP] ):
        print("up")
    elif ( keys[pygame.K_DOWN] ):
        print("down")
    elif ( keys[pygame.K_LEFT] ):
        print("left")
    elif ( keys[pygame.K_RIGHT] ):
        print("right")
    elif ( keys[pygame.K_q] and ( keys[pygame.K_RCTRL] or keys[pygame.K_LCTRL] ) ):
        print("^Q")
        done = True

    # Update the window, but not more than 60fps
    WINDOW.fill( SKY_BLUE )
    for l in lines:
        l.draw( WINDOW )

    pygame.display.flip()

    # Clamp FPS
    clock.tick_busy_loop(60)

pygame.quit()

En esta animación, el progreso es un poco desigual, pero esa es la animación, no la demostración.
slow_lines.gif

Usted debe display.flip() para actualizar la pantalla y Deje que los eventos de la ventana se procesen usando event.get():

def draw_red_line(i):
    y = 0
    while y < 300:
        pygame.draw.line(screen, RED, (i*100+50, 0), (i*100+50, y))
        pygame.display.flip()
        pygame.event.get()
        y+=1

Si desea que un dibujo sea visible en la pantalla, debe actualizar la pantalla (p. Ej. pygame.display.flip()), y tienes que manejar los eventos por pygame.event.get() o pygame.event.pump().
También tenga en cuenta que los parámetros para pygame.draw.line debe ser integral. Usar round para convertir un valor de coma flotante en un valor integral.

Dibujar la línea en un bucle y actualizar la pantalla no hace lo que desea porque la línea se dibuja sin demora. No recomiendo crear animaciones en un ciclo separado dentro del ciclo principal de la aplicación. Utilice el bucle principal de la aplicación para trazar la línea.

Cree una función que pueda dibujar una línea desde un start apuntar a un end punto, dependiente de un valor p en rango [0.0, 1.0]. Si el valor es 0, no se traza ninguna línea. Si el valor es 1, se traza la línea completa. De lo contrario, se dibujará parte de la línea:

def draw_red_line(surf, color, start, end, w):
    xe = start[0] * (1-w) + end[0] * w
    ye = start[1] * (1-w) + end[1] * w
    pygame.draw.line(surf, color, start, (round(xe), round(ye)))

Utilice esta función en el bucle principal de la aplicación:

w = 0
while True:
    # [...]

    draw_red_line(window, (255, 0, 0), line_start[i], line_end[i], w)
    if w < 1:
        w += 0.01

Véase también Forma y contorno.


Ejemplo mínimo:

import pygame

pygame.init()
window = pygame.display.set_mode((300,300))
clock = pygame.time.Clock()

line_start = [(100, 0),   (200, 0),   (0, 100),   (0, 200)]
line_end   = [(100, 300), (200, 300), (300, 100), (300, 200)]

def draw_red_line(surf, color, start, end, w):
    xe = start[0] * (1-w) + end[0] * w
    ye = start[1] * (1-w) + end[1] * w
    pygame.draw.line(surf, color, start, (round(xe), round(ye)))

count=0
run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    window.fill(0)

    for i in range(int(count)):
        draw_red_line(window, (255, 255, 255), line_start[i], line_end[i], 1)
    if count < 4:
        i = int(count)
        draw_red_line(window, (255, 0, 0), line_start[i], line_end[i], count-i)
        count += 0.01
    else:
        count = 0
        
    pygame.display.flip()

pygame.quit()
exit()
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *