====== Gráficos ======
===== Geometría =====
==== Problemas geométricos comunes ====
* [[http://www.devmag.org.za/articles/245-BASIC-VECTOR-RECIPES/#topall]]
Ampliaremos la siguiente clase en [[highlevel:processing|Processing]]:
class Point {
int x, y;
Point (int x, int y) {
this.x = x;
this.y = y;
}
void draw () {
point (x, y);
}
}
class Line {
Point p1, p2;
Line (Point p1, Point p2) {
this.p1 = new Point(p1.x, p1.y);
this.p2 = new Point(p2.x, p2.y);
}
void draw () {
line(p1.x, p1.y, p2.x, p2.y);
}
}
* **Distancia entre dos puntos**. //En la clase Point//:
float distance (Point p) {
// Resta y magnitud de dos vectores
int subX = this.x - p.x;
int subY = this.y - p.y;
return sqrt(pow(subX,2) + pow(subY,2));
}
* **Distancia entre un punto y una línea**, el siguiente código devuelve la distancia entre un punto P a la lína que pasa entre A y B:
public double pointToLineDistance(Point A, Point B, Point P)
{
double normalLength = Math.sqrt((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y));
return Math.abs((P.x - A.x) * (B.y - A.y) - (P.y - A.y) * (B.x - A.x)) / normalLength;
}
**Saber si dos líneas son paralelas**
**Saber si dos líneas son perpendiculares** \\
**Teniendo dos puntos y una línea, están los puntos al mismo lado de la línea?** \\
**Está la línea sobre dos puntos?** \\
**Un punto está en el ángulo de visión?** \\
===== Transformaciones =====
==== Traslación ====
{{numbers:graf:translacion.png?300|}}
=== Por suma ===
Trasladaremos la posición (20, 30) unas (50, -100) posiciones: \\
{{numbers:graf:translacion_por_suma.png|}}
=== Por multiplicación ===
Trasladaremos la posición (20, 30) unas (50, -100) veces: \\
{{numbers:graf:translacion_por_multiplicacion.png|}}
==== Rotación ====
=== Rotación 2d ===
La multiplicación de un vector 2d por la siguiente matriz: \\
delim{[} { matrix{2}{2}{{cos(alpha)} {-sen(alpha)} {sen(alpha)} {cos(alpha)} } } {]}
\\
devolverá otro vector 2d que será el equivalente al primero pero rotado //alpha// radianes.
=== Rotación 3d (X, Y, Z) ===
Se utilizarán vectores de 3 dimensiones (o de cuatro con el último término a 0). \\
:?: No tengo claro cual es cual: \\
delim{[} { matrix{4}{4}{ 1 0 0 0 0 {cos(alpha)} {-sen(alpha)} 0 0 {sen(alpha)} {cos(alpha)} 0 0 0 0 1 } } {]}
\\
delim{[} { matrix{4}{4}{ {cos(alpha)} 0 {sen(alpha)} 0 0 1 0 0 {-sen(alpha)} 0 {cos(alpha)} 0 0 0 0 1 } } {]}
\\
delim{[} { matrix{4}{4}{ {cos(alpha)} {-sen(alpha)} 0 0 {sen(alpha)} {cos(alpha)} 0 0 0 0 0 1 } } {]}
==== Escalado ====
===== Animaciones =====
==== Aceleración mediante vectores ====
* [[numbers:maths#vectores|Operaciones con vectores]]
Para los ejemplos se ha utilizado el lenguaje [[highlevel:processing|Processing]] y la [[highlevel:processing#otras_clases|clase PVector]], utilizaremos tres vectores: uno para la posición, otro para la velocidad y otro para la aceleración. \\
La parte más importante del código es la clase ''Mover'', esta tiene cuatro métodos: el constructor que inicializa los vectores e indica un máximo de velocidad, ''update'' que es con la que jugaremos para modificar la velocidad, ''display'' que mostrará la pelota según la posición actualy ''checkEdges'' que se encarga de controlar cuando la pelota sale de los bordes. La función ''setup'' inicializa la vista y el objeto ''Mover'' y la función ''update'' actualiza la vista. \\ \\
Para crear una **aceleración constante** simplemente debemos ir sumando la aceleración a la velocidad y la velocidad a la posición. Limitaremos la velocidad a ''maxSpeed''.
Mover mover;
void setup() {
size(200,200);
smooth();
background(255);
mover = new Mover();
}
void draw() {
clean();
mover.update();
mover.checkEdges();
mover.display();
}
void clean () {
noStroke();
fill(255,10);
rect(0,0,width,height);
}
class Mover {
PVector location;
PVector velocity;
PVector acceleration;
float maxSpeed;
Mover() {
location = new PVector(width/2,height/2);
velocity = new PVector(0, 0);
acceleration = new PVector(-0.001,0.01);
maxSpeed = 10.0;
}
void update() {
velocity.add(acceleration);
velocity.limit(maxSpeed);
location.add(velocity);
}
void display() {
stroke(0);
fill(175);
ellipse(location.x,location.y,16,16);
}
void checkEdges() {
if (location.x > width) {
location.x = 0;
} else if (location.x < 0) {
location.x = width;
}
if (location.y > height) {
location.y = 0;
} else if (location.y < 0) {
location.y = height;
}
}
}
Crear una **aceleración aleatoria** es muy sencillo, en el ''update'' crearemos un nuevo vector con valores aleatorios y que asignaremos a la aceleración.
acceleration = new PVector(random(-1,1),random(-1,1));
Para realizar una aceleración condicionada por la distancia a la que se encuentra la pelota del mouse necesitamos un nuevo vector que nos indique la dirección hacia donde está el mouse, este nuevo vector será ''dir'' y lo creamos a partir de restar la posición del ratón y la posición de la bola. Luego normalizaremos el vector (esto nos creará un vector "proporcional" a la distancia) y lo multiplicaremos por un escalar (''0.2'' en nuestro caso).
maxSpeed = 4.0;
void update() {
PVector mouse = new PVector(mouseX,mouseY);
PVector dir = PVector.sub(mouse,location);
dir.normalize();
dir.mult(0.2);
acceleration = dir;
velocity.add(acceleration);
velocity.limit(maxSpeed);
location.add(velocity);
}
==== Interpolaciones ====
Si queremos mover la variable ''X'' de un punto ''pA'' a otro ''pB'' en ''N'' pasos tendremos la siguiente función (siendo ''i'' el paso actual): X = ((pB · (N - i)) + (pA · i)) / N \\ \\
En Python con PyGame lo podríamos programar de la siguiente forma:
import pygame
from pygame.locals import *
import sys
def main ():
i = 0
pointA = 100
pointB = 500
x = pointA
NumFrames = 100
pygame.init()
screen = pygame.display.set_mode((640,480))
last_update = pygame.time.get_ticks()
color = (0,0,0)
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
screen.fill(color)
if (pygame.time.get_ticks() - last_update > 10):
if (i> NumFrames):
i = 0
(pointA, pointB) = (pointB, pointA)
last_update = pygame.time.get_ticks()
x = ((pointB * i) + (pointA * (NumFrames - i))) / NumFrames
i = i + 1
pygame.draw.circle(screen, (0,0,255), (x, 200), 20)
pygame.display.update()
if __name__ == '__main__':
main()
O, el mismo código, con Processing:
int pointA = 100;
int pointB = 500;
float x = pointA;
float v = 0.0f;
int i = 0;
int numFrames = 100;
void setup ()
{
size (640, 480);
}
void draw ()
{
background(255);
if (i > numFrames)
{
i = 0;
int tmp = pointB;
pointB = pointA;
pointA = tmp;
}
x = ((pointB * i) + pointA * (numFrames - i)) / numFrames;
i ++;
ellipse(x, 200, 20, 20);
}
=== Lineal ===
Poniendolo de otra forma tenemos que la velocidad es: v = i / N y la función quedaría de la siguiente forma: x = ((pA · (1 - v)) + (pB · v)). \\
En python:
v = i / float(NumFrames)
x = (pointA * (1 - v)) + (pointB * v)
En processing:
v = i / (float)numFrames;
x = (pointA * (1 - v)) + (pointB * v);
{{numbers:graf:img1.png|}}
===== Algoritmos =====
==== Perlin Noise ====
Es una función matemática que se usa para crear un gran número de vectores con el significado de intensidad y esta intensidad puede hacer referencia a varios conceptos que requieran aleatoriedad sin perder continuidad (degradados para texturas (de madera), posición de elementos en un mapa en un juego, creación de terrenos 3d...).
* [[http://freespace.virgin.net/hugo.elias/models/m_perlin.htm]]
* [[http://devmag.org.za/articles/48-HOW-TO-USE-PERLIN-NOISE-IN-YOUR-GAMES/2/#top1]]
===== Ecuaciones de formas =====
==== Elipse ====
import pygame
from pygame.locals import *
import sys
import math
w = 640
h = 489
screen = pygame.display.set_mode((w,h))
sampling = 50
r1 = 100
r2 = 160
def drawPoint (x,y, c=(255,0,0)):
centX = w/2
centY = h/2
nx = centX + x
ny = centY + y
pygame.draw.circle(screen, c, (int(nx), int(ny)), 1)
for i in range(sampling):
theta = i * (2. * 3.1421)/sampling
cosTheta = math.cos(theta)
sinTheta = math.sin(theta)
tmp1 = (cosTheta*cosTheta)/(r2*r2)
tmp2 = (sinTheta * sinTheta)/(r1*r1)
tmp = math.sqrt(tmp1+tmp2)
drawPoint(cosTheta/tmp, sinTheta/tmp)
pygame.display.flip()
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
pygame.display.update()