geometria aplicada - walderson.comwalderson.com/site/wp-content/uploads/2014/02/02-geometria... ·...

12
Computação Gráfica Walderson Shimokawa 19 2 Geometria Aplicada Antes de prosseguirmos com assuntos específicos da computação gráfica, será necessário fazer uma abordagem matemática, que serão utilizados com frequência nos próximos capítulos. Após a apresentação geral, alguns algoritmos úteis serão apresentados para serem usados posteriormente. Nos capítulos 6 e 7 trataremos de polígonos que sejam faces de objetos 3D sólidos. Como polígonos de modo geral são difíceis de serem manipulados, será melhor dividirmos em triângulos, conforme será apresentado da última seção deste capítulo. 2.1 Vetores Começaremos com a noção matemática de vetor, que não deve ser confundido com a classe- padrão Vector, disponível em Java, para armazenar um número arbitrário de objetos. Um vetor é um segmento de reta orientado, caracterizado apenas pelo seu comprimento e por sua direção. A Figura 9 mostra duas representações do mesmo vetor u = PQ = v = RS. Assim, assim um vetor não é alterado por sua translação. Figura 9: Dois vetores iguais A soma dos w vetores u e v, escrita w = u + v pode ser obtida como a diagonal de um paralelogramo com u, v e w começando no mesmo ponto, conforme mostrado na figura 10. Figura 10: Adição de vetores A Figura 11 mostra três vetores unitários i, j e k em um espaço tridimensional. Eles são mutuamente perpendiculares e têm comprimento igual a 1 (um). Suas direções são as direções positivas dos eixos de coordenadas. Dizemos que i, j e k formam um terno de vetores unitários ortogonais. Muitas vezes escolhemos a origem O do sistema de coordenadas como o ponto inicial de todos os vetores. Qualquer vetor v pode ser escrito como uma combinação linear dos vetores unitários i, j e k: v = xi + yj + zk Os números reais x, y e z são as coordenadas da extremidade P do vetor v = OP. Muitas vezes escrevemos esse vetor como v = [ x y z ] ou v = (x,y,z) Os números x, y e z às vezes são chamados de elementos ou componentes do vetor v.

Upload: dinhthuy

Post on 03-Dec-2018

216 views

Category:

Documents


0 download

TRANSCRIPT

Computação Gráfica Walderson Shimokawa 19

2 Geometria Aplicada Antes de prosseguirmos com assuntos específicos da computação gráfica, será necessário fazer

uma abordagem matemática, que serão utilizados com frequência nos próximos capítulos. Após a

apresentação geral, alguns algoritmos úteis serão apresentados para serem usados posteriormente. Nos

capítulos 6 e 7 trataremos de polígonos que sejam faces de objetos 3D sólidos. Como polígonos de modo

geral são difíceis de serem manipulados, será melhor dividirmos em triângulos, conforme será apresentado

da última seção deste capítulo.

2.1 Vetores Começaremos com a noção matemática de vetor, que não deve ser confundido com a classe-

padrão Vector, disponível em Java, para armazenar um número arbitrário de objetos. Um vetor é um

segmento de reta orientado, caracterizado apenas pelo seu comprimento e por sua direção. A Figura 9

mostra duas representações do mesmo vetor u = PQ = v = RS. Assim, assim um vetor não é alterado por sua

translação.

Figura 9: Dois vetores iguais

A soma dos w vetores u e v, escrita

w = u + v

pode ser obtida como a diagonal de um paralelogramo com u, v e w começando no mesmo ponto,

conforme mostrado na figura 10.

Figura 10: Adição de vetores

A Figura 11 mostra três vetores unitários i, j e k em um espaço tridimensional. Eles são

mutuamente perpendiculares e têm comprimento igual a 1 (um). Suas direções são as direções positivas

dos eixos de coordenadas. Dizemos que i, j e k formam um terno de vetores unitários ortogonais. Muitas

vezes escolhemos a origem O do sistema de coordenadas como o ponto inicial de todos os vetores.

Qualquer vetor v pode ser escrito como uma combinação linear dos vetores unitários i, j e k:

v = xi + yj + zk

Os números reais x, y e z são as coordenadas da extremidade P do vetor v = OP. Muitas vezes

escrevemos esse vetor como

v = [ x y z ] ou v = (x,y,z)

Os números x, y e z às vezes são chamados de elementos ou componentes do vetor v.

Computação Gráfica Walderson Shimokawa 20

Figura 11: Sistema de coordenadas com orientação positiva

2.2 Produto Interno O produto interno ou produto escalar de dois vetores a e b é um número real, escrito como a b e

