Computação Gráfica
Walderson Shimokawa
31
3 Transformações Geométricas
Para que possamos entender projeções em perspectiva, que serão discutidas mais à frente,
precisamos estar familiarizados com rotações em 3D. Elas estão intimamente relacionadas à multiplicação
de matrizes, que é o assunto com o qual começaremos.
3.1 Multiplicação de Matrizes
Uma matriz é um arranjo retangular de números dentro de colchetes (ou parênteses). Por exemplo,
2
1
0 0.1 3
4 2 10
é uma matriz 2 X 4: ela é formada por duas linhas e quatro colunas. Se uma matriz possuir apenas uma
linha, nós a chamamos de matriz linha ou vetor linha. Da mesma maneira, usamos o termo matriz coluna
para designar uma matriz que possua apenas uma coluna.
Se A e B forem matrizes e o número de colunas de A for igual ao número de colunas de B, podemos
calcular o produto de matrizes AB. Esse produto é outra matriz, com tantas linhas quanto A e tantas colunas
quanto B.
Escrevendo
=
e expressões semelhantes tanto para a matriz B quanto para o produto de matrizes C = AB, temos
=
Cada elemento c (encontrado na linha i e coluna j do produto de matrizes C) é igual ao produto
escalar da linha de índice i de A com a coluna de índice j de B. Por exemplo:
c23 = (a21,a22,a23)(b13,b23,b33) = (a21b13 + a22b23 + a23b33)
Então, de modo geral, os elementos da matriz C anterior são calculados da seguinte maneira:
Cij = (ai1,ai2,ai3)(b1j,b2j,b3j) = (ai1b1j + ai2b2j + ai3b3j)
3.2 Transformações Lineares
Podemos escrever qualquer transformação linear como uma multiplicação de matrizes. Por
exemplo, considere a seguinte transformação linear:
= 2 = +
Podemos escrever isto como o produto de matrizes
′
2
=
′
1
0
1
ou da seguinte maneira:
Computação Gráfica
[ ′
′] = [
Walderson Shimokawa
32
] 2 1
0 1
A notação apresentada na seção 3.1, apesar de ser usada muito em livros-texto de matemática,
adotaremos a notação apresentada nesta seção, usando vetores linha. Observe que as linhas da matriz de
transformação 2 X 2 são as imagens dos vetores unitários (1,0) e (0,1), respectivamente, enquanto essas
imagens são as colunas na seção 3.1. Podemos verificar isto facilmente substituindo [1 0] e [0 1] por [x y]
nas equações acima, como os elementos em negrito da matriz a seguir ilustram:
[
] = [1 0]
0
1
[
] = [0 1] 2
1
Esse princípio também se aplica a outras transformações lineares. Ele nos fornece uma forma
conveniente de encontrar as matrizes de transformação.
3.2.1
Rotação
Para rotacionar todos os pontos no plano xy de um ângulo , em torno de O, podemos agora
escrever facilmente a matriz de transformação, usando a regra recém-apresentada. Simplesmente
encontramos as imagens dos vetores unitários (1,0) e (0,1). Como sabemos da trigonometria elementar,
rotacionar os pontos P(1,0) e Q(0,1) de um ângulo  em torno de O resulta em P’(cos , sen ) e Q’(-sen ,
cos ). Segue-se que (cos , sen ) e (-sen , cos ) são as imagens desejadas dos vetores unitários (1,0) e
(0,1), como a Figura 22 ilustra:
Figura 22: Rotação dos vetores unitários
Então, tudo o que precisamos fazer é escrever essas duas imagens como as linhas da nossa matriz
de rotação:
[ ′
′] = [
cos
] −
cos
3.2.2
Um Exemplo de Programação
Para vermos a rotação em ação, rotacionaremos uma seta e, torno da origem O. Antes dessa
rotação, a seta está na vertical, aponta para cima e pode ser encontrada à direita de O. Rotacionaremos a
seta de um ângulo de 120 em torno da origem O, que é o centro da área de desenho. A Figura 23 mostra
os eixos das coordenadas (com a intersecção em O) e a seta antes e depois da rotação.
Logo abaixo da Figura 23, se encontra o código-fonte (Arrow.java) que faz a rotação da seta. Se
alterarmos o tamanho da janela, a origem continua no centro e os tamanhos das setas e do círculo sobre o
qual elas estão mudam apropriadamente, de modo que o círculo permanece como um círculo.
Computação Gráfica
Walderson Shimokawa
Figura 23: Seta antes e depois da rotação de 120 em torno da origem
import java.awt.*;
import java.awt.event.*;
public class Arrow extends Frame
{ public static void main(String[] args){new Arrow();}
Arrow()
{ super("Seta rotacionada de 120 graus em torno da origem");
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(400, 300);
add("Center", new CvArrow());
show();
}
}
class CvArrow extends Canvas
{ int centerX, centerY, currentX, currentY;
float pixelSize, rWidth = 100.0F, rHeight = 100.0F;
void initgr()
{ Dimension d = getSize();
int maxX = d.width - 1, maxY = d.height - 1;
pixelSize = Math.max(rWidth/maxX, rHeight/maxY);
centerX = maxX/2; centerY = maxY/2;
}
int iX(float x){return Math.round(centerX + x/pixelSize);}
int iY(float y){return Math.round(centerY - y/pixelSize);}
void moveTo(float x, float y)
{ currentX = iX(x); currentY = iY(y);
}
void lineTo(Graphics g, float x, float y)
{ int x1 = iX(x), y1 = iY(y);
g.drawLine(currentX, currentY, x1, y1);
currentX = x1; currentY = y1;
}
void drawArrow(Graphics g, float[]x, float[]y)
{ moveTo(x[0], y[0]);
33
Computação Gráfica
lineTo(g,
lineTo(g,
lineTo(g,
lineTo(g,
Walderson Shimokawa
x[1],
x[2],
x[3],
x[1],
34
y[1]);
y[2]);
y[3]);
y[1]);
}
public void paint(Graphics g)
{ float r = 40.0F;
float[] x = {r, r, r-2, r+2},
y = {-7, 7, 0, 0};
initgr();
// Mostra eixos de coordenadas:
moveTo(30, 0); lineTo(g, 0, 0); lineTo(g, 0, 30);
// Mostra a seta inicial:
drawArrow(g, x, y);
float phi = (float)(2 * Math.PI / 3), //120 graus
c = (float)Math.cos(phi), s = (float)Math.sin(phi),
r11 = c, r12 = s, r21 = -s, r22 = c;
for (int j=0; j<4; j++)
{ float xNew = x[j] * r11 + y[j] * r21,
yNew = x[j] * r12 + y[j] * r22;
x[j] = xNew; y[j] = yNew;
}
// Seta após a rotação:
drawArrow(g, x, y);
}
}
As coordenadas lógicas dos quatro pontos relevantes da seta são armazenadas nos vetores x e y e
as variáveis r11, r12, r21 e r22 denotam os elementos da matriz de rotação.
3.2.3
Mudança de Escala
Suponha que queiramos mudar a escala com fatores de escala sx, para x e s e sy para y e com ponto
O permanecendo no mesmo lugar; essa última condição também é expressa pela referência a O como um
ponto fixo ou por uma escala com referência a O. Isso pode obviamente ser escrito como
=
=
que também pode ser escrito como uma multiplicação de matrizes muito simples:
[ ′
′] = [
]
0
0
Há alguns casos especiais importantes:
sx = sy = -1
sx = 1, sy = -1
sx = -1, sy = 1
3.2.4
reflexão em torno de O;
reflexão em torno do eixo x;
reflexão em torno do eixo y.
Cisalhamento
Considere transformação linear dada por:
(1,0)  (1,0)
(0,1)  (,1)
Como as imagens dos vetores unitários aparecem como linhas da matriz de transformação, podemos
escrever essa transformação, conhecida como cisalhamento, como:
Computação Gráfica
[ ′
′] = [
Walderson Shimokawa
] 1
35
0
1
ou
= +
= Esse conjunto de equações expressa que cada ponto (x, y) se move a uma distância y para a
direita, o que tem o efeito de cisalhamento ao longo do eixo x, conforme ilustrado na Figura 24. Podemos
usar essa transformação para transformar caracteres comuns em itálico; por exemplo, L se torna L.
Figura 24: Efeitos do cisalhamento (retas tracejadas) sobre um objeto quadrado (retas sólidas)
O cisalhamento ao longo do eixo y, também mostrado na figura 24, pode ser expresso de maneira
similar como:
[ ′
′] = [
] 1
0 1
ou
= =
+
3.3 Translações
O deslocamento de todos os pontos do plano xy por uma distância constante em uma direção fixa é
chamado de translação. Essa é outra transformação, que escrevemos como:
=
=
+
+
ou
[ ′
′] = [
][
]
Referimo-nos ao par de números (a, b) como o vetor de deslocamento, ou vetor de translação.
Embora essa transformação seja muito simples, ela não é linear, como podemos perceber pelo fato de a
imagem na origem (0, 0) ser (a, b), e não a própria origem, como ocorre com as transformações lineares.
3.4 Coordenadas Homogêneas
Para expressar todas as transformações introduzidas até aqui como matrizes de transformações de
modo que se possam combinar diversos efeitos de transformações, acrescentamos mais uma dimensão.
Conforme ilustrado na Figura 25, a dimensão adicional W faz com que qualquer ponto P = (x, y) de
coordenadas normais tenha uma família inteira de representações em coordenadas homogêneas (wx, wy,
Computação Gráfica
Walderson Shimokawa
36
w) para qualquer valor de w exceto 0. Por exemplo, (3, 6, 1), (0,3; 0,6; 0,1) e (6, 12, 2) representam o
mesmo ponto no espaço bidimensional.
Figura 25: Um sistema de coordenadas homogêneas com o plano W = 1
Podemos descrever uma translação como uma multiplicação de matrizes usando uma matriz 3 X 3
em vez de uma matriz 2 X 2:
[ ′
′
1
1] 0
1] = [
0
1
0
0
1
Podemos usar a seguinte equação em vez da equação apresentada na seção 3.2.1 para uma
rotação de um ângulo  em torno do O:
[ ′
′
cos
1] −
0
1] = [
cos
0
0
0
1
3.5 Transformações Inversas e Matriz de Inversão
Uma transformação pode ou não ser reversível. Por exemplo, se realizarmos uma rotação em torno
da origem de um ângulo  e depois realizarmos outra rotação, também em torno da origem de um ângulo
-, essas duas transformações se cancelam uma à outra. Denotaremos a matriz de transformação da
equação apresentada na seção 3.2 por R. Segue-se que a rotação inversa, pelo ângulo - em vez de , é
descrita pela equação:
[ ′
′] = [
]
em que
=
−
(− )
(− )
cos
(− )
=
(− )
−
cos
A matriz R-1 é chamada de inversa da matriz R. De modo geral, se uma matriz A possuir uma
inversa, ela é escrita A-1 e temos:
AA-1 = A-1A = I
em que I é a matriz identidade, que consiste em elementos iguais a zero exceto pela diagonal principal, que
contém elementos iguais a 1.
Computação Gráfica
Walderson Shimokawa
37
3.6 Rotação em Torno de um Ponto Arbitrário
Até aqui só realizamos rotações em torno da origem O. Uma rotação em torno de qualquer ponto
diferente de O não é uma transformação linear, já que não mapeia a origem em si mesma. Ela pode ser
descrita como uma multiplicação de matrizes, desde que usemos coordenadas homogêneas.
3.6.1
Uma Aplicação
Para ver esse tipo geral de rotação em ação, veremos agora um programa que rotaciona de 30
uma seta em torno de um ponto selecionado pelo usuário. Inicialmente, uma seta, apontando
verticalmente para cima, aparece no centro da área de desenho. Assim que o usuário clica um botão do
mouse, uma segunda seta aparece. Essa seta é a imagem da primeira, e resulta da rotação de um ângulo de
30 em torno da posição do cursor. Essa posição é mostrada como um cursor cruzado na Figura 26.
Figura 26: Seta antes e depois da rotação de 30 em torno de um ponto selecionado pelo usuário
Essa ação pode ser executada repetidamente, de forma que a seta rotacionada mais recentemente
é novamente rotacionada quando o usuário clica um botão do mouse, e essa última rotação é realizada em
torno do último ponto selecionado. Se o usuário tivesse clicado do lado direito, a seta apareceria abaixo da
anterior em vez de acima, em razão da rotação ser executada no sentido anti-horário. O programa
ArrowPt.java mostra como esta rotação é calculada.
import java.awt.*;
import java.awt.event.*;
public class ArrowPt extends Frame
{ public static void main(String[] args){new ArrowPt();}
ArrowPt()
{ super("Seta rotacionada em torno de um ponto arbitrário");
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(400, 300);
add("Center", new CvArrowPt());
setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
show();
}
}
class CvArrowPt extends Canvas
{ int centerX, centerY, currentX, currentY;
Computação Gráfica
Walderson Shimokawa
float pixelSize, xP = 1e9F, yP,
rWidth = 100.0F, rHeight = 100.0F;
float[] x = {0, 0, -2, 2}, y = {-7, 7, 0, 0};
CvArrowPt()
{ addMouseListener(new MouseAdapter()
{ public void mousePressed(MouseEvent evt)
{ xP = fx(evt.getX()); yP = fy(evt.getY());
repaint();
}
});
}
void initgr()
{ Dimension d = getSize();
int maxX = d.width - 1, maxY = d.height - 1;
pixelSize = Math.max(rWidth/maxX, rHeight/maxY);
centerX = maxX/2; centerY = maxY/2;
}
int iX(float x){return Math.round(centerX + x/pixelSize);}
int iY(float y){return Math.round(centerY - y/pixelSize);}
float fx(int x){return (x - centerX) * pixelSize;}
float fy(int y){return (centerY - y) * pixelSize;}
void moveTo(float x, float y)
{ currentX = iX(x); currentY = iY(y);
}
void lineTo(Graphics g, float x, float y)
{ int x1 = iX(x), y1 = iY(y);
g.drawLine(currentX, currentY, x1, y1);
currentX = x1; currentY = y1;
}
void drawArrow(Graphics g, float[]x, float[]y)
{ moveTo(x[0], y[0]);
lineTo(g, x[1], y[1]);
lineTo(g, x[2], y[2]);
lineTo(g, x[3], y[3]);
lineTo(g, x[1], y[1]);
}
public void paint(Graphics g)
{ initgr();
// Mostra a seta inicial:
drawArrow(g, x, y);
if (xP > 1e8F) return;
float phi = (float)(Math.PI / 6), //30 graus de rotação
c = (float)Math.cos(phi), s = (float)Math.sin(phi),
r11 = c, r12 = s,
r21 = -s, r22 = c,
r31 = -xP * c + yP * s + xP, r32 = -xP * s - yP * c + yP;
for (int j=0; j<4; j++)
{ float xNew = x[j] * r11 + y[j] * r21 + r31,
yNew = x[j] * r12 + y[j] * r22 + r32;
x[j] = xNew; y[j] = yNew;
}
// Seta após a rotação:
drawArrow(g, x, y);
}
}
38
Computação Gráfica
Walderson Shimokawa
39
Em comparação com o programa Arrow.java da seção 3.2, esse novo programa ArrowPt.java usa a
matriz de rotação 3 X 3. Os elementos da matriz r31 e r32 na terceira linha da matriz dependem do ponto (xp,
yp) selecionado pelo usuário e que atua como o centro C(xc, yc) apresentada na discussão acima.
3.7 Mudando o Sistema de Coordenadas
Nas seções anteriores, usamos um sistema de coordenadas fixo e aplicamos transformações a
pontos dados por suas coordenadas nesse sistema, usando determinados cálculos. Podemos usar
exatamente os mesmos cálculos para um propósito diferente, deixando os pontos inalterados mas
alterando o sistema de coordenadas. É importante ter em mente que a direção na qual o sistema de
coordenadas se move é oposta à direção do movimento do ponto. Isto pode ser comprovado claramente
no caso de uma translação. Na Figura 27 temos uma translação normal com ponto qualquer P(x, y)
mapeado para sua imagem P’(x’, y’), em que:
x’ = x + a
y’ = y + b
Na Figura 27 não mapeamos o ponto P para outro ponto, mas expressamos sua posição em um novo
sistema de coordenadas x’y’.
Figura 27: Mudança de coordenadas
3.8 Rotações em Torno de Eixos de Coordenadas Tridimensionais
Usaremos um sistema de coordenadas tridimensionais com orientação positiva, com eixo x positivo
apontando em nossa direção, o eixo y apontando para a direita e o eixo z apontando para cima, conforme
mostrado na Figura 28.
Figura 28: Rotações em torno dos eixos de coordenadas
Podemos derivar as matrizes de uma forma trivial da matriz de rotação 2 X 2, e assim poderemos
escrever as matrizes de transformação para as rotações em torno dos eixos x, y e z de um ângulo  da
seguinte forma:
Computação Gráfica
Walderson Shimokawa
1
0
= 0 cos
0 −
cos
0
=
0
1
0
cos
= −
0
40
0
cos
−
0
cos
0
0
1
cos
0
3.9 Rotação em Torno de um Eixo Arbitrário
Para nos prepararmos para uma rotação tridimensional em torno de um eixo arbitrário, primeiro
executaremos tal rotação em torno de um eixo que passa pela origem O. Na verdade, a rotação ocorrerá
em torno de um vetor, de modo que podemos definir sua orientação, conforme ilustrado na Figura 29.
Se um ponto for rotacionado em torno do vetor v de um ângulo  positivo, essa rotação ocorrerá
de forma que corresponda a um movimento na direção do vetor da mesma maneira que girar um parafuso
(para a direita) corresponde a seu movimento para a frente.
Em vez das coordenadas cartesianas (v1, v2, v3), usaremos os ângulos  e  para especificar a
direção do vetor v. O comprimento desse vetor é irrelevante para o nosso propósito atual. Como podemos
ver na Figura 29,  é o ângulo entre o eixo x positivo e a projeção do vetor v no plano xy e  é o ângulo
entre o eixo z positivo e o vetor v.
Figura 29: Rotações em torno de um vetor com origem em O
Para derivarmos uma matriz de rotação 3 X 3 que descreve a rotação em torno do vetor v de um
ângulo , mudando o sistema de coordenadas de modo que o vetor v fique sobre o eixo z positivo. Como as
transformações no sistema de coordenadas requerem as inversas das matrizes que serão usadas para
rotações normais dos pontos, as matrizes Rz-1 e Ry-1 devem ser obtidos:
cos
=
0
cos
0
=
−
−
cos
0
0
1
0
0
0
1
0
cos
Assim, a rotação desejada em torno do vetor v pode ser expressa a partir da seguinte matriz:
Computação Gráfica
Walderson Shimokawa
cos
= −
0
cos
0
41
0
0
1
O eixo z deverá ser rotacionado de volta para a posição original, obtendo as seguintes matrizes:
cos
0
=
0
1
0
cos
= −
0
−
0
cos
0
0
1
cos
0
A matriz R resultante, a ser usada na equação
[ ′
′
′] = [
]
para executar a rotação em torno de v de um ângulo , pode agora ser encontrada como se segue:
=
=
Conforme discutido nas seções 3.4 e 3.6, precisamos usar coordenadas homogêneas para descrever
translações por multiplicação de matrizes. Como usamos matrizes 3 X 3 para transformações no espaço
tridimensional, temos que usar matrizes 4 X 4 em conexão com essas coordenadas homogêneas. Tomando
as coordenadas a1, a2 e a3 do ponto A sobre o eixo de rotação, a matriz seguinte descreve a translação de A
para O:
1
0
=
0
−
0
1
0
−
0
0
1
−
0
0
0
1
Após a translação, executamos a rotação em torno do vetor v, que se inicia no ponto A, usando a
matriz R anterior, que escrevemos como R* após adicionarmos uma linha e uma coluna extras da forma
habitual:
0
0
0
1
=
0
0
0
Finalmente, usamos uma translação de O de volta para A:
1
0
=
0
0
1
0
0
0
1
0
0
0
1
Escrevendo RGEN para a matriz de rotação geral (4 X 4), temos:
RGEN = T-1R*T
Como RGEN é uma matriz 4 X 4, nós a usamos da seguinte maneira:
Computação Gráfica
[ ′
′
′
Walderson Shimokawa
42
1]
1] = [
3.9.1
Implementação
Já que agora estamos lidando com pontos no espaço tridimensional, comecemos definindo a
seguinte classe para representar esses pontos:
class Point3D
{ float x, y, z;
Point3D(double x, double y, double z)
{ this.x = (float)x;
this.y = (float)y;
this.z = (float)z;
}
}
Como normalmente temos muitos pontos a serem rotacionados, vale a pena calcular a matriz RGEN
antecipadamente. Embora esse calculo possa ser feito numericamente, também é possível tornar nosso
programa um pouco mais rápido realizando-o simbolicamente, isto é, expressando todos os elementos rij
da matriz RGEN em seis valores constantes para a rotação: os ângulos ,  e  e as coordenadas a1, a2 e a3 do
ponto A sobre o eixo de rotação. Em vez de escrever aqui esses elementos da matriz, utilizando fórmulas
matemáticas costumeiras, será apresentado o código Java resultante (Rota3D.java), que está descrito a
seguir:
class Rota3D
{ static double r11, r12, r13, r21, r22, r23,
r31, r32, r33, r41, r42, r43;
/* O método initRotate calcula a matriz geral de rotação
R =
|
|
|
|
r11
r21
r31
r41
r12
r22
r32
r42
r13
r23
r33
r43
0
0
0
1
|
|
|
|
a ser usada como [x1 y1 z1 1] = [x y z 1] R
pelo método 'rotate'.
O ponto (x1, y1, z1) é a imagem de (x, y, z).
A rotação ocorre em torno do eixo orientado
AB e por um ângulo alfa.
*/
static void initRotate(Point3D a, Point3D b,
double alpha)
{ double v1 = b.x - a.x,
v2 = b.y - a.y,
v3 = b.z - a.z,
theta = Math.atan2(v2, v1),
phi = Math.atan2(Math.sqrt(v1 * v1 + v2 * v2), v3);
initRotate(a, theta, phi, alpha);
}
static void initRotate(Point3D a, double theta,
double phi, double alpha)
{ double cosAlpha, sinAlpha, cosPhi, sinPhi,
cosTheta, sinTheta, cosPhi2, sinPhi2,
cosTheta2, sinTheta2, pi, c,
a1 = a.x, a2 = a.y, a3 = a.z;
cosPhi = Math.cos(phi); sinPhi = Math.sin(phi);
cosPhi2 = cosPhi * cosPhi; sinPhi2 = sinPhi * sinPhi;
cosTheta = Math.cos(theta);
sinTheta = Math.sin(theta);
Computação Gráfica
Walderson Shimokawa
cosTheta2 = cosTheta * cosTheta;
sinTheta2 = sinTheta * sinTheta;
cosAlpha = Math.cos(alpha);
sinAlpha = Math.sin(alpha);
c = 1.0 - cosAlpha;
r11 = cosTheta2 * (cosAlpha * cosPhi2
+ cosAlpha * sinTheta2;
r12 = sinAlpha * cosPhi + c * sinPhi2
r13 = sinPhi * (cosPhi * cosTheta * c
r21 = sinPhi2 * cosTheta * sinTheta *
r22 = sinTheta2 * (cosAlpha * cosPhi2
+ cosAlpha * cosTheta2;
r23 = sinPhi * (cosPhi * sinTheta * c
r31 = sinPhi * (cosPhi * cosTheta * c
r32 = sinPhi * (cosPhi * sinTheta * c
r33 = cosAlpha * sinPhi2 + cosPhi2;
r41 = a1 - a1 * r11 - a2 * r21 - a3 *
r42 = a2 - a1 * r12 - a2 * r22 - a3 *
r43 = a3 - a1 * r13 - a2 * r23 - a3 *
43
+ sinPhi2)
*
c
+
cosTheta * sinTheta;
sinAlpha * sinTheta);
- sinAlpha * cosPhi;
sinPhi2)
+ sinAlpha * cosTheta);
+ sinAlpha * sinTheta);
- sinAlpha * cosTheta);
r31;
r32;
r33;
}
static Point3D rotate(Point3D
{ return new Point3D(
p.x * r11 + p.y * r21 +
p.x * r12 + p.y * r22 +
p.x * r13 + p.y * r23 +
}
p)
p.z * r31 + r41,
p.z * r32 + r42,
p.z * r33 + r43);
}
Agora vejamos a classe Rota3D, descrita acima em ação. Embora possa ser usada para qualquer
eixo e para qualquer ângulo de rotação, ela é usada aqui apenas de uma forma muito simples, de modo
que possamos verificar facilmente o resultado, mesmo sem uma saída gráfica. O programa a seguir
(Rota3DTest.java) usa as classes Point3D e Rota3D anteriores para executar a rotação discutida neste
capítulo:
public class Rota3DTest
{ public static void main(String[] args)
{ Point3D a = new Point3D(0, 0, 1), b = new Point3D(1, 1, 1);
double alpha = Math.PI;
// Especifica AB como eixo orientado de rotação
// e alfa como ângulo de rotação:
Rota3D.initRotate(a, b, alpha);
// Vértices de um cubo; 0, 1, 2, 3 na parte inferior,
// 4, 5, 6, 7 na parte superior. Vértice 0 na origem O:
Point3D[] v = {
new Point3D(0, 0, 0), new Point3D(1, 0, 0),
new Point3D(1, 1, 0), new Point3D(0, 1, 0),
new Point3D(0, 0, 1), new Point3D(1, 0, 1),
new Point3D(1, 1, 1), new Point3D(0, 1, 1)};
System.out.println(
"Cubo rotacionado de 180 graus em torno do segmento AB,");
System.out.println(
"em que A = (0, 0, 1) e B = (1, 1, 1)");
System.out.println("Vértices do cubo:");
System.out.println(
"
Antes da rotação
Após da rotação");
for (int i=0; i<8; i++)
{ Point3D p = v[i];
// Calcula P1, o resultado da rotação de P:
Point3D p1 = Rota3D.rotate(p);
System.out.println(i + ": " +
p.x + " " + p.y + " " + p.z + "
" +
Computação Gráfica
Walderson Shimokawa
44
f(p1.x) + " " + f(p1.y) + " " + f(p1.z));
}
}
static double f(double x){return Math.abs(x) < 1e-10 ? 0.0 : x;}
}
Como ainda não discutimos como produzir visões em perspectiva, produzimos apenas saída em
texto neste programa, como listado a seguir:
Cubo rotacionado de 180 graus em torno do segmento AB,
em que A = (0, 0, 1) e B = (1, 1, 1)
Vértices do cubo:
Antes da rotação
Após da rotação
0: 0.0 0.0 0.0
0.0 0.0 2.0
1: 1.0 0.0 0.0
0.0 1.0 2.0
2: 1.0 1.0 0.0
1.0 1.0 2.0
3: 0.0 1.0 0.0
1.0 0.0 2.0
4: 0.0 0.0 1.0
0.0 0.0 1.0
5: 1.0 0.0 1.0
0.0 1.0 1.0
6: 1.0 1.0 1.0
1.0 1.0 1.0
7: 0.0 1.0 1.0
1.0 0.0 1.0
3.10 Exercício
Nossa forma de testar se um determinado ponto P está em um triângulo ABC, discutida na seção
2.8, resultou no método insideTriangle, que supõe que A, B e C estejam no sentido anti-horário. Desenvolva
um método diferente para o mesmo propósito, baseado nos vetores a = (a1, a2) = CA e b = (b1, b2) = CB (veja
a Figura 14 na seção 2.5). Escrevamos:
CP = p = (p1, p2) = a + b
ou, na forma de um produto de matrizes,
[
]=[
]
Através do cálculo da inversa de uma matriz 2 X 2, podemos obter os valores de  e  da seguinte maneira:
[
]=[
]
O ponto P está dentro do triângulo ABC (ou sobre um de seus lados) se, e somente se,  ≥ 0,  ≥ 0 e  +  
1. Escreva a classe TriaTest, para que possa ser usada da seguinte forma:
Point2D a, b, c, p;
TriaTest tt = new TriaTest(a, b, c);
if (tt.area2() != 0 && tt.insideTriangle(p)) ...
// Ponto P dentro do triângulo ABC.
Assim como na seção 2.5, o método area2 retorna o dobro da área de um triângulo ABC, precedido por um
sinal de menos se ABC tiver sentido horário. O valor de retorno também é igual ao determinante da matriz
2 X 2. Não devemos chamar o método insideTriangle de TriaTest se este determinante for zero, já que
nesse caso a matriz inversa da equação não existe.
Computação Gráfica
Walderson Shimokawa
45
3.11 Resposta do Exercício
Se há muitos pontos para os quais temos que verificar se estão dentro de um único triângulo (ou
em um lado dele), o método insideTriangle da classe a seguir é mais eficiente do que o discutido na seção
2.8, já que a maioria do trabalho é feita aqui pelo construtor, que precisa ser chamado apenas uma vez
para esse triângulo:
class TriaTest
{ private Point2D c;
private double a1, a2, b1, b2, c1, c2, d1, d2, det;
TriaTest(Point2D a, Point2D b, Point2D c)
{ this.c = c;
a1 = a.x - c.x; a2 = a.y - c.y;
b1 = b.x - c.x; b2 = b.y - c.y;
det = a1 * b2 - b1 * a2;
if (det != 0)
{ c1 = b2/det; c2 = -a2/det;
d1 = -b1/det; d2 = a1/det;
}
}
double area2(){return det;}
boolean insideTriangle(Point2D p)
{ double p1 = p.x - c.x, p2 = p.y - c.y,
lambda, mu;
return (lambda = p1 * c1 + p2 * d1) >= 0 &&
(mu = p1 * c2 + p2 * d2) >= 0 &&
lambda + mu <= 1;
}
}
Download

3 Transformações Geométricas