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; } }