definido como

a b = |a||b| cos se a ≠ 0 e b ≠ 0

a b = 0 se a = 0 ou b = 0

em que é o ângulo entre a e b. Resulta da primeira equação que a b também é zero se = 90.

Aplicando-se essa definição aos vetores unitários i, j e k, encontramos

i i = j j = k k = 1

i j = j i = j k = k j = k i = i k = 0

O produto interno de dois vetores u = [u1 u2 u3] e v = [v1 v2 v3] pode ser calculado como

u v = u1v1 + u2v2 + u3v3

2.3 Determinantes Antes de prosseguirmos produtos de vetores, devemos dar atenção aos determinantes. Suponha

que queiramos resolver o seguinte sistema de duas equações lineares para x e y:

Podemos então multiplicar a primeira equação por b2, a segunda por –b1, e adicioná-las, isolando x

e y, encontrando (se (a1b2 – a2b1) ≠ 0):

Usando determinantes, podemos escrever a solução da equação como:

em que:

Computação Gráfica Walderson Shimokawa 21

Determinantes são muito úteis em álgebra linear e geometria analítica. Eles têm muitas

propriedades interessantes, algumas das quais são listadas a seguir:

1. O valor de um determinante permanece o mesmo se suas linhas forem escritas como colunas na

mesma ordem;

2. Se duas linhas (ou colunas) forem intercambiadas, o valor do determinante é multiplicado por -1;

3. Se uma linha ou coluna for multiplicada por um fator, o valor do determinante é multiplicado por

este fator;

4. Se uma linha for alterada pela adição de uma outra linha multiplicada por uma constante, o valor

do determinante permanece inalterado;

5. Se uma linha (ou uma coluna) for uma combinação linear de outras linhas (ou colunas), o valor do

determinante é zero.

2.4 Produto Vetorial O produto vetorial ou produto cruzado de dois vetores u e v é escrito

u X v

e é um vetor w com as seguintes propriedades. Se u = cv para algum escalar c, então w = 0. Caso contrário,

o comprimento de w é igual a:

|w| = |u||v| sen

em que é o ângulo entre u e v. Observe que o comprimento de |w| é igual á área de um paralelogramo

que tenha como lados os vetores u e v, como a Figura 12 mostra:

Figura 12: Produto vetorial u X v

As seguintes propriedades do produto vetorial vêm desta definição, para qualquer número real k:

Computação Gráfica Walderson Shimokawa 22

Usando esses produtos vetoriais na expansão de:

que pode ser escrito como:

Reescrevemos isso em uma forma que é mais fácil de lembrar:

Esse é um auxílio mnemônico, e não é um verdadeiro determinante, já que os elementos da primeira linha

são vetores em vez de números.

2.5 A Orientação de Três Pontos Suponha que tenhamos recebido um termo ordenado (A, B, C) de três pontos no plano xy e que

queiramos saber sua orientação; em outras palavras, queremos saber se giramos no sentido anti-horário ou

horário ao visitar esses pontos na ordem dada. A figura 13 mostra as possibilidades, às quais também nos

referimos como orientação positiva e negativa, respectivamente.

Figura 13: Orientação anti-horária (orientação > 0) e horária de (orientação < 0) A, B, C

Existe a necessidade de se conhecer um modo de descobrir a orientação por meio de cálculos,

usando apenas as coordenadas xA, yA, xB, yB, xC e yC. Definiremos dois vetores a = CA e b = CB, conforme

mostra a figura 14.

Computação Gráfica Walderson Shimokawa 23

Figura 14: Usando os vetores a e b em vez dos lados CA e CB

a = a1i + a2j + 0k

b = b1i + b2j + 0k

De modo geral, temos

���� − ���� �> 0= 0< 0

2.5.1 Um Método Java Útil

O método area2 no fragmento a seguir é baseado nos resultados encontrados. Esse método recebe

três argumentos da classe Point2D, apresentado no final do Capítulo 1. Usaremos a classe Tools2D para

diversos métodos estáticos a serem usados como ferramentas bidimensionais.

