Você está na página 1de 18

INFORME PROYECTO FINAL

PAINT ME A TRIANGLE

ALEJANDRO CAMACHO (1201800)

INTRODUCCIÓN A LA COMPUTACIÓN GRÁFICA

TUTOR:
WILSON SARMIENTO

UNIVERSIDAD MILITAR NUEVA GRANADA

INGENIERÍA MULTIMEDIA

BOGOTÁ D.C.

29-Nov-2017
1. Introducción

Como muestra del progreso realizado a lo largo del semestre, así como de los múltiples
conocimientos adquiridos en el área de la computación gráfica, al menos en lo que se podría
considerar su parte “básica” se propuso realizar un proyecto final en código que implementara en
lo posible los temas más relevantes vistos durante el curso si no es por decir todos. A manera de
resumen corto, la propuesta solicitaba un programa en el cual, al hacer click en la pantalla 3 veces
se pudiera pintar un triángulo definido por esos 3 puntos. El triángulo debía poder ser cambiado de
color y adicionalmente debía poder ser rellenado con una imagen externa. Del mismo modo, una
vez tenido el triángulo, haciendo click 2 veces más dentro del mismo (por fuera no debía pasar nada)
se debía de poder pintar una línea entre esos dos puntos, cuyo punto medio no solo debía ser
resaltado, sino que, además, se le debían calcular sus perfiles de color tanto en el modelo RGB como
en el HSV.

