algunas estructuras de datos y algoritmos para
TRANSCRIPT
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
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.
Dedicado a mis padres, mis hermanos y a todas aquellas
personas que hicieron posible terminar este largo camino.
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.
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
Contenido vi
5. Conclusiones y Recomendaciones 74
5.1. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.2. Recomendaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Bibliografía 76
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
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.
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
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.
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
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
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.
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
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.
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
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
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.
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.
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.
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.
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í,
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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
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
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
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");
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
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.
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.
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
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
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
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
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
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).
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.
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.
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
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.
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.
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
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.
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).
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.
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);
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.
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
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.
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).
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
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.
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.
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.
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.
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
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.
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.
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
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.
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
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.