class Tools2D { static float area2(Point2D a, Point2D b, Point2D c) { return (a.x – c.x) * (b.y – c.y) - (a.y – c.y) * (b.x – c.x); } //Veja a seção 2.13 }

Esse método calcula a área do triângulo ABC multiplicado por 2, ou, se, A, B e C estiverem

orientados no sentido horário, por -2. Se estivermos interessados apenas na orientação dos pontos A, B e C,

cada um do tipo Point2D, podemos escrever:

if (Tools2D.area2(a, b, c) > 0) { //A, B e C no sentido anti-horário } else { //A, B e C no sentido horário, a menos que area2 retorne 0 (zero); //neste caso, A, B e C pertencem à mesma reta }

2.6 Polígonos Um polígono é uma sequência P0, P1, ..., Pn-1 de vértices, em que n ≥ 3, com lados associados P0P1,

P1P2, ..., Pn-1P0. Neste curso nos restringiremos a polígonos simples, em que os lados não adjacentes não

tem intersecção.

Se todos os vértices de um polígono forem convexos, diz-se que o próprio polígono é convexo.

Vértices não convexos são chamados de reflexos. Se um polígono possuir pelo menos um vértice reflexo,

diz-se que o polígono é côncavo. Veja a Figura 15 que exemplifica os polígonos convexo e côncavo.

: orientação de A,B e C positiva (sentido anti-horário) : A, B e C na mesma reta : orientação de A, B e C negativa (sentido horário)

Computação Gráfica Walderson Shimokawa 24

Figura 15: Polígonos convexo e côncavo

O seguinte método pode ser definido em uma classe para desenhar polígonos (seção 2.13, classe

PolyTria), para se identificar se um dado polígono, expresso em coordenadas de pontos usando a classe

Point2D está desenhado no sentido anti-horário:

static boolean ccw(Point2D[] p) { int n = p.length, k = 0; for (int i=1; i<n; i++) if (p[i].x <= p[k].x && (p[i].x < p[k].x || p[i].y < p[k].y)) k = i; // p[k] é um vértice convexo. int prev = k - 1, next = k + 1; if (prev == -1) prev = n - 1; if (next == n) next = 0; return Tools2D.area2(p[prev], p[k], p[next]) > 0; }

2.7 A Área de Um Polígono Como vimos na Figura 12, o produto vetorial a X b é um vetor cujo comprimento é igual à área de

um paralelogramo do qual a e b sejam dois lados. Como esse paralelogramo é de dois triângulos de área

igual, segue-se para Figura 14, obtendo:

Se A, B e C está no sentido anti-horário e :

Que pode ser generalizada para obter a área de um polígono obtendo a fórmula (P0 e Pn no mesmo vértice):

2.8 Teste Ponto-no-Triângulo Determinar a orientação de três pontos como acabamos de ver é útil em um teste para ver se um

determinado ponto P está localizado dentro de um triângulo ABC. Como a Figura 16 mostra, esse é o caso

se a orientação dos triângulos ABP, BCP e CAP for a mesma do triângulo ABC.

Computação Gráfica Walderson Shimokawa 25

Figura 16: Orientação usada para testar se P está dentro do triângulo ABC

Sabendo-se que a orientação de ABC é no sentido anti-horário. Podemos chamar o seguinte

método para testar se P está dentro do triângulo ABC (ou em um de seus lados):

static boolean insideTriangle(Point2D a, Point2D b, Point2D c, Point2D p) { //Admite-se que ABC tem sentido anti-horário return Tools2D.area2(a, b, p) >= 0 && Tools2D.area2(b, c, p) >= 0 && Tools2D.area2(c, a, p) >= 0; }

2.9 Teste de Ponto-no-Polígono A noção de orientação também é útil também quando precisamos determinar se um dado ponto P

está localizado dentro de um polígono. Será então conveniente se houver um método disponível que

receba como argumentos o polígono em questão e o ponto P, e retorne verdadeiro se P estiver dentro e

falso se estiver fora do polígono. Cruzando o polígono desde o ponto P para a direita podemos dizer se

estamos dentro ou fora do polígono: se quantidade de intersecções for par, estamos fora; se for ímpar,

estamos dentro. Veja a Figura 17 para exemplificar este cálculo.

Figura 17: Determinando se um ponto está dentro ou fora do polígono

Devemos ter cuidado com alguns casos especiais, como mostra a figura 18:

Figura 18: Polígono e semi-reta com origem em P

Computação Gráfica Walderson Shimokawa 26

Vejamos como ficaria a implementação de um método para se verificar se um dado ponto P está

dentro de um polígono (método a ser incluído na classe Tools2D, que pode ser utilizado para o

preenchimento do polígono também):

static boolean insidePolygon(Point2D p, Point2D[] pol) { int n = pol.length, j = n - 1; boolean b = false; float x = p.x, y = p.y; for (int i = 0; i < n; i++) { if (pol[j].y <= y && y < pol[i].y && Tools2D.area2(pol[j], pol[i], p) > 0 && pol[i].y <= y && y < pol[j].y && Tools2D.area2(pol[i], pol[j], p) > 0) b = !b; j = i; } return b; }

2.10 Teste Ponto-na-Reta Testar se um ponto P está localizado em uma determinada reta é muito simples se essa reta for

dada como uma equação, digamos,

ax + by = h

Então tudo o que precisamos fazer é testar se as coordenadas de P satisfazem esta equação (eps é o

épsilon, que indica um pequeno intervalo real aceitável de margem de erro a ser tolerado):

if (Math.abs(Tools2D.area2(a, b, p)) < eps) //P pertence à reta AB

2.10.1 Testando se um Ponto Pertence a um Segmento de Reta

Em vez de testar se P pertence ao segmento AB, podemos querer aplicar um teste semelhante na

projeção P’ de P sobre AB, como a Figura 19 mostra.

Figura 19: Projeção de P’ de P sobre a reta AB entre A e B

2.11 Distância Entre um Ponto e uma Reta Podemos descobrir a distância entre um ponto P e uma reta r de diferentes formas, dependendo da

maneira pela qual a reta é especificada. Então as propriedades e fórmulas geométricas podem ser aplicadas

para se descobrir as distâncias entre um ponto e uma reta.

2.12 Projeção de um Ponto em uma Reta Suponha que uma reta r e um ponto P (não sobre r) sejam dados e que queiramos calcular a

projeção P’ sobre r (conforme Figura 19). Este ponto P’ possui três propriedades interessantes:

1. P’ é o ponto de r que está mais próximo de P.

Computação Gráfica Walderson Shimokawa 27

2. O comprimento de PP’ é a distância entre P e r.

3. PP’ e r são perpendiculares.

2.13 Triangulação de Polígonos Em muitas aplicações gráficas é desejável dividir um polígono em triângulos. Esse problema pode

ser resolvido de diversas formas. Discutiremos um algoritmo comparativamente simples, que recebe um

polígono na forma de um vetor de elementos da classe Point2D, contendo os vértices do polígono em

sentido anti-horário. O método triangulate apresentado recebe tal vetor como argumento. Para armazenar

os triângulos resultantes, ele também recebe, como segundo argumento, um vetor de elementos da classe

Triangle, que é definida da seguinte forma:

class Triangle { Point2D a, b, c; Triangle(Point2D a, Point2D b, Point2D c) { this.a = a; this.b = b; this.c = c; } }

Se um dado polígono tiver n vértices, o vetor de triângulos deve ter comprimento n – 2. O

algoritmo funciona da seguinte maneira: percorrendo os vértices do polígono no sentido anti-horário, para

cada vértice P, Q e R sucessivos dos quais Q seja um vértice convexo (com ângulo menor que 180),

removemos o triângulo PQR do polígono se esse triângulo não contiver algum dos outros vértices do

polígono. Por exemplo, começando com o polígono ABCDE da Figura 20, não podemos remover o triângulo

ABC, porque contém o vértice D. O triângulo CDE também não é um bom candidato, porque D não é um

vértice convexo. Esses problemas não existem no triângulo BCD, de forma que o removeremos, reduzindo o

polígono ABCDE ao mais simples, ABDE.

Figura 20: Removendo um triângulo

Para isto, usamos o método estático triangulate, que, junto com alguns outros já discutidos, é

listado na classe Tools2D a seguir:

class Tools2D { static float area2(Point2D a, Point2D b, Point2D c) { return (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x); } static boolean insideTriangle(Point2D a, Point2D b, Point2D c, Point2D p) // Admite-se que ABC tem sentido anti-horário { return Tools2D.area2(a, b, p) >= 0 && Tools2D.area2(b, c, p) >= 0 && Tools2D.area2(c, a, p) >= 0;

Computação Gráfica Walderson Shimokawa 28

} static boolean insidePolygon(Point2D p, Point2D[] pol) { int n = pol.length, j = n - 1; boolean b = false; float x = p.x, y = p.y; for (int i = 0; i < n; i++) { if (pol[j].y <= y && y < pol[i].y && Tools2D.area2(pol[j], pol[i], p) > 0 && pol[i].y <= y && y < pol[j].y && Tools2D.area2(pol[i], pol[j], p) > 0) b = !b; j = i; } return b; } static void triangulate(Point2D[] p, Triangle[] tr) { // p contém todos os n vértices do polígono no sentido anti-horário // Os triângulos resultantes serão armazenados em um array tr. // O array tr tem comprimento n - 2. int n = p.length, j = n - 1, iA=0, iB, iC; int[] next = new int[n]; for (int i=0; i<n; i++) { next[j] = i; j = i; } for (int k=0; k<n-2; k++) { // Encontre um triângulo apropriado, que consista em // dois lados e uma diagonal interna: Point2D a, b, c; boolean triaFound = false; int count = 0; while (!triaFound && ++count < n) { iB = next[iA]; iC = next[iB]; a = p[iA]; b = p[iB]; c = p[iC]; if (Tools2D.area2(a, b, c) >= 0) { // Lados AB e BC; diagonal AC. // Teste para ver se nenhum outro vértice de polígono // está localizado dentro do triângulo ABC: j = next[iC]; while (j != iA && !insideTriangle(a, b, c, p[j])) j = next[j]; if (j == iA) { // O triângulo ABC não contém outro vértice: tr[k] = new Triangle(a, b, c); next[iA] = iC; triaFound = true; } } iA = next[iA]; } if (count == n) { System.out.println("Não é um polígono simples" + " ou a sequência de vértices não tem sentido anti-horário."); System.exit(1); } } } static float distance2(Point2D p, Point2D q) { float dx = p.x - q.x, dy = p.y - q.y; return dx * dx + dy * dy; }

Computação Gráfica Walderson Shimokawa 29

}

O método distance2, mostrado no final da classe acima simplesmente calcula o quadrado da

distância entre dois pontos P e Q. Se queremos apenas comparar duas distâncias, podemos comparar seus

quadrados para economizar a operação bastante custosa do cálculo de raízes quadradas.

O programa a seguir permite ao usuário definir um polígono, da mesma forma que fizemos no

programa DefPoly.java na seção 1.5, mas dessa vez o polígono será dividido em triângulos, os quais

aparecem em diferentes cores. Esse programa, PolyTria.java, usa as classes Triangle e Tools2D desta seção,

assim como a classe CvDefPoly, que se encontra no programa DefPoly.java da seção 1.5. Na nossa

subclasse, CvPolyTria, aplicamos o método ccw, discutido na seção 2.6, ao polígono dado para examinar a

orientação de sua sequência de vértices. Se ela for no sentido horário, colocamos os vértices em ordem

reversa no vetor P, de modo que a sequência de vértices terá o sentido anti-horário nesse vetor, que pode

então ser passado com segurança para o método triangulate:

import java.awt.*; import java.awt.event.*; import java.util.*; public class PolyTria extends Frame { public static void main(String[] args){new PolyTria();} PolyTria() { super("Clique para definir os vértices do polígono"); addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e){System.exit(0);}}); setSize(500, 300); add("Center", new CvPolyTria()); setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); show(); } } class CvPolyTria extends CvDefPoly // veja seção 1.5 { public void paint(Graphics g) { int n = v.size(); if (n > 3 && ready) { Point2D[] p = new Point2D[n]; for (int i=0; i<n; i++) p[i] = (Point2D)v.elementAt(i); // Se não possuir sentido anti-horário, reverta a ordem: if (!ccw(p)) for (int i=0; i<n; i++) p[i] = (Point2D)v.elementAt(n - i - 1); int ntr = n - 2; Triangle[] tr = new Triangle[ntr]; Tools2D.triangulate(p, tr); initgr(); for (int j=0; j<ntr; j++) { g.setColor(new Color(rand(), rand(), rand())); int[] x = new int[3], y = new int[3]; x[0] = iX(tr[j].a.x); y[0] = iY(tr[j].a.y); x[1] = iX(tr[j].b.x); y[1] = iY(tr[j].b.y); x[2] = iX(tr[j].c.x); y[2] = iY(tr[j].c.y); g.fillPolygon(x, y, 3); } } g.setColor(Color.black); super.paint(g); }

Computação Gráfica Walderson Shimokawa 30

int rand(){return (int)(Math.random() * 256);} static boolean ccw(Point2D[] p) { int n = p.length, k = 0; for (int i=1; i<n; i++) if (p[i].x <= p[k].x && (p[i].x < p[k].x || p[i].y < p[k].y)) k = i; // p[k] é um vértice convexo. int prev = k - 1, next = k + 1; if (prev == -1) prev = n - 1; if (next == n) next = 0; return Tools2D.area2(p[prev], p[k], p[next]) > 0; } }

A classe CvPolyTria é uma subclasse de CvDefPoly, de forma que a construção de um polígono com

vértices especificados pelo usuário é feita da mesma forma que na seção 1.5. Nessa subclasse, chamamos o

método triangulate para construir um vetor tr de triângulos. Esses são então exibidos em cores geradas

com um gerador de números aleatórios, de maneira que possamos distingui-los claramente. O resultado na

tela ficará parecido com a apresentado na Figura 21.

Figura 21: Triangulação de um polígono