Paint Me a Triangle surgió entonces como la respuesta a esta propuesta comprendiendo dentro de
sí la implementación y visualización de múltiples tópicos de la computación grafica con el fin de
poder llegar a dar solución al resultado final que se buscaba. Cumpliendo el objetivo de demostrar
lo que se había aprendido a lo largo del curso, Paint Me a Triangle cubrió en forma los siguientes
temas de la materia dentro de su estructuración: Manejo de puertos de vista y coordenadas de
puerto de vista/mundo, el algoritmo de Bressenham para graficación de líneas rectas, los algoritmos
de relleno tipo scan line o por línea de rastreo, los algoritmos para pruebas dentro fuera, los
algoritmos para lectura de imágenes, los algoritmos de conversión de modelos de color, los
algoritmos de lectura de color para un pixel especifico y finalmente pero no menos importante los
algoritmos de manejo de estructuras de datos. Este último, cumpliendo el único fin de organizar la
información gráfica pertinente (aunque bien se sabe que este aspecto no hace parte como tal del
área de la computación gráfica, es importante aclarar que sin él no hubiera sido posible realizar el
programa.

Por consiguiente, en el presente informe se procederá a dar registro, así como explicar y relatar
que planteamientos, procedimientos y dificultades se llegaron a presentar a la hora de hacer la
esquematización y posteriormente la implementación del programa Paint Me a Triangle tal que
este cumpliese los requisitos que solicitaba la propuesta de proyecto final y sirviera como solución
a la misma.
2. Metodología, contratiempos e implementación

2.1. Primeros pasos: implementación de funciones de manejo de datos

Primeramente, se sabía de antemano la necesidad de utilizar estructuras de datos para


poder “ir y venir” en el código de una forma óptima, rápida y sencilla. Por lo que, antes que
nada, la construcción de estructuras de datos, así como sus herramientas facilitadoras se
hicieron presentes. Aspectos como generación de funciones que enlazaran nodos a
diferentes cabezas de listas según se necesitaran, así como funciones para eliminación total
de nodos no se hicieron esperar. En especial esta última debido a la gran cantidad de
información que se tendría que estar actualizando constantemente en la ejecución del
programa y por lo tanto era pertinente deshacerse de la información anterior para así poder
utilizar información nueva. Una vez realizado este proceso, aparecería la primera solicitud
de la propuesta respecto al como: pintar un triángulo al hacer click 3 veces en la pantalla.

Para dar solución a esto, y aplicando el primer concepto teórico de computación grafica en
el programa, se procedido a construir e implementar el algoritmo de cambio de
coordenadas de puerto de vista a ventana. Se planteó la necesidad de este algoritmo ya que
su funcionalidad permite tomar una coordenada (x, y) cualquiera dentro del puerto de vista
(tenga este el tamaño que tenga y este donde este) y transformarla de tal forma que toda
la pantalla o ventana haga parte del mismo puerto de vista mas solo se grafiquen cosas
dentro de este. Esta lógica permite al usuario dar click en cualquier parte de la pantalla y
que estos entren directamente al puerto de vista dando en algunos casos la sensación de
que se está graficando “por detrás” (ver Figura 1.). pero siempre trabajando en el puerto
de vista como tal dando la escala correcta de posición de los clicks en la pantalla.

Figura No.1 Triangulo graficado con clicks que han pasado por transformación de coordenadas de puerto de vista a
ventana. Los dos primeros clicks han sido dados dentro del puerto de vista (zona negra) mientras que el tercero ha sido
dado por fuera de este, lo cual es leído mas no graficado, dando la sensación de que el triángulo se graficó por “detrás”
Esto permitió leer los clicks directamente sobre la pantalla sin restricción alguna de
ubicación o tamaño del puerto de vista más allá de que solo los clicks que se hicieran por
dentro del puerto serian graficados (aunque los que se hicieran por fuera si existirían como
tal mas no se verían). Por lo que al hacer cada click, estos serían guardados en la primera
lista estructurada que se estaba manejando: la lista de vértices. Guardando así el número
de clicks con un máximo de 3 (por los vértices de un triángulo) y las coordenadas (x, y) de
cada click transformadas a la escala del puerto de vista actual para así trabajar las
graficaciones posteriores dentro del mismo.

2.2. Manos a la obra: Implementación del algoritmo de Línea de rastreo y Bressenham

Haciendo uso del algoritmo de salvado de vértices ya mencionado y guardando las


coordenadas (x, y) de los mismos (los 3 primeros clicks que diera el usuario) en la primera
cabeza de lista, se procedió a realizar la esquematización del cómo, usando estos 3 vértices,
se podría pintar o rellenar un triángulo de una forma que tanto para el usuario fuese sencillo
de manejar como que funcionara de forma óptima para la máquina. Dos algoritmos propios
de la computación grafica fueron la solución esta vez. El primero se trataba del algoritmo
de relleno por línea de rastreo, el cual comprende la utilización de una línea horizontal
“imaginaria” que atraviese una figura geométrica cerrada tal que se pinte solo la parte de
esta línea de rastreo que se encuentra por dentro de la figura desde el vértice de más arriba
de esta hasta el vértice que se encuentra más abajo. El segundo algoritmo era nada más y
nada menos que el famoso algoritmo de Bressenham para trazado de líneas rectas, pilar
fundamental de la computación gráfica, pues permite a través del análisis tanto de la
pendiente de la recta como de su punto medio y un análisis de diferencias trazar líneas
rectas básicamente en cualquier dirección que se desee de una forma bastante optima
trabajando solo con aumentos constantes y algebra entera. Sin embargo, Aquella
implementación fue algo más complicada que la descripción que se cuenta, pues se necesitó
combinar los dos algoritmos mencionados (el de relleno por línea de rastreo y el de
Bressenham) para que se pudiera realizar el relleno correctamente.

Comenzando por el principio, el algoritmo de línea de rastreo conllevó un proceso


estructurado de 5 partes. La primera comprendía el agregar los 3 vértices del triángulo a
una “lista de vértices” con el fin de poder trabajar con esta información. Aunque es cierto
que ya estos puntos se encontraban en una primera lista de vértices menciona
anteriormente, se decidió hacer una nueva para que la lista de clicks no se entrelazara o
confundiera con la lista de graficación como tal. Acto seguido, utilizando un algoritmo de
organización de datos por selección se procedió a ordenar de menor a mayor tanto las
coordenadas en “y” de los vértices como en “x” con el fin de poder saber desde donde
empezaría esta línea imaginaria y hasta donde iría tanto de arriba hacia abajo como de
izquierda a derecha.
La tercera parte de esta implementación, conllevaba reposicionar los vértices ingresados de
tal forma que su ubicación correspondiera con su orden de ascendencia numérica en el eje
“y” (es decir, entre más pequeño su valor en “y” más arriba quedaría el vértice, cambiando
con el que estuviese más arriba en ese momento). Esto aseguraría que la línea de rastreo se
moviese siempre del menor valor al mayor y de arriba hacia abajo. la cuarta parte,
correspondía el conocer, ya con los vértices organizados, que aristas imaginarias estarían
chocándose con la línea de rastreo si esta recorría el triángulo de arriba hacia abajo. Aquello
se lograba aplicando la lógica sencilla de que, si la coordenada actual en “y” de la línea de
rastreo imaginaria se encontraba entre las coordenadas de dos vértices cualesquiera del
triángulo, entonces la línea estaría atravesando la arista “imaginaria” que unía a los dos
vértices y por lo tanto esa arista se encontraría activa. Esta información también se
guardaba en una lista de aristas activas para su posterior uso.

Finalmente, la quinta parte del proceso y probablemente la más complicada de todas,


comprendía el hallar los intersectos en “x” de estas aristas activas imaginarias para así poder
saber qué puntos de la línea de rastreo estarían dentro del triángulo y por tanto serian
coloreados. En esta quinta parte es entonces donde se usó la aplicación cuasi-directa del
algoritmo de Bressenham, el cual, aunque su función principal es descrita por su nombre
completo: el algoritmo de Bressenham para trazado de líneas rectas. Realmente, puede
servir para muchas cosas más allá de solo pintar puntos que conformen líneas rectas. Se
pensó que si en esencia, un triángulo estaba compuesto por vértices que al unirse con líneas
rectas (las aristas) formarían esta geometría cerrada visualmente hablando, entonces debía
existir alguna forma de hallar, conociendo la coordenada en “y” de la arista, que coordenada
en “x” le correspondía. La forma se descifro trazando teóricamente la línea recta que unía
a los dos vértices que conformaban la arista activa con Bressenham (es decir solo
numéricamente, sin graficación alguna como tal) y preguntante para cada valor en “y” de la
recta teórica si este correspondía con el valor buscado (que sería la posición actual en “y”
donde la línea de rastreo se encontraría para estar atravesando la arista activa). Una vez
encontrada la coincidencia, utilizando los aumentos constantes que utiliza el algoritmo de
Bressenham hallados por medio de un análisis en papel y lápiz de punto medio según la
pendiente que tenga la recta, se calculaba el valor entero de la coordenada actual en “x”
que hacia pareja con el valor en “y”, y se procedía a guardar este valor. La misma acción era
repetida para todas las aristas que se consideran activas, es decir que la línea de rastreo las
estuviera “atravesando”, en todos los puntos “y” de arriba hacia abajo.

Esta implementación del algoritmo de Bressenham dentro del algoritmo de rastreo de línea
que se diseñó permitiría entonces conocer los valores enteros exactos de cruce en “x” de la
línea de rastreo con las diferentes aristas (trabajando estas últimas como si fuesen líneas
graficadas con el algoritmo de Bressenham). Tal que luego de tener tales valores, estos se
organizaban de menor a mayor para luego proceder a pintar todos los puntos de la línea de
rastreo que compartieran la coordenada en “y” actual de esta de arriba hacia abajo y las
coordenadas en “x” de cruce desde la menor (la más hacia la izquierda) hasta la mayor (la
más hacia la derecha).
Finalmente, esta lógica se corroboro graficando ahora si netamente con el algoritmo de
Bressenham para trazado de líneas rectas, el cual se construyó previamente durante el
curso de introducción a la computación gráfica por medio de análisis de punto medio
realizados a papel y lápiz. Tal que, con los resultados visibles que este algoritmo proveía se
graficaron las líneas que unían a los diferentes vértices del triángulo en otro color, bajo la
hipótesis de que, si el planteamiento realizado en el algoritmo de línea de rastreo era
correcto, estas líneas debían superponerse a la perfección con el relleno que se encontraba
por debajo. Lo cual, si sucedió, (ver figura No.2)

Figura No.2 Triangulo coloreado con el algoritmo de relleno por línea de rastreo con líneas rectas uniendo sus vértices
graficadas encima y con Bressenham. La forma de corroborar que la lógica había sido hecha correctamente fue que al
graficar líneas rectas con el algoritmo de Bressenham que uniesen a los vértices estas debían de encajar perfecto con el
relleno que yacía por debajo el cual había usado ese mismo algoritmo para calcular los cortes en “x”

2.3. Primer contratiempo ¿Qué hacer cuando nos falta una función?
Implementación de función de lectura del color de un pixel

Bien pudo ser por ignorancia propia o por algún fallo en el llamado de la función (más la
primera que la segunda) pero la función que en un principio se pensaba utilizar para la
lectura del color de un pixel en específico dentro de la pantalla no presentó los resultados
esperados. Bien era una función necesaria para poder hallar los valores de color del punto
medio de la línea que se pretendía pintar dentro del triángulo como lo solicitaba la
propuesta, bien era una función que proveía por defecto OpenGl (por lo que técnicamente
no debía presentar fallos), pero sin embargo los presento. A manera de resumen, ReadPixels
(el nombre de la función), solo calculaba para todos los pixeles en la pantalla un color
unánime siendo este el del fondo que se le había asignado por defecto al iniciar el programa
más ningún otro color. Por lo que surgió la necesidad de generar una función “propia” que
permitiese obtener correctamente el color actual del pixel que se le solicitara si se deseaba
continuar con el proyecto.
Se pensó entonces que al diseño que se había utilizado en el algoritmo de relleno por línea
de rastreo se le podía implementar un factor adicional que no cambiaría en nada su función
de rellenar triángulos por medio de líneas rectas, más si colaboraría con el problema del
cómo saber el color de un pixel. Se consideró que realmente tal color de píxel solo se
necesitaba conocer si este se encontraba dentro del triángulo, pues por fuera realmente no
se graficaría nada (la línea por fuera del triángulo no sería leída) por lo que afirmo que si el
algoritmo de relleno por línea de rastreo permitía pintar cada pixel del triángulo (es decir
dentro de este) con un color en particular (el que se le asignara) fuese este uno general para
todos (monocromático) o uno específico por pixel (como en el caso de la imagen o en un
caso de degrade de colores) entonces debía poderse guardar justo antes de ser pintado el
pixel, tanto su color en el modelo RGB así como su posición (x,y) en coordenadas de puerto
de vista.

Aquello concluyo en la generación de una nueva lista enlazada la cual guardaría toda la
información correspondiente de que pixeles en la pantalla (y cuantos) eran los que
conformaban al triangulo y que color tenia cada uno en sus valores R, G & B. Como
complemento de lo anterior, se optó por una lista enlazada en vez de por ejemplo una
matriz de datos pues se consideró que al no saber exactamente cuántos pixeles tendría el
triángulo a pintarse ( depende de los clicks, puede ser grande o pequeño) entonces la lista
enlazada generaría un nodo por pixel consumiendo exactamente la cantidad de memoria
que se necesitara para solo los pixeles del triángulo (por fuera, nuevamente, no era
relevante saber el color) haciendo optimo el algoritmo más allá de por ejemplo haber
declarado una matriz muy grande a la cual bien le podrían sobrar o faltar espacios de
información .

Con la lista terminada, el problema de la lectura del color del punto medio de la línea recta
se resumía a comparar la posiciona actual en x & y de ese punto con las posiciones de los
pixeles que conformaban al triangulo dentro de la lista de “colores” y buscar la respectiva
correspondencia. Lo que permitía conocer sin problema alguno los perfiles en RGB y HSV de
ese pixel en particular. Obviamente, en el momento en el que el usuario deseara trabajar
con otro triangulo de dimensiones y colores diferentes se hacían los respectivos llamados a
las funciones de liberación espacio de memoria, borrando toda la lista de colores y dejando
campo para nuevos triángulos con colores diferentes en posiciones diferentes.

2.4. ¿Qué es dentro y que es fuera?


Implementación de una línea dentro de un triangulo

Para la siguiente parte de la construcción del código: la lectura de la línea recta dentro del
triángulo, se “copio” la lógica de listas que se había diseñado previamente para la lectura
de los vértices del triángulo (de hacer click 3 veces y guardar los 3 puntos). Salvo que esta
vez, dos factores harían un valor agregado adicional a ese diseño. Número uno, la limitante
esta vez fue de 2 vértices (para una línea) y no 3 (para un triángulo) y número dos, se incluyó
la necesidad y presencia de un algoritmo que permitiese saber si aquellos clicks se
encontraban por dentro o por fuera del triángulo. Es decir, un algoritmo de pruebas dentro
fuera.

Se consideró que la lógica de las pruebas dentro fuera tipo par/impar sería la más adecuada
de trabajar debido a la facilidad de información que el algoritmo de relleno por línea de
rastreo proveía con sus múltiples listas (información de color por pixel, información de los
cruces con el eje x para una posición particular en y, información de los vértices en orden
de arriba abajo y de izquierda a derecha, etc.). Pues con toda esta información a la mano
resultaba sencillo hacer comparaciones y contarlas para deducir sencillamente si el número
de estas era un numero par o impar.

Básicamente, se esquematizo la idea de que al hacer click en cualquier punto (fuera dentro
o fuera del triángulo), se tomarían las coordenadas en (x, y) de este punto y se recorrería
horizontalmente desde allí hasta la coordenada en “x” del vértice más externo, contando la
cantidad de cruces teóricos en “x” que este recorrido presentaría al toparse con las aristas
del triángulo para una coordenada “y” constante. Donde, si el número de cruces
comprendía un numero par, entonces significaría que la línea había entrado al triangulo y
luego salido de este, por lo que el punto se encontraría fuera del mismo y por lo tanto no
se pintaba. Mientras que, si el número de cruces era impar, significaba que la línea había
entrado, pero no había salido, y por lo tanto el punto se encontraría dentro del triángulo y
por tanto se pintaría. (existía adicionalmente el caso donde el número de cortes fuese cero
y significaba que sencillamente el punto jamás toco al triangulo, es decir estaba por fuera y
además lejos de este).

Aplicando esta lógica se logró configurar adecuadamente el trazado de una línea por medio
de clicks (dos clicks y luego un bloqueo) que estuvieran por dentro del triángulo para luego
poder, haciendo uso de la ecuación paramétrica, tomar al punto inicial como el primer click
y respectivamente al punto final como el segundo click, y calcularles su punto medio. Donde
tales coordenadas, eran automáticamente guardadas y comparadas en la lista de colores
para conocer el perfil del color de ese punto. Graficando posteriormente una línea recta con
Bressenham que uniera los clicks y resaltando el punto medio en cuestión.

2.5. Segundo contratiempo, Cambios de color: Modelo RGB a modelo HSV

Aunque bien viene siendo cierto que OpenGl, en su forma básica, trabaja con el modelo de
color RGB y por tanto obtener y trabajar con esta información es relativamente “Sencillo”,
el hecho de que la propuesta solicitara un perfil en el modelo de color HSV complicaba un
poco las cosas. Sin embargo, y tal vez de forma obvia, la utilización del algoritmo de
conversión de modelo RGB a HSV se presentaba como la solución al problema. Pero no todo
podía ser tan fácil, pues se encontró un aspecto particular en este algoritmo que, aunque
se logró corregir, inicialmente nunca se tuvo en cuenta y por lo tanto generaba errores. El
algoritmo de conversión de RGB a HSV funciona, en su forma más básica, tomando el
máximo de los valores entre rojo, azul y verde y calculando con estos por medio de algunas
conversiones matemáticas específicas, las equivalencias en valor (V) y saturación (S) entre
0 y 1 (igual que funciona la escala en RGB). Hasta allí todo bien. La parte particular venia
cuando se trataba de calcular el tono actual (H) del color, pues este ya no funcionaba de 0
a 1 como la saturación (S) o el valor (V) sino que funcionaba en una escala de grados de 0 a
360.

Figura No.3 Modelo de color HSV. Nótese como el Hue (tono) arranca en
rojo para 0 grados y da la vuelta completa hacia la izquierda volviendo a
llegar a rojo luego de haber pasado el ultimo color siendo este magenta.
Figura tomada de:
https://upload.wikimedia.org/wikipedia/commons/thumb/6/6c/Cono
_de_la_coloración_HSV.png/250px-Cono_de_la_coloración_HSV.png

El problema radicaba en que, en el momento en que, para un tono rojo máximo, el valor en
azul fuese mayor que el valor en verde, matemáticamente se interpretaba como que el
circulo de colores había logrado dar una vuelta completa y, por tanto, los grados se
reiniciaban a arrancar desde 0 nuevamente. Sin embargo, en su comportamiento teórico
esto no sucedía, pues la vuelta completa se realiza al completar el ciclo de color desde rojo
hasta nuevamente rojo (según el modelo HSV, ver Figura No.3) por lo que, para esta
condición particular, la solución que se pensó fue la de restarle el excedente en grados para
así mantener la equivalencia teórica. Cabe aclarar que, aunque se investigó sobre este
algoritmo con anterioridad para poder aplicarlo en el proyecto presente, tal particularidad
jamás se mencionó por ningún lado, por lo que adicional a la implementación del algoritmo,
el ajuste de ese desperfecto fue bastante por cuenta propia por medio de varias pruebas de
escritorio y comprobándolo con algoritmos reales en páginas de conversión de modelos de
color. (la gracia no era copiar algo ya hecho sino diseñar uno propio y entender lo que se
estaba haciendo).

2.6. El formato PNM: una espada de doble filo.


Implementación de la imagen dentro del triangulo

Para la graficación de la imagen dentro del triángulo se trabajó con un formato particular
de imágenes poco usual pero bastante útil si se buscan resultados rápidos mas no
necesariamente óptimos, las imágenes tipo PNM o Portable AnyMap.
La espada de doble filo que es el formato PNM el cual softwares libres de graficación en 2D
como GIMP pueden ofrecer en encriptado tipo ASCII (no en formato crudo que es diferente)
se encarga de guardar toda la información del color RGB en la escala de 0 a 255 por pixel en
un archivo de texto que podrá ser leído fácilmente por una máquina. Sin embargo, el hecho
de que contengan absolutamente toda la información de todos los pixeles de una imagen
cualquiera hacen de este tipo de archivos unos bastante pesados de leer si se está
trabajando con imágenes grandes (que poseen muchos más pixeles) por, evidentemente, la
cantidad de información que tienen. Del mismo modo, la organización de esta información
en RGB pixel por pixel se vuelve mucho más complicada de separar si se tiene en cuenta que
esta va de largo y sin interrupciones (ver Figura No.4).

Figura No.4 Imagen usada en el programa Paint Me a Triangle en formato PNM. La imagen fue transformada a este
formato usando el software libre GIMP. Nótese como posee un header de identificación (P3#) y como los primeros datos
numéricos corresponden al tamaño de la imagen (para el caso 600 x 600) para luego ser seguido por los valores de color de
0 a 255 de cada pixel. Nótese lo pequeña que es la barra de desplazamiento lateral, pues se trata de mucha información.

Sin embargo, el uso de estas imágenes se implementó gracias las librerías de lectura de las
mismas que el Dr. Wilson Sarmiento facilitó para poder realizar el programa con éxito (al
cual se le da el respectivo crédito y autoría de las mismas. Es decir que toda la parte de
lectura de la imagen en formato PNM, así como su posterior guardado y obtención del color
NO es implementación propia sino utilizada con previa autorización del autor y para uso
educativo solamente). Aclarado esto, se prosigue con la explicación.

Aunque ya se recalcó que estas librerías de lectura y sus funciones no son de autoría propia
sino externas, si se logró comprender su funcionamiento básico, pues la estrategia que el
Dr. Sarmiento ideó para la lectura de la información de la imagen era una bastante
coherente y lógica realmente. En palabras simples, se recorría toda la imagen en ancho y
largo y se iban guardando todos los datos del formato de texto en una matriz triple cuyas
primeras dos componentes referenciarían las pociones (x, y) del pixel de la imagen más su
tercera componente referenciaría el color del mismo, contando esta con 3 sub espacios para
así guardar los valores en RGB en la escala de 0 a 255 de ese pixel en específico. Resulta
curioso que, si esta lógica se compara con la lógica utilizada en Paint Me a Triangle, ese si
de autoría propia, para conocer los colores de un pixel particular dentro del triángulo, la
idea es casi la misma. Recorrer dentro de unos límites definidos una serie de píxeles y para
cada uno ir guardando su pareja ordenada de posición, así como su color. Una lógica muy
útil y funcional, cabe resaltar, para que al final solo sea indicarle al algoritmo de relleno que
valores de color deberá usar y en que posiciones los deberá usar.

Sin embargo, como no podría faltar, en este punto de la lectura y graficación de la imagen
se llegó a presentar un último contratiempo adicional que generaba que, aunque se tuviese
ya leída la imagen, esta no pudiese ser graficada en la pantalla. Y es que incluso con la
información de los colores de la imagen por pixel almacenada en una matriz de fácil acceso,
se debía tener en cuenta que, estos pixeles correspondían como tal a los pixeles de la
imagen y no a los del triángulo. Una afirmación obvia, pero de vital importancia a la hora de
graficar la imagen, pues la información de los colores de los pixeles de la misma se pasaba
de forma directa (es decir en escala uno a uno) el algoritmo de relleno por línea de rastreo
presentaría errores al quedarse sin información a graficar si el triángulo resultaba ser más
grande que la imagen. Por lo que para solucionar este inconveniente se planteó y luego
procedió a realizar una transformación de coordenadas.

La utilización del algoritmo de transformación de coordenadas de ventana a puerto de vista


se mostró como el más útil para este caso. Se pensó que, si se tomaban los puntos más
extremos tanto en “x” como en “y” de los vértices del triángulo en forma de rectángulo, se
podría entonces definir una bounding box que rodeara al triangulo perfectamente (Ver
Figura No 5). Haciendo de esta bounding box que contenía al triangulo por dentro la
ventana, entonces se podían (por medio de la transformación de coordenadas) conocer a
que puntos de la imagen correspondían los puntos del triángulo funcionando la imagen
como puerto de vista. Bajo esta lógica, se podía conocer que pixel del triángulo
correspondería con que pixel de la imagen y por tanto tomar los colores del pixel de la
imagen para rellenar con el algoritmo de relleno por línea de rastreo el triángulo como tal.

Figura No.5 ejemplificación de la lógica de cambio de coordenadas de ventana a puerto de vista para graficar la imagen
dentro del triángulo. Si se toman los vértices más externos del triángulo y con él se arma un “rectángulo” o bounding box,
entonces partiendo de ese como ventana se podrán realizar transformaciones para saber qué punto del triángulo
corresponde a que pixel de la imagen siendo el puerto vista y así conocer el color correcto a graficar.
2.7. Detalles adicionales: Funciones de Reset e interfaz gráfica de colores

Finalmente, para darle un toque más interactivo al usuario, se implementaron funciones


propias de “Reset” de las figuras, tanto de las líneas trazadas dentro de los triángulos, como
del triángulo completo como tal. Las cuales se encargaban de borrar toda la información
existente en colores y posiciones de los triángulos o líneas actuales, así como desbloquear
la restricción de graficar más puntos luego de bien haber graficado el triángulo haciendo
click 3 veces o graficado la línea haciendo click 2 veces luego de tener el triángulo.

Asimismo, utilizando el algoritmo de Bressenham para trazado de líneas rectas se graficaron


múltiples rectángulos de color en la parte superior de la ventana del programa los cuales se
ajustaron de tal forma que, al hacer click sobre estos, el usuario notara como se cambiaba
el color del triángulo dinámicamente. Esto se logró, sencillamente pasando el nuevo
parámetro del color al algoritmo de relleno por línea de rastreo y volviéndolo a ejecutar
(obviamente realizando primero el respectivo borrado de la información de color actual del
mismo). Entre estos colores se encontraban colores monocromáticos como el verde o el
rojo, así como degrades de colores, los cuales eran útiles para probar que la lectura de color
funcionaba no solo para la imagen sino para el color “puro” como tal (pues en el color
monocromático todas las lecturas daban el mismo valor y no resultaba útil para probar si el
algoritmo de lectura de color funcionaba). Estos degrades se trabajaron por medio del
algoritmo de degrade de color que usa la ecuación paramétrica para pasar de un color a
otro dado un parámetro (en este caso, el tamaño neto del triángulo en “Y”).

3. Resultados:

Los resultados obtenidos luego de las implementaciones de las soluciones previamente


explicadas para poder cumplir con los requisitos solicitados en la propuesta se muestran
en las siguientes imágenes:
Figura No.1 Interfaz de Paint Me a Triangle sin modificaciones

Figura No.2 Triangulo pintado en degrade luego de haber hecho click 3 veces. Se resalta la parte
derecha de la interfaz donde aún no se modifican los valores pues solo tomaran al punto medio de
la línea dentro del triángulo.
Figura No.3 Triangulo pintado en degrade con línea dentro de él. Se resalta la parte derecha de la
interfaz donde se pueden ver los valores RGB, así como HSV del punto medio entre los dos puntos
de la línea que se tomaron con dos clicks.

Figura No.4 Triangulo pintado con imagen y línea dentro de él. Los valores RGB y HSV del punto
medio corresponden al brillo azul que se encuentra en la cara en la imagen
Figura No.5 Triangulo pintado con imagen y línea dentro de él. Los valores RGB y HSV del punto
medio corresponden al brillo semi rosado del casco en la imagen

Figura No.6 Triangulo pintado en degrade con línea horizontal dentro de él.
Figura No.7 Triangulo pintado con un solo color con línea dentro de él.

Figura No.8 Triangulo pintado en degradé con línea sin completar dentro de él. Se resalta como
el punto rojo al encontrarse por fuera del triángulo no es leído para realizar la línea y por tanto
esta aún no se gráfica.
4. Conclusiones:

Del proyecto final realizado junto con todo lo que conllevó esquematizarlo, implementarlo
y testearlo para verlo funcionado se pueden resaltar las siguientes conclusiones al
respecto:

El algoritmo de Bressenham para trazado de líneas rectas tiene una funcionalidad


mucho más amplia que la de solo trazar líneas rectas. Puede llegar a hacer una
combinación muy potente y casi complementar la parte más complicada del
algoritmo de relleno por línea de rastreo al detectar de forma óptima y en números
enteros (al menos para polígonos, es decir figuras que se conformen por líneas
rectas) los puntos de corte en x de las aristas activas que la línea de rastreo ubicada
en una coordenada “y” especifica este cruzando. De otra forma habría que detectar
estos puntos usando la ecuación de la línea recta lo cual no sería optimo, daría
resultados flotantes y por tanto inconveniencias en la graficación final como figuras
con relleno incompleto o puntos por fuera de los limites. Asimismo, La
corroboración de que esta lógica funciona puede probarse graficando líneas con el
mismo algoritmo de Bressenham tal que estas unan los vértices del polígono, el
resultado será que deben superponerse a la perfección.

Una forma bastante útil de poder saber el color de un pixel particular en un área
definida dentro de la pantalla (como lo es por ejemplo los colores de una imagen o
el color actual de un pixel dentro de un triángulo coloreado para el caso) en
ausencia de una función que realice este trabajo, será el recorrer las dimensiones
del área en una lista o matriz triple, la cual guarde en sus primer y segundo campo
las coordenadas x & y del pixel y en su tercer campo los valores del color del pixel
en cuestión (donde sí se está trabajando en el modelo RGB, que usualmente es así,
este tercer campo deberá poseer 3 subcampos mas). Esta lógica puede resultar
bastante optima si el área trabajada es bastante más pequeña en relación con la
pantalla (como lo era el caso de los triángulos) pues no se necesitará guardar los
colores de toda la pantalla sino solo de esa área pequeña en cuestión.
Adicionalmente si se hace uso de una lista enlazada, se podrá utilizar exactamente
la cantidad de memoria que se necesite en proporcionalidad directa con el número
de pixeles de la figura, lo cual es bastante óptimo. Cabe aclarar que este método es
consistente únicamente si al dejar de utilizarlo se libera la memoria utilizada para
guardar la información de posición y color de los pixeles.

Las imágenes en formato PNM se presentan como una espada de doble filo a la hora
de trabajar con imágenes de una forma dígase así sencillas en comparación con el
uso de otros formatos. Por el lado positivo, si su formateado de datos se encuentra
en ASCII entonces se podrá acceder casi que de forma libre y directa a la
información del color en RGB para cada pixel sin ningún tipo de conversión adicional
ni proceso externo más allá de organizar la información recibida. Por lo que
básicamente, al utilizar softwares que trabajen con este tipo de imágenes como
GIMP es posible convertir cualquier imagen que se desee a este formato y cargar su
información de color para luego graficarla. Sin embargo, por el lado negativo, el
proceso de organizar la información es complicado de realizar por la forma en que
esta esta presentada (toda de largo por lo que es difícil distinguir entre el fin de un
pixel y el comienzo de otro) sin contar que a mayor tamaño tenga la imagen, mayor
información se trabajará y por tanto resultara volviéndose un proceso poco optimo
en el que la maquina deberá cargar cantidades absurdas de información para
imágenes grandes y por tanto se arriesga a quedarse colgada. Adicionalmente, si su
formateo de datos no se realiza en ASCII sino en crudo entonces no se podrá
trabajar de la forma que se hizo en este proyecto.

Un algoritmo de relleno por línea de rastreo correctamente implementado puede


llegar a ser la mayor fuente de información que se tenga a la hora de graficar
cualquier figura en pantalla, lo que lo convierte en un algoritmo muy eficiente y útil.
Al utilizar la lógica de recorrer con líneas rectas la figura y pintar solo lo que se
encuentre por dentro permitirá la recolección de información tal como: ¿qué está
dentro y que esta fuera de la figura?, ¿qué dimensiones tiene la figura?, ¿cuáles son
los bordes de la figura?, ¿cuántos pixeles tiene la figura? y finalmente ¿qué color
posee cada uno? Esto permitirá tener un manejo muy amplio y flexible para poder
jugar con diferentes cosas y optimizar los procesos de graficación. El que tiene la
información, también tiene el control.

Cuando se grafica una imagen dentro de un área específica en la pantalla, se debe


tener en cuenta siempre el realizar una transformación de coordenadas para escalar
y ajustar el tamaño y posición de los pixeles de la imagen tal que conserven la
proporción dentro del área específica. Sin la transformación (es decir si se procede
a graficar 1 a 1) se pueden llegar a presentar errores tales como que el área de
graficación se quede sin información en algunos pixeles puesto que la imagen es
más pequeña que el área. Por lo que es necesario establecer límites (bounding box)
y en base a estos realizar las transformaciones adecuadas.

Você também pode gostar