algunas estructuras de datos y algoritmos para

83

Upload: others

Post on 18-Apr-2022

8 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Algunas estructuras de datos y algoritmos para

UNIVERSIDAD DE LOS ANDES

FACULTAD DE INGENIERIA

ESCUELA DE INGENIERIA DE SISTEMAS

Algunas estructuras de datos yalgoritmos para geometría

computacional

Autor: José Orlando Ruiz Hernández

Tutor: Leandro R. León

Proyecto de grado presentado ante la Ilustre Universidad de los Andes como

requisito �nal para optar al título de Ingeniero de Sistemas.

MAYO, 2007

Page 2: Algunas estructuras de datos y algoritmos para

Algunas estructuras de datos y algoritmos parageometría computacional

José Orlando Ruiz Hernández

Resumen

Este proyecto estudia la necesidad que presenta la biblioteca Aleph de tener una

extensión en el área de geometría computacional que contribuya con futuros desa-

rrollos en esta rama de la computación y con su estudio en la Escuela de Ingeniería

de Sistemas de la Universidad de Los Andes. Por esta razón se propone elaborar un

aporte instrumental que forme parte de la biblioteca Aleph y que siente las bases

en el desarrollo de la geometría computacional en la Escuela de Ingeniería de Sis-

temas de la Universidad de Los Andes. El trabajo se concentra en la implantación

de los problemas de triangulación de polígonos y cascarón convexo de un conjunto

de puntos y en la creación de un prototipo grá�co, usando las bibliotecas grá�cas

GTK, para realizar pruebas y demostrar el cumplimiento de los objetivos trazados.

Page 3: Algunas estructuras de datos y algoritmos para

Dedicado a mis padres, mis hermanos y a todas aquellas

personas que hicieron posible terminar este largo camino.

Page 4: Algunas estructuras de datos y algoritmos para

Agradecimientos

Este trabajo fue posible gracias a la contribución de varias personas a las que quiero

agradecer:

A mi tutor el Profesor Leandro León, por su guía, ayuda, con�anza y paciencia.

Al Profesor Jose Aguilar y la Profesora Isabel Besembel por sus sugerencias.

A Mary Pili Vargas por sus consejos.

A mis padres y mis hermanos por creer en mi.

A Ruth Molina por apoyarme en esta última fase de mi carrera.

A todas aquellas personas que de alguna manera contribuyeron con la realiza-

ción de este proyecto.

Page 5: Algunas estructuras de datos y algoritmos para

Contenido

1. Introducción 1

2. Planteamiento del Problema 3

2.1. Antecedentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2.1.1. Geometría Computacional . . . . . . . . . . . . . . . . . . . . 3

2.1.2. Aleph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.2. De�nición del Problema . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.2.1. La Geometría Computacional en Aleph . . . . . . . . . . . . . 8

2.2.2. Descripción del Problema . . . . . . . . . . . . . . . . . . . . 9

2.3. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

3. Diseño e Implementación 10

3.1. Clases de Base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

3.1.1. Clase Point . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

3.1.2. Clase Vertex . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

3.1.3. Clase Segment . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

3.1.4. Clase Polygon . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

3.2. El Problema de la Triangulación de Polígonos . . . . . . . . . . . . . 37

3.3. El Problema del Cascarón Convexo . . . . . . . . . . . . . . . . . . . 56

4. Prototipo Grá�co y Pruebas 63

4.1. GTK (Gimp Tool Kit) . . . . . . . . . . . . . . . . . . . . . . . . . . 63

4.2. Prototipo Grá�co . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

4.3. Pruebas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

v

Page 6: Algunas estructuras de datos y algoritmos para

Contenido vi

5. Conclusiones y Recomendaciones 74

5.1. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

5.2. Recomendaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

Bibliografía 76

Page 7: Algunas estructuras de datos y algoritmos para

Capítulo 1

Introducción

La geometría computacional es el estudio de algoritmos para dar solución a proble-

mas relacionados con geometría mediante computadoras, su función es dar solución

en tiempos reducidos a problemas que de otra manera podrían tomar gran cantidad

de tiempo resolverlos. Esta rama de la computación en los últimos años ha ganado

gran interés por parte de investigadores por su gran importancia en las ciencias de

la computación.

Aleph es un biblioteca de programación elaborada con �nes didácticos y con el

propósito de permitir a los usuarios el desarrollo de programas y rutinas que uti-

licen estructuras de datos además de permitir también el desarrollo de sistemas y

aplicaciones distribuidas.

A pesar de que Aleph posee una gran cantidad de algoritmos y estructuras de

datos, aún no contiene ningún desarrollo en el área de la geometría computacional,

por esta razón este proyecto se centró en sentar las bases para la creación de una

extensión de la biblioteca Aleph en el área de la geometría computacional que con-

tribuya con el estudio de esta rama en la Escuela de Ingeniería de Sistemas de la

Universidad de Los Andes.

Los problemas geométricos abarcados en este proyecto son los de triangulación

por remoción de asas, triangulación basada en partición monótona del polígono y el

problema de cascarón convexo de un conjunto de puntos.

1

Page 8: Algunas estructuras de datos y algoritmos para

Capítulo 1. Introducción 2

Para este cometido:

Se realizó un estudio sobre la geometría computacional y en especial sobre los

problemas planteados.

Se diseñaron e implantaron las abstracciones básicas (Punto, Vértice, Segmen-

to y Polígono).

Se implantaron los algoritmos necesarios para la triangulación de polígonos.

Se implantaron los algoritmos necesarios para generar el cascarón convexo de

un conjunto de puntos.

Se realizó un estudio acerca de las bibliotecas grá�cas de GTK.

Se creó un prototipo grá�co que utiliza los algoritmos implantados para que

demuestre el cumplimiento de los objetivos planteados.

El resto del documento se encuentra estructurado de la siguiente manera:

Capítulo 2 (Planteamiento del problema): en este capítulo se explica la pro-

blemática que se presenta en la biblioteca Aleph con respecto a la geometría

computacional. Además se exponen los objetivos planteados en la realización

de este proyecto.

Capítulo 3 (Diseño e Implementación): aquí se explica la manera como fueron

de�nidas e implantadas las clases de base y también de que forma fueron

implantados los algoritmos de triangulación y cascarón convexo.

Capítulo 4 (Prototipo Grá�co y Pruebas): en esta sección del documento se

expone lo que son la librerías grá�cas de GTK, el funcionamiento del prototipo

grá�co desarrollado y las pruebas de desempeño realizadas sobre los algortimos.

Capítulo 5 (Conclusiones y Recomendaciones): por último se muestran las

conclusiones y recomendaciones generadas con la creación de este proyecto.

Page 9: Algunas estructuras de datos y algoritmos para

Capítulo 2

Planteamiento del Problema

2.1. Antecedentes

2.1.1. Geometría Computacional

La geometría computacional es el estudio de algoritmos para dar solución a pro-

blemas relacionados con geometría mediante computadoras. Su función es generar

la solución en tiempos reducidos a problemas que de otra manera podrían tomar

gran cantidad de tiempo resolverlos.

Entre los problemas fundamentales de la geometría computacional se tienen:

Triangulación: la mayor parte de la geometría computacional realiza sus cálcu-

los en un objeto geométrico conocido como polígono, el cual es una región del

plano limitada por una colección �nita de segmentos que forman una curva ce-

rrada simple. Los polígonos son a menudo un modelo preciso de objetos reales

y son fácilmente manipulados computacionalmente. Pero los polígonos pueden

ser objetos complicados, y a menudo se presenta la necesidad de verlos como

una composición de piezas más simples, estas piezas más simples podrían ser

triángulos, que son polígonos igualmente, pero de tan solo tres lados y muy

simples de manejar computacionalmente.

La triangulación de un polígono es entonces, la subdivisión de su interior en

triángulos, tal que los vértices del polígono sean vértices de los triángulos y

3

Page 10: Algunas estructuras de datos y algoritmos para

2.1. Antecedentes 4

viceversa, tal como se muestra en la Figura 2.1. Entre sus aplicaciones se puede

citar el cálculo del área del polígono, el cual se obtiene sumando el área de cada

triangulo que lo compone, además es ampliamente usada en la teselación de

geometrías curvadas y posee gran importancia en diversos algoritmos, como

por ejemplo en el problema de la galería de arte que consiste en: dado un

polígono simple P, que representa un área, dónde poner guardias para que

cada punto del área esté vigilado por alguno de ellos.

Figura 2.1 Triangulación de un polígono

Cascarón Convexo: el cascarón convexo de un conjunto de puntos en 2 dimen-

siones es el polígono convexo más pequeño que contiene a todos los puntos del

conjunto. Metafóricamente el cascarón convexo de un sistema de puntos en el

plano es la forma tomada por una goma o liga estirada alrededor de los clavos

colocados en cada punto de borde.

El cascarón convexo es usado en aplicaciones tales como la búsqueda de rutas

de robots previniendo colisión con obstáculos, ya que es mas fácil encontrar

su ruta si el robot tiene forma de polígono convexo, también es usado en los

algoritmos usados para encontrar el rectángulo de menor área que encierra a

un polígono. Un ejemplo del cascarón convexo de un conjunto de puntos se

puede observar en la Figura 2.2.

Page 11: Algunas estructuras de datos y algoritmos para

2.1. Antecedentes 5

Figura 2.2 Cascarón Convexo

Diagramas de Voronoi: un diagrama de Voronoi es una estructura geométrica

que representa información de proximidad acerca de un conjunto de puntos u

objetos, es una partición del espacio en celdas, cada una de la cuales colinda

con otras celdas. En la Figura 2.3 se puede apreciar un Diagrama de Voronoi

para 4 puntos.

Figura 2.3 Diagrama de Voronoi de 4 puntos

Los diagramas de Voronoi tienen numerosas aplicaciones en diversas áreas,

entre ellas se encuentran la cristalografía (Interpolación del espacio), estudios

de mercado (distribución de comercios), la sociología, la cartografía, la an-

tropología, la arqueología, la biología, la ecología, estudios forestales (control

de incendios), la geografía, la meteorología, la �siología, la plani�cación ur-

bana y regional, etc. Por ejemplo, los diagramas de Voronoi son esenciales en

Page 12: Algunas estructuras de datos y algoritmos para

2.1. Antecedentes 6

la solución de un problema de comunicación sin hilos. Dado un conjunto de

subestaciones con n-micro celdas. ¾Cómo pueden ser pre-procesados de mo-

do que cualquier llamada celular arbitraria sea procesada por la estación más

cercana?.

Arreglos: los arreglos de líneas forman la tercera estructura de datos más im-

portante usada en la geometría computacional. Consiste en una colección de

líneas dispuestas en el plano. Estas líneas producen una partición del plano en

regiones convexas (llamadas celdas), segmentos (entre las líneas cruzadas), y

vértices (donde se unen las líneas) (J. O'Rourke 1998).

Las disposiciones pueden parecer muy abstractas para tener gran utilidad,

pero en realidad ellas surgen en una extensa variedad de contextos. Entre ellos

se pueden mencionar la visibilidad de grafos, la remoción de super�cies ocultas,

que consiste en calcular cuales super�cies en una escena de 3 dimensiones están

ocultas desde un punto de vista y removerlas para construir una imagen grá�ca

en 2 dimensiones, y también dado un conjunto de puntos encontrar el polígono

convexo vacío con mayor número de vértices (J. O'Rourke 1998). Un ejemplo

de arreglo de líneas se puede observar en la Figura 2.4 donde se muestra una

arreglo de 10 líneas.

Figura 2.4 Arreglo de 10 líneas

Page 13: Algunas estructuras de datos y algoritmos para

2.1. Antecedentes 7

Plani�cación del Movimiento: la robótica ha inspirado el estudio de una colec-

ción de problemas en geometría computacional llamado Plani�cación del Mo-

vimiento, que consiste en encontrar caminos óptimos, evitando obstáculos,

desde un punto inicial a un punto �nal. Esto no solo es usado en robótica,

sino también en plani�cación de rutas de máquinas controladas numéricamen-

te, sistemas de información geográ�ca, y navegación virtual en computadoras

grá�cas. En la Figura 2.5 se pueden apreciar varios obstáculos sobre el plano

así como un punto inicial (start) y un punto �nal (end) y la plani�cación de la

ruta que debe tomar un robot para evitar los obstáculos y llegar desde start

hasta end tomando el camino mas corto.

Figura 2.5 Movimiento en el plano desde start hasta end

2.1.2. Aleph

Aleph es una biblioteca de programación elaborada con �nes didácticos con el

propósito de permitir a los usuarios el desarrollo de programas y rutinas que uti-

licen estructuras de datos, además de permitir también el desarrollo de sistemas

y aplicaciones distribuidas. Esta biblioteca de programación fue creada por el gru-

po de trabajo del Centro de Estudios de Microelectrónica y Sistemas Distribuidos

(CEMISID), perteneciente a la Universidad de Los Andes, Mérida-Venezuela.

Page 14: Algunas estructuras de datos y algoritmos para

2.2. De�nición del Problema 8

Aleph contiene las siguientes características:

Posee un conjunto de plantillas que permite la utilización de la estructuras de

datos más conocidas como son: arreglos, listas, pilas, colas, vectores, árboles

binarios y sus variaciones como: árboles splay, aleatorizados, rojo y negro, avl,

grafos, etc.

Posee un indicador de memoria solicitada al computador, que al �nalizar la

ejecución de cualquier programa despliega un resumen de la cantidad de soli-

citudes de memoria y cuanto de la misma ha sido devuelta al computador.

Posee un sistema IPC (InterprocessComunication), mediante el cual logra

a�anzarse como un sistema de comunicación remota, con�able, sobre Inter-

net para aplicaciones distribuidas.

Permite la localización de objetos distribuidos móviles, con el �n de actualizar

referencias distribuidas cada vez que un objeto migra de un sitio a otro.

Permite la migración de procesos a nivel de usuario, de forma tal que el mismo

puede especi�car el orden de transferencia de los datos, los tiempos en que

las transferencias deben ser realizadas, cuales datos deben ser transferidos y

el tamaño de los datos a transferir.

Permite el uso de memoria distribuida en un sistema programado, gestionando

un conjunto de procesos cada uno con su propio espacio de memoria, en el

cual una porción de memoria es manipulada en modo compartido por todos

los procesos.

Permite el manejo de excepciones y aserciones.

2.2. De�nición del Problema

2.2.1. La Geometría Computacional en Aleph

A pesar que Aleph es una biblioteca que contiene una gran cantidad de clases,

métodos y funciones, aún no posee ningún tipo de desarrollo relacionado con el área

Page 15: Algunas estructuras de datos y algoritmos para

2.3. Objetivos 9

de la geometría computacional.

Aleph fue desarrollada principalmente con �nes didácticos, por esta razón se hace

imprescindible una extensión de esta biblioteca en el área de geometría computacio-

nal para contribuir con el estudio de esta rama de la computación en la Escuela de

Ingeniería de Sistemas.

2.2.2. Descripción del Problema

Este proyecto aborda la problemática que se presenta con la necesidad de que la

biblioteca Aleph contenga una extensión en el área de la geometría computacional

que contribuya con el estudio de esta rama en la Escuela de Ingeniería de Sistemas.

Debido a estas circunstancias, este proyecto se centra en sentar la bases para

el desarrollo de una extensión de la biblioteca Aleph de estructuras de datos y

algoritmos relacionados con geometría computacional en lenguaje de programación

C++, y que facilite futuros estudios y desarrollos en esta área.

2.3. Objetivos

Para evaluar si el �n de�nido anteriormente es alcanzado, se plantean los siguien-

tes objetivos:

De�nir e implementar las abstracciones básicas requeridas (Punto, Vértice,

Segmento y Polígono).

Implantar los algoritmos necesarios para generar la triangulación de polígonos.

Implantar los algoritmos necesarios para generar el cascarón convexo de un

conjunto de puntos en 2 dimensiones.

Desarrollar un prototipo grá�co utilizando las librerías grá�cas GTK para

probar el funcionamiento de los algoritmos desarrollados.

Page 16: Algunas estructuras de datos y algoritmos para

Capítulo 3

Diseño e Implementación

Este capítulo explica la implantación y funcionamiento de las clases básicas uti-

lizadas para dar solución a los problemas de triangulación de polígonos y cascarón

convexo, así como también la implantación de la solución a estos problemas.

3.1. Clases de Base

Para la solución de los problemas de triangulación de polígonos y cascarón con-

vexo, es esencial de�nir e implantar algunas clases básicas.

Entre las clases básicas de�nidas tenemos:

Clase Point

Clase Vertex

Clase Segment

Clase Polygon

3.1.1. Clase Point

En la geometría, el punto es uno de los entes fundamentales, pues es un elemen-

to que tiene posición pero no extensión (su área es nula), y está de�nido por sus

coordenadas.

10

Page 17: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 11

Figura 3.1 Representación grá�ca de un punto en dos (2) dimensiones.

La clase Point representa un punto en 2 dimensiones que posee dos atributos

llamados x e y que corresponden a sus coordenadas, como se muestra en la Figura 3.1,

el tipo de dato usado para almacenar los valores de estas coordenadas fue de�nido

como Geom_Number. Todo esto se encuentra implantado en el archivo point.H

como se muestra en el Código 3.1.

/* punto en 2 dimensiones (x,y)*/

class Point

{

private:

Geom_Number x;

Geom_Number y;

public:

/* Métodos Públicos */

}

Código 3.1 Implantación de la clase Point.

En la Figura 3.2 se muestra el diagrama UML de la clase Point.

Figura 3.2 Point

Page 18: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 12

Geom_Number

Geom_Number es un tipo de dato para números geométricos que fue de�nido

para ser usado en las clases y funciones desarrolladas en este proyecto. Fue creado con

la �nalidad de tener un tipo de dato ideal para realizar las operaciones geométricas

necesarias, que permitan realizar cálculos con gran precisión, pero que a su vez no

permita desbordamiento (over�ow).

Este proyecto no se concentra en el estudio de este tipo de dato, simplemente fue

de�nido como un tipo de dato long long que posteriormente pueda ser modi�cado y

no afecte el funcionamiento de los desarrollos realizados.

Los métodos públicos implantados en la clase Point se mencionan en la Tabla 3.1.

Tabla 3.1 Métodos públicos clase Point

Método Funcionamiento

Point() crea un punto con coordenadas x e y

iguales a cero (0).

Point(const Geom_Number & __x,

const Geom_Number & __y)

crea un punto a partir de las coordena-

das x e y.

Point (const Point & p) crea una copia del punto p.

�Point() destruye el punto.

const bool operator == (const

Point & point) const

compara dos puntos y retorna verdade-

ro si los puntos son iguales, es decir, si

las coordenadas x de ambos puntos son

iguales y las coordenadas y también.

const bool operator != (const

Point & point) const

compara dos puntos y retorna verdade-

ro si los puntos son distintos.

const Geom_Number & get_x()

const

retorna una referencia constante de la

coordenada x del punto.

const Geom_Number & get_y()

const

retorna una referencia constante de la

coordenada y del punto.

Page 19: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 13

El archivo point.H también contiene algunas constantes y funciones pertinentes

para la creación del prototipo grá�co que se explicará en el capítulo 4. En la Tabla

3.2 se explican brevemente estas constantes.

Tabla 3.2 Constantes archivo Point.H

Max_X_Geom_Number valor máximo que puede tomar la coordenada x de un

punto.

Max_Y_Geom_Number valor máximo que puede tomar la coordenada y de un

punto.

Max_X_Pixels valor máximo en pixels que puede retornar el prototipo

grá�co para la coordenada x de un punto.

Max_Y_Pixels valor máximo en pixels que puede retornar el prototipo

grá�co para la coordenada y de un punto.

Las funciones de�nidas en el archivo point.H pertinentes para el funcionamiento

del prototipo grá�co se explican en la Tabla 3.3.

Tabla 3.3 Funciones del prototipo grá�co archivo Point.H

const Geom_Number

x_pixel_to_geom_number(const int&

x_coord)

realiza la conversión de la coorde-

nada x de un punto de pixels a

Geom_Number.

const Geom_Number

y_pixel_to_geom_number(const int&

y_coord)

realiza la conversión de la coorde-

nada y de un punto de pixels a

Geom_Number.

const int x_geom_number_to_pixel(const

Geom_Number& x_coord)

realiza la conversión de la coor-

denada x de un punto de

Geom_Number a pixels.

const int y_geom_number_to_pixel(const

Geom_Number& y_coord)

realiza la conversión de la coor-

denada y de un punto de

Geom_Number a pixels.

Page 20: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 14

Utilitarios

En el archivo points_utils.H se implantaron algunas utilidades pertinentes para

la solución del problema de la triangulación del polígono. Una de estas utilidades

es la del cálculo del área del triangulo, la forma más común para calcular el área

de un triangulo es multiplicando su base por la altura y dividiéndola entre dos. Sin

embargo esta fórmula no es muy útil si queremos calcular el área de un triangulo

cuyos tres vértices son puntos arbitrarios (a, b, c).

Gracias al álgebra lineal sabemos que la magnitud del producto cruz de dos vec-

tores es el área del paralelogramo que ellos determinan. Si A y B son dos vectores

como se muestran en la Figura 3.3, entonces el producto cruz de A y B (|A x B|)

es el área del paralelogramo. Dado que cualquier triangulo puede ser visto como

la mitad de un paralelogramo, esto da un método directo para calcular el área del

triangulo. Si A = b - a y B = c - a, entonces el área del triangulo es la mitad de la

magnitud del producto cruz entre A y B (J. O'Rourke 1998). El área del triangulo

formado por los puntos (a, b, c) es positiva si los puntos en este orden forman un

circuito en sentido anti-horario.

Figura 3.3 Producto Cruz Paralelogramo

Sea A(T) el área del triangulo, al realizar el producto cruz entre A y B se obtiene

el siguiente resultado: A(T) = 1/2 [ (bo - ao)(c1 - a1) - (co - ao)(b1 - a1) ]

La función que calcula el área del triangulo se implantó como se muestra en el

Código 3.2, recibe tres puntos y retorna el área del triangulo formado por ellos.

Page 21: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 15

const Geom_Number triangle_area( const Point & a,

const Point & b,

const Point & c)

{

Geom_Number result = ((b.get_x() - a.get_x()) *

(c.get_y() - a.get_y()) -

(c.get_x() - a.get_x()) *

(b.get_y() - a.get_y()))/2;

return result;

}

Código 3.2 Función que calcula el área de un triangulo.

A partir de la función que calcula el área del triangulo se crean también las utili-

dades que se muestran en el Código 3.3.

const bool left(const Point & a, const Point & b, const Point & c)

const bool left_on(const Point & a, const Point & b, const Point & c)

const bool colinear_points(const Point & a,

const Point & b,

const Point & c)

Código 3.3 Prototipo de las funciones left, left_on y colinear_points.

La función left es usada para saber si un punto se encuentra a la izquierda de

una línea dirigida, la cual se determina por dos puntos en un orden particular (a,

b). Si un punto c se encuentra a la izquierda de una línea determinada por (a, b)

como se muestra en la Figura 3.4, entonces (a, b, c) forman un circuito en sentido

anti-horario. De esta c se encuentra a la izquierda de la línea (a, b) si el área del

triangulo (a, b, c) es positiva.

Page 22: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 16

Figura 3.4 c se encuentra a la izquierda de ab

La función colinear_points indica si los puntos (a, b, c) son colineales como se

muestra en la Figura 3.5, es decir si se encuentran sobre una misma línea recta, esto

sucede cuando (a, b, c) tiene un área igual a cero.

Figura 3.5 Puntos (a, b, c) son colineales

La función left_on nos indica si c se encuentra a la izquierda o sobre la línea

dirigida (a, b), es decir si (a, b, c) tiene un área mayor o igual a cero. Incluye ambos

casos anteriores left y colinear_points.

En la geometría algunas veces se presenta la necesidad de conocer si un punto

c, se encuentra entre el segmento formado por los puntos a y b como se muestra

en la Figura 3.6, esta información puede ser obtenida muy fácilmente. La primera

condición que se debe cumplir es que los puntos a, b y c sean colineales. Si el segmento

ab no es vertical , entonces c se encuentra entre a y b, si y solo sí, la coordenada x de

c se encuentra en el intervalo determinado por las coordenadas x de a y b. Si por el

contrario el segmento ab es vertical, entonces c se encuentra entre a y b, si y solo sí,

Page 23: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 17

la coordenada y de c se encuentra en el intervalo determinado por las coordenadas

y de a y b.

Figura 3.6 c se encuentra entre el segmento ab

Esta utilidad fue implantada en la función between, que nos indica si el punto c

se encuentra entre el segmento formado por los puntos a y b. Ver Código 3.4.

const bool between(const Point & a, const Point & b, const Point & c)

{

if (not colinear_points(a, b, c))

return false;

/* si ab no es vertical */

if (a.get_x() != b.get_x())

return (((a.get_x() <= c.get_x()) and (c.get_x() <= b.get_x())) or

((a.get_x() >= c.get_x()) and (c.get_x() >= b.get_x())));

/* si ab es vertical */

return (((a.get_y() <= c.get_y()) and (c.get_y() <= b.get_y())) or

((a.get_y() >= c.get_y()) and (c.get_y() >= b.get_y())));

}

Código 3.4 Implantación de la función between.

Las dos últimas funciones que se encuentran en el archivo points_utils.H se re-

lacionan con la intersección de segmentos y utilizan las funciones collinear_points,

between y left explicadas anteriormente.

Dos segmentos ab y cd se intersectan si c y d son separados por la línea L1 que

contiene a ab (c está a un lado y d al otro) y de igual manera a y b están separados

por la línea L2 que contiene a cd.

Page 24: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 18

Una intersección propia se produce cuando dos segmentos intersectan un punto

interior del otro segmento. Esto se puede determinar veri�cando que no ocurra que

tres de los cuatro puntos sean colineales. En la Figura 3.7(b) la intersección es

impropia porque los puntos a, c y d son colineales.

Figura 3.7 (a) Intersección Propia, (b) Intersección Impropia

Las funciones intersect_prop e intersect_improp fueron implantadas como se

muestra en el Código 3.5 y 3.6, estas veri�can si existe intersección propia o impro-

pia entre dos segmentos, también fue implantada la función intersectp que incluye

ambos casos. Ver Código 3.7.

const bool intersect_prop(const Point & a, const Point & b,

const Point & c, const Point & d)

{

if (colinear_points(a, b, c) or

colinear_points(a, b, d) or

colinear_points(c, d, a) or

colinear_points(c, d, b)

)

return false;

return ((left(a, b, c) xor left(a, b, d)) and

(left(c, d, a) xor left(c, d, b)));

}

Código 3.5 Implantación de la función intersect_prop.

Page 25: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 19

const bool intersect_improp(const Point & a, const Point & b,

const Point & c, const Point & d)

{

if (between(a, b, c) or

between(a, b, d) or

between(c, d, a) or

between(c, d, b))

return true;

return false;

}

Código 3.6 Implantación de la función intersect_improp.

const bool intersectp(const Point & a, const Point & b,

const Point & c, const Point & d)

{

if (intersect_prop(a, b, c, d))

return true;

if (intersect_improp(a, b, c, d))

return true;

return false;

}

Código 3.7 Implantación de la función intersectp.

3.1.2. Clase Vertex

El vértice de un polígono es el punto donde se unen dos de sus lados formando

un ángulo.

Page 26: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 20

Figura 3.8 Sección de un Polígono

Cada vértice del polígono se forma tras la unión de dos de sus lados en un mismo

punto, cada lado del polígono es un segmento que se encuentra entre dos vértices

contiguos del polígono. Si nos enfocamos en un trozo de polígono como el que se

muestra en la Figura 3.8, se observa que se encuentran involucrados tres vértices y

dos segmentos, el vértice v1 se forma con la unión de los segmentos s1 y s2, y los

otros dos vértices (v2 y v3), que podrían ser llamados vértices inmediatos de V1, son

los que se encuentran al otro extremo de los segmentos s1 y s2. Esta propiedad se

mantiene para todos los vértices del polígono, es decir que cada vértice del polígono

tiene dos vértices inmediatos.

La información relevante de cada vértice del polígono es su posición y los dos lados

del polígono que concurren en él, basado en estas necesidades se implantó la clase

Vertex como un punto con dos enlaces hacia sus vértices inmediatos, de esta manera

se podía obtener la información de la posición del vértice (con las coordenadas x

e y del punto) y la información de los lados que concurren en él (con sus vértices

inmediatos).

El TAD Dlink (Enlace Doble)

El Tipo Abstracto de Dato (TAD) Dlink pertenece a la biblioteca Aleph, de�ne

un enlace doble contenido en un nodo perteneciente a una lista simplemente enlazada

circular.

Page 27: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 21

El TAD Dlink esta de�nido e implantado en el archivo dlink.H, en la Tabla 3.4 se

explican brevemente sus métodos.

Tabla 3.4 Métodos públicos clase Dlink

Método Funcionamiento

Dlink(): Crea un Dlink con sus lazos apuntando

así mismo.

Dlink(const Dlink & link): Constructor por copia. La copia de un

Dlink solo es permitida si el Dlink es-

tá vacío; es decir, si el Dlink no está

enlazado a otros nodos.

Dlink operator=(const Dlink &

link):

Retorna link=this.

void reset(): Coloca next=prev=this;

bool isEmpty()const: Retorna (true) si prev y next apuntan

a this.

void insert(Dlink* node): Inserta node como sucesor de this.

void append(Dlink* node): Inserta node como predecesor de this.

Dlink*& getNext(): Dada la dirección de un nodo retorna

el sucesor de dicho nodo.

Dlink*& getPrev(): Dada la dirección de un nodo retorna

el predecesor de dicho nodo.

void del(): Esta rutina permite que otras estructu-

ras de datos almacenen referencias eli-

minables a elementos de una lista en-

lazada. Es decir un nodo cualquiera x

puede suprimirse así mismo de la lista.

La clase vértice se creó heredando las propiedades de la clase punto y las propie-

dades de la clase Dlink como se muestra en la Figura 3.9.

Page 28: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 22

Figura 3.9 Herencia de Vertex

La clase Vertex es una derivación de Dlink (enlace doble), pero con la diferencia de

que almacena un elemento de tipo Point, esto se puede apreciar en la representación

grá�ca de Vertex que se muestra en la Figura 3.10.

Figura 3.10 Representación de Vertex

Esta clase también posee un atributo privado de tipo booleano llamado ear, este

atributo es usado para resolver el problema de la triangulación del polígono y será

explicado más adelante en este mismo capítulo. La implantación de la clase Vertex

se realiza en al archivo vertex.H como se muestra en el Código 3.8.

Page 29: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 23

class Vertex : public Point, public Dlink //herencia

{

private:

bool ear;

public:

/* Métodos Públicos */

}

Código 3.8 Implantación de la clase Vertex.

En la Figura 3.11 se muestra el diagrama UML de la clase Vertex.

Figura 3.11 Vertex

En la Tabla 3.5 se explican brevemente los métodos públicos de la clase Vertex.

Tabla 3.5 Métodos públicos clase Vertex

Método Funcionamiento

Vertex() crea un vértice con coordenadas x e y

iguales a cero y enlazado a él mismo.

Vertex(const Point & p) crea un vértice con las mismas coorde-

nadas del punto p y enlazado a el mis-

mo.

Vertex(const Vertex & p) crea un vértice copia del vértice p.

�Vertex() destruye el vértice.

Page 30: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 24

const bool operator == (const

Vertex & vertex) const

compara dos vértices, returna verdade-

ro si los vértices poseen la misma posi-

ción.

const bool operator == (const

Vertex * vertex) const

compara dos vértices, returna verdade-

ro si los vértices poseen la misma posi-

ción.

static Vertex*

dlink_to_vertex(Dlink * link)

Realiza la conversión de Dlink a Vertex

static Dlink*

vertex_to_dlink(Vertex * vertex)

Realiza la conversión de Vertex a Dlink

void set_ear(const bool & __ear) permite asignar o modi�car el valor del

elemento privado ear.

const bool & is_ear() const returna el valor del elemento privado

ear.

3.1.3. Clase Segment

Dados dos puntos a y b, se llama segmento ab a la intersección de la semirrecta

de orígen a que contiene al punto b, y la semirrecta de orígen b que contiene al

punto a. Los puntos a y b se denominan extremos del segmento.

La clase Segment fue implantada en el archivo segment.H como se muestra en el

Código 3.9, posee dos atributos llamados a y b que son dos punteros a datos de tipo

Point y que representan los dos extremos del segmento.

Page 31: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 25

class Segment

{

private:

const Point * a;

const Point * b;

public:

/* Métodos Públicos */

}

Código 3.9 Implantación de la clase Segment.

En la Figura 3.12 se muestra el diagrama UML de la clase Segment.

Figura 3.12 Segment

En la Tabla 3.6 se explican brevemente los métodos públicos de la clase Segment.

Tabla 3.6 Métodos públicos clase Segment

Método Funcionamiento

Segment(): Crean un segmento con sus dos punte-

ros apuntando a NULL.

Segment(const Point * __a, const

Point * __b):

crea un segmento a partir de los punte-

ros a puntos a y b.

Segment(const Point & __a, const

Point & __b):

crea un segmento a partir de los puntos

a y b.

Page 32: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 26

�Segment(): destruye el segmento.

const bool operator == (const

Segment & segment) const:

compara dos segmentos, retorna verda-

dero si son iguales.

const bool intersect(const

Segment & segment) const:

retorna si hay intersección entre los seg-

mentos.

const Point * get_a(): retorna un puntero al punto a del seg-

mento.

const Point * get_b(): retorna un puntero al punto b del seg-

mento.

Point horiz_line_inter(const

Geom_Number & __y):

retorna el punto perteneciente al seg-

mento que contenga coordenada y igual

a __y.

const Point * highest_point(): retorna un puntero al punto a del seg-

mento si su valor de la coordenada y es

mayor que la de b, de lo contrario retor-

na un puntero al punto b del segmento.

const Point * lowest_point(): retorna un puntero al punto a del seg-

mento si su valor de la coordenada y es

menor que la de b, de lo contrario retor-

na un puntero al punto b del segmento.

3.1.4. Clase Polygon

La clase Polygon fue de�nida e implantada en el archivo polygon.H como se

muestra en el Código 3.10. Posee tres atributos llamados:

vertex_list: es la cabecera de la lista que contendrá los vértices del polígono.

closed: indica si el polígono se encuentra cerrado, si es así no se pueden agregar

más vértices al polígono.

num_vertex: indica el número de vértices que contiene el polígono.

Page 33: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 27

class Polygon

{

private:

Vertex vertex_list;

bool closed;

size_t num_vertex;

public:

/* Métodos Públicos */

}

Código 3.10 Implantación de la clase Polygon.

En la Figura 3.13 se muestra el diagrama UML de la clase Polygon.

Figura 3.13 Polygon

Los métodos públicos de la clase Polygon se expondrán a continuación, algunos

se explicarán de manera más detallada que otros, dependiendo de su complejidad.

Page 34: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 28

struct Iterator : public Dlink::Iterator: la clase Polygon posee una estruc-

tura llamada Iterator que hereda sus propiedades de Dlink:Iterator. Este iterador

sirve para desplazarse a través de la lista de vértices del polígono, y posee una fun-

ción llamada get_current_vertex que retorna el vértice sobre el cual se encuentra

el iterador en ese instante.

Polygon(): crea la cabecera de la lista de vértices para que sean añadidos los

vértices necesarios, inicializa el número de vértices en cero y el valor de closed en

falso, es decir el polígono se encuentra abierto.

Polygon (const Polygon & polygon): crea una copia de Polygon insertando una

copia de todos sus vértices mediante el método append_vertex y copia el valor del

atributo closed que indica si el polígono está cerrado o no.

Polygon(): remueve uno a uno todos los vértices del polígono y libera la memoria.

const int is_empty() const: retorna si el polígono está vacio, es decir si no tiene

ningún vértice.

const int size() const: retorna el número de vértices del polígono.

const bool & is_closed(): retorna si el polígono está cerrado.

Vertex & get_first_vertex(): retorna el primer vértice de la lista de vértices

del polígono. Ver Código 3.11.

Page 35: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 29

Vertex & get_first_vertex()

{

if (vertex_list.is_empty())

throw std::underflow_error("Polygon is empty");

return *Vertex::dlink_to_vertex(vertex_list.get_next());

}

Código 3.11 Implantación de la función get_�rst_vertex.

Vertex & get_last_vertex(): retorna el último vértice de la lista de vértices del

polígono. Ver Código 3.12.

Vertex & get_last_vertex()

{

if (vertex_list.is_empty())

throw std::underflow_error("Polygon is empty");

return *Vertex::dlink_to_vertex(vertex_list.get_prev());

}

Código 3.12 Implantación de la función get_last_vertex.

Vertex & get_next_vertex(Vertex & vertex): retorna el vértice próximo a ver-

tex, si vertex es el último de la lista entonces retorna el primer vértice. Ver Código

3.13.

Page 36: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 30

Vertex & get_next_vertex(Vertex & vertex)

{

Vertex & first_vertex = get_first_vertex();

Vertex & last_vertex = get_last_vertex();

if (vertex == last_vertex)

return first_vertex;

Dlink * link = vertex.get_next();

return *Vertex::dlink_to_vertex(link);

}

Código 3.13 Implantación de la función get_next_vertex.

Vertex & get_prev_vertex(Vertex & vertex): retorna el vértice anterior a ver-

tex, si vertex es el primero de la lista entonces retorna el último vértice. Ver Código

3.14.

Vertex & get_prev_vertex(Vertex & vertex)

{

Vertex & first_vertex = get_first_vertex();

Vertex & last_vertex = get_last_vertex();

if (vertex == first_vertex)

return last_vertex;

Dlink * link = vertex.get_prev();

return *Vertex::dlink_to_vertex(link);

}

Código 3.14 Implantación de la función get_prev_vertex.

const int validate_segment(): esta función es usada por el prototipo grá�co

para veri�car si el vértice que se está insertando es colineal con los dos últimos vér-

tices que han sido insertados en el polígono.

Page 37: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 31

const int validate_close_segment(): esta función es usada por el prototipo

grá�co al momento de cerrar el polígono para veri�car si el primer vértice del polí-

gono es colineal con sus dos últimos vértices.

const bool append_vertex(const Vertex & v): inserta el vértice v al polígono.

Primero se veri�ca que el polígono no este cerrado, de lo contrario no se pueden

añadir más vértices. Ver Código 3.15.

if (closed)

throw std::domain_error("Polygon is already closed");

Código 3.15 Veri�ca si el polígono está cerrado.

Si el polígono no está vacío, es decir, tiene uno o más vértices, se veri�ca que el

vértice que se va a insertar no sea igual al primero o al último vértice del polígono

de lo contrario no se inserta el vértice. Ver Código 3.16.

if (not this->is_empty())

{

Vertex & first_vertex = get_first_vertex();

Vertex & last_vertex = get_last_vertex();

if (v == first_vertex or v == last_vertex)

return false;

}

Código 3.16 Veri�ca que el vértice que se va a insertar no sea igual al primero o al

último vértice del polígono.

Luego si el polígono tiene menos de 2 vértices se inserta el vértice al polígono y se

incrementa el número de vértices. Ver Código 3.17.

Page 38: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 32

if (num_vertex < 2)

{

vertex_list.append(new Vertex(v));

++num_vertex;

return true;

}

Código 3.17 Inserta un vértice si el polígono tiene menos de 2 vértices.

Si el polígono contiene dos o mas vértices, se chequea que el segmento formado

por el último vértice del polígono y el vértice que se desea insertar, no ocasione un

cruce con un lado del polígono, si no hay intersección se inserta el nuevo vértice y

se incrementa el número de vértices del polígono, de lo contrario no se inserta el

vértice. Ver Código 18.

Vertex & last_vertex = get_last_vertex();

Segment new_segment(last_vertex, v);

Vertex & previous_to_last = get_prev_vertex(last_vertex);

for (Polygon::Iterator it(*this); it.has_current() ; it.next())

{

Vertex & v_src = *it.get_current_vertex();

if (v_src == previous_to_last){ break; }

Vertex & v_tgt = get_next_vertex(v_src);

Segment segment(v_src, v_tgt);

if (new_segment.intersect_imp(segment))

return false;

}

vertex_list.append(new Vertex(v));

++num_vertex;

return true;

Código 3.18 Veri�ca que el vértice a insertar no genere un segmento que ocasione

cruce.

Page 39: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 33

void append_vertex(const Geom_Number & x, const Geom_Number & y): inser-

ta un vértice en el polígono a partir de las coordenadas.

const bool is_counterclockwise(): esta función determina si la lista de vér-

tices del polígono esta orientada en sentido anti-horario.

Sea v2 el vértice de menor coordenada y del polígono, v1 el vértice anterior a v2 y

v3 el vértice próximo a v2 como se muestra en la Figura 3.14. Si v3 se encuentra

a la izquierda del lado L1 formado por los vértices v1 y v2 entonces, los lados del

polígono se encuentran orientados en sentido anti-horario, si por el contrario esta

condición no se cumple, entonces los lados del polígono se encuentran orientados en

sentido horario. Para saber si el vértice v3 se encuentra a la izquierda del lado L1 se

utiliza la función left_on explicada anteriormente de la siguiente manera left_on(v1,

v2, v3). Ver Código 3.19.

Figura 3.14 Polígono orientado en sentido: (a) horario (b) anti-horario

const bool is_counterclockwise()

{

Vertex & low_vertex = this->lowest_vertex();

Vertex & next_low_vertex = this->get_next_vertex(low_vertex);

Vertex & prev_low_vertex = this->get_prev_vertex(low_vertex);

return left_on(prev_low_vertex,

low_vertex, next_low_vertex);

}

Código 3.19 Implantación de la función is_counterclockwise.

Page 40: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 34

void counterclockwise_vertex_list(): esta función se encarga de cambiar la

orientación del polígono en sentido anti-horario si no lo está. Ver Código 3.20. Esto

se consigue invirtiendo la lista de vértices del polígono utilizando un método de la

clase Dlink llamado reverse_list.

void counterclockwise_vertex_list()

{

if(!this->is_counterclockwise())

vertex_list.reverse_list();

}

Código 3.20 Implantación de la función is_counterclockwise.

const bool close(): se encarga de veri�car si el polígono puede ser cerrado con la

lista de vértices que posee actualmente, si es así lo cierra de lo contrario no permite

cerrarlo.

Si la cantidad de vértices del polígono es menor o igual a dos el polígono no puede

ser cerrado. Esto se debe a que un polígono no puede tener menos de tres vértices.

Para cerrar un polígono se debe unir su último vértice con el primero, si el lado

formado por estos dos vértices intersecta a otro lado del polígono no adyacente

entonces este no es un polígono válido. Por esta razón lo siguiente que hace la

función es veri�car que no haya intersección con ningún lado del polígono al cerrarlo

como se muestra en el Código 3.21.

Page 41: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 35

Vertex & first_vertex = get_first_vertex();

Vertex & last_vertex = get_last_vertex();

Segment last_segment(last_vertex, first_vertex);

Polygon::Iterator it(*this);

Vertex & previous_to_last = get_prev_vertex(last_vertex);

for (it.next(); it.has_current(); it.next())

{

Vertex & v_src = *it.get_current_vertex();

if (v_src == previous_to_last)

break;

Vertex & v_tgt = get_next_vertex(v_src);

Segment segment(v_src, v_tgt);

if (last_segment.intersect(segment))

return false;

}

Código 3.21 Veri�ca que no haya cruce al cerrar el polígono.

Por último antes de asignar el valor true al atributo closed del polígono que indica

que este está cerrado, se veri�ca con la función counterclockwise_vertex_list si el

polígono tiene sus lados orientados en sentido anti-horario, de no ser así se invierte

el orden de la lista de vértices. Ver Código 3.22.

counterclockwise_vertex_list();

closed = true;

Código 3.22 Invierte la lista de vértices del polígono y lo cierra.

Vertex & lowest_vertex(): retorna el vértice más bajo del polígono, es decir, el

vértice de menor coordenada y.

Page 42: Algunas estructuras de datos y algoritmos para

3.1. Clases de Base 36

Vertex & hightest_vertex(): retorna el vértice más alto del polígono, es decir,

el vértice de mayor coordenada y.

Dlist<Vertex *>sort_desc_vtx_list(): esta función retorna una lista de pun-

teros a los vértices del polígono, ordenada en forma descendente con respecto a la

coordenada y de cada vértice.

const bool in_cone(Vertex & a, Vertex & b): sea a un vértice del polígono.

Esta función nos permite saber si un segmento ab parte hacia el interior del polí-

gono o afuera de él.

Como sabemos los vértices del polígono siempre van a estar orientados en sentido

anti-horario después de que es cerrado, es muy importante que esto sea así para el

correcto funcionamiento de esta función.

La función in_cone se divide en dos casos, el primero es cuando el vértice a es

convexo y el segundo caso es cuando el vértice a es re�ejo. Entonces lo primero que

hay que saber es que tipo de vértice es a, esto se puede determinar usando la función

left_on.

Si el vértice anterior a el vértice a (a_prev) se encuentra a la izquierda o sobre la

línea formada por el vértice a y su vértice próximo (a_next), entonces el vértice a

es convexo de lo contrario es un vértice re�ejo.

Si el vértice a es convexo como el que se muestra en la Figura 3.15(a), podemos

determinar si el segmento parte hacia el interior del polígono usando la función left.

Se debe cumplir que el vértice a_prev este a la izquierda de ab y que a_next este

a la izquierda de ba. Ver Código 3.23.

Page 43: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 37

return ( left(a, b, a_prev) &&

left(b, a, a_next) );

Código 3.23 Veri�ca si el segmento parte hacia el interior del polígono para el caso

convexo.

Figura 3.15 (a)caso convexo (b)caso re�ejo

Estas condiciones mencionadas anteriormente no se cumplen si el vértice a es

re�ejo, como el que se puede apreciar en la Figura 3.15(b), pero podemos darnos

cuenta que el exterior del polígono en el vértice a es un cono como en el caso convexo.

De esta manera es más fácil solucionar el problema diciendo que ab parte hacia el

interior del polígono si y solo sí, ab no parte hacia el exterior. Ver Código 3.24.

return !( left_on(a, b, a_next) &&

left_on(b, a, a_prev) );

Código 3.24 Veri�ca si el segmento parte hacia el interior del polígono para el caso

re�ejo

3.2. El Problema de la Triangulación de Polígonos

Esta sección explicará cómo se implantaron los algoritmos para la triangulación

de polígonos a través de dos métodos, el método de triangulación por remoción de

Page 44: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 38

orejas y el método de triangulación basada en la partición monótona del polígono,

este último método consiste en dividir el polígono en polígonos monótonos y luego

cada uno de estos polígonos triangularlo usando un método de triangulación de

polígonos monótonos.

La triangulación de un polígono se realiza dividiéndolo en triángulos mediante

diagonales entre sus vértices. Una diagonal de un polígono es un segmento de línea

entre dos de sus vértices, que son claramente visibles el uno del otro.

Triangulación por Remoción de Asas

Antes de comenzar a explicar el algoritmo de triangulación es necesario poner en

claro dos funciones relacionadas con las diagonales de un polígono. Las diagonales

de un polígono están caracterizadas por dos condiciones: no se intersectan con lados

del polígono y se encuentran en el interior del polígono.

Primero se de�ne una función que garantiza la primera condición, es decir que el

segmento no tenga intersección con algún lado del polígono. La función es llamada

segment_is_candidate_diagonal y recorre todos los lados del polígono revisando

si hay intersección con alguno de ellos, exceptuando los lados que inciden en los

vértices del segmento que se está estudiando, se puede observar la implantación de

esta función en el Código 3.25.

Page 45: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 39

const bool segment_is_candidate_diagonal(const Vertex & a,

const Vertex & b)

{

Vertex & first_vertex = get_first_vertex();

Vertex & last_vertex = get_last_vertex();

Vertex & previous_to_last = get_prev_vertex(last_vertex);

for (Polygon::Iterator it(*this); it.has_current(); it.next())

{

Vertex & v_src = *it.get_current_vertex();

if (v_src == previous_to_last)

break;

Vertex & v_tgt = get_next_vertex(v_src);

/* No se chequean los lados que inciden en a y b */

if( (v_src != a) && (v_tgt != a) &&

(v_src != b) && (v_tgt != b) &&

intersectp(a ,b, v_src, v_tgt) )

{

return false;

}

}

/* chequea el ultimo segmento del poligono */

if( (last_vertex != a) && (first_vertex != a) &&

(last_vertex != b) && (first_vertex != b) &&

intersectp(a ,b, last_vertex, first_vertex) )

{

return false;

}

return true;

};

Código 3.25 Implantación de la función segment_is_candidate_diagonal

La función que nos permite saber si un segmento formado por dos vértices del po-

lígono es una diagonal se llama diagonal. Ver Código 3.26. Con la función anterior

garantizamos la primera condición, ahora solo queda saber si el segmento se encuen-

tra en el interior del polígono, para esto se usa la función in_cone explicada en la

Page 46: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 40

sección anterior. Entonces un segmento ab es diagonal del polígono si éste parte de

a hacia el interior del polígono, si parte de b hacia el interior del polígono y además

no hay intersección con algún lado del polígono que no incida sobre los vértices a y

b. Un ejemplo de la diagonal de un polígono se puede apreciar en la Figura 3.16.

Figura 3.16 Diagonal de un Polígono

const bool diagonal(Vertex & a, Vertex & b)

{

return (in_cone(a, b) &&

in_cone(b, a) &&

segment_is_candidate_diagonal(a, b));

}

Código 3.26 Implantación de la función diagonal

Este método de triangulación básicamente consiste en ir buscando asas (triángu-

los) formadas por las diagonales del polígono e ir removiendo estas asas del polígono

recursivamente hasta que ya no se encuentren más diagonales en el polígono.

Si tenemos tres vértices consecutivos del polígono (v1, v2, v3), v2 es un extremo

de asa si el segmento entre los vértices v1 y v3 puede ser una diagonal del polígono,

si es así v1, v2 y v3 formarían el asa E2 = (v1, v2, v3).

Inicialmente a todos los vértices del polígono se les asigna la propiedad de si son

o no extremos de asa, esto se hace en el atributo ear de la clase Vertex, si el vértice

es extremo de asa ear = true de lo contrario ear = false. Para hacer la inicialización

Page 47: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 41

de estos valores se utiliza una función que recorra todos los vértices del polígono y

en cada vértice tome su anterior y próximo vértice y veri�que que el segmento que

une estos dos vértices puede ser una diagonal del polígono, si es así que asigne true

al atributo ear del vértice actual de lo contrario que asigne false. Ver Código 3.27.

void ear_init()

{

for (Polygon::Iterator it(*this); it.has_current(); it.next())

{

Vertex & vertex = *it.get_current_vertex();

Vertex & next_v = get_next_vertex(vertex);

Vertex & prev_v = get_prev_vertex(vertex);

vertex.set_ear(diagonal(prev_v, next_v));

}

}

Código 3.27 Implantación de la función ear_init

Supongamos que v0, v1, v2, v3, v4 son cinco vértices consecutivos del polígono

y que v2 es un extremo de asa como se muestra en la Figura 3.17, entonces el asa

E2 = (v1, v2, v3) debe ser removida del polígono. El valor del atributo ear de los

vértices v1 y v3 debe ser actualizado después de haber sido removido el vértice v2

del polígono. En este caso el valor del atributo ear de v1 es actualizado usando

los vértices v0 y v3 ya que v2 fue removido, y el valor del atributo ear de v4 es

actualizado usando los vértices v1 y v4 (J. O'Rourke 1998).

Figura 3.17 Remoción del Asa de un polígono

Page 48: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 42

El algoritmo de triangulación del polígono por remoción de asas con un tiempo

de ejecución O(n2) sigue los siguientes pasos :

1 Inicializar la propiedad ear de cada vértice del polígono.

2 Mientras el número de vértices del polígono > 3 hacer:

2.1 Encontrar un vértice v2 donde ear = true

2.2 Generar el triángulo v1v2v3

2.3 Insertar el triángulo en la lista

2.4 Remover v2

2.5 Actualizar el valor de ear de los vértices v1 y v3.

3 Insertar en la lista el Polígono restante (triángulo)

En la Figura 3.18 se muestra un ejemplo de triangulación por remoción de asas de

un polígono de 9 vértices.

Figura 3.18 Triangulación por Remoción de Asas

Dlist<Polygon> triangulate ()

{

/* Hace una copia del polígono original */

Polygon polygon_copy = *this;

/* Crea la lista donde se almacenarán los triángulos */

Dlist<Polygon> polygons_list;

if(this->is_empty())

throw std::domain_error("Polygon is empty");

Page 49: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 43

if(!polygon_copy.is_closed())

throw std::domain_error("Polygon is not closed");

int n = polygon_copy.size();

polygon_copy.ear_init();

while(n > 3)

{

Polygon::Iterator it(polygon_copy);

while(it.has_current())

{

Vertex & v2 = *it.get_current_vertex();

it.next();

if(v2.is_ear())

{

Vertex & v3 = polygon_copy.get_next_vertex(v2);

Vertex & v4 = polygon_copy.get_next_vertex(v3);

Vertex & v1 = polygon_copy.get_prev_vertex(v2);

Vertex & v0 = polygon_copy.get_prev_vertex(v1);

/* Crea el triángulo generado */

Polygon * triangle = new Polygon();

triangle->append_vertex(v1);

triangle->append_vertex(v2);

triangle->append_vertex(v3);

triangle->close();

/* Inserta el triángulo en la lista */

Dlist<Polygon>::Node * node;

node = new Dlist<Polygon>::Node(*triangle);

polygons_list.insert(node);

/* Actualiza el valor ear de v1 y v3 */

v1.set_ear(polygon_copy.diagonal(v0, v3));

v3.set_ear(polygon_copy.diagonal(v1, v4));

v2.del(); /* elimina el vértice v2 */

--n;

break;

}

}

}

/* cuando al polígono le quedan tan solo tres vértices */

Dlist<Polygon>::Node * node;

node = new Dlist<Polygon>::Node(polygon_copy);

polygons_list.insert(node);

return polygons_list;

};

Código 3.28 Implantación del algoritmo de triangulación por remoción de asas

Page 50: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 44

Finalmente el algoritmo de triangulación por remoción de asas es mostrado en el

Código 3.28.

Partición del polígono en piezas monótonas

La monotoneidad es una propiedad de los polígonos que es de�nida con respecto

a una línea. Una cadena poligonal es una colección de vértices unidos entre si. Una

cadena poligonal C es estrictamente monótona con respecto a una línea L' si cada

línea L ortogonal a L' tiene intersección con C en no más de un punto (J. O'Rourke

1998). En la Figura 3.19(a) y 3.19(b) se muestra una cadena poligonal monótona

con respecto al eje Y y una cadena poligonal no monótona con repecto al eje Y.

Un polígono P es monótono con respecto a una línea L si puede ser dividido en

dos cadenas poligonales A y B tales que cada cadena sea monótona con respecto a

la línea L (J. O'Rourke 1998).

Figura 3.19 Cadena Poligonal (a) Monótona (b) No Monótona con respecto al eje Y

En este proyecto de�niremos la línea de monotoeidad como una línea vertical,

entonces una cadena poligonal C es monótona con respecto a una línea vertical si

toda línea horizontal tiene intersección con C como máximo en un punto.

El polígono de la Figura 3.20 es monótono con respecto a una línea vertical, ya que

las cadenas poligonales A = (v0, ... , v8), B = (v8, v9, ..., v14, v0) son monótonas.

Page 51: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 45

Figura 3.20 Polígono Monótono con Respecto al Eje Y

Para realizar la partición de un polígono en piezas monótonas es necesario primero

hacer una clasi�cación de los vértices del polígono en 5 tipos como se muestra en la

Figura 3.21:

Vértices de comienzo (start vertex): son los vértices cuyo ángulo interior es

menor a pi y ambos vértices adyacentes están por debajo de él. Ver Código

3.29.

Vértices de �n (end vertex): son los vértices cuyo ángulo interior es menor a

pi y ambos vértices adyacentes están por encima de él. Ver Código 3.30.

Vértices de división (split vertex): son los vértices cuyo ángulo interior es mayor

a pi y ambos vértices adyacentes están por debajo de él. Ver Código 3.31.

Vértices de unión (merge vertex): son los vértices cuyo ángulo interior es mayor

a pi y ambos vértices adyacentes están por encima de él. Ver Código 3.32.

Vértices de regulares (regular vertex): son todos los otros vértices que no cla-

si�can en ninguno de los anteriores. Ver Código 3.33.

Page 52: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 46

Figura 3.21 Clasi�cación de los Vértices de un Polígono

const bool start_vertex(Vertex * vertex)

{

Vertex * next_v, * prev_v;

next_v = &get_next_vertex(*vertex);

prev_v = &get_prev_vertex(*vertex);

if (vertex->get_y() == next_v->get_y() and

vertex->get_x() > next_v->get_x() and

vertex->get_y() > prev_v->get_y())

{

return true;

}

if (vertex->get_y() == prev_v->get_y() and

vertex->get_x() < prev_v->get_x() and

vertex->get_y() > next_v->get_y())

{

return true;

}

if (vertex->get_y() > prev_v->get_y() and

vertex->get_y() > next_v->get_y() and

left(*prev_v, *vertex, *next_v))

{

return true;

}

return false;

}

Código 3.29 Implantación de la función start_vertex

Page 53: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 47

const bool end_vertex(Vertex * vertex)

{

Vertex * next_v, * prev_v;

next_v = &get_next_vertex(*vertex);

prev_v = &get_prev_vertex(*vertex);

if (vertex->get_y() == next_v->get_y() and

vertex->get_x() < next_v->get_x() and

vertex->get_y() < prev_v->get_y())

{

return true;

}

if (vertex->get_y() == prev_v->get_y() and

vertex->get_x() > prev_v->get_x() and

vertex->get_y() < next_v->get_y())

{

return true;

}

if (vertex->get_y() < prev_v->get_y() and

vertex->get_y() < next_v->get_y() and

left(*prev_v, *vertex, *next_v))

{

return true;

}

return false;

}

Código 3.30 Implantación de la función end_vertex

Page 54: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 48

const bool split_vertex(Vertex * vertex)

{

Vertex * next_v, * prev_v;

next_v = &get_next_vertex(*vertex);

prev_v = &get_prev_vertex(*vertex);

if (vertex->get_y() == next_v->get_y() and

vertex->get_x() < next_v->get_x() and

vertex->get_y() > prev_v->get_y())

{

return true;

}

if (vertex->get_y() == prev_v->get_y() and

vertex->get_x() > prev_v->get_x() and

vertex->get_y() > next_v->get_y())

{

return true;

}

if (vertex->get_y() > prev_v->get_y() and

vertex->get_y() > next_v->get_y() and

left(*next_v, *vertex, *prev_v))

{

return true;

}

return false;

}

Código 3.31 Implantación de la función split_vertex

Page 55: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 49

const bool merge_vertex(Vertex * vertex)

{

Vertex * next_v, * prev_v;

next_v = &get_next_vertex(*vertex);

prev_v = &get_prev_vertex(*vertex);

if (vertex->get_y() == next_v->get_y() and

vertex->get_x() > next_v->get_x() and

vertex->get_y() < prev_v->get_y())

{

return true;

}

if (vertex->get_y() == prev_v->get_y() and

vertex->get_x() < prev_v->get_x() and

vertex->get_y() < next_v->get_y())

{

return true;

}

if (vertex->get_y() < prev_v->get_y() and

vertex->get_y() < next_v->get_y() and

left(*next_v, *vertex, *prev_v))

{

return true;

}

return false;

}

Código 3.32 Implantación de la función merge_vertex

Page 56: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 50

const bool regular_vertex(Vertex * vertex)

{

if (!start_vertex(vertex) and !end_vertex(vertex) and

!merge_vertex(vertex) and !split_vertex(vertex))

return true;

return false;

}

Código 3.33 Implantación de la función regular_vertex

El algoritmo de partición de polígonos en piezas monótonas posee un tiempo

de ejecución O(nlog(n)) y fue implantado en la función monotone_partition que

retorna una lista de segmentos que son las diagonales que dividen al polígono en

piezas monótonas. Se debe recorrer el polígono con una línea de barrido que va

desde el vértice más alto del polígono hasta el vértice más bajo.

Lo primero que hay que hacer es ordenar los vértices del polígono en forma descen-

dente usando la función sort_desc_vtx. Se crea una lista de estado que contendrá

los lados intersectados por la línea de barrido. Cada lado e tendrá un helper(e) que

será el vértice más bajo que se encuentre por encima de la línea de barrido visible

a la derecha del lado. El helper(e) podría ser el punto extremo más alto del lado e.

La estructura que se muestra en el Código 3.34 fue creada para que cada lado del

polígono que se inserta en la lista de estado tenga un helper asignado.

struct Elem_Sls

{

Segment * seg;

Vertex * helper;

bool operator < (const Elem_Sls & elem) const

}

Código 3.34 Implantación de la estructura Elem_Sls

Page 57: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 51

Dependiendo del tipo de vértice en el cual se encuentre la línea de barrido, se

realizarán distintas acciones. El lado ei es el que se encuentra inmediatamente al

vértice vi en sentido anti-horario.

Vértices de comienzo (start vertex):

insertar ei en la lista de estado.

asignar helper(ei) = vi.

Vértices de �n (end vertex):

buscar en la lista a ei−1 y si helper(ei−1) es un vértice de unión entonces

conectar vi a helper(ei−1).

remover ei−1 de la lista de estado.

Vértices de división (split vertex):

buscar en la lista de estado para encontrar el lado ej inmediatamente a la

izquierda del vértice vi.

conectar vi a helper(ej).

helper(ej) = vi.

Insertar a ei en la lista de estado.

asignar helper(ei) = vi.

Vértices de unión (merge vertex):

si helper(ei−1) es un vértice de unión, conectar vi a helper(ei−1).

remover ei−1 de la lista de estado.

buscar en la lista de estado el lado ej inmediatamente a la izquierda de de vi.

si helper(ej) es un vértice de unión entonces conectar vi a helper(ej).

Page 58: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 52

asignar helper(ej) = vi.

Vértices de regulares (regular vertex):

Si a la derecha de vi se encuentra el interior del polígono entonces:

si helper(ei−1) es un vértice de unión entonces conectar vi a helper(ei−1).

remover ei−1 de la lista de estado.

insertar ei a la lista de estado.

asignar helper(ei) = vi

De lo contrario si el interior del polígono está a la izquierda de vi:

buscar en la lista de estado el lado ej inmediatamente a la izquierda de de vi.

si helper(ej) es un vértice de unión entonces conectar vi a helper(ej).

asignar helper(ej) = vi.

En la Figura 3.22 se muestra la partición de un polígono en piezas monótonas.

Figura 3.22 Partición del Polígono en Piezas Monótonas

La estructura de dato usada para implantar la lista de estado fue un árbol

Treap_Rk de la biblioteca Aleph porque permite el acceso a cualquier nodo del

árbol en un tiempo esperado de O(log n), además posee iteradores para desplazarse

a la izquierda o derecha de cualquier nodo del árbol.

Page 59: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 53

La función que se utiliza para saber si el interior del polígono se encuentra a la

izquierda de un vértice regular se implantó con el nombre de interior_left_vertex.

Ver Código 3.35. Debido a que un polígono tiene sus vértices orientados en sentido

anti-horario podemos saber si el interior del polígono se encuentra a la izquierda de

un vértice regular v veri�cando que su vértice próximo tenga mayor coordenada y,

y su vértice anterior tenga menor coordenada y, es decir que su próximo vértice este

por encima de él y el anterior vértice por debajo.

const bool interior_left_vertex(Vertex * vertex)

{

if (!regular_vertex(vertex)){ return false; }

Vertex * next_v, * prev_v;

next_v = &get_next_vertex(*vertex);

prev_v = &get_prev_vertex(*vertex);

if (vertex->get_y() < next_v->get_y() and

vertex->get_y() > prev_v->get_y())

{

return true;

}

return false;

}

Código 3.35 Implantación de la función interior_left_vertex

Dado que esta función retorna una lista de diagonales se creó una función llamada

diagonals_to_polygons que se encarga de tomar estas diagonales y partir el polí-

gono original, para retornar la lista de sub-polígonos correspondientes a la lista de

diagonales.

Esta función va tomando una diagonal de la lista, parte el polígono en dos sub-

polígonos y va realizando esta acción de manera recursiva con cada sub-polígono,

cuando el sub-poligono es monótono se inserta en la lista de polígonos que �nalmente

será retornada.

Page 60: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 54

Triangulación de Polígonos Monótonos

La idea principal del algoritmo de triangulación de polígonos monótonos es muy

simple. Primero los vértices del polígono deben ser ordenados con respecto a la

línea de monotoneidad en este caso el eje Y (ordenar los vértices con respecto a su

coordenada Y).

Los vértices de un polígono monótono pueden ser ordenados respecto a su coor-

denada Y en tiempo lineal: se busca el vértice más alto y más bajo del polígono y

se divide en dos cadenas poligonales. Los vértices de cada cadena están ordenados

con respecto a la coordenada Y así que pueden ser unidas en tiempo lineal para

generar una lista de vértices ordenados con respecto a la coordenada Y. Después de

ordenar los vértices del polígono, los triángulos van siendo cortados desde el tope

del polígono.

El algoritmo de triangulación de polígonos monótonos fue implantado con el nom-

bre de monotone_triangulate y pertenece a los métodos públicos de la clase Polygon.

Este algoritmo consta de los siguientes pasos:

1 Ordenar los vértices por la linea de monotoneidad (Eje Y)

2 Inicializar una lista L con los 2 vértices más altos del polígono.

3 Asignar a v el tercer vértice mas alto del polígono.

4 Mientras v!= vértice mas bajo de la cadena hacer

4.1 Caso 1: Si v esta en la cadena opuesta del ultimo vértice de la

lista L

4.1.1 Crear un triángulo con v, el primer vértice y el segundo

vértice de la lista. (La diagonal del polígono es formada

por el segmento entre v y el segundo vértice de la lista).

4.1.2 Eliminar el primer elemento de la lista.

4.1.3 Si L tiene un solo elemento

4.1.3.1 Agregar v y avanzar v en la lista

4.2 Caso 2. v es adyacente al último vértice de L

4.2.1 Caso 2a. V+ es un ángulo estrictamente convexo

4.2.1.1 Crear un triángulo con v, el penúltimo vértice y

el último vértice de la lista. (La diagonal

del polígono es formada por el segmento entre v

y el penúltimo vértice de la lista)

4.2.1.2 Remover el ultimo vértice de la lista

4.2.1.3 Si L tiene un sólo elemento

Page 61: Algunas estructuras de datos y algoritmos para

3.2. El Problema de la Triangulación de Polígonos 55

4.2.1.3.1 Agregar v y avanzar v en la lista

4.2.2 Caso 2b. V+ es un ángulo reflejo o plano

4.2.2.1 Agregar v a la lista y avanzar v

Para la implantación de este algoritmo se utilizan las funciones hightest_vertex y

lowest_vertex, al momento de conseguir el vértice más alto y más bajo del polígono.

Los vértices en este caso no se ordenaron con respecto a la línea de monotoneidad,

lo que se hizo fue colocar dos iteradores en el vértice más alto del polígono, luego cada

uno va descendiendo por cada cadena poligonal hasta el vértice más bajo, llevando el

control del vértice al que le corresponde el turno y a qué cadena poligonal pertenece.

Se creó una estructura llamada Vertex_Element que posee un puntero a Vertex y

un dato de tipo char que indica a que cadena pertenece el vértice. Ver Código 3.36.

De esta manera se puede saber si un vértice está en la cadena opuesta o en la misma

cadena de otro vértice.

struct Vertex_Element

{

Vertex * ptr_vertex;

char chain;

};

Código 3.36 Estructura Vertex_Element

En la Figura 3.23 se muestra un ejemplo de la triangulación de un polígono monótono

con respecto al eje Y.

Page 62: Algunas estructuras de datos y algoritmos para

3.3. El Problema del Cascarón Convexo 56

Figura 3.23 Triangulación de un Polígono Monótono

3.3. El Problema del Cascarón Convexo

En esta sección se explicará como se dio solución al problema del cascarón con-

vexo usando el algoritmo de Graham. El algoritmo que genera el cascarón convexo

de un conjunto de puntos fue implantado en el archivo geom_funcs.H, es llamado

graham_convex_hull y tiene un tiempo de ejecución O(n log n).

Lo primero que hay que hacer es conseguir el punto x que se encuentre más abajo

y a la derecha en el conjunto de puntos y ordenar el resto de puntos por el ángulo en

sentido anti-horario que forman con la horizontal que pasa por x como se muestra en

la Figura 3.24. Lafunción que realiza esta acción se llama lowest_righmost_point y

su implantación se muestra en el Código 3.37.

Page 63: Algunas estructuras de datos y algoritmos para

3.3. El Problema del Cascarón Convexo 57

Figura 3.24 Cascarón Convexo de un Conjunto de Puntos

En la implantación el conjunto de puntos se guardan en un arreglo dinámico

(DynArray de la biblioteca Aleph). Después de encontrar el punto x, se coloca en la

posición cero del arreglo.

void lowest_righmost_point(DynArray<Point> & points_array, size_t tam)

{

int m = 0; /* Indice del punto mas bajo y a la derecha */

for(int i = 0; i < tam; i++)

{

if(points_array.access(i).get_y() < points_array.access(m).get_y() or

(points_array.access(i).get_y() == points_array.access(m).get_y() and

points_array.access(i).get_x() > points_array.access(m).get_x())

)

m=i;

}

/* Swap */

Point p = points_array[0];

points_array[0] = points_array[m];

points_array[m] = p;

}

Código 3.37 Implantación de la función lowest_righmost_point

Page 64: Algunas estructuras de datos y algoritmos para

3.3. El Problema del Cascarón Convexo 58

La estructura mostrada en el Código 3.38 es creada para asignar a cada punto el

valor del atributo del, que se utiliza para saber si el punto debe ser eliminado de los

candidatos a formar parte del cascarón convexo.

struct Point_Element

{

Point p;

bool del;

Point_Element() : del (false)

{

/* empty */

}

};

Código 3.38 Implantación de la función lowest_righmost_point

Para comparar los puntos de manera que puedan ser ordenados por ángulo con

respecto al punto x. Se creó una función llamada compare mostrada en el Código

3.39, esta función calcula el área formada por tres puntos para saber cual de dos

puntos dados tiene mayor ángulo con respecto a x.

Dados dos puntos a y b, si triangle_area(x, a, b) >0 entonces a tiene un ángulo

mayor que b, si por el contrario triangle_area(x, a, b) <0 entonces b tiene un ángulo

mayor que a. Si triangle_area(x, a, b) = 0 quiere decir que ambos puntos tienen el

mismo ángulo y se debe eliminar uno de los dos. Para escoger cual punto de estos

dos debe ser eliminado se calculan las distancias xa y xb el punto más cercano a x

se le asigna del=true, de esta manera se sabe que este punto no formará parte del

cascarón convexo, ni debe ser tomado en cuenta para calcularlo.

Page 65: Algunas estructuras de datos y algoritmos para

3.3. El Problema del Cascarón Convexo 59

int compare(Point_Element * elem_0, Point_Element * elem_i,

Point_Element * elem_j)

{

Geom_Number area = triangle_area (elem_0->p, elem_i->p, elem_j->p);

if(area > 0){ return -1; }

if(area < 0){ return 1; }

Geom_Number x = abs(elem_i->p.get_x() - elem_0->p.get_x()) -

abs(elem_j->p.get_x() - elem_0->p.get_x());

Geom_Number y = abs(elem_i->p.get_y() - elem_0->p.get_y()) -

abs(elem_j->p.get_y() - elem_0->p.get_y());

if( (x < 0) or (y < 0) )

{

elem_i->del = true;

return -1;

}

if( (x > 0) or (y > 0) )

{

elem_j->del = true;

return 1;

}

elem_i->del = true;

return 0;

}

Código 3.39 Implantación de la función compare

El algoritmo usado para ordenar los puntos en el arreglo fue quick_sort.

En el algoritmo de cascarón convexo es necesario también el uso de una estructura

de dato de tipo Pila (Stack), para satisfacer este requerimiento se utiliza la estructura

ListStack de la biblioteca Aleph, que posee los métodos básicos necesarios en este

algoritmo (push y pop).

Page 66: Algunas estructuras de datos y algoritmos para

3.3. El Problema del Cascarón Convexo 60

Los pasos a seguir en algoritmo de cascarón convexo son los siguientes:

1 Encontrar el punto x más bajo y a la derecha del conjunto.

2 Eliminar los puntos repetidos.

3 Ordenar los puntos por su ángulo formado con x.

4 Insertar en la pila los puntos de la posición cero y uno del arreglo.

5 Crear un contador k=2

6 Mientras (k < número de puntos en el arreglo) entonces

6.1 p2 = al punto tope de la pila.

6.2 p1 = al punto siguiente al puto tope de la pila.

6.3 Si pi está estrictamente a la izquierda de p1 p2 (left(p1,p2,pi))

entonces

6.3.1 insertar a pi en la pila

6.3.2 asignar k = k+1

6.4 De lo contrario Sacar el punto tope de la pila

7 Retornar un polígono donde sus vértices son los puntos que forman el

cascarón convexo.

En la Figura 3.25 se muestra un ejemplo del cascarón convexo de un conjunto de 13

puntos.

Figura 3.25 Cascarón Convexo de un Conjunto de Puntos

Finalmente la implantación del algoritmo que calcula el cascarón convexo de un

conjunto de puntos se puede observar en el Código 3.40.

Page 67: Algunas estructuras de datos y algoritmos para

3.3. El Problema del Cascarón Convexo 61

Polygon graham_convex_hull(DynArray<Point> & points_array, int tam)

{

lowest_righmost_point(points_array, tam);

DynArray<Point_Element> elems_array;

for(int i=0; i < tam; i++)

{

Point_Element * elem = new Point_Element;

elem->p = points_array[i];

elems_array[i] = *elem;

}

quick_sort(elems_array, 1, tam-1);

DynArray<Point> cleaned_array;

unsigned int i=0;

unsigned int j=0;

/* Si no esta marcado para ser borrado lo

copia en el nuevo arreglo */

while(i < tam)

{

if(!elems_array.access(i).del)

{

cleaned_array[j] = elems_array.access(i).p;

j++;

}

i++;

}

tam = j;

/* Inicializa la pila */

ListStack<Point> stack;

ListStack<Point>::Item* node;

node = new ListStack<Point>::Item;

node->get_data() = cleaned_array[0];

stack.push(node);

node = new ListStack<Point>::Item;

node->get_data() = cleaned_array[1];

stack.push(node);

Page 68: Algunas estructuras de datos y algoritmos para

3.3. El Problema del Cascarón Convexo 62

int k=2;

while(k < tam)

{

Point p1 = stack.top()->get_next()->get_data();

Point p2 = stack.top()->get_data();

if( left(p1, p2, cleaned_array[k]) )

{

node = new ListStack<Point>::Item;

node->get_data() = cleaned_array[k];

stack.push(node);

k++;

}

else { stack.pop(); }

}

/* Crea el poligono para agregar los vertices

que conforman la envolvente convexa */

Polygon poly;

while(!stack.is_empty())

{

poly.append_vertex(stack.pop()->get_data());

}

poly.close();

return poly;

}

Código 3.40 Implantación del algoritmo que calcula el cascarón convexo de un

conjunto de puntos.

Page 69: Algunas estructuras de datos y algoritmos para

Capítulo 4

Prototipo Grá�co y Pruebas

En este capítulo se explicará brevemente el funcionamiento del prototipo grá�co

utilizado para probar los algoritmos geométricos creados, además se mostrarán las

pruebas realizadas para varios casos sobre los algoritmos creados.

4.1. GTK (Gimp Tool Kit)

GTK (GIMP Toolkit) es una biblioteca para crear interfaces grá�cas de usuario.

Posee licencia LGPL, así que puedes desarrollar software abierto, software libre,

o aún software no libre comercial usando GTK sin tener que pagar nada por las

licencias o los derechos.

Es llamado GIMP toolkit porque fue originalmente escrito para desarrollar el

programa de manipulación de imágenes GIMP. GTK ha sido usado en un gran

número de proyectos, entre ellos el proyecto GNOME. GTK se construye encima

de GDK (GIMP Drawing Kit) que es básicamente una envoltura alrededor de las

funciones de bajo nivel, para tener acceso a las funciones subyacentes del windowing

(Xlib en el caso del sistema de las ventanas de X).

GTK está escrito totalmente en C, se implanta usando la idea de clases y funciones

callback (punteros a funciones).

GTK+ se basa en tres bibliotecas desarrolladas por el equipo de GTK+:

Glib: es la biblioteca de bajo nivel que forma la base de GTK+ y GNOME.

63

Page 70: Algunas estructuras de datos y algoritmos para

4.2. Prototipo Grá�co 64

Pango: es una biblioteca para la disposición y rendirizado del texto, con un

énfasis en la internacionalización. Forma la base del texto y de las fuentes

manejadas por GTK+-2.0.

ATK: es una biblioteca que proporciona un conjunto de interfaces para ac-

cesibilidad. Una aplicación puede ser manejada utilizando herramientas tales

como lectores de pantalla, y otros dispositivos de entrada alternativos.

GTK+ ha sido diseñado para soportar una gama de lenguajes, no sólo C/C++.

Usar GTK+ con lenguajes como Perl y Python (conjuntamente con el constructor

de GUI de Glade) proporciona un método e�caz de rápido desarrollo de aplicaciones.

4.2. Prototipo Grá�co

El prototipo grá�co fue desarrollado usando el conjunto de librerías grá�cas de

GTK (Gimp Tool Kit) versión 2.0, consta de dos lienzos que le permiten al usuario

dibujar polígonos y puntos para luego ejecutar sobre ellos los algoritmos creados.

Permite visualizar por el usuario las respuestas generadas por los algoritmos, es

decir la triangulación del polígono y el cascarón convexo de un conjunto de puntos.

Además posee una regla vertical y horizontal en escala de pixels, y un indicador

sobre estas reglas que señala la posición del cursor sobre el lienzo.

Para desplazarse a través de los lienzos se usan las solapas llamadas Triangulation

y Convex Hull, en cada una de estas solapas se encuentran los lienzos correspon-

dientes y los botones necesarios para ejecutar los algoritmos.

El lienzo de la solapa Triangulation fue diseñado para dibujar polígonos como

el que se muestra en la Figura 4.1. Usando el botón izquierdo del ratón se van

agregando los vértices del polígono, cuando ya no se desea agregar más vértices, el

polígono puede ser cerrado utilizando el botón derecho del ratón.

Page 71: Algunas estructuras de datos y algoritmos para

4.2. Prototipo Grá�co 65

Figura 4.1 Prototipo Grá�co: Polígono dibujado en el lienzo.

Si al intentar insertar un vértice o cerrar el polígono se genera un segmento que

ocasione cruce con otro segmento del polígono, no se realizará la acción y se emitirá

un mensaje de error que indique que el segmento ocasiona cruce. Ver Figura 4.2.

Figura 4.2 Prototipo Grá�co: Mensaje emitido cuando hay cruce.

Después de cerrar el polígono se puede ejecutar cualquiera de las tres acciones

que se encuentran en los botones de la solapa Triangulation. (Triangulation O(n2),

Monotone Partitioning y Monotone Triangulation).

Page 72: Algunas estructuras de datos y algoritmos para

4.2. Prototipo Grá�co 66

El botón Triangulation O(n2) genera la triangulación basada en remoción de asas

del polígono que se encuentra dibujado en el lienzo. Ver Figura 4.3.

Figura 4.3 Prototipo Grá�co: Triangulación por remoción de asas.

El botón Monotone Partitioning crea la partición monótona del polígono que se

encuentra dibujado en el lienzo, esta partición es necesaria para la triangulación

monótona del polígono. Ver Figura 4.4.

Figura 4.4 Prototipo Grá�co: Partición monótona del polígono.

El botón Monotone Triangulation genera la triangulación del polígono basada en

su partición monótona. Primero genera la partición monótona del polígono y luego a

Page 73: Algunas estructuras de datos y algoritmos para

4.2. Prototipo Grá�co 67

cada polígono resultante le aplica la triangulación de piezas monótonas para obtener

una triangulación total del polígono. Nótese que las tres diagonales de la partición

monótona de la Figura 4.4 coinciden con tres de las diagonales de la triangulación

basada en la partición monótona de la Figura 4.5.

Figura 4.5 Prototipo Grá�co: Triangulación basada en partición monótona.

Además de estos botones también se encuentran en esta solapa dos botones lla-

mados Clear y Exit. El botón Clear limpia todo lo que se haya dibujado en el lienzo.

El botón Exit cierra la aplicación.

El lienzo de la solapa Convex Hull fue diseñado para dibujar puntos usando el

botón izquierdo del ratón. Ver Figura 4.6. Sobre estos puntos se pueden realizar

varias acciones.

Page 74: Algunas estructuras de datos y algoritmos para

4.2. Prototipo Grá�co 68

Figura 4.6 Prototipo Grá�co: Conjunto de puntos dibujados sobre el lienzo.

El botón Graham Convex Hull genera el cascarón convexo del conjunto de puntos

que ha sido dibujado en el lienzo. Ver Figura 4.7.

Figura 4.7 Prototipo Grá�co: Cascarón convexo.

El botón Triangulate Convex Hull O(n lg n) genera la triangulación basada en

partición monótona del polígono devuelto por el algoritmo de cascarón convexo. Ver

Figura 4.8.

Page 75: Algunas estructuras de datos y algoritmos para

4.2. Prototipo Grá�co 69

Figura 4.8 Prototipo Grá�co: Triangulación monotona del cascarón convexo.

El botón Triangulate Convex Hull O(n2) genera la triangulación por remoción de

asas del polígono devuelto por el algoritmo de cascarón convexo. Ver Figura 4.9.

Figura 4.9 Prototipo Grá�co: Triangulación por remoción de asas del cascarón

convexo.

Al igual que la solapa Triangulation, la solapa Convex Hull posee los botones

Clear que limpia el lienzo y Exit que cierra la aplicación.

Page 76: Algunas estructuras de datos y algoritmos para

4.3. Pruebas 70

El prototipo grá�co posee una menu llamado File donde se encuentran las opciones

Save File As... y Open File... que le permiten al usuario guardar lo que haya dibujado

en ambos lienzos ya sea un polígono, un conjunto de puntos o ambos, y luego volver

a abrirlo a partir del archivo en el que fue guardado. Ver Figura 4.10.

Figura 4.10 Prototipo Grá�co: Abrir archivo

4.3. Pruebas

Las pruebas sobre los algoritmos creados fueron realizadas en un computador con

las siguientes especi�caciones:

Notebook Toshiba Satellite A64

Procesador Intel Celeron 2.8 GHz

Memoria Ram 256 MB

Disco Duro 80GB

Sistema Operativo:

Linux Debian Etch

kernel 2.6.17

Para la compilación de la aplicación se uso gcc 4.0.3.

La Tabla 4.1 muestra las pruebas realizadas para el algoritmo de triangulación

basada en partición monótona, sobre polígonos con diferentes números de vértices.

Page 77: Algunas estructuras de datos y algoritmos para

4.3. Pruebas 71

En esta tabla se observa que la función diagonals_to_polygons posee un tiempo

de ejecución alto en comparación con las otras dos funciones y esto hace que el

tiempo total del algoritmo de triangulación sea elevado.

Tabla 4.1 Tiempo de ejecución de las funciones principales que conforman el

algoritmo de triangulación basada en partición monótona, para polígonos de

diferentes números de vértices

Número de

Vértices

Partición Mo-

nótona

Diagonales a

Polígonos

Triangulación

Monótona

Total

100 0.00123398 0.0306639 0.000780906 0.0340329

500 0.018092 0.905937 0.0031061 0.928551

900 0.044945 7.74107 0.0070801 7.7945

1400 0.051056 51.8561 0.024931 51.9335

1500 0.0511261 64.8825 0.00810804 64.9431

En la Tabla 4.2 se aprecian los tiempos para distintos números de vértices del

algoritmo de triangulación por remoción de asas. Se observa que los tiempos son

menores con respecto a la triangulación basada en partición monótona. Esto se debe

al problema explicado anteriormente con la función diagonals_to_polygons, que

como ya mencionamos, es la función que se encarga de generar la lista de polígonos

monótonos a partir de la lista de diagonales.

Tabla 4.2 Tiempo de ejecución del algoritmo de triangulación por remoción de

asa, para polígonos de diferentes números de vértices

Número de

Vértices

Triangulación por

remoción de asas

100 0.0163741

500 0.38862

900 1.23724

1400 2.92539

1500 3.3654

Page 78: Algunas estructuras de datos y algoritmos para

4.3. Pruebas 72

Para demostrar que el rendimiento del algoritmo basado en partición monótona

puede llegar a ser mejor que el del algoritmo de triangulación por remoción de asas

(si no existiera el problema planteado), se realizó un conjunto de pruebas de ambos

algoritmos sobre polígonos monótonos, de esta manera se estaría evitando en parte

el problema planteado, ya que si el polígono es monótono no se generan particiones

del polígono.

En la Tabla 4.3 se observa como a medida que la cantidad de vértices comienza a

aumentar se empieza a marcar la diferencia de tiempos entre los dos algoritmos, a

favor del algoritmo de triangulación basada en partición monótona, esto demuestra

que en realidad el problema de tiempo de ejecución que presenta el algortimo de

triangulación basado en partición monótona radica en el alto tiempo de ejecución

que posee la función polygons_to_diagonals.

Tabla 4.3 Comparación de tiempos de ejecución de los algoritmos de triangulación

por remoción de asas y triangulación basada en partición monótona, para

polígonos monótonos de diferentes números de vértices

Número de

Vértices

Triangulación por

remoción de asas

Triangulación ba-

sada en partición

monótona

100 0.013053 0.0127179

150 0.029959 0.0141459

250 0.094634 0.042932

1000 1.1605 0.579466

Las pruebas para el algoritmo de cascarón convexo para polígonos con diferentes

números de vértices arrojaron los resultados que se muestran en la Tabla 4.4.

Page 79: Algunas estructuras de datos y algoritmos para

4.3. Pruebas 73

Tabla 4.4 Tiempo de ejecución del algoritmo de cascarón convexo, para diferentes

cantidades de puntos

Número de

Puntos

Cascarón Convexo

1000 0.0944351

5000 0.621639

10000 1.35227

Los tiempos arrojados por el algoritmo de cascarón convexo representan los re-

sultados esperados. Se observa como a medida que aumenta el número de vértices

aumenta el tiempo de ejecución pero en una proporción relativamente aceptable.

Page 80: Algunas estructuras de datos y algoritmos para

Capítulo 5

Conclusiones y Recomendaciones

5.1. Conclusiones

Después de la realización de este proyecto se llega a las siguientes conclusiones:

Los objetivos planteados en la realización de este proyecto fueron cumplidos

en su totalidad, esto se demuestra con el prototipo grá�co creado para este

propósito.

Las estructuras de datos de la biblioteca Aleph (árboles, listas, pilas, etc.)

fueron de gran utilidad en la implantación de los algoritmos desarrollados en

este proyecto asi como también en la creación de las clases básicas necesarias.

La elaboración de este proyecto sienta las bases necesarias para el inicio del

estudio de la geometría computacional en la Escuela de Ingeniería de Sistemas

de la Universidad de Los Andes.

La elaboración de este proyecto también contribuye con el inicio de la creación

de una extensión en el área de geometría computacional en la biblioteca Aleph

La biblioteca grá�ca GTK proporcionó todas las herramientas necesarias para

la elaboración del prototipo grá�co, por esta razón se considera que fue una

buena elección para este propósito.

En las pruebas de desempeño los algoritmos de triangulación por remoción de

asas y de cascarón convexo respondieron en tiempos considerablemente buenos,

el algoritmo de triangulación basado en partición monótona del polígono posee

74

Page 81: Algunas estructuras de datos y algoritmos para

5.2. Recomendaciones 75

un retardo ocasionado por la función diagonals_to_polygons que debe ser

corregido para un mejor desempeño.

5.2. Recomendaciones

Al �nalizar este proyecto se cree conveniente emitir las siguientes recomendaciones:

Realizar un estudio sobre Geom_Number para determinar cual sería el tipo

de dato que más se adapte a los requerimientos de un número geométrico.

Estudiar de manera detallada la función diagonals_to_polygons para encon-

trar una función óptima que elimine el retardo ocasionado por esta función en

el algoritmo de triangulación basada en la partición monótona del polígono.

Page 82: Algunas estructuras de datos y algoritmos para

Bibliografía

Joseph O'Rourke. (1998) Computational Geometry in C (Second Edition).

Portal Aleph. http://150.185.182.136. Portal de la Biblioteca de Progra-

mación ALEPH. Consulta: Enero 2006, Julio 2006, Enero 2007.

http://www.gtk.org. Sitio Web O�cial del proyecto GTK (GTK+ - The

GIMP Toolkit), Consulta: Abril 2006.

http://wwwdi.ujaen.es/�jmserrano/teaching/geometriacomputacional/pdfs/

transpa4.pdf. Sitio web del Departamento de Informatica de la Universidad

de Jaén, Consulta: Noviembre 2006.

http://echostar.org/corner/quicksort.pdf Personal Page of Ryan Ma-

cKenzie, Consulta: Diciembre 2006.

http://www.me.cmu.edu/faculty1/shimada/cg97/bader/index.html. The

Department of Mechanical Engineering at Carnegie Mellon University.

Consulta: Noviembre 2006.

http://www.eecs.harvard.edu/�gotsman/compgeom/lectures/Chapter3.pdf.

Electrical Engineering and Computer Science at Harvard University. Consul-

ta: Noviembre 2006.

http://en.wikipedia.org/wiki/Computational_geometry. Enciclopédia Li-

76

Page 83: Algunas estructuras de datos y algoritmos para

Bibliografía 77

bre (The free encyclopedia). Consulta: Enero 2007.

http://pt.wikipedia.org/wiki/Geometria_computacional. Enciclopédia

Libre (A enciclopédia livre). Consulta: Enero 2007.

http://www.lawebdelprogramador.com/diccionario/. Diccionario Infor-

mático. Consulta: Enero 2007.