Verificando corretude da triangulação Tássio Naia dos Santos 19 de dezembro de 2011 1. O programa Este é um pequeno programa utilitário para verificação automatizada das respostas dadas por um programa de triangulação. Recebe como entrada dois arquivos: o arquivo que serviu de entrada para o programa de triangulação, e um arquivo com a saída da triangulação. Este programa imprime Correto caso a triangulação esteja correta, e Incorreto caso a triangulação não seja uma triangulação válida para o polígono de entrada. A validação usa quatro fatos: 1. Toda triangulação possui n 3 diagonais, onde n é o número de vértices do polígono; 2. Toda diagonal tem por extremidades vértices distintos do polígono; 3. As diagonais não ligam vértices consecutivos do polígono; 4. Se duas diagonais se intersectam, sua interseção é um vértice do polígono. Se em algum momento descobrimos que a triangulação não é a correta, mudamos o estado da variável incorreto. Ao término do programa, imprimimos Correto ou Incorreto de acordo com seu estado. Cabeçalho 5 int main (int argc, char argv ) { short incorreto = 0; Variáveis locais de main 3 Argumentos corretos? 2 Lê o polígono 6 Lê e diagonais no segundo arquivo e verifica se estão corretas 9 end : Libera estruturas de dados 20 if (incorreto) printf ("Incorreto\n"); else printf ("Correto\n"); return 0; 2. O programa deve receber dois argumentos. Argumentos corretos? 2 if (argc 3) { fprintf (stderr, "! uso: %s <arquivo com poligono> <arquivo com diagonais>\n", argv [0]); exit (0); 3. Como não sabemos a princípio quantos pontos o polígono tem, iniciamos usando um vetor estaticamente alocado de pontos, e passamos para alocação dinâmica quando ele é totalmente usado. Seu tamanho é tamanho buffer, e o o tamanho estático é tamanho estatico. #define tamanho estatico 1000 1
Variáveis locais de main 3 double X estatico[tamanho estatico], Y estatico[tamanho estatico], X = X estatico, Y = Y estatico; int tamanho buffer = tamanho estatico; Veja também os trechoss 7, 10 e 14. 4. Para aumentar o tamanho do buffer usamos a macro aumenta tamanho( ). Se o buffer é o vetor estático, alocamos memória e copiamos os dados; caso contrário simplesmente realocamos memória. A liberação de memória restaura a situação inicial (memória alocada estaticamente). #define aumenta tamanho xy () { while (0) #define libera xy () { do if (tamanho buffer tamanho estatico) { tamanho buffer = 2; X = realloc(x, sizeof (double) tamanho buffer ); Y = realloc(y, sizeof (double) tamanho buffer ); else { X = malloc(sizeof (double) tamanho buffer 2); Y = malloc(sizeof (double) tamanho buffer 2); memcpy (X, X estatico, sizeof (double) tamanho buffer ); memcpy (Y, Y estatico, sizeof (double) tamanho buffer ); tamanho buffer = 2; do if (tamanho buffer tamanho estatico) { free (X); free (Y ); X = X estatico; Y = Y estatico; tamanho buffer = tamanho estatico; while (0) 5. Cabeçalho 5 #include <string.h> / Para memcpy ( ). / Veja também os trechoss 8 e 15. 6. Mantemos o número de pontos armazenado em uma variável num pontos lidos. Lê o polígono 6 if ((entrada = fopen (argv [1], "r")) Λ) { fprintf (stderr, "! N~ao consegui abrir o arquivo %s.\n", argv [1]); exit (1); while (fscanf (entrada, "%lf %lf", &X[num pontos lidos ], &Y [num pontos lidos ]) 2) { ++num pontos lidos ; if (num pontos lidos tamanho buffer ) aumenta tamanho xy ( ); printf ("Li %d pontos.\n", num pontos lidos ); Fecha o arquivo de entrada 19 2
7. Variáveis locais de main 3 + FILE entrada = Λ; int num pontos lidos = 0; 8. Cabeçalho 5 + #include <stdio.h> #include <stdlib.h> 9. Uma diagonal é um conjunto de dois pontos, representado no arquivo de saída por quatro coordenadas. O que fazemos aqui é ler uma diagonal, e verificar se por alguma razão ela invalida a triangulação. Juntamente com os testes, encontramos os índices u e v dos vértices extremos da diagonal. Vamos armazenar as diagonais em um vetor de inteiros com 2(n 3) posições. As entradas 2i e 2i+1 representam que existe uma diagonal de extremos (X[2 i],y [2 i]) e (X[2 i + 1],Y [2 i + 1]). Lê e diagonais no segundo arquivo e verifica se estão corretas 9 diagonal = malloc(sizeof (int) 2 (num pontos lidos 3)); num diagonais lidas = 0; u = v = 1; Abre o arquivo de entrada 18 while (fscanf (entrada, "%lf %lf %lf %lf", &x1, &y1, &x2, &y2 ) 4) { Mais diagonais do que o esperado? 11 Diagonal cruza alguma das arestas? Termina em vértices do polígono? 13 Diagonal cruza alguma das diagonais? 16 diagonal [2 num diagonais lidas ] = u; diagonal [2 num diagonais lidas + 1] = v; ++num diagonais lidas ; Menos diagonais do que o esperado? 17 free (diagonal ); diagonal = Λ; Fecha o arquivo de entrada 19 10. Variáveis locais de main 3 + int diagonal = Λ, num diagonais lidas = 0, u, v; double x1, y1, x2, y2 ; 11. Mais diagonais do que o esperado? 11 if (num diagonais lidas num pontos lidos 3) { / Já temos n 3 diagonais e acabamos de ler mais uma. / printf ("[Mais diagonais do que esperado]\n"); break; 12. Vamos precisar de um modo para testar se dois segmentos se cruzam. Para isso usamos a seguinte técnica: sejam i = (x i, y i ) para i = 1, 2, 3, 4. Os segmentos (1, 2) cruza o segmento (3, 4) se quando percorremos o segmento (1, 2), os pontos 3 e 4 ficam de lados diferentes; e vice-versa: quando percorremos o segmento (3, 4), os pontos 3 e 4 ficam de lados diferentes. O lado em um ponto i fica do segmento orientado jk segmento está associado ao sinal de uma das coordenadas do produto vetorial dos vetores ij ik, quando os consideramos imersos em um espaço 3D, no plano z = 0. Esse produto é dado por (x j x i )(y k y i ) (x k x i )(y j y i ) Assim, estar de lados diferentes equivale a ter sinais diferentes. 3
#define prin (x1, y1, x2, y2, x3, y3 ) / Produto interno. / (((x2 ) (x1 )) ((y3 ) (y1 )) ((x3 ) (x1 )) ((y2 ) (y1 ))) #define cruza (x1, y1, x2, y2, x3, y3, x4, y4 ) ((prin ((x1 ), (y1 ), (x2 ), (y2 ), (x3 ), (y3 )) prin ((x1 ), (y1 ), (x2 ), (y2 ), (x4 ), (y4 )) < eps ) (prin ((x3 ), (y3 ), (x4 ), (y4 ), (x1 ), (y1 )) prin ((x3 ), (y3 ), (x4 ), (y4 ), (x2 ), (y2 )) < eps )) 13. Vamos percorrer todas as arestas do polígono, testando a interseção entre a diagonal e as arestas do polígono. Durante esse processo, identificamos os vértices u e v que são os extremos da diagonal. #define eps 1 10 7 #define ponto eh extremo da diagonal (i, x, y) ((fabs ((x) X[i]) < eps ) (fabs ((y) Y [i]) < eps )) Diagonal cruza alguma das arestas? Termina em vértices do polígono? 13 for (i = 0; i < num pontos lidos ; ++i) { if (ponto eh extremo da diagonal (i, x1, y1 )) u = i; else if (ponto eh extremo da diagonal (i, x2, y2 )) v = i; / Só um dos extremos pode ser o vértice i. / if (cruza (X[i], Y [i], X[(i + 1) % num pontos lidos ], Y [(i + 1) % num pontos lidos ], x1, y1, x2, y2 )) { printf ("[Diagonal (%5.2f,%5.2f)--(%5.2f,%5.2f) cruza a aresta (%5.2f,%5.2f)--(%5.2f,%5.\ 2f)]\n", x1, y1, x2, y2, X[i], Y [i], X[(i + 1) % num pontos lidos ], Y [(i + 1) % num pontos lidos ]); break; if (u 1 v 1) { printf ("[Algum dos extremos da diagonal n~ao é vertice. u=%d, v=%d]\n", u, v); if (u (v + 1) % num pontos lidos v (u + 1) % num pontos lidos ) { printf ("[Extremos s~ao vértices consecutivos do polígono. u=%d, v=%d]\n", u, v); 14. Variáveis locais de main 3 + int i; 15. Cabeçalho 5 + #include <math.h> / Para fabs ( ). / 16. Diagonal cruza alguma das diagonais? 16 for (i = 0; i < num diagonais lidas ; i += 2) { if (cruza (X[diagonal [i]], Y [diagonal [i]], X[diagonal [i + 1]], Y [diagonal [i + 1]], x1, y1, x2, y2 )) { printf ("[Diagonal (%5.2f,%5.2f)--(%5.2f,%5.2f) cruza a diagonal (%5.2f,%5.2f)--(%5.2f,%\ 5.2f)]\n", x1, y1, x2, y2, X[diagonal [i]], Y [diagonal [i]], X[diagonal [i + 1]], Y [diagonal [i + 1]]); break; 17. Menos diagonais do que o esperado? 17 if (num diagonais lidas < num pontos lidos 3) { printf ("[Menos diagonais do que esperado]\n"); 4
18. Abre o arquivo de entrada 18 if ((entrada = fopen (argv [2], "r")) Λ) { fprintf (stderr, "! N~ao consegui abrir o arquivo %s.\n", argv [2]); goto end ; 19. Fecha o arquivo de entrada 19 fclose (entrada ); entrada = Λ; Código utilizado nos trechos 6 e 9. 20. Libera estruturas de dados 20 libera xy ( ); 5
Índice Remissivo argc: 1, 2. argv : 1, 2, 6, 18. aumenta tamanho: 4. aumenta tamanho xy : 4, 6. cruza : 12, 13, 16. diagonal : 9, 10, 16. end : 1, 18. entrada : 6, 7, 9, 18, 19. eps : 12, 13. exit : 2, 6. fabs : 13, 15. fclose : 19. fopen : 6, 18. fprintf : 2, 6, 18. free : 4, 9. fscanf : 6, 9. i: 14. incorreto: 1, 11, 13, 16, 17. libera xy : 4, 20. main : 1. malloc: 4, 9. memcpy : 4, 5. num diagonais lidas : 9, 10, 11, 16, 17. num pontos lidos : 6, 7, 9, 11, 13, 17. ponto eh extremo da diagonal : 13. prin : 12. printf : 1, 6, 11, 13, 16, 17. realloc: 4. stderr : 2, 6, 18. tamanho buffer : 3, 4, 6. tamanho estatico: 3, 4. u: 10. v: 10. X: 3. X estatico: 3, 4. x1 : 9, 10, 12, 13, 16. x2 : 9, 10, 12, 13, 16. x3 : 12. x4 : 12. Y : 3. Y estatico: 3, 4. y1 : 9, 10, 12, 13, 16. y2 : 9, 10, 12, 13, 16. y3 : 12. y4 : 12. 6
Lista de trechos Abre o arquivo de entrada 18 Utilizado no trecho 9. Argumentos corretos? 2 Utilizado no trecho 1. Cabeçalho 5, 8, 15 Utilizado no trecho 1. Diagonal cruza alguma das arestas? Termina em vértices do polígono? 13 Utilizado no trecho 9. Diagonal cruza alguma das diagonais? 16 Utilizado no trecho 9. Fecha o arquivo de entrada 19 Utilizado nos trechos 6 e 9. Lê e diagonais no segundo arquivo e verifica se estão corretas 9 Utilizado no trecho 1. Lê o polígono 6 Utilizado no trecho 1. Libera estruturas de dados 20 Utilizado no trecho 1. Mais diagonais do que o esperado? 11 Utilizado no trecho 9. Menos diagonais do que o esperado? 17 Utilizado no trecho 9. Variáveis locais de main 3, 7, 10, 14 Utilizado no trecho 1. 7