Mudanças entre as edições de "Vetores e Trigonometria"

De Aulas
 
Linha 1: Linha 1:
 +
  
  
Linha 272: Linha 273:
 
Evitar o cálculo do tamanho reduz muito processamento, por isso é bom usar vetores normalizados para representar direções. No C++, o método para o cálculo do produto escalar fica assim:
 
Evitar o cálculo do tamanho reduz muito processamento, por isso é bom usar vetores normalizados para representar direções. No C++, o método para o cálculo do produto escalar fica assim:
  
<syntaxhighlight lang=c line>
+
Código em C++:
 +
 
 +
<syntaxhighlight lang="c++">
 
float Vector2D::dot(const Vector2D& other) const {
 
float Vector2D::dot(const Vector2D& other) const {
 
return (x * other.x) + (y * other.y);
 
return (x * other.x) + (y * other.y);
Linha 278: Linha 281:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
E o do ângulo entre os dois vetores unitários, assim:
+
Código em Python:<syntaxhighlight lang="python3">
 +
    def dot(self, other: Vector2D) -> float:
 +
        return (self.x * other.x) + (self.y * other.y)
 +
</syntaxhighlight>E o do ângulo entre os dois vetores unitários, assim (C++):
  
<syntaxhighlight lang=c line>
+
<syntaxhighlight lang="c++">
 
float Vector2D::angleBetween(const Vector2D& other) const {
 
float Vector2D::angleBetween(const Vector2D& other) const {
 
float dp = dot(other);
 
float dp = dot(other);
Linha 292: Linha 298:
 
return dot(other) > 0 ? -angPi : angPi;
 
return dot(other) > 0 ? -angPi : angPi;
 
}
 
}
 +
</syntaxhighlight>Código em Python:<syntaxhighlight lang="python3">
 +
    def angle_between(self, other: Vector2D) -> float:
 +
        dp = self.dot(other)
 +
 +
        # Garantir que o valor do produto escalar esteja entre -1 e 1
 +
        dp = max(-1.0, min(1.0, dp))
 +
 +
        # Calcular o ângulo em radianos
 +
        angPi = math.acos(dp)
 +
 +
        # Retornar o ângulo positivo ou negativo dependendo do sinal do produto escalar
 +
        return -angPi if self.dot(other) > 0 else angPi
 
</syntaxhighlight>
 
</syntaxhighlight>
  

Edição atual tal como às 18h18min de 28 de março de 2025


Links Relacionados: Matemática e Física para Jogos, Jogos Digitais

Descrição

Na Computação Gráfica, e consequentemente nos Games que se utiliza desse recurso para a apresentação e interação visual, são utilizadas algumas grandezas como direção e força do vento, gravidade, direção que um personagem está olhando, etc. Essa é descrita como uma grandeza vetorial.

Essas direções e intensidades são representadas por vetores. Estes possuem informações referentes a distância, sentido e tamanho, geralmente representados graficamente por setas.

Vetores.png

Características dos Vetores

Vetores representacao.png
  • Tamanho: é a intensidade. É também chamado de magnitude;
  • Direção: indica a inclinação do vetor no espaço;
  • Sentido: mostra para onde o vetor está apontando;
Observação: vetores não possuem como propriedade a posição.

Vetores representam deslocamentos com sinal em cada eixo.

  • Em 2 dimensões têm-se um valor para x e y.
  • Em 3 dimensões, têm-se x, y e z.
  • Em 4 dimensões, têm-se x, y, z e w.

Graficamente, representamos vetores como uma flecha. A posição da flecha não importa, desde que sua direção, tamanho e sentido sejam mantidos.

Representação na Programação

No C++, python ou no Java, esses valores virariam variáveis float:

Código em C++

class Vector2D {
public:
	float x;
	float y;

	Vector2D() : x (0.0f), y (0.0f) {}
	Vector2D(float _x, float _y) : x(_x), y(_y) {}
};

Código em Python

class Vector2D:
    def __init__(self, x: float, y: float):
        self.x: float = x
        self.y: float = y

Tamanho

Para encontrarmos o tamanho, utilizamos a fórmula definida na última aula. Transcrevendo-a em C++, temos:

Código em C++

#include <math.h>

float Vector2D::size() {
	return sqrt((x * x) + (y * y));
}

Código em Python

    def size(self) -> float:
        return math.sqrt((self.x * self.x) + (self.y * self.y))

Soma de Vetores

Também podemos utilizar operações de soma de vetores. Por exemplo:

Vetores soma.png

Com o código em C++:

Vector2D Vector2D::operator +(const Vector2D& other) const {
	return Vector2D(x + other.x, y + other.y);
}

Vector2D& Vector2D::operator +=(const Vector2D& other) {
	x += other.x;
	y += other.y;
	return *this;
}

Código em Python

    def add(self, other: Vector2D) -> None:
        self.x += other.x
        self.y += other.y

    @staticmethod
    def sum(a: "Vector2D", b: "Vector2D") -> "Vector2D":
        return Vector2D(a.x + b.x, a.y + b.y)

Um exemplo prático pode se ver na figura a seguir. Se quisermos representar a trajetória da bola, podemos somar o vetor gravidade ao vetor da trajetória. É possível também adicionar vetores como vento, por exemplo.

Vetores chute.png

Vetores também podem ser multiplicados por valores. Para tal, o código em C++ efetua essa operação:

Vector2D Vector2D::operator *(float scalar) const {
	return Vector2D(x * scalar, y * scalar);
}

Vector2D& Vector2D::operator *=(float scalar) {
	x *= scalar;
	y *= scalar;
	return *this;
}

Código em Python

    def multiply(self, other: Vector2D) -> None:
        self.x *= other.x
        self.y *= other.y

    @staticmethod
    def multiply_vectors(a: "Vector2D", b: "Vector2D") -> "Vector2D":
        return Vector2D(a.x * b.x, a.y * b.y)

Normalização

Para normalizar um vetor, ou seja, impor um tamanho a ele, utilizamos a operação de normalização. Isso reduz o tamanho do vetor para 1 (um). Este vetor norm alizado pode então ser multiplicado por um escalar para obtermos tamanho desejado. Para normalizar um vetor, basta dividirmos x e y pelo tamanho do vetor. Segue o código em C++:

Vector2D& Vector2D::normalize() {
	return (*this /= size());
}

Cóidigo em Python:

    def normalize(self) -> Vector2D:
        s: float = self.size()
        return Vector2D(self.x / s, self.y / s)

Vetores e Ângulos

Se quisermos que o nosso atacante chute uma bola baseado no ângulo e na força, podemos criar um vetor com essas informações.

Se for dado um chute de ângulo 50 graus e a uma força 10, poderíamos chamar a função a baixo. Lembrando que em C/C++ o ângulo é em radianos (1 grau = PI/180 radianos).

Código em C++

Vector2D Vector2D::bySizeAndAngle(float size, float angle) {
    float radians = angle * M_PI / 180.0f;  // Converte graus para radianos
    return Vector2D(cos(radians) * size, sin(radians) * size);
}

Código em Python:

    @staticmethod
    def by_size_and_angle(size: float, angle: float) -> "Vector2D":
        radians = math.radians(angle)  # Converte graus para radianos
        return Vector2D(math.cos(radians) * size, math.sin(radians) * size)

Da mesma forma, podemos retornar qual o ângulo (em radianos) de um vetor por meio do arco-tangente de x e y, conforme o código C++ abaixo:

float Vector2D::angle() const {
	return atan2f(y, x);
}

Código em Python:

    def angle(self) -> float:
        return math.atan2(self.y, self.x) # Retorna o ângulo em radianos

Para facilitar, podemos fazer funções de conversão de radianos para graus e graus para radianos:

#define PI 3.1416

// Converte graus para radianos
float grad2radians(float grad) {
    return (grad * PI) / 180;  // Correção do fator de conversão
}

// Converte radianos para graus
float radians2grad(float radians) {
    return (radians * 180) / PI;
}

Código em Python:

# Converte graus para radianos
def grad2radians(grad: float) -> float:
    return (grad * math.pi) / 180

# Converte radianos para graus
def radians2grad(radians: float) -> float:
    return (radians * 180) / math.pi

Também podemos rotacionar um vetor, utilizando a operação de rotação vista na última aula. Contudo, nesse caso estamos trabalhando apenas com vetores, em que a posição não importa. Então estamos sempre tendo como base o eixo (0, 0) do vetor, não necessitando das operações de translação.

#define PI 3.1416

Vector2D& Vector2D::rotate(float angle) {
    // Assumindo que 'angle' está em radianos. Se estiver em graus, converta para radianos:
    // angle = grad2radians(angle); // Se você quiser usar graus como entrada

    // Calculando o seno e o cosseno uma única vez para maior eficiência.
    float s = sin(angle);
    float c = cos(angle);

    // Fórmula de rotação
    float newX = (x * c) - (y * s);
    float newY = (x * s) + (y * c);

    // Atualizando as coordenadas do vetor
    x = newX;
    y = newY;

    // Retorna o próprio objeto para permitir encadeamento de métodos
    // (por exemplo, v.rotate(...).rotate(...))
    return *this;
}

Código em Python:

    def rotate(self, angle: float):
        # Calculando o seno e o cosseno uma única vez
        s = math.sin(angle)
        c = math.cos(angle)

        # Fórmula de rotação
        new_x = (self.x * c) - (self.y * s)
        new_y = (self.x * s) + (self.y * c)

        # Atualizando as coordenadas do vetor
        self.x = new_x
        self.y = new_y

        return self  # Retorna o próprio objeto para encadeamento de métodos
                     # (ex: v.rotate(...).rotate(...))

Ângulo entre dois vetores

Para encontrar o ângulo entre dois vetores, é utilizado o produto escalar, onde A ⋅ B é o produto escalar entre os pontos A e B:

ou

Assim, podemos dizer que:

Onde |A| e |B| são respectivamente os tamanhos de A e B e α o ângulo que queremos calcular. Agora, note como o cálculo seria simplificado se A e B fossem vetores de tamanho 1:

ou, simplesmente:

Evitar o cálculo do tamanho reduz muito processamento, por isso é bom usar vetores normalizados para representar direções. No C++, o método para o cálculo do produto escalar fica assim:

Código em C++:

float Vector2D::dot(const Vector2D& other) const {
	return (x * other.x) + (y * other.y);
}

Código em Python:

    def dot(self, other: Vector2D) -> float:
        return (self.x * other.x) + (self.y * other.y)

E o do ângulo entre os dois vetores unitários, assim (C++):

float Vector2D::angleBetween(const Vector2D& other) const {
	float dp = dot(other);

	if (dp >= 1.0) {
		dp = 1.0f;
	} else if(dp <= -1.0) {
		dp = -1.0f;
	}
	float angPi = (float) acos(dp);
	return dot(other) > 0 ? -angPi : angPi;
}

Código em Python:

    def angle_between(self, other: Vector2D) -> float:
        dp = self.dot(other)

        # Garantir que o valor do produto escalar esteja entre -1 e 1
        dp = max(-1.0, min(1.0, dp))

        # Calcular o ângulo em radianos
        angPi = math.acos(dp)

        # Retornar o ângulo positivo ou negativo dependendo do sinal do produto escalar
        return -angPi if self.dot(other) > 0 else angPi

Exercícios

1. Implemente o header (.h) e o arquivo fonte (.cpp) do vetor 2D para utilizá-lo no resto dos exercícios.


2. Faça um programa com as seguintes características:

  • Clique na tela para definir o primeiro ponto (Ponto A)
  • Clique na tela para definir o segundo ponto (Ponto B)
    • Desenhe uma linha entre o Ponto A e o Ponto B
    • Informe na tela, em elementos tipo texto:
      • a distância entre os pontos
      • o ângulo do Ponto A para o Ponto B
  • A cada novo clique, o Ponto B se tornará o Ponto A e o Ponto B será o novo local onde foi dado o clique. Redesenhe a linha entre A e B e refaça os cálculos e apresente-os na tela.


3. Faça um programa com as seguintes características:

  • Desenhe do lado esquerdo da tela e abaixo um pequeno canhão.
  • A movimentação do mouse para cima e para baixo altera o ângulo de tiro.
  • A movimentação do mouse para a esquerda e direita altera a força do tiro.
  • O clique do mouse efetua um disparo (represente o disparo com um ponto grande ou com uma imagem)
  • Quando a bala do canhão cair, calcule a distância entre o canhão e a bala.
  • Considere a gravidade como 9.8 m/s.
  • Mostre na tela as informações de:
    • Ângulo do tiro
    • Força do tiro
    • Distância percorrida pela bala do canhão.


4. No programa anterior, acrescente vento. Para isso:

  • Crie um vetor de direção e velocidade do vento. Como estamos em um espaço bidimensional, considere apenas contra o canhão ou a favor do tiro do canhão (direita para esquerda ou esquerda para direita).
  • Altere a velocidade do vento por meio das setas direcionais ( <- e -> ).
  • Acrescente a informação da direção do vento e força.