Introdução à programação
com Python em um contexto visual


Operações com vetores e a classe PVector

Vetores no fundo são são listas de números, algo como [105, 42] ou [120, 81, 35]. Os números são os componentes e os vetores, que podem ter o número de componentes que você quiser, mas nos interessam mais neste momento os vetores com 2 ou 3 componentes pois eles são úteis para representar coordenadas, velocidades e acelerações em duas e três dimensões. Podemos visualizá-los como pontos ou como segmentos de reta orientados, a representação gráfica usual é como uma seta.

É possível trabalhar com vetores de muito mais dimensôes, que não são fáceis de visualizar, muito usados em computação científica, da álgebra linear à representação de palavras para processamento de linguagem natural e tradução automatizada.

É possível somar e subtrair vetores somando os componentes de cada um nas mesmas posições, é fácil também multiplicar ou dividir um vetor por um número comum, multiplicando cada componente pelo número (e isso multiplica ou divide a magnitude do vetor). Já para multiplicar vetores entre si tem mais de uma maneira de fazer, o chamado produto escalar (dot product) e o produto vetorial (cross product). Mas podemos pedir para o computador fazer para nós essas operações!

A classe PVector

Os vetores 2-D e 3-D, de duas e três dimensões, podem ser expressos no Processing como objetos da classe PVector, construídos com PVector(x, y)e PVector(x, y, z), respectivamente. Acredito que em breve você vai concordar comigo que essa classe vai facilitar muito fazer operações, contas e transformações, com vetores, o que é especialmente útil para animar partículas e calcular diversas geometrias.

pos = PVector(75, 400) # posição inicial
vel = PVector(2, -8)   # velocidade inicial
acel = PVector(0, 0.1) # aceleração, para baixo

def setup():
    size(500, 500)
    strokeWeight(5)
    
def draw():
    global pos, vel
    point(pos.x, pos.y)
    pos += vel
    vel += acel

vetores2

No exemplo acima, o vetor pos representa a posição de uma partícula, o vetor vel a sua velociade, e o vetor acel uma aceleração para baixo (que pode ser interpretada como resultado de uma força causada pela ação da gravidade, por exemplo).

No código o operador de atribuição aumentada +=, aplicado à variável global pos, faz a velocidade alterar a posição, algo equivalente a pos = pos + vel, e da mesma maneira a velocidade é alterada pela aceleração acel.

Uma classe de partículas que fogem do mouse

particles = []

def setup():
    size(400, 400)
    strokeWeight(5)
    for i in range(200):
        particles.append(Particle(random(width),random(height)))
     
def draw():
    background(200)
    mouse_pos = PVector(mouseX, mouseY)
    for p in particles:
        p.update(mouse_pos)

class Particle:
    def __init__(self, x, y):
        self.pos = PVector(x, y)
        self.vel = PVector(0, 0)  # PVector.random2D()

    def update(self, mouse_pos):
        delta = self.pos - mouse_pos
        d = delta.mag()
        if 0 < d < 50:
            acel = delta.normalize() * 0.01 
            self.vel += acel
        self.pos += self.vel
        self.vel = self.vel * 0.995 # slow down
         
        stroke(max(0, 255 - d * 5,), 0, 0)    
        point(self.pos.x, self.pos.y)
        
        if self.pos.x < 0:
            self.pos.x = width
        elif self.pos.x > width:
            self.pos.x = 0
        if self.pos.y < 0:
            self.pos.y = height
        elif self.pos.y > height:
            self.pos.y = 0 

vetores1

Neste segundo exemplo a diferença (subtração) entre um vetor criado com as cordenadas da posição do mouse (mouse_pos) e o vetor que indiaca a posição de cada partícula (self.pos) é usada para calcular um vetor delta cuja magnitude delta.mag() afeta a aceleração das partículas. Podem ser visto também o uso do método .normalize() para obter um vetor unitário (de magnitude 1) que, mantendo apenas a direção do vetor original delta, é usado para calcular a direção da aceleração. O código poderia talvez ser tornado mais eficiente usando o método que cálcula o quadrado da magnitude (.magSq()) que é mais econômico que o cálculo da magnitude.

Métodos dos vetores PVector

método descrição
set() Define (altera) os componentes do vetor.
random2D() Cria um novo vetor unitário 2D com uma direção aleatória.
random3D() Cria um novo vetor unitário 3D com uma direção aleatória.
fromAngle() Cria um novo vetor unitário 2D a partir de um ângulo.
copy() Produz um novo vetor, cópia do original.
mag() Calcula a magnitude do vetor.
magSq() Calcula a magnitude ao quadrado do vetor. É mais rápido que calcular a magnitude!
add() Soma compontentes x, y, e z, ou um segundo vetor a um vetor. Pode-se usar o operador +  para somar dois vetores também.
sub() Subtrai compontentes x, y, e z, ou um segundo vetor a um vetor. Pode-se usar o operador - pra subtrair um vetor de outro também.
mult() Multipica um vetor por um valor escalar. Pode-se usar o operador *
div() Divide um vetor por um valor escalar. Pode-se usar o operador /
dist() Calcula a distância entre dois pontos representados por dois vetores.
dot() Calcula o produto escalar (dot product) entre dois vetores.
cross() Calcula o produto vetorial (cross product) entre dois vetores.
normalize() Normaliza o vetor, isto é faz com que fique com a magnitude 1, mantendo a mesma direção.
limit() Limita a magnitude do vetor a um certo valor.
setMag() Define, isto é altera, a magnitude do vetor.
heading() Calcula o ângulo de rotação do vetor, isto é, para onde ele aponta.
rotate() Gira o vetor um certo ângulo em radianos (funciona apenas em 2D).
lerp() Produz o resultado da interpolação linear entre dois vetores.
angleBetween() Calcula o ângulo entre dois vetores.