You are on page 1of 225

Bismarck Salvador Traña López__________________________________________UNI

Introducción a la Programación grafica en C.


Para la mejor comprensión de este tutor hemos realizado un análisis de las Funciones que
se suelen utilizar con mucha frecuencia al programar en el modo grafico de C, dando como
resultado la librería conio.h, dos.h, stdlib.h, y evidentemente graphics.h.

En este pequeño tutor solo haremos énfasis en estas librerías, puesto que al final del libro
se encuentran las demás librerías que comúnmente encontramos con frecuencia en el
programa.

Muchos libros nos marean dándonos conceptos los cuales se aprenden conforme
programando; esto no significa, que no sean importan tantees los ejercicios que aquí te
enseñamos te ayudaran a darte todo el conocimiento así como también te los daremos
conforme avancemos así que dejemos de hablar y aventurémonos en el mundo de la
programación grafica.

Lo primero que debemos saber al realizar una grafica son las dimensiones del ordenador
(Monitor) en modo texto las dimensiones de este es 25X80 esto dependiendo de la versión
de video instalada. En el modo grafico estas dimensiones cambian dependiendo del tipo de
macro de inicialización se realice(ver Macro modos). Quedando la mas común de 640X480
modo VGA.

A la hora de desarrollar un programa gráfico se debe tener en cuenta los siguientes cuatro
puntos:

1. Especificar el fichero o librería graphics.h

# include<graphics.h>

2. Inicializar el modo grafico correspondiente mediante la Función initgraph;

Initgraph(&adaptador,&modo,”dirección de los archivos bgi”);

3. Crear y manipular las figuras graficas.

/*Desarrollo del programa*/

4. Restaurar el modo de video antes de salir del programa(restaurar el modo texto)

closegraph() o restorecrtmode()

Juan Carlos Gutiérrez Barquero____________________________________________ 1


Bismarck Salvador Traña López__________________________________________UNI

Otra de las cosas importantes que debemos saber es que Funciones contiene graphics.h

Funciones de Graphics.h
Esta librería se encuentra los prototipos de las Funciones que manipulan la parte gráfica
en el entorno de MS-DOS.

Arc getmaxy putimage


bar getmodename putpixel
bar3d getmoderange rectangle
circle getpalette registerbgidriver
cleardevice getpalettesize registerbgifont
clearviewport getpixel restorecrtmode
closegraph gettextsettings sector
detectgraph getviewsettings setactivepage
drawpoly getx setallpalette
ellipse gety setaspectratio
fillellipse graphdefaults setbkcolor
fillpoly grapherrormsg setfillpattern
floodfill graphfreemem setfillstyle
getarccoords graphgetmem setgraphbufsize
getaspectratio graphresult setgraphmode
getbkcolor imagesize setlinestyle
getcolor initgraph setpalette
getdefaultpalette installuserdriver setrgbpalette
getdrivername installuserfont settextjustify
getfillpattern line settextstyle
getfillsettings linerel setusercharsize
getgraphmode lineto setviewport
getimage moverel setvisualpage
getlinesettings moveto setwritemode
getmaxcolor outtext textheight
getmaxmode outtextxy textwidth
getmaxx pieslice

Es necesario conocer hacerca de los macros y estructuras, entre las estructuras


tenemos:

arccoordstype linesettingstype textsettingstype


fillsettingstype palettetype viewporttype

Entre los macros se encuentran:

colores errores modos


drivers fuentes put_op
enlazar línea trama

Antes de comenzar a programar en modo grafico debemos estudiar lo que son los
macros, que son instrucciones que nos ayudaran a realizar de una manera más efectiva
nuestros gráficos.

Juan Carlos Gutiérrez Barquero____________________________________________ 2


Bismarck Salvador Traña López__________________________________________UNI

Colores :

Colores de Fondo

Constante Valor Significado


BLACK 0 Negro
BLUE 1 Azul
GREEN 2 Verde
CYAN 3 Cían
RED 4 Rojo
MAGENTA 5 Magenta
BROWN 6 Marrón
LIGHTGRAY 7 Gris Claro
DARKGRAY 8 Gris Oscuro
LIGHTBLUE 9 Azul Claro
LIGHTGREEN 10 Verde Claro
LIGHTCYAN 11 Cían Claro
LIGHTRED 12 Rojo Claro
LIGHTMAGENTA 13 Magenta Claro
YELLOW 14 Amarillo
WHITE 15 Blanco

Modo de 16 Colores

Constante Valor Significado


BLACK 0 Negro
BLUE 1 Azul
GREEN 2 Verde
CYAN 3 Cían
RED 4 Rojo
MAGENTA 5 Magenta
BROWN 6 Marrón
LIGHTGRAY 7 Gris Claro
DARKGRAY 8 Gris Oscuro
LIGHTBLUE 9 Azul Claro
LIGHTGREEN 10 Verde Claro
LIGHTCYAN 11 Cían Claro
LIGHTRED 12 Rojo Claro
LIGHTMAGENTA 13 Magenta Claro
YELLOW 14 Amarillo
WHITE 15 Blanco

Juan Carlos Gutiérrez Barquero____________________________________________ 3


Bismarck Salvador Traña López__________________________________________UNI

Color del modo CGA

Número Paleta Color 1 Significado Color 2 Significado Color 3 Significado

0 CGA_LIGHT Verde Claro CGA_LIGHTRED Rojo Claro CGA_YELLOW Amarillo


GREEN
1 CGA_LIGHT Cían Claro CGA_LIGHTMAGENTA Magenta Claro CGA_WHITE Blanco
CYAN
2 CGA_GREE Verde CGA_RED Rojo CGA_BROWN Marrón
N
3 CGA_CYAN Cían CGA_MAGENTA Magenta CGA_LIGHTGRAY Gris Claro

Valor asignado: 1 2 3

Nota: Color 0 se reserva para el color de fondo y se asigna con lo función setbkcolor, pero
los demás colores son fijos. Estas constantes se usan con setcolor.

Colores para las paletas.

Constante (CGA) Valor Constante (EGA/VGA) Valor


BLACK 0 EGA_BLACK 0
BLUE 1 EGA_BLUE 1
GREEN 2 EGA_GREEEN 2
CYAN 3 EGA_CYAN 3
RED 4 EGA_RED 4
MAGENTA 5 EGA_MAGENTA 5
BROWN 6 EGA_LIGHTGRAY 7
LIGHTGRAY 7 EGA_BROWN 20
DARKGRAY 8 EGA_DARKGRAY 56
LIGHTBLUE 9 EGA_LIGHTBLUE 57
LIGHTGREEN 10 EGA_LIGHTGREEN 58
LIGHTCYAN 11 EGA_LIGHTCYAN 59
LIGHTRED 12 EGA_LIGHTRED 60
LIGHTMAGENTA 13 EGA_LIGHTMAGENTA 61
YELLOW 14 EGA_YELLOW 62
WHITE 15 EGA_WHITE 63

Nota: Estas constantes se usan con las Funciones setpalette y setallpalette.el cual
veremos en capítulos posteriores

Juan Carlos Gutiérrez Barquero____________________________________________ 4


Bismarck Salvador Traña López__________________________________________UNI

Fuentes:
Fuentes para Texto

Constante Valor Valor


DEFAULT_FONT 0 Fuente bitmap de 8x8
TRIPLEX_FONT 1 Fuente escalable de tipo triple
SMALL_FONT 2 Fuente escalable pequeña
SANS_SERIF_FONT 3 Fuente escalable de tipo sans serif
GOTHIC_FONT 4 Fuente escalable de tipo gótico
SCRIPT_FONT 5 Fuente escalable de tipo manuscrito
SIMPLEX_FONT 6 Fuente escalable de tipo manuscrito simple
TRIPLEX_SCR_FONT 7 Fuente escalable de tipo manuscrito triple
COMPLEX_FONT 8 Fuente escalable de tipo complejo
EUROPEAN_FONT 9 Fuente escalable de tipo europeo
BOLD_FONT 10 Fuente escalable en negrita

Dirección del Texto

Constante Valor Significado


HORIZ_DIR 0 Texto horizontal

VERT_DIR 1 Texto vertical

Justificación de Texto en la Horizontal

Constante Valor Significado


LEFT_TEXT 0 Justificar a la izquierda
CENTER_TEXT 1 Centrar el texto
RIGHT_TEXT 2 Justificar a la derecha

Justificación de Texto en la Vertical

Constante Valor Significado


BOTTOM_TEXT 0 Justificar debajo
CENTER_TEXT 1 Centrar el texto

TOP_TEXT 2 Justificar arriba

Juan Carlos Gutiérrez Barquero____________________________________________ 5


Bismarck Salvador Traña López__________________________________________UNI

Tramas:
Tramas predefinidas

Constante Valor Significado


EMPTY_FILL 0 Rellena con el color de fondo
SOLID_FILL 1 Rellena enteramente
LINE_FILL 2 Rellena con líneas horizontales: ---
LTSLASH_FILL 3 Rellena con rayas finas: ///
SLASH_FILL 4 Rellena con rayas gruesas: ///
BKSLASH_FILL 5 Rellena con rayas inversas y finas: \\\
LTBKSLASH_FILL 6 Rellena con rayas inversas y gruesas: \\\
HATCH_FILL 7 Rellena con líneas cruzadas cuadriculadamente: +++
XHATCH_FILL 8 Rellena con líneas cruzadas diagonalmente: XXXX
INTERLEAVE_FILL 9 Rellena con líneas entrelazadas
WIDE_DOT_FILL 10 Rellena con lunares bastante distanciados
CLOSE_DOT_FILL 11 Rellena con lunares poco distanciados
USER_FILL 12 Rellena con la trama definida por el usuario

Nota: Todos los tipos de tramas menos EMPTY_FILL usan el color de relleno
seleccionado; EMPTY_FILL usa el color de fondo para rellenar.

Driver:
Dispositivos Gráficos

Dispositivo/Constante Valor
DETECT 0
CGA 1
MCGA 2
EGA 3
EGA64 4
EGAMONO 5
IBM8514 6
HERCMONO 7
ATT400 8
VGA 9
PC3270 10
Líneas:
Estilos de Líneas

Constante Valor Significado


SOLID_LINE 0 Línea continua _______
DOTTED_LINE 1 Línea hecha con puntos ……..
CENTER_LINE 2 Línea centrada –––––––––––
DASHED_LINE 3 Línea discontinua _._._._.
USERBIT_LINE 4 Línea definida por el usuario

Nota: El grosor es definido escribiendo NORM_WIDTH para rallas normales y


THICK_WIDTH para líneas mas gruesos

Juan Carlos Gutiérrez Barquero____________________________________________ 6


Bismarck Salvador Traña López__________________________________________UNI

Grosores para Líneas Modos de Escritura

Constante Grosor Valor Significado Constantes Valor Significado


COPY_PUT 0 Píxeles de la línea sobrescriben
los píxeles existentes
NORM_THICK 1 Grosor es de 1 píxel
XOR_PUT 1 Píxel de la pantalla son el
Resulta do de la operación
THICK_WIDTH 3 Grosor es de 3 OR de los píxeles existentes
píxeles y los de la línea

Modos:
Modos Gráficos

Dispositivo Modo/Constante Código Resolución Paleta Pagina


CGA CGAC0 0 320X200 4 Clores 1
CGAC1 1 320X200 4 Clores 1
CGAC2 2 320X200 4 Clores 1
CGAC3 3 320X200 4 Clores 1
CGAHI 4 640X200 2 Clores 1
MCGA MCGAC0 0 320X200 4 Clores 1
MCGAC1 1 320X200 4 Clores 1
MCGAC2 2 320X200 4 Clores 1
MCGAC3 3 320X200 4 Clores 1
MCGAMED 4 640X200 2 Clores 1
MCGAHI 5 640X480 2 Clores 1
EGA EGALO 0 640X200 16 Colores 4
EGAHI 1 640x350 16 Colores 2
EGA64 A64LO 0 640X200 16 Colores 1
EGA64HI 1 640X350 4 Colores 1
EGAMONO AMONOHI 3 640x200 2 Colores 1* / 2**
VGA VGALO 0 640X200 16 Colores 2
VGAMED 1 640x350 16 Colores 2
VGAHI 2 640X480 16 Colores 1
ATT400 ATT400C0 0 320x200 4 Colores 1
ATT400C1 1 320x200 4 Colores 1
ATT400C2 2 320x200 4 Colores 1
ATT400C3 3 320x200 4 Colores 1
ATT400MED 4 640x400 2 Colores 1
ATT400HI 5 640x400 2 Colores 1
HERC HERCMONOHI 0 720X348 2 Colores 2
PC3270HI 0 720X350 2 Colores 1
IBM8514LO 0 640X480 256 Colores
IBM8514HI 1 1024X768 256 Colores

Si la tarjeta es de 64 K o la tarjeta es de 256 K

Juan Carlos Gutiérrez Barquero____________________________________________ 7


Bismarck Salvador Traña López__________________________________________UNI

Errores:

Códigos de Errores

Constante Código Significado


grOk 0 Ningún error
grNoInitGraph -1 Gráficos no iniciados
grNotDetected -2 Ningún adaptador gráfico detectado
grFileNotFound -3 Fichero de dispositivo no encontrado
grInvalidDriver -4 Fichero de dispositivo no válido
grNoLoadMem -5 No hay memoria para cargar dispositivo
grNoScanMem -6 No hay memoria para rellenar
grNoFloodMem -7 No hay memoria para usar floodfill
grFontNotFound -8 Fichero de fuente no encontrado
grNoFontMem -9 No hay memoria para cargar la fuente
grInvalidMode -10 Modo gráfico no válido
grError -11 Error gráfico
grIOerror -12 Error gráfico de Entrada/Salida
grInvalidFont -13 Fichero de fuente no válido
grInvalidFontNum -14 Número de fuente no válido
grInvalidDeviceNum -15 Número de dispositivo no válido
grInvalidVersion -18 Número de versión no válido

Put_op:
Operaciones con putimage

Constante Valor Significado


COPY_PUT 0 Sobrescribir los píxeles existentes
XOR_PUT 1 Operación OR Exclusivo con los píxeles
OR_PUT 2 Operación OR Inclusivo con los píxeles
AND_PUT 3 Operación AND con los píxels
NOT_PUT 4 Invertir la imagen

Nota: Estas operaciones se usan exclusivamente con la función putimage. El cual


veremos en capítulos posteriores.

Juan Carlos Gutiérrez Barquero____________________________________________ 8


Bismarck Salvador Traña López__________________________________________UNI

Métodos de Inicialización del modo grafico.


Antes de conocer los métodos de inicialización debemos conocer la función initgraph la cual
nos permitirá realizar gráficos.

Función initgraph

void far initgraph(int far *driver, int far *modo, int far *path);

Esta función es usada para cargar o validar un dispositivo gráfico y cambiar el sistema de
vídeo a modo gráfico. La función initgraph debe ser llamada antes de cualesquier
Funciónes que generan una salida gráfica sean usadas. Existen varios valores a ser
usados para el argumento *driver. Si *driver es asignado a DETECT, ó 0, la
función detectgraph es llamada, y un dispositivo y modo gráfico apropiados son
seleccionados. Asignando a *driver cualquier otro valor predefinido inicia la carga del
dispositivo gráfico correspondiente. Existen varios valores a ser usados para el argumento
*modo. Estos valores deberían corresponder al dispositivo especificado en el argumento
*driver. El argumento *path especificad el directorio donde los dispositivos gráficos están
localizados. La función initgraph buscará el dispositivo primeramente en este directorio. Si
no es encontrado, la función buscará en el directorio de inicio. Cuando el argumento *path
es NULL, solamente el directorio de inicio es buscado.

Otra forma para evitar cargando el dispositivo desde el disco cada vez que el programa es
ejecutado es ligarlo o enlazarlo al dispositivo apropiado en un programa ejecutable.

La función initgraph no retorna ningún valor. Sin embargo, cuando la función initgraph es
llamada, el código de error interno es activado. Si la función initgraph termina con éxito, el
código es asignado un 0. Si no, el código es asignado así:
-2 grNotDetected La tarjeta gráfica no se encontró
-3 grFileNotFound El fichero del dispositivo no se encontró
-4 grInvalidDriver El fichero del dispositivo es inválido
-5 grNoLoadMem No hay suficiente memoria para cargar el dispositivo

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
circle( 300, 200, 80 );
getch(); /* Pausa */
closegraph();
getch();
}

Existen diferentes métodos de inicialización pero en este pequeño tutorial trataremos dos
que suelen ser los más utilizados hoy en día.

Juan Carlos Gutiérrez Barquero____________________________________________ 9


Bismarck Salvador Traña López__________________________________________UNI

1. Método de inicialización del modo grafico a prueba de errores.

#include <graphics.h>
# include<process.h>
# include <conio.h>
void main() {
int driver = VGA;
int modo = VGAHI;
int resultado;
initgraph(&driver,&modo,"c:\\tc20\\bin");
resultado=graphresult();
if(resultado!=grOk)
{
getch();
exit(0);
}
getch();
}
cuerpo del programa

2. Método de inicialización rápido del modo grafico.

#include <graphics.h>
void main() {
int driver = DETECT,modo;
initgraph(&driver,modo,” c:\\tc20\\bin”);
cuerpo del programa

driver: Es la dirección de la variable que contiene el código del tipo de de driver o


adaptador que tiene la computadora.

modo: Es la dirección de la variable que contiene el código del modo en que se podrá
adaptar.

c:\\tc20\\bin: Es el directorio donde se encuentra el código del manejador, puede ser


camino relativo o absoluto.

graphresult():Regresa el código error de la ultima llamada a una función grafica de turbo


c un valor de 0 significa que no hubo error.

grOk : regresa el código de error de la función graphresult().

DETECT: Dispositivo grafico de auto detección del la tarjeta de video y del modo grafico.

Juan Carlos Gutiérrez Barquero____________________________________________ 10


Bismarck Salvador Traña López__________________________________________UNI

¿Qué son los primitivos gráficos…?


Definición.

Un primitivo gráfico es un elemento fundamental de dibujo en un paquete grafico tal como


un punto, línea, o circulo; puede ser un carácter, o puede ser una operación tal como
relleno, coloreado o trasferido de la imagen. C cuenta con 5 grupos de primitivos gráficos.

 Figuras geométricas: Dibujan las figuras de la geometría clásica: líneas,


círculos, rectángulos, arcos, polígonos etc.

 Relleno: tiene dos formas de realizarse. El primero es con polígonos, donde se


definen los vértices del polígono a ser rellenado segunda es una operación grafica
que busca algorítmicamente las fronteras de la región del relleno.

 Rasterop: Es una operación grafica que copia el área de una imagen para luego
dibujarla en cualquier región de la pantalla.

 Gráficas Matemáticas: Dibujan los primitivos barras y sectores para conseguir


dibujar las herramientas del sector.

 Texto Gráfico: Sirve para escribir texto en modo grafico, utilizando diferentes
fuentes.

 Figuras geométricas:

LINEAS.

void far line(int x1, int y1, int x2, int y2);

Esta función es usada para conectar dos puntos con una línea recta. El primer punto es
especificado por los argumentos x1 e y1. El segundo punto es especificado por los
argumentos x2 e y2. La línea se dibuja usando el estilo de línea actual, el grosor, y el color
actual. La posición del cursor gráfico no es afectada por la función line.

La función line() no retorna ningún valor.

Juan Carlos Gutiérrez Barquero____________________________________________ 11


Bismarck Salvador Traña López__________________________________________UNI

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA,modo = EGAHI;
initgraph( &driver, &modo, "c:\\tc20\\bin" );
line( 20, 40, 350, 100 );
line( 400, 30, 50, 250 );
getch();
closegraph();
}

CIRCULOS.

void far circle(int x, int y, int radio);

Esta función se utiliza para dibujar un círculo. Los argumentos x e y definen el centro del
círculo, mientras que el argumento radio define el radio del círculo. El círculo no es
rellenado pero es dibujado usando el color actual.

El grosor de la circunferencia puede ser establecido por la función setlinestyle; sin


embargo, el estilo de la línea es ignorado por la función circle.

La función circle no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
circle( 300, 200, 80 );
getch(); /* Pausa */
closegraph();
}

Juan Carlos Gutiérrez Barquero____________________________________________ 12


Bismarck Salvador Traña López__________________________________________UNI

RECTANGULOS.

void far rectangle(int izquierda,int superior, int derecha, int inferior);

Esta función dibujará un rectángulo sin rellenar su interior usando el color actual. La
esquina superior izquierda del rectángulo está definida por los argumentos izquierdos y
superiores. Estos argumentos corresponden a los valores x e y de la esquina superior
izquierda. Similarmente, los argumentos derecha e inferior definen la esquina inferior
derecha del rectángulo. El perímetro del rectángulo es dibujado usando el estilo y grosor
de línea actuales.

La función rectangle() no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA,modo = EGAHI;
initgraph( &driver, &modo, "C:\\tc20\\bin" );
rectangle( 20, 20, 400, 300 );
getch(); /* Pausa */
closegraph();
}

ARCOS

void far arc(int x, int y,int comienzo_angulo, int final_angulo, int radio);

Esta función creará un arco circular. El arco tiene como centro el punto especificado por
los argumentos x e y, y es dibujado con el radio especificado: radio. El arco no está
rellanado, pero es dibujado usando el color actual. El arco comienza al ángulo
especificado por el argumento comienzo_angulo y es dibujado en la dirección contraria al
de las agujas del reloj hasta llegar al ángulo especificado por el argumento final_angulo.
La función arc usa el este (extendiéndose hacia la derecha del centro del arco en la
dirección horizontal) como su punto de 0 grados. La función setlinestyle puede usarse

Juan Carlos Gutiérrez Barquero____________________________________________ 13


Bismarck Salvador Traña López__________________________________________UNI

para establecer el grosor del arco. La función arc, sin embargo, ignorará el argumento
trama de la función setlinestyle.

La función arc no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA,modo = EGAHI,radio;
initgraph( &driver, &modo, "C:\\tc20\\bin" );
for( radio = 25; radio < 175; radio += 25 )
arc( 320, 175, 45, 135, radio );
getch(); /* Pausa */
closegraph();
}

PIXELES.

void far putpixel(int x, int y, int color);

Esta función es usada para colocar a un píxel en una posición en particular la cual es
cuestionada por los argumentos x e y. El argumento color específico el valor del color del
píxel.

La función putpixel no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA,modo = EGAHI,t;
initgraph( &driver, &modo, “c:\\tc20\\bin” );
for( t=0; t<200; t++ )
putpixel( 100+t, 50+t, t%16 );
getch();

Juan Carlos Gutiérrez Barquero____________________________________________ 14


Bismarck Salvador Traña López__________________________________________UNI

closegraph();
}
ELIPSES.

void far ellipse(int x, int y, int comienzo_angulo, int final_angulo, int x_radio, int y_radio);

Esta función es usada para dibujar un arco elíptico en el color actual. El arco elíptico está
centrado en el punto especificado por los argumentos x e y. Ya que el arco es elíptico el
argumento x_radio especifica el radio horizontal y el argumento y_radio especifica el radio
vertical. El arco elíptico comienza con el ángulo especificado por el argumento
comienzo_angulo y se extiende en un sentido contrario a las agujas del reloj al ángulo
especificado por el argumento final_angulo. La función ellipse considera este - el eje
horizontal a la derecha del centro del elipse - ser 0 grados. El arco elíptico es dibujado con
el grosor de línea actual como es establecido por la función setlinestyle. Sin embargo, el
estilo de línea es ignorado por la función ellipse.

La función ellipse no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA,modo = EGAHI;
initgraph( &driver, &modo, "C:\\tc20\\bin" );
ellipse( 300, 150, 45, 225, 100, 50 );
getch(); /* Pausa */
closegraph();
}

 Funciones básicas de graphics.h.

Función setbkcolor

void far setbkcolor(int color);

Esta función es usada para asignar el color de fondo al valor del color de fondo
especificado por el argumento color. Existen varios valores para ciertos colores de fondo.

Juan Carlos Gutiérrez Barquero____________________________________________ 15


Bismarck Salvador Traña López__________________________________________UNI

La función setbkcolor no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
setbkcolor( 4 );
circle( 300, 150, 50 );
getch(); /* Pausa */
closegraph();
getch();
}

Función getbkcolor.

int far getbkcolor(void);

Esta función es usada para obtener el valor del color de fondo actual. El color de fondo,
por defecto, es el color 0. Sin embargo, este valor puede cambiar con una llamada a la
función setbkcolor. Existen varios valores para ciertos colores de fondo.

La función getbkcolor retorna el valor del color de fondo actual.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA,modo = EGAHI,color;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
setbkcolor( 4 );
circle( 300, 150, 50 );
color = getbkcolor();
getch(); /* Pausa */
closegraph();
printf( "Color de fondo: %d\n", color );
getch();
}

void far setcolor(int color)

Esta función coloca el atributo color es decir escoge un color entre 0 y 15 o su


equivalente en inglés (ver pagina 3 Modo de 16 colores) todo lo que se dibuje después de
esta instrucción tendrá el color establecido por la función setcolor el valor de esta es
función es WHITE.

Juan Carlos Gutiérrez Barquero____________________________________________ 16


Bismarck Salvador Traña López__________________________________________UNI

Esta función no devuelve ningún valor.

Ejemplo.

# include <graphics.h>
# include <dos.h>
# include <stdlib.h>
# include <conio.h>
void main(){
int driver=DETECT,modo,i;
initgraph(&driver,&modo,"c:\\tc20\\BIN");
i=0;
do
{
setcolor(i);
circle(random(639),random(479),random(i+8));/*Random definida en stdlib*/
delay(30000); /*Detiene la ejecución del programa durante 30000 milisegundos*/
i=(i<16)?i:0;
i++;
}while(!kbhit());
}

Función cleardevices

void far cleardevice(void);

Esta función es usada para despejar una pantalla gráfica. La función cleardevice usa el
color de fondo actual, como es establecido por la función setbkcolor, para rellenar la
pantalla. La posición del cursor gráfico es la esquina superior izquierda de la pantalla -
posición (0,0) - después de que la pantalla haya sido borrado.

La función cleardevice no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
int relleno, color;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
relleno = 1;
color = 1;
setlinestyle( SOLID_LINE, relleno, THICK_WIDTH );
circle( 300, 200, 80 );
getch(); /* Pausa */
setbkcolor( color );
cleardevice();
setlinestyle( SOLID_LINE, relleno, THICK_WIDTH );

Juan Carlos Gutiérrez Barquero____________________________________________ 17


Bismarck Salvador Traña López__________________________________________UNI

circle( 400, 200, 20 );


getch(); /* Pausa */
closegraph();
getch();
}

Función getmaxx

int far getmaxx(void);

Esta función es usada para obtener la coordenada máxima de la pantalla en la dirección


horizontal. Este valor suele ser la resolución horizontal máxima menos 1.

La función getmaxx retorna la coordenada máxima de la pantalla en la dirección


horizontal.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA,modo = EGAHI,x_max;
initgraph( &driver, &modo, "C:\\TC20\\BIN" );
x_max = getmaxx();
closegraph();
printf( "X máxima: %d\n", x_max );
getch();
}

Función getmaxy

int far getmaxy(void);

Esta función es usada para obtener la coordenada máxima de la pantalla en la dirección


vertical. Este valor suele ser la resolución vertical máxima menos 1.

La función getmaxy retorna la coordenada máxima de la pantalla en la dirección vertical.


Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = VGA;
int modo = EGAHI;
int x_max, y_max;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
x_max = getmaxx();
y_max = getmaxy();
closegraph();

Juan Carlos Gutiérrez Barquero____________________________________________ 18


Bismarck Salvador Traña López__________________________________________UNI

printf( "X máxima: %d\tY máxima: %d\n", x_max, y_max );


getch();
}

 Rellenos

Es el proceso de rellenar una región de la pantalla con un patrón o color. Turbo C utiliza
dos métodos para definir la región de rellenos. El primero relleno de polígonos, usa la lista
de vértices del polígono para calcular la geometría del interior, El segundo relleno es por
el método de inundación, busca desde un punto inicial llamado la semilla en todas las
direcciones para encontrar una frontera que encierre la regio. La frontera se reconoce
como el valor del píxel que tiene.

Antes de estudiar los dos métodos estudiaremos la función setfillstyle que será de gran
importancia a la hora de realizar los dos tipos de rellenado y bar que es una función
similar a rectangle.

Función bar

void far bar(int izquierda, int superior, int derecha, int inferior);

Esta función dibujará una barra rectangular y rellenada de dos dimensiones. La esquina
superior izquierda de la barra rectangular está definida por los argumentos izquierdos y
superiores. Estos argumentos corresponden a los valores x e y de la esquina superior
izquierda. Similarmente, los argumentos derecha e inferior definen la esquina inferior
derecha de la barra. La barra no tiene borde, pero es rellenada con la trama de relleno
actual y el color de relleno como es establecido por la función setlinestyle.

La función bar no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA,modo = EGAHI, x, y, color,fill;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
x = 20;
y = 20;
color = 1;
fill = 1;
do {
setfillstyle( fill, color );
bar( x, y, x+40, 320 );
x += 40;
y += 10;
color = (color+1) % 16;
fill = (fill+1) % 12;
} while( x < 620 );
getch(); /* Pausa */
closegraph();

Juan Carlos Gutiérrez Barquero____________________________________________ 19


Bismarck Salvador Traña López__________________________________________UNI

}
El patrón de relleno se define con las Funciónes setfillstyle y setfillpattern

Función setfillstyle

void far setfillstyle(int trama, int color);

Esta función es usada para seleccionar una trama predefinida y un color de relleno. El
argumento trama especifica la trama predefinida, mientras que el argumento color
especifica el color de relleno. Existen trece valores ya definidos para tramas. Sin
embargo, la trama USER_FILL (valor 12) no debería usarse para asignar una trama
definida por el usuario. En su lugar, se debería usar la función setfillpattern.

La función setfillstyle no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA,modo = EGAHI;
initgraph( &driver, &modo, "c:\\tc20\\bin" );
setfillstyle( LTSLASH_FILL, 6 );
bar( 50, 50, 350, 300 );
getch(); /* Pausa */
closegraph();
}

LTSLASH_FILL es un estilo de relleno si desea estudiarlos se encuentran en el libro(Ver


pagina # 5 ).

Función setfillpattern

void far setfillpattern(char far *trama, int color);

Esta función es usada para seleccionar una trama de relleno definido por el usuario. El
argumento *trama apunta a una serie de ocho bytes que representa una trama de relleno
de bits de 8 x 8. Cada byte representa una fila de ocho bits, donde cada bit está
encendido o no (1 ó 0). Un bit de 0 indica que el píxel correspondiente será asignado el
color de relleno actual. Un bit de 0 indica que el píxel correspondiente no será alterado. El
argumento color especifica el color de relleno que será usado para la trama.

La función setfillpattern no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {

Juan Carlos Gutiérrez Barquero____________________________________________ 20


Bismarck Salvador Traña López__________________________________________UNI

int driver = EGA,modo = EGAHI;


char trama1[8] = { 0x33, 0xEE, 0x33, 0xEE, 0x33, 0xEE, 0x33, 0xEE };
char trama2[8] = { 0x0A, 0xF0, 0xF0, 0x0A, 0x0A, 0xF0, 0xF0, 0x0A };
initgraph( &driver, &modo, "C:\\tc20\\bin" );
bar( 50, 50, 150, 150 );
setfillpattern( trama1, 9 );
bar( 160, 50, 260, 150 );
setfillpattern( trama2, 4 );
bar( 105, 160, 205, 260 );
getch(); /* Pausa */
closegraph();
}

1. Relleno por medio de un polígono.

void far fillpoly(int numpuntos, int far *puntos);

Esta función es usada para crear un polígono rellenado. El argumento numpuntos es


usado para definir el número de puntos en el polígono. Al contrario que la función
drawpoly, la función automáticamente cierra el polígono. El argumento *puntos apunta a
un array de números de longitud numpuntos multiplicado por 2.

Los dos primeros miembros del array identifica las coordenadas x e y del primer punto,
respectivamente, mientras que los dos siguientes especifican el siguiente punto, y así
sucesivamente. La función fillpoly dibuja

el perímetro del polígono con el estilo de línea y color actuales. Luego, el polígono es
rellenado con la trama de relleno y color de relleno actuales.

La función fillpoly no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA,modo = EGAHI,trama, color;
int puntos[6] = { 300, 50, 500, 300, 100, 300 };
initgraph( &driver, &modo, "C:\\tc20\\bin" );
trama = SLASH_FILL;
for(color=0;color<=15;color++)
{
setfillstyle( trama, color );
fillpoly( 3, puntos );
getch();
}
getch(); /* Pausa */
closegraph();
}

Juan Carlos Gutiérrez Barquero____________________________________________ 21


Bismarck Salvador Traña López__________________________________________UNI

/*Este programa te muestra los diferentes colores con una misma trama trata de
modificarlo para que obtengas todas las tramas con todos los colores*/

2. Relleno por el método de inundación.

void far floodfill(int x, int y, int borde);

Esta función es usada para rellenar un área cerrado con el color de relleno y trama de
relleno actuales. Los argumentos x e y especifican el punto de comienzo para el algoritmo
de relleno. El argumento borde especifica el valor del color del borde del área. Para que la
función fillpoly Funcione como es esperado, el área a ser rellenado debe estar rodeada
por el color especificado por el argumento borde. Cuando el punto especificado por los
argumentos x e y se encuentra dentro del área a ser rellenada, el interior será rellenado.
Si se encuentra fuera del área, el exterior será rellenado.

Nota: Esta función no Funciona con el driver IBM-8514.La función floodfill no retorna
ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA,modo = EGAHI,trama, color;
int puntos[8] = { 300, 50, 500, 300, 100, 300, 300, 50 };
initgraph( &driver, &modo, "C:\\tc20\\bin" );
setcolor( 10 ); /*verde*/
drawpoly( 4, puntos );
trama = SLASH_FILL;
for(color = 0;color<=15;color++)
{
setfillstyle( trama, color );
floodfill( 400, 250, 10 );
getch();
}
getch(); /* Pausa */
closegraph();
}

 Funciónes de coordenadas relativas

Entre ellas tenemos las Funciónes moveto, moverel, lineto, linerel, getx, gety.
Estudiaremos cada una de ellas para afianzar mas conocimientos.

Función moveto.

void far moveto(int x, int y);

Juan Carlos Gutiérrez Barquero____________________________________________ 22


Bismarck Salvador Traña López__________________________________________UNI

Esta función es usada para colocar el cursor gráfico al punto especificado por los
argumentos x e y. Ya que el cursor es movido desde su posición anterior al punto
especificado por los argumentos x e y, no hay dibujo alguno.

La función moveto no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA,modo = EGAHI;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
moveto( 20, 20 );
lineto( 40, 60 );
lineto( 80, 90 );
getch();
closegraph();
}

Función lineto.

void far lineto(int x, int y);

Esta función es usada para dibujar una línea recta desde la posición actual del cursor
gráfico hasta el punto especificado por los argumentos x e y. La línea se dibuja usando el
estilo de línea actual, el grosor, y el color actual. Después de que la línea recta haya sido
dibujado, la posición del cursor gráfico es actualizado a la posición especificado por los
argumentos x e y (el punto final de la línea).

La función lineto no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA, modo = EGAHI;
initgraph( &driver, &modo, "C:\\tc20\\bin" );
moveto( 20, 20 );
lineto( 40, 60 );
lineto( 80, 90 );
getch();
closegraph();
}

Función moverel.

void far moverel(int dx, int dy);

Juan Carlos Gutiérrez Barquero____________________________________________ 23


Bismarck Salvador Traña López__________________________________________UNI

Esta función es usada para mover la posición del cursor gráfico a una distancia relativa
como los argumentos dx y dy. El argumento dx define la distancia relativa a moverse en
la dirección horizontal. El argumento dy define la distancia relativa a moverse en la
dirección vertical. Estos valores pueden ser positivos o negativos. No se dibuja ya que el
cursor es mudado.

La función moverel no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA,modo = EGAHI;
initgraph( &driver, &modo, "C:\\tc20\\bin" );
moveto( 20, 20 );
linerel( 20, 40 );
moverel( 50, 50 );
linerel( 40, 30 );
getch();
closegraph();
}

Función linerel.

void far linerel(int dx, int dy);

Esta función es usada para dibujar una línea recta a una distancia y dirección
predeterminadas desde la posición actual del cursor gráfico. El argumento dx especifica el
número relativo de píxels para atravesar en la dirección horizontal. El argumento dy
especifica el número relativo de píxels para atravesar en la dirección vertical. Estos
argumentos pueden ser tanto valores positivos como negativos. La línea se dibuja usando
el estilo de línea actual, el grosor, y el color actual desde la posición actual del cursor
gráfico a través de la distancia relativa especificada. Cuando la línea esté terminada, la
posición del cursor gráfico es actualizado al
último punto de la línea.

La función linerel no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA,modo = EGAHI;
initgraph( &driver, &modo, "C:\\tc20\\bin" );
moveto( 20, 20 );
linerel( 20, 40 );
linerel( 40, 30 );
getch();
closegraph();

Juan Carlos Gutiérrez Barquero____________________________________________ 24


Bismarck Salvador Traña López__________________________________________UNI

Función getx.

int far getx(void);

Esta función es usada para obtener la posición, en la dirección horizontal, del cursor
gráfico. El valor retornado especifica el lugar del píxel horizontal del cursor gráfico (la
coordenada x), relativo a la pantalla del usuario actual.

La función getx retorna la coordenada x del cursor gráfico.

Ejemplo:
#include <graphics.h>
#include <stdio.h>
void main() {
int driver = EGA,modo = EGAHI, x, y;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
moveto( 300, 150 );
x = getx();
y = gety();
closegraph();
printf( "Cursor gráfico\n\nX: %d\tY: %d\n", x, y );
}

Función gety.

int far gety(void);

Esta función es usada para obtener la posición, en la dirección vertical, del cursor gráfico.
El valor retornado especifica el lugar del píxel vertical del cursor gráfico (la coordenada y),
relativo a la pantalla del usuario actual.

La función gety retorna la coordenada y del cursor gráfico.

Ejemplo:

#include <graphics.h>
#include <stdio.h>
void main() {
int driver = EGA,modo = EGAHI, x, y;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
moveto( 300, 150 );
x = getx();
y = gety();
closegraph();
printf( "Cursor gráfico\n\nX: %d\tY: %d\n", x, y );
}

Juan Carlos Gutiérrez Barquero____________________________________________ 25


Bismarck Salvador Traña López__________________________________________UNI

Otras funciones de la librería graphics.h.

En este capitulo describimos una a una las librerías de la librería graphics.h. Las funciones
serán explicadas mediante un orden.

Cuando sabemos como dibujar líneas, círculos, rectángulos, y otras figuras es hora de que
empieces a conocer otro tipo de funciones en las cuales no hacen mucho énfasis los
profesores como lo son las funciones bar3d, palette y otras que presentan una gran gama
de tareas de gran calidad grafica. Dejemos de hablar y comencemos ya que los puedo
aburrir.

Función bar3d
void far bar3d(int izquierda, int superior,int derecha, int inferior, int profundidad, int banderin_tapa);

Esta función creará una barra rectangular y rellenada de tres dimensiones. La esquina
superior izquierda de la barra rectangular más frontal está definida por los argumentos
izquierdos y superiores. Estos argumentos corresponden a los valores x e y de la
esquina superior izquierda del rectángulo más frontal. Similarmente, los argumentos
derecha e inferior definen la esquina inferior derecha del rectángulo más frontal. La barra
tiene borde, en todas las tres dimensiones, rellenada con el color y estilo de línea
actuales. El rectángulo más frontal es rellenado usando la trama de relleno actual y el
color de relleno como es establecido por la función setlinestyle. El argumento
banderin_tapa es usado para especificar si es o no es posible apilar varias barras
encima de cada una. Si banderin_tapa tiene un valor distinto a cero, entonces la barra
está "tapada". Si banderin_tapa tiene un valor de cero, entonces la barra no está
"tapada", permitiendo otras barras ser apiladas encima de ésta.

La función bar3d no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA,modo = EGAHI,color, relleno;
color = 10;
relleno = 11;
initgraph(&driver,&modo,"c:\\tc20\\bin");
setfillstyle( relleno, color );
bar3d( 100, 50, 300, 150, 25, 1 );
getch(); /* Pausa */
closegraph();
getch();
}

Juan Carlos Gutiérrez Barquero____________________________________________ 26


Bismarck Salvador Traña López__________________________________________UNI

Función clearviewport

void far clearviewport(void);

Esta función es usada para rellenar la pantalla actual del usuario con el color de fondo
actual. El color de fondo puede ser establecido con la función setbkcolor. La posición del
cursor gráfico es la esquina superior izquierda de la pantalla actual del usuario. Esta
posición es (0,0) según la pantalla actual del usuario.

La función clearviewport no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int a = EGA,b = EGAHI,color;
initgraph( &a, &b, "C:\\tc20\\BIN" );
setviewport( 150, 150, 350, 350, 0 );
for( color = 0; color<16; color++ ) {
circle( 100, 100, 60 );
getch();
setbkcolor( color );
clearviewport();
}
getch(); /* Pausa */
closegraph();
}

Función closegraph

void far closegraph(void);

Esta función es usada para cerrar el sistema gráfico como es iniciada por la función
initgraph. La función closegraph libera toda la memoria usada por el sistema gráfico y
luego restaura el modo de vídeo al modo de texto que estaba en uso anteriormente a la
llamada a la función initgraph.

La función closegraph no retorna ningún valor.


Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA,modo = EGAHI;
initgraph( &driver, &modo, "C:\\tc20\\BIN”);
circle( 300, 200, 80 );
getch(); /* Pausa */
closegraph();

Juan Carlos Gutiérrez Barquero____________________________________________ 27


Bismarck Salvador Traña López__________________________________________UNI

getch();
}

Función detectgraph

void far detectgraph(int far *driver, int far *modo);

Esta función es usada para detectar el adaptador gráfico y el modo óptimo para usar con
el sistema en uso. Si la función detectgraph no puede detectar ningún dispositivo gráfico,
el argumento *driver es asignado grNotDetected (-2). Una llamada a graphresult resultará
en un valor de retorno de -2, o grNotDetected. Existen varios valores que indican los
diferentes dispositivos gráficos que pueden ser usados por el argumento *driver. Un valor
de 0, o DETECT, inicia la Funciónalidad de auto detección, el cual determina el driver
óptimo a usar. Para cada dispositivo existen varios valores que indican los diferentes
modos gráficos que pueden ser usados por el argumento *modo. Sin embargo, si el
argumento *driver es asignado el valor de 0, o DETECT, el argumento *modo es
automáticamente establecido al modo de resolución mas alto para el driver.

La función detectgraph no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver, modo;
detectgraph( &driver, &modo, );
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
circle( 300, 200, 80 );
getch(); /* Pausa */
closegraph();
printf( "Driver: %d\tModo: %d\n\n", driver, modo );
getch();
}

Función drawpoly

void far drawpoly(int numpuntos, int far *puntos);

Esta función es usada para crear un polígono con un número especificado de puntos. El
argumento numpuntos es usado para definir el número de puntos en el polígono. Para la
función drawpoly, el número de puntos debe ser el número actual de puntos más 1 para
poder crear un polígono cerrado. En otras palabras, el primer punto debe ser igual al último
punto. El argumento *puntos apunta a un array de números de longitud numpuntos
multiplicado por 2. Los dos primeros miembros del array identifica las coordenadas x e y
del primer punto, respectivamente, mientras que los dos siguientes especifican el siguiente
punto, y así sucesivamente. La función drawpoly dibuja el perímetro del polígono con el
estilo de línea y color actuales, pero no rellena el polígono.

La función drawpoly no retorna ningún valor.

Juan Carlos Gutiérrez Barquero____________________________________________ 28


Bismarck Salvador Traña López__________________________________________UNI

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
int puntos[8] = { 300, 50, 500, 300, 100, 300, 300, 50 };
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
drawpoly( 4, puntos );
getch(); /* Pausa */
closegraph();
getch();
}

Función fillellipse

void far fillellipse(int x, int y,int x_radio, int y_radio);

Esta función es usada para dibujar y rellenar una elipse. El centro de la elipse es
especificado por los argumentos x e y. El argumento x_radio especifica el radio horizontal
y el argumento y_radio especifica el radio vertical de la elipse. La elipse es dibujado con
el perímetro en el color actual y rellenada con el color de relleno y la trama de relleno
actuales.

La función fillellipse no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void inicializar(void);
void main() {
int trama, color;
inicializar();
trama = SOLID_FILL;
color = 4;
setfillstyle( trama, color );
fillellipse( 300, 150, 100, 50 );
getch(); /* Pausa */
closegraph();
}

void inicializar(void)
{
int driver = EGA,modo = EGAHI;
initgraph( &driver, &modo, "C:\\tc20\\BIn" );
}

Juan Carlos Gutiérrez Barquero____________________________________________ 29


Bismarck Salvador Traña López__________________________________________UNI

Función getarccords

void far getarccoords(struct arccoordstype far *coordenadas_arco);

Esta función es usada para recoger las coordenadas del centro, y los puntos del comienzo
y final de la última llamada con éxito a la función arc. El argumento *coordenadas_arco
apunta a la estructura de tipo arccoordstype que guarda la información recogida. La
sintaxis de la estructura arccoordstype es:

struct arccoordstype {
int x, y;
int xstart, ystart;
int xend, yend;
};

Los miembros x e y definen el centro del arco. Los miembros xstart e ystart definen las
coordenadas x e y del punto de comienzo del arco. Similarmente, los miembros xend e
yend definen las coordenadas x e y del punto de final del arco.

La función getarccoords no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI,radio;
struct arccoordstype info_arco;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
for( radio=25; radio<=100; radio+=25 ) {
arc( 300, 150, 45, 315, radio );
getarccoords( &info_arco );
moveto( info_arco.xstart, info_arco.ystart );
lineto( info_arco.xend, info_arco.yend );
}
getch(); /* Pausa */
closegraph();
getch();
}

Función getaspectratio

void far getaspectratio(int far *x_proporcion,int far *y_proporcion);

Esta función es usada para obtener la proporción anchura-altura del modo gráfico actual.
La proporción anchura-altura puede definirse como la proporción de la anchura del píxel
del modo gráfico y la altura del píxel. Esta proporción, usando los modos gráficos
existentes, es siempre menor o igual que 1. El valor para determinar la proporción

Juan Carlos Gutiérrez Barquero____________________________________________ 30


Bismarck Salvador Traña López__________________________________________UNI

anchura-altura con respecto al eje horizontal es retornado en el argumento


*x_proporcion. Similarmente, el valor para el eje vertical es retornado en el argumento
*y_proporcion. El argumento *y_proporcion es asignado 10000, el cual es retornado
cuando se llama a la función getaspectratio. El argumento *x_proporcion es casi siempre
menor que el valor de *y_proporcion. Esto es debido al hecho de que la mayoría de los
modos gráficos tiene píxeles más altos que anchos. La única excepción es en los modos
de VGA que produce píxeles cuadrados; es decir, x_proporcion = y_proporcion.

La función getaspectratio no retorna ningún valor, directamente.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA, modo = EGAHI;
int x_proporcion, y_proporcion;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
getaspectratio( &x_proporcion, &y_proporcion );
circle( 300, 150, 50 );
getch(); /* Pausa */
closegraph();
printf( "Proporción anchura-altura.\nFactor x: %d\tFactor y: %d\n",
x_proporcion, y_proporcion );
getch();
}

Función getcolor

int far getcolor(void);

Esta función obtiene el valor del color actual. El color actual es el color usado para dibujar
líneas, arcos, etc.. Este color no es el mismo que el color de relleno. El valor del color
obtenido es interpretado según el modo que esté en uso. Existen varios valores para
ciertos colores de fondo.

La función getcolor retorna el valor del color actual.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI,color;
initgraph( &driver, &modo, "C:\\Tc20\\BIN" );
setcolor( 4 );
circle( 300, 150, 50 );
color = getcolor();

Juan Carlos Gutiérrez Barquero____________________________________________ 31


Bismarck Salvador Traña López__________________________________________UNI

getch(); /* Pausa */
closegraph();
printf( "Color del perímetro: %d\n", color );
getch();
}

Función getdefaultpalette

struct palettetype far *getdefaultpalette(void);

Esta función es usada para obtener una estructura que define la paleta según el
dispositivo en la inicialización - esto es, cuando se llama a initgraph. La estructura
palettetype se define de la siguiente manera:

#define MAXCOLORS 15
struct palettetype {
unsigned char size;
signed char colors[MAXCOLORS+1];
}

El campo size indica el tamaño de la paleta. El campo colors contiene los valores
numéricos que representan los colores que ofrece el dispositivo en su paleta de colores.

La función getdefaultpalette retorna un puntero a una estructura del tipo palettetype.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
struct palettetype *palette = NULL;
int i;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
palette = getpalettetype();
circle( 300, 150, 50 );
getch(); /* Pausa */
closegraph();
printf( "Paleta\n\nTamaño: %d\nColores: %d",
palette->size, palette->colors[0] );
for( i=1; i<palette->size; i++ )
printf( ", %d", palette->colors[i] );
printf( "\n" );
getch();
}

Juan Carlos Gutiérrez Barquero____________________________________________ 32


Bismarck Salvador Traña López__________________________________________UNI

Función getdrivename

char *far getdrivername(void);

Esta función es usada para obtener una cadena de caracteres que contiene el nombre del
dispositivo gráfico actual. Esta función debería ser llamada después de que un dispositivo
haya sido definido e inicializado – esto es, después de llamar a initgraph.

La función getdrivername retorna una cadena de caracteres conteniendo el nombre del


dispositivo gráfico.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
void main() {
int driver = EGA, modo = EGAHI;
char *nombre;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
strcpy( nombre, getdrivername() );
circle( 300, 150, 50 );
getch(); /* Pausa */
closegraph();
printf( "Nombre del dispositivo gráfico: %s\n", nombre );
getch();}

Función getfillpattern

void far getfillpattern(char far *trama);

Esta función es usada para obtener una trama de relleno definido por el usuario, como es
definida por la función setfillpattern, y la guarda en memoria. El argumento *trama es un
puntero a una serie de ocho bytes que representa una trama de relleno de bits de 8 x 8.
Cada byte representa una fila de ocho bits, donde cada bit está encendido o no (1 ó 0).
Un bit de 0 indica que el píxel correspondiente será asignado el color de relleno actual. Un
bit de 0 indica que el píxel correspondiente no será alterado.

La función getfillpattern no retorna ningún valor, directamente.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
char trama1[8] = { 0x33, 0xEE, 0x33, 0xEE, 0x33, 0xEE, 0x33, 0xEE };

Juan Carlos Gutiérrez Barquero____________________________________________ 33


Bismarck Salvador Traña López__________________________________________UNI

char trama2[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
initgraph( &driver, &modo, "C:\\tc20\\bin" );
getfillpattern( trama2 );
bar( 50, 50, 150, 150 );
setfillpattern( trama1, 9 );
bar( 160, 50, 260, 150 );
setfillpattern( trama2, 4 );
bar( 105, 160, 205, 260 );
getch(); /* Pausa */
closegraph();
getch();
}

Función getfillsettings

void far getfillsettings(struct fillsettingstype far *info);

Esta función es usada para obtener la información de tramas de relleno. El argumento


*info apunta a una estructura de tipo fillsettingstype, el cual es actualizado cuando se
llama a la función getfillsettings. La estructura es:

struct fillsettingstype {
int pattern;
int color;
};

El campo pattern es la trama y el campo color es el color de relleno de la trama.

Existen trece valores ya definidos para tramas.

La función getfillsettings no retorna ningún valor, directamente.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
struct fillsettingstype info;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
getfillsettings( &info );
bar( 50, 50, 350, 300 );
getch(); /* Pausa */
closegraph();
printf( "Trama de relleno: %d\tColor de relleno: %d\n", info.pattern, info.color );
getch();
}

Juan Carlos Gutiérrez Barquero____________________________________________ 34


Bismarck Salvador Traña López__________________________________________UNI

Función getgraphmode

int far getgraphmode(void);

Esta función es usada para obtener el valor del modo gráfico actual. El dispositivo actual
debe ser considerado cuando se interprete el valor de retorno. Esta función sólo debería
ser llamada después de que el sistema gráfico haya sido inicializado con la función
initgraph. Existen varios valores para los modos de cada dispositivo.

La función getgraphmode retorna el modo gráfico como es establecido por initgraph o


setgraphmode.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA, modo = EGAHI,modo;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
modo = getgraphmode();
bar( 50, 50, 350, 300 );
getch(); /* Pausa */
closegraph();
printf( "Modo gráfico: %d\n", modo );
getch();
}

Función getlinesettings

void far getlinesettings(struct linesettingstype far *info);

Esta función obtiene la información actual para las líneas. Esta información es guardada
en una estructura de tipo linesettingstype que es apuntado por el argumento *info. El
estilo de línea, trama, y grosor actuales son guardados en esta estructura. La sintaxis
para la estructura linesettingstype:

struct linesettingstype {
int linestyle;
unsigned upattern;
int thickness;
}

El campo linestyle es el estilo de la línea recta. El campo upattern es la trama de la línea


del usuario solamente cuando el campo linestyle es igual a USERBIT_LINE, ó 4. Cuando
esto sea el caso, el miembro upattern contiene una trama de línea definido por el usuario
de 16 bits. Un bit 1 en esta trama indica que el píxel correspondiente será asignado el
color actual. Un bit 0 indica que el píxel correspondiente no será alterado. El campo

Juan Carlos Gutiérrez Barquero____________________________________________ 35


Bismarck Salvador Traña López__________________________________________UNI

thickness es el grosor de la línea. Existen varios valores para los diferentes estilos y
grosores de líneas rectas.

La función getlinesettings no retorna ningún valor, directamente.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
struct linesettingstype info;
initgraph( &driver, &modo, "C:\\tc20\\bin" );
setlinestyle( DOTTED_LINE, 0xFF33, THICK_WIDTH );
circle( 350, 250, 50 );
getlinesettings( &info );
getch(); /* Pausa */
closegraph();
printf( "Líneas rectas.\nEstilo: %d\tTrama: %X\tGrosor: %d\n",
info.linestyle, info.upattern, info.thickness );
getch();
}

Función getmaxcolor

int far getmaxcolor(void);

Esta función es usada para obtener el valor más alto de color en la paleta actual. La
paleta en uso depende del dispositivo y modo inicializados. Para los modos de 16 colores,
el valor de retorno es 15. Similarmente, para los modos de dos colores, el valor de retorno
es 1.

La función getmaxcolor retorna el valor máximo del color en la paleta en uso.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver =EGA,modo =EGAH,color_max;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
color_max = getmaxcolor();
closegraph();
printf( "Color m ximo: %d\n", color_max );
getch();
}

Juan Carlos Gutiérrez Barquero____________________________________________ 36


Bismarck Salvador Traña López__________________________________________UNI

Función getmodename

char *far getmodename(int num_modo);

Esta función es usada para obtener el nombre del modo gráfico especificado por el
argumento num_modo.

La función getmodename retorna el nombre del modo gráfico que está contenido en todos
los dispositivos gráficos.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
void main() {
int driver = IBM8514;
int modo = IBM8514HI,i;
char *nombre;
int num_modo;
initgraph( &driver, &modo,"C:\\TC20\\BIN");
num_modo = getgraphmode();
strcpy(nombre,getmodename(num_modo));
closegraph();
for(i=0;nombre[i];i++)
printf("%c",nombre[i]);
getch();
}

Función getmoderange

void far getmoderange(int driver, int far *modo_bajo, int far *modo_alto);
Esta función es usada para obtener los valores altos y bajos del modo gráfico del
dispositivo especificado por el argumento driver. El valor más bajo del modo es retornado
en *modo_bajo, y el valor más alto del modo es retornado en *modo_alto. Si el
dispositivo especificado es inválido, el valor de -1 es retornado en ambos argumentos:
*modo_bajo y *modo_alto. Sin embargo, si el argumento driver es asignado -1, los
modos alto y bajo del dispositivo actual son retornados.

La función getmoderange no retorna ningún valor, directamente.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {

Juan Carlos Gutiérrez Barquero____________________________________________ 37


Bismarck Salvador Traña López__________________________________________UNI

int driver = EGA;


int modo = EGAHI;
int modo_bajo, modo_alto;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
getmoderange( driver, &modo_bajo, &modo_alto );
closegraph();
printf( "Alcance de modos, de %d á %d\n", modo_bajo, modo_alto );
getch();
}

Función getpalette

void far getpalette(struct palettetype far *paleta);

Esta función es usada para obtener la información de la paleta actual. El argumento


*paleta apunta a una estructura del tipo palettetype donde la información de la paleta es
guardada. La estructura palettetype se define de la siguiente manera:

#define MAXCOLORS 15
struct palettetype {
unsigned char size;
signed char colors[MAXCOLORS+1];
}

El campo size indica el número de colores en la paleta. El campo colors contiene los
valores numéricos que representan los colores que ofrece el dispositivo en su paleta de
colores.

La función getpalette no retorna ningún valor, directamente.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI,i;
struct palettetype palette;
initgraph( &driver, &modo, "C:\\TC20\\BIN" );
getpalette( &palette );
closegraph();
printf( "\t\t\t\t\tPaleta\n\nTama¤o: %d\n",palette.size);
printf("Colores: %d,",palette.colors[0] );
for( i=1; i<palette.size; i++ )
printf( "%d, ", palette.colors[i] );
printf( "\n" );
getch();
}

Juan Carlos Gutiérrez Barquero____________________________________________ 38


Bismarck Salvador Traña López__________________________________________UNI

Función getpalettesize

int far getpalettesize(void);

Esta función es usada para obtener el número de entradas de paleta válidas para la
paleta actual, considerando el modo gráfico en uso.

La función getpalettesize retorna el número de colores en la paleta actual. Para modos de


16 colores, la función getpalettesize retorna 16.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
int num_colores;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
num_colores = getpalettesize();
closegraph();
printf( "Paleta\n\nNúmero de colores: %d\n", num_colores );
getch();
}

Función getpixel

unsigned far getpixel(int x, int y);

Esta función es usada para obtener el valor del color del píxel especificado por los
argumentos x e y. Estos argumentos especifican las coordenadas de la pantalla del píxel
a ser evaluado. Cuando se evalúa el valor del color retornado, el modo gráfico en uso
debe ser considerado. Existen varios valores para describir colores.

La función getpixel retorna el número del color del píxel especificado.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA, modo = EGAHI, x, y, color;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
x = 300;
y = 100;
setfillstyle( SOLID_FILL, 2 );

Juan Carlos Gutiérrez Barquero____________________________________________ 39


Bismarck Salvador Traña López__________________________________________UNI

fillellipse( 300, 160, 50, 150 );


color = getpixel( x, y );
getch();
closegraph();
printf( "Colores\n\nEl color del píxel (%d,%d): %d\n", x, y, color );
getch();
}

Función gettextsettings

void far gettextsettings(struct textsettingstype far *info);

Esta función es usada para obtener información acerca de la fuente gráfica actual. Esta
información es guardada en una estructura de tipo textsettingstype, la cual es apuntada
por el argumento *info. Esta estructura contiene información de la fuente actual en uso, la
orientación del texto, el tamaño del carácter, y la justificación horizontal y vertical. La
síntaxis de la estructura textsettingstype es la siguiente:

struct textsettingstype {
int font;
int direction;
int charsize;
int horiz;
int vert;
};

Existen varios valores para describir el tipo, la orientación, y justificación de fuentes.

La función gettextsettings no retorna ningún valor.

Ejemplo:

#include <graphics.h>
# include<conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
struct textsettingstype info;
initgraph( &driver, &modo, "C:\\TC20\\BIN" );
gettextsettings( &info );
closegraph();
printf( "Texto\n\nFuente: %d\tSentido: %d\tTamaño: %d\n""Justificación:\nHorizontal: %d,
Vertical: %d\n",
info.font, info.direction, info.charsize, info.horiz, info.vert);
getch();
}

Función getviewsettings

void far getviewsettings(struct viewporttype far *info);

Juan Carlos Gutiérrez Barquero____________________________________________ 40


Bismarck Salvador Traña López__________________________________________UNI

Esta función es usada para obtener información acerca del área gráfica actual. Esta
información es guardada en una estructura de tipo viewporttype, la cual es apuntada por
el argumento *info. Esta estructura contiene información acerca de las esquinas superior
izquierda e inferior derecha, también como el banderín de recorte del área gráfica. La
síntaxis de la estructura viewporttype es la siguiente:

struct viewporttype {
int left, top;
int right, bottom;
int clip;
};

La función getviewsettings no retorna ningún valor, directamente.

Ejemplo:

#include <graphics.h>
# include<conio.h>
#include <stdio.h>
void main() {
int driver = VGA, modo = VGAHI;
struct viewporttype info;
initgraph( &driver, &modo, "C:\\TC20\\BIN" );
getviewsettings( &info );
closegraph();
printf( "\t\t\t\tPantalla\n\nIzquierda: %d\tSuperior: %d\tDerecha: %d\t"
"Inferior: %d\tBander¢n: %d\n",
info.left, info.top, info.right, info.bottom, info.clip );
getch();
}

Función graphdefaults

void far graphdefaults(void);

Esta función es usada para reiniciar todos los datos gráficos a sus valores originales, o
por defecto. La función graphdefaults reinicia la pantalla del usuario para que cubra la
pantalla entera, mueve el cursor a la posición (0,0), y reinicia la paleta actual a sus colores
por defecto. También reinicia el color de fondo y el actual a sus valores por defecto,
reinicia el estilo y trama de relleno a sus valores por defecto, y reinicia la fuente y
justificación de texto.

La función graphdefaults no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = VGA;

Juan Carlos Gutiérrez Barquero____________________________________________ 41


Bismarck Salvador Traña López__________________________________________UNI

int modo = VGAHI;


initgraph( &driver, &modo, "C:\\TC20\\BIN" );
setcolor( 4 );
setviewport( 250, 150, 350, 250, 1 );
graphdefaults();
circle( 300, 200, 50 );
getch();
closegraph();
getch();
}

Función grapherrormsg

char *far grapherrormsg(int codigo_error);

Esta función es usada para obtener una cadena de caracteres conteniendo el mensaje de
error para un código de error especificado. El argumento codigo_error específica el valor
del código de error. La función graphresult debe ser usada para obtener el código de error
usado para el argumento codigo_error.

La función grapherrormsg retorna una cadena de caracteres.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
int codigo_error;
char *mensaje_error;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
setgraphmode( 40 ); /* Creamos un ERROR */
codigo_error = graphresult();
strcpy( mensaje_error, grapherrormsg( codigo_error ) );
closegraph();
printf( "ERROR: \"%s\" (%d)\n", mensaje_error, codigo_error );
getch();
}

Función graphfreemem

void far _graphfreemem(void far *ptr, unsigned tamanyo);

Esta función es usada por la librería gráfica para desadjudicar memoria previamente
reservada mediante una llamada a la función _graphgetmem. Esta función es llamada por
la librería gráfica cuando se quiere liberar memoria. Por defecto, la función simplemente
llama a free, pero se puede controlar la administración de memoria de la librería gráfica.
La forma de hacer esto es simplemente creando la definición de la función, con el mismo
prototipo mostrado aquí.

Juan Carlos Gutiérrez Barquero____________________________________________ 42


Bismarck Salvador Traña López__________________________________________UNI

La función _graphfreemem no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
void far _graphfreemem( void far *ptr, unsigned tamanyo ) {
printf( "__graphfreemem ha sido llamado para "
"desadjudicar %d bytes en memoria\n" );
printf( "para el montón (heap) interno\n", tamanyo );
printf( "Pulse cualquier tecla...\n\n" );
getch();
free( ptr );
}
void far * far _graphgetmem( unsigned tamanyo ) {
printf( "__graphgetmem ha sido llamado para "
"adjudicar %d bytes en memoria\n" );
printf( "para el montón (heap) interno\n", tamanyo );
printf( "Pulse cualquier tecla...\n\n" );
getch();
return malloc( tamanyo );
}
void main() {
int driver = EGA,modo = EGAHI;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
circle( 200, 100, 50 );
getch();
closegraph();
getch();
}

Función graphgetmem

void far * far _graphgetmem(unsigned tamanyo);

Esta función es usada por la librería gráfica para adjudicar memoria gráfica para búferes
internos, dispositivos gráficos, y fuentes. Esta función tiene como intención ser llamada
por la librería gráfica cuando se quiere adjudicar memoria. Por defecto, la función
simplemente llama a malloc, pero se puede controlar la administración de memoria de la
librería gráfica. La forma de hacer esto es simplemente creando la definición
de la función, con el mismo prototipo mostrado aquí.

La función _graphgetmem no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>

Juan Carlos Gutiérrez Barquero____________________________________________ 43


Bismarck Salvador Traña López__________________________________________UNI

#include <stdio.h>
#include <stdlib.h>
void far _graphfreemem( void far *ptr, unsigned tamanyo ) {
printf( "__graphfreemem ha sido llamado para "
"desadjudicar %d bytes en memoria\n" );
printf( "para el montón (heap) interno\n", tamanyo );
printf( "Pulse cualquier tecla...\n\n" );
getch();
free( ptr );
}
void far * far _graphgetmem( unsigned tamanyo ) {
printf( "__graphgetmem ha sido llamado para "
"adjudicar %d bytes en memoria\n" );
printf( "para el montón (heap) interno\n", tamanyo );
printf( "Pulse cualquier tecla...\n\n" );
getch();
return malloc( tamanyo );
}
void main() {
int driver = EGA, modo = EGAHI;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
circle( 200, 100, 50 );
getch();
closegraph();
getch();
}

Función graphresult

int far graphresult(void);

Esta función obtiene y retorna el código de error para la última llamada sin éxito. Además,
reinicia el nivel de error a 0, o grOk. Existen varios valores de códigos de error.

La función graphresult retorna el código de error de la última llamada gráfica sin éxito.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
int codigo_error;
char *mensaje_error;
initgraph( &driver, &modo, "C:\\TC20\\BIN" );
setgraphmode( 40 ); /* Creamos un ERROR debe de ser 0 o 1*/
codigo_error = graphresult();
strcpy( mensaje_error, grapherrormsg( codigo_error ) );
closegraph();

Juan Carlos Gutiérrez Barquero____________________________________________ 44


Bismarck Salvador Traña López__________________________________________UNI

printf( "ERROR: \"%s\" (%d)\n", mensaje_error, codigo_error );


getch();
}

Función installuserdriver

int far installuserdriver(char far *nombre, int huge (*detectar)(void));

Esta función permite al usuario añadir dispositivos adicionales de otras compañías o


grupos a la tabla interna BGI de los dispositivos. El argumento *nombre define el nombre
del fichero nuevo del dispositivo .BGI. El parámetro *detectar es un puntero a una función
opcional para autodetectar que puede ser o no ser provisto con el dispositivo nuevo. La
función de autodetectación espera no recibir ningún parámetro y retorna un valor entero.

La función installuserdriver retorna el parámetro del número del dispositivo que


hubiese sido pasado a la función initgraph para seleccionar un dispositivo nuevo.

Ejemplo:

/* Este programa no Funcionará, ya que se


necesitaría otra tarjeta gráfica
desconocida por las librerías gráficas de BGI.
Esto sólo es para poner un ejemplo.*/

#include <graphics.h>
#include <conio.h>
int huge detectarSMGGA( void ) {
int driver, modo, modo_sugerirdo=0;
detectgraph( &driver, &modo );
if( SMGGA == driver ) return modo_sugerido;
return grError;
}

void main() {
int driver, modo;
/* Intentamos instalar nuestra tarjeta gráfica:
** Súper Mega Guay Graphics Array (SMGGA)
** Ya sé que suena muy cursi, pero esto sólo es un ejemplo :)
*/
driver = installuserdriver( "SMGGA", detectarSMGGA );
/* Forzamos a que use nuestra función para autodetectar */
driver = DETECT;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
closegraph();
getch();
}

Función installuserfont

int far installuserfont(char far *nombre);

Juan Carlos Gutiérrez Barquero____________________________________________ 45


Bismarck Salvador Traña López__________________________________________UNI

Esta función carga un fichero de fuente escalable que no está provisto con el sistema BGI.
El parámetro *nombre especifica el nombre del fichero fuente a cargar, en el directorio de
inicio. El sistema gráfico puede tener hasta veinte fuentes instaladas a la vez.

La función installuserfont retorna el número de identificación de la fuente que es usada


para seccionar la fuente nueva a través de la función settextstyle. Si la tabla interna de
fuentes está llena, un valor de -11 (grError) es retornado, indicando un error.

Ejemplo

/* Este programa no Funcionará, ya que se


* necesitaría tener una fuente nueva
* y desconocida por las librerías gráficas de BGI.
* Esto sólo es para poner un ejemplo.
*/

#include <graphics.h>
#include<stdio.h>
# include<conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
int fuente_SMCF;
initgraph( &driver, &modo,"C:\\TC20\\BIN" );
/* Intentamos instalar nuestra fuente nueva:
** Súper Mega Chula Fuente (SMCF)
** Ya sé que suena muy cursi, pero esto sólo es un ejemplo :)
*/
if( (fuente_SMGF = installuserfont( "SMGF.CHR" )) != grError ){
settextstyle( fuente_SMGF, HORIZ_DIR, 4 );
printf("Instalación completada");getch();}

else{
settextstyle( DEFAULT_FONT, HORIZ_DIR, 4 );
printf("Error de al instalar la aplicación");getch();}
closegraph();
getch();
}

Función kbhit

int kbhit(void);

Revisa si se ha presionado una tecla. Cualesquier pulsadas disponibles pueden ser


recogidas con las Funciones getch o getche.

La función kbhit retorna 0 si no se ha registrado una pulsada de tecla; si lo fue, entonces


el valor retornado es distinto a cero.

Veremos el uso de esta función en el modo grafico; para utilizar esta función se tiene que
declarar la cabecera conio.h

Juan Carlos Gutiérrez Barquero____________________________________________ 46


Bismarck Salvador Traña López__________________________________________UNI

Ejemplo:

#include <graphics.h>
#include <conio.h>
# include <dos.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI,i;
initgraph( &driver, &modo, "C:\\TC20\\BIN" );
setcolor(BLUE);
i=1;
do{ delay(30000);
rectangle(10+i,10,100+i,100);
setfillstyle(i,BLUE);
floodfill(20,20,BLUE);
i++;
}while(!kbhit());
}

Función outtext

void far outtext(char far *cadena_texto);

Esta función es usada para mostrar una cadena de caracteres. El argumento


*cadena_texto define la cadena de texto a ser mostrado. La cadena es mostrado donde
está el cursor gráfico actualmente usando el color actual y fuente, dirección, valores, y
justificaciones de texto. La posición del cursor permanece sin ser cambiado al
menos que la justificación horizontal actual es LEFT_TEXT y la orientación del texto es
HORIZ_DIR. Cuando esto sea el caso, la posición del cursor es colocada horizontalmente
a la anchura del píxel de la cadena de texto. Además, cuando se use la fuente por
defecto, cualquier texto que se extiende a fuera del área gráfica actual es truncado.
Aunque la función outtext está diseñada para texto sin formato, texto con formato puede
ser mostrada a través del uso de un búfer de caracteres y la función sprintf.

La función outtext no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
char mensaje[40];
char nombre[25];
printf( "Escribe tu nombre: " );

Juan Carlos Gutiérrez Barquero____________________________________________ 47


Bismarck Salvador Traña López__________________________________________UNI

scanf( "%s", nombre );


sprintf( mensaje, "Hola %s!", nombre );
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
outtext( mensaje );
outtextxy( 300, 150, mensaje );
getch();
closegraph();
getch();
}

Función outtextxy

void far outtextxy(int x, int y, char far *cadena_texto);

Esta función es usada para mostrar una cadena de caracteres. El argumento


*cadena_texto define la cadena de texto a ser mostrado. La cadena es mostrada en la
posición descrita por los argumentos x e y usando el color actual y fuente, dirección,
valores, y justificaciones de texto. Cuando se use la fuente por defecto, cualquier texto
que se extiende fuera del área gráfica actual es truncado. Aunque la función outtextxy
está diseñada para texto sin formato, texto con formato puede ser mostrada a través del
uso de un búfer de caracteres y la función sprintf.

La función outtextxy no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
char mensaje[40];
char nombre[25];
printf( "Escribe tu nombre: " );
scanf( "%s", nombre );
sprintf( mensaje, "Hola %s!", nombre );
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
outtext( mensaje );
outtextxy( 300, 150, mensaje );
getch();
closegraph();
getch();
}

Función pieslice

void far pieslice(int x, int y,int comienzo_angulo, int final_angulo, int radio);

Esta función es usada para dibujar y rellenar un una cuña circular. La cuña circular está
centrada en el punto especificado por los argumentos x e y. La porción circular de la cuña

Juan Carlos Gutiérrez Barquero____________________________________________ 48


Bismarck Salvador Traña López__________________________________________UNI

comienza con el ángulo especificado por el argumento comienzo_angulo y se extiende


en un sentido contrario a las agujas del reloj al ángulo especificado por el argumento
final_angulo. La función pieslice considera este – el eje horizontal a la derecha del centro
– como su punto de referencia de 0 grados. El perímetro de la cuña es dibujado con el
color actual y es rellenado con la trama y color de relleno actual.

La función pieslice no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
pieslice( 300, 150, 45, 225, 50 );
getch(); /* Pausa */
closegraph();
getch();
}

Función registerbgidriver

int registerbgidriver(void (*driver)(void));

Esta función es usada para cargar y registrar un dispositivo gráfico. El argumento *driver
apunta a un dispositivo. Un fichero de dispositivo registrado puede ser tanto cargado
desde el disco o convertido en un formato .OBJ y ligado (o enlazado) dentro del
programa. Registrando el dispositivo de esta manera, el fichero .EXE no depende de un
fichero externo de dispositivo para poder ejecutarse.

La función registerbgidriver retorna número del dispositivo cuando tiene éxito. Un código
de error, un número negativo, es retornado si el dispositivo especificado es inválido.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
registerbgidriver(IBM8514LO);
initgraph( &driver, &modo, "C:\\TC20\\BIN" );
rectangle( 20, 20, 400, 300 );
getch(); /* Pausa */
closegraph();
getch();
}

Juan Carlos Gutiérrez Barquero____________________________________________ 49


Bismarck Salvador Traña López__________________________________________UNI

Función registerbgifont

int registerbgifont(void (*fuente)(void));

Esta función es usada para informar al sistema que la fuente apuntada por el argumento
*fuente fue incluida durante el enlazo. Un fichero de fuente registrado ha de ser
convertido en un fichero objeto .OBJ y ligado (o enlazado) dentro del programa.
Registrando la fuente de esta manera, el fichero .EXE no depende de un fichero externo
de fuentes para poder ejecutarse.

Nota: La fuente de defecto es la única que está disponible en el programa, ya que forma
parte del sistema gráfico; no es necesario ligarlo al programa.

La función registerbgifont retorna número del dispositivo cuando tiene éxito. Un código de
error, un número negativo, es retornado si el dispositivo especificado es inválido.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
registerbgifont(1);
initgraph( &driver, &modo,"C:\\TC20\\BIN" );
outtext( "Esto es una prueba con la fuente \"Sans Serif\"" );
getch(); /* Pausa */
closegraph();
getch();
}

Función restorecrtmode

void far restorecrtmode(void);

Esta función es usada para reiniciar el modo gráfico del vídeo al modo en uso anterior a la
inicialización del sistema gráfico. Esta función suele ser usada en conjunción con la
función setgraphmode para cambiar entre ambos modos de texto y de gráficos.

La función restorecrtmode no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;

Juan Carlos Gutiérrez Barquero____________________________________________ 50


Bismarck Salvador Traña López__________________________________________UNI

int modo = EGAHI;


initgraph( &driver, &modo, “C:\\TC20\\BIN” );
outtext( "Esto es una prueba para cambiar entre modo gráfico..." );
getch();
restorecrtmode();
printf( "...y en modo texto.\nPulsa una tecla para volver\n" );
getch();
setgraphmode( modo );
rectangle( 200, 100, 400, 250 );
getch(); /* Pausa */
closegraph();
getch();
}

Función sector

void far sector(int x, int y, int comienzo_angulo, int final_angulo, int x_radio, int y_radio);

Esta función es usada para dibujar y rellenar la parte de una elipse. El centro de la cuña
elíptica es especificado por los argumentos x e y. El argumento x_radio especifica el
radio horizontal y el argumento y_radio especifica el radio vertical de la cuña elíptica. La
cuña elíptica comienza al ángulo especificado por el argumento comienzo_angulo y es
dibujado en la dirección contraria al de las agujas del reloj hasta llegar al ángulo
especificado por el argumento final_angulo. La cuña elíptica es dibujado con el perímetro
en el color actual y rellenada con el color de relleno y la trama de relleno actuales.

La función sector no retorna ningún valor.

Ejemplo:
#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
setfillstyle( SOLID_FILL, 6 );
sector( 300, 150, 45, -45, 150, 50 );
getch(); /* Pausa */
closegraph();
getch();
}

Función setactivepage

void far setactivepage(int pagina);

Esta función es usada para especificar un número de página que representa una sección
de memoria del vídeo donde todos los datos gráficos para mostrar son enviados. Está
sección de memoria se denomina una página activa. El argumento pagina específica el
número de la página activa. Para usar esta función con eficacia, el adaptador de vídeo
usado debe ser EGA o VGA y tener suficiente memoria para soportar múltiples páginas

Juan Carlos Gutiérrez Barquero____________________________________________ 51


Bismarck Salvador Traña López__________________________________________UNI

para gráficos. Esta función es usada con la función setvisualpage para dibujar páginas no
visuales y para crear animación.

La función setactivepage no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
int visual=1;
printf( "Instrucciones:\nPulsa el espacio para cambiar de "
"página, cualquier otra tecla para salir\n" );
printf( "(Pulsa cualquier tecla para entrar en modo gráfico)\n" );
getch();
initgraph( &driver, &modo, "C:\\TC20\\BIN" );
setactivepage( 0 );
setfillstyle( SOLID_FILL, 6 );
sector( 300, 150, 45, 315, 150, 50 );
setactivepage( 1 );
setfillstyle( SOLID_FILL, 6 );
sector( 300, 150, 90, 270, 150, 50 );
while( getch() == ' ' ) {
setvisualpage( visual );
visual = 0==visual ? 1 : 0;
}
closegraph();
getch();
}

Función setallpalette

void far setallpalette(struct palettetype far *paleta);

Esta función es usada para asignar la paleta actual a la paleta definida en la estructura del
tipo palettetype que es apuntado por el argumento *paleta. Todos los colores de la paleta
actual son asignados a aquéllos definidos en la estructura palettetype. La síntaxis de la
estructura palettetype es:

#define MAXCOLORS 15
struct palettetype {
unsigned char size;
signed char colors[MAXCOLORS+1];
}

El campo size indica el número de colores de la paleta actual. El campo colors es un


array que contiene los valores numéricos que representan los colores que ofrece el

Juan Carlos Gutiérrez Barquero____________________________________________ 52


Bismarck Salvador Traña López__________________________________________UNI

dispositivo en su paleta de colores. Si la entrada de cualquier elemento del array es -1, el


valor del color de ese elemento no cambiará.

Nota: Recuerda que todos los cambios hechos a la paleta tiene un efecto visual inmediato
y que la función setallpalette no debería usarse con el dispositivo IBM-8514.

La función setallpalette no retorna ningún valor; sin embargo, si los valores pasados son
inválidos, entonces la función graphresult retorna grError (-11) y la paleta no es alterada.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
struct palettetype palette;
int size, temp, i, y=0;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
getpalette( &palette );
size = palette.size;
for( i=0; i<size; i++ ) {
y += 30;
setcolor( palette.colors[i] );
line( 20, y, 520, y );
}
getch(); /* Pausa */
for( i=0; i<size/2; i++ ) {
temp = palette.colors[i];
palette.colors[i] = palette.colors[size-1-i];
palette.colors[size-1-i] = temp;
}
setallpalette( &palette );
getch(); /* Pausa */
closegraph();
getch();
}

Función setaspectratio

void far setaspectratio(int x_proporcion, int y_proporcion);

Esta función es usada para modificar la proporción anchura-altura del modo gráfico actual.
La proporción anchura-altura puede definirse como la proporción de la anchura del píxel
del modo gráfico y la altura del píxel. Esta proporción es usada por el sistema gráfico para
calcular círculos y arcos. Por ello, alterando la proporción anchura-altura afectará la
visualización de estas Funciones. La función getaspectratio puede ser usada para obtener
las opciones por defecto del modo actual anteriormente a ser modificados.

La función setaspectratio no retorna ningún valor.

Juan Carlos Gutiérrez Barquero____________________________________________ 53


Bismarck Salvador Traña López__________________________________________UNI

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA, modo = EGAHI,x_proporcion, y_proporcion;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
getaspectratio( &x_proporcion, &y_proporcion );
circle( 300, 150, 50 );
getch(); /* Pausa */
setaspectratio( 2*x_proporcion, y_proporcion );
circle( 300, 150, 50 );
getch(); /* Pausa */
closegraph();
getch();
}

Función setgraphbufsize

unsigned far setgraphbufsize(unsigned bufer_tam);

Esta función es usada para cambiar el tamaño del búfer gráfico interno como es asignado
por la función initgraph cuando el sistema gráfico es inicializado. El búfer gráfico es usado
por varias Funciones gráficos; por ello, se debería tener un mayor cuidado cuando se
altera este búfer del tamaño por defecto de 4096. La función setgraphbufsize se debería
llamar antes de llamar a la función initgraph.

La función setgraphbufsize retorna el tamaño anterior del búfer gráfico interno.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
int buf_inicial, buf_nuevo=10000;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
buf_inicial = setgraphbufsize( buf_nuevo );
closegraph();
printf( "Búfer inicial: %d\tBúfer nuevo: %d\n", buf_inicial, buf_nuevo );
getch();
}
Función setgraphmode

void far setgraphmode(int modo);


Esta función es usada para seleccionar el modo gráfico actual pero solamente cuando el
sistema gráfico haya sido inicializado con la función initgraph. El argumento modo define
el modo a usar según el dispositivo actual. Además de seleccionar un nuevo modo, la
función setgraphmode despeja la pantalla y reinicia todas las opciones gráficas a sus

Juan Carlos Gutiérrez Barquero____________________________________________ 54


Bismarck Salvador Traña López__________________________________________UNI

valores por defecto. Esta función suele usarse conjuntamente con restorecrtmode para
cambiar entre modos gráficos y de texto.

La función setgraphmode no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA, modo = EGAHI;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
outtext( "Esto es una prueba para cambiar entre modo gráfico..." );
getch();
restorecrtmode();
printf( "...y en modo texto.\nPulsa una tecla para volver\n" );
getch();
setgraphmode( modo );
rectangle( 200, 100, 400, 250 );
getch(); /* Pausa */
closegraph();
getch();
}

Función setlinestyle

void far setlinestyle(int estilo,unsigned trama, int grosor);

Esta función es usada para definir las características de líneas para líneas rectas.
El argumento estilo específica la trama de línea predefinida para su uso. El argumento
trama es una trama de 16 bits que describe el estilo de línea cuando el argumento estilo
es USERBIT_LINE, ó 4. Un bit 1 en esta trama indica que el píxel correspondiente será
asignado el color actual. Un bit 0 indica que el píxel correspondiente no será alterado. El
argumento grosor define el grosor de la línea. Existen varios valores para los diferentes
estilos y grosores de líneas rectas.

La función setlinestyle no retorna ningún valor; sin embargo, si un argumento es inválido,


entonces la función graphresult retorna grError (11).

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA,modo = EGAHI;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
setlinestyle( DOTTED_LINE, 0, THICK_WIDTH );
line( 200, 300, 400, 50 );
getch(); /* Pausa */
closegraph();

Juan Carlos Gutiérrez Barquero____________________________________________ 55


Bismarck Salvador Traña López__________________________________________UNI

getch();
}

Función setpalette

void far setpalette(int num_paleta, int color);

Esta función es usada para modificar una sola entrada en la paleta actual. El argumento
num_paleta especifica el miembro de la paleta a cambiar. El argumento color especifica
el nuevo valor de color para el miembro de la paleta. Existen varios valores para los
colores dependiendo del dispositivo.
Nota: Recuerda que todos los cambios hechos a la paleta tiene un efecto visual inmediato
y que la función setpalette no debería usarse con el dispositivo IBM-8514.

La función setpalette no retorna ningún valor; sin embargo, si los valores pasados son
inválidos, entonces la función graphresult retorna grError (-11) y la paleta no es alterada.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
struct palettetype palette;
int size, temp, i, y=0;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
getpalette( &palette );
size = palette.size;
for( i=0; i<size; i++ ) {
y += 30;
setcolor( palette.colors[i] );
line( 20, y, 520, y );
}
getch(); /* Pausa */
for( i=0; i<size/2; i++ ) {
temp = palette.colors[i];
setpalette( i, palette.colors[size-1-i] );
setpalette( size-1-i, temp );
}
getch(); /* Pausa */
closegraph();
getch();
}

Función setrgbpalette

void far setrgbpalette(int num_paleta,int rojo, int verde, int azul);

Esta función es para usarse con los dispositivos de IBM 8514 y VGA. El argumento
num_paleta especifica el miembro de la paleta a ser modificado. Para la IBM 8514 (y

Juan Carlos Gutiérrez Barquero____________________________________________ 56


Bismarck Salvador Traña López__________________________________________UNI

para el modo de 256K de la VGA), el intervalo de la paleta es de 0 á 255. Para los modos
de VGA, el intervalo es de 0 á 15. Los argumentos rojo, verde, y azul especifican la
intensidad del color para el miembro de la paleta. De cada byte (de cada argumento) sólo
los seis bits más significativos son cargados en la paleta. Por razones de compatibilidad
con otros adaptadores gráficos de IBM, el dispositivo BGI define las primeras dieciséis
entradas a la paleta de la IBM 8514 a los colores por defecto de la EGA/VGA.

Nota: Recuerda que todos los cambios hechos a la paleta tiene un efecto visual inmediato
y que la función setrgbpalette no debería usarse con el dispositivo IBM-8514.

La función setrgbpalette no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
struct palettetype palette;
int size, i, y=0;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
getpalette( &palette );
size = palette.size;
for( i=0; i<size; i++ ) {
y += 30;
setcolor( palette.colors[i] );
line( 20, y, 520, y );
}
getch(); /* Pausa */
for( i=0; i<size; i++ )
setrgbpalette( i, 2*i+33, 42, 63-4*i ); /* Tonos de naranja y azul */
getch(); /* Pausa */
closegraph();
getch();
}

Función settextjustify

void far settextjustify(int horizontal, int vertical);

Esta función es usada para especificar el método en el cual el texto es colocado en la


pantalla con relación a la posición del cursor. El argumento horizontal define la
justificación horizontal, mientras que el argumento vertical indica la justificación vertical.
Existen varios valores y constantes para las justificaciones.

La función settextjustify no retorna ningún valor; sin embargo, si los valores pasados son
inválidos, entonces la función graphresult retorna grError (-11) y la paleta no es alterada.

Ejemplo:

Juan Carlos Gutiérrez Barquero____________________________________________ 57


Bismarck Salvador Traña López__________________________________________UNI

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
settextjustify( RIGHT_TEXT, BOTTOM_TEXT );
moveto(300, 200);
outtext( "(RIGHT_TEXT, BOTTOM_TEXT)" );
settextjustify( RIGHT_TEXT, TOP_TEXT );
moveto(300, 200);
outtext( "(RIGHT_TEXT, TOP_TEXT)" );
settextjustify( LEFT_TEXT, BOTTOM_TEXT );
moveto(300, 200);
outtext( "(LEFT_TEXT, BOTTOM_TEXT)" );
settextjustify( LEFT_TEXT, TOP_TEXT );
moveto(300, 200);
outtext( "(LEFT_TEXT, TOP_TEXT)" );
setcolor( 1 );
line( 300, 200, 300, 100 );
setcolor( 2 );
line( 300, 200, 300, 300 );
setcolor( 3 );
line( 300, 200, 100, 200 );
setcolor( 4 );
line( 300, 200, 500, 200 );
getch();
closegraph();
getch();
}

Función settextstyle

void far settextstyle(int fuente,int orientacion, int tam_caracter);

Esta función es usada para especificar las características para la salida de texto con
fuente. El argumento fuente especifica la fuente registrada a usar. La fuente ha de estar
registrada para resultados predecibles; es decir, usa registerbgifont antes de usar esta
función. El argumento orientacion especifica la orientación en que el texto ha de ser
mostrado. La orientación por defecto es HORIZ_DIR. El argumento tam_caracter define l
factor por el cual la fuente actual será multiplicada. Un valor distinto a 0 para el argumento
tam_caracter puede ser usado con fuentes escalables o de bitmap. Sin embargo, un
valor distinto a 0 para el argumento tam_caracter, el cual selecciona el tamaño del
carácter definido por el usuario usando la función setusercharsize, solamente Funciona
con fuentes escalables. El argumento tam_caracter puede agrandar el tamaño de la
fuente hasta 10 veces su tamaño normal. Existen varios valores y constantes para las
justificaciones.

La función settextstyle no retorna ningún valor.

Juan Carlos Gutiérrez Barquero____________________________________________ 58


Bismarck Salvador Traña López__________________________________________UNI

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
char mensaje[40];
char nombre[25];
printf( "Escribe tu nombre: " );
scanf( "%s", nombre );
sprintf( mensaje, "Hola %s!", nombre );
/* Esta fuente ha de ser enlazada antes de poder registrarla
registerbgifont( sansserif_font );
*/
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
settextstyle( DEFAULT_FONT, 0, 2 );
outtextxy( 100, 50, mensaje );
settextstyle( DEFAULT_FONT, 1, 2 );
outtextxy( 200, 125, mensaje );
settextstyle( SANS_SERIF_FONT, 1, 3 );
outtextxy( 400, 150, mensaje );
getch();
closegraph();
getch();
}

Función setusercharsize

void far setusercharsize(int x_dividendo, int x_divisor,int y_dividendo, int y_divisor);

Esta función extablece las características de fuentes escalables. Para que esta función
afecte el tamaño del carácter, el argumento tam_caracter de la función settextstyle debe
ser 0. La anchura del carácter se establece con los argumentos x_dividendo y x_divisor
que representan la proporción. Similarmente, los argumentos y_dividendo e y_divisor
especifican la altura del carácter.

La función setusercharsize no retorna ningún valor.


Ejemplo:
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
char mensaje[40];
char nombre[25];

Juan Carlos Gutiérrez Barquero____________________________________________ 59


Bismarck Salvador Traña López__________________________________________UNI

printf( "Escribe tu nombre: " );


scanf( "%s", nombre );
sprintf( mensaje, "Hola %s!", nombre );
initgraph( &driver, &modo, “C:\\TC20\\BIN” );
settextstyle( SANS_SERIF_FONT, 0, 0 );
setusercharsize( 1, 4, 1, 2 ); /* 25% de ancho; 50% de alto */
outtextxy( 100, 50, mensaje );
settextstyle( SANS_SERIF_FONT, 0, 1 );
outtextxy( 100, 125, mensaje );
settextstyle( SANS_SERIF_FONT, 1, 0 );
setusercharsize( 1, 2, 3, 4 ); /* 50% de ancho; 75% de alto */
outtextxy( 400, 150, mensaje );
getch();
closegraph();
getch();
}

Función setviewport

void far setviewport(int izquierda, int superior,int derecha, int inferior, int recorte_banderin);
Esta función es usada para definir el área gráfico. La esquina superior izquierda del área
gráfica está definida por los argumentos izquierda y superior. Estos argumentos
corresponden a los valores x e y de la esquina superior izquierda. Similarmente, los
argumentos derecha e inferior definen la esquina inferior derecha del área gráfica. El
argumento recorte_banderin define si los datos para la salida gráfica serán recortados
por el borde del área gráfico. Un valor de 0 para recorte_banderin indica que los datos
de salida no serán recortados, mientras que un valor distinto a 0 indica que los datos
serán recortados. Cuando el área gráfica es inicializada, la posición del cursor será
mudado a la posición (0,0) (la esquina superior izquierda). Todos los datos de salida
después de que el área gráfica haya sido inicializada serán con relación a este punto. El
área gráfica por defecto cubre la pantalla entera.

La función setviewport no retorna ningún valor; sin embargo, si los valores pasados son
inválidos, entonces la función graphresult retorna grError (-11) y el área gráfica no será
alterada.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA, modo = EGAHI;
initgraph( &driver, &modo, "C:\\TC20\\BIN" );
lineto( 100, 100 );
outtextxy( 15, 5, "Inicial" );
getch();
setviewport( 250, 200, 450, 300, 0 );
setcolor( 9 );
lineto( 100, 100 );
outtextxy( 15, 5, "Nueva" );
moveto( 0, 0 );

Juan Carlos Gutiérrez Barquero____________________________________________ 60


Bismarck Salvador Traña López__________________________________________UNI

lineto( -50, -20 ); /* Fuera del área */


getch();
setviewport( 250, 200, 450, 300, 1 );
setcolor( 4 );
moveto( 120, 40 );
lineto( 150, -20 ); /* Fuera del área */
outtextxy( 25, 15, "Con recorte" );
getch(); /* Pausa */
closegraph();
getch();
}

Función setvisualpage

void far setvisualpage(int pagina);

Esta función es usada para establecer la página visual como es especificado por el
argumento pagina. Una página es una sección de memoria donde se guarda la
información del vídeo. Cuando se usa con un sistema (EGA o VGA) con suficiente
memoria de vídeo para soportar múltiples páginas de gráficos, la función setvisualpage
(junto con la función setactivepage) permite al programador crear gráficos en páginas
escondidas y pasar de página entre las que se han definido con información gráfica. Esto
es la base para crear animación.

La función setvisualpage no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
int visual=1;
printf( "Instrucciones:\nPulsa el espacio para cambiar de página, cualquier otra tecla para
salir\n" );
printf( "(Pulsa cualquier tecla para entrar en modo gráfico)\n" );
getch();
initgraph( &driver, &modo, "C:\\TC20\\BIN" );
setactivepage( 0 );
setfillstyle( SOLID_FILL, 6 );
sector( 300, 150, 45, 315, 150, 50 );
setactivepage( 1 );
setfillstyle( SOLID_FILL, 6 );
sector( 300, 150, 90, 270, 150, 50 );
while( getch() == ' ' ) {
setvisualpage( visual );
visual = 0==visual ? 1 : 0;
}
closegraph();

Juan Carlos Gutiérrez Barquero____________________________________________ 61


Bismarck Salvador Traña López__________________________________________UNI

getch();
}

Función setwritemode

void far setwritemode(int modo);

Esta función es usada para establecer el modo lógico de escritura para líneas rectas. El
argumento modo especifica el modo de escritura, el cual determina la interacción entre
valores de píxeles existentes y los valores de píxeles en la línea. Existen dos valores para
los modos de escritura.

La función setwritemode no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
initgraph( &driver, &modo, "C:\\TC20\\BIN" );
setfillstyle( SOLID_FILL, 1 );
bar( 50, 50, 500, 300 );
setwritemode( COPY_PUT );
setcolor( 10 );
line( 20, 60, 220, 100 );
setwritemode( XOR_PUT );
line( 20, 80, 220, 120 );
getch();
closegraph();
getch();
}

Función textheight

int far textheight(char far *texto);

Esta función es usada para determinar la altura, en píxeles, de la cadena de texto


especificada por el argumento *texto. La altura del texto se determina usando la fuente
actual y el tamaño del carácter.

La función textheight retorna la altura, en píxeles, del texto especificado por el argumento.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;

Juan Carlos Gutiérrez Barquero____________________________________________ 62


Bismarck Salvador Traña López__________________________________________UNI

int modo = EGAHI;


int anchura, altura;
char mensaje[5] = "Hola";

initgraph( &driver, &modo, "C:\\TC20\\BIN" );


outtextxy(10,10,mensaje);
anchura = textwidth( mensaje );
altura = textheight( mensaje );
getch();
closegraph();
printf( "El mensaje: \"%s\" tiene de anchura: %d y altura: %d\n", mensaje,
anchura, altura );
printf( "Pulsa una tecla para continuar...\n" );
getch();
}

Función textwidth

int far textwidth(char far *texto);

Esta función es usada para determinar la anchura, en píxeles, de la cadena de texto


especificada por el argumento *texto. La anchura del texto se determina usando la fuente
actual y el tamaño del carácter.

La función textwidth retorna la anchura, en píxeles, del texto especificado por el


argumento.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver = EGA;
int modo = EGAHI;
int anchura, altura;
char mensaje[5] = "Hola";

initgraph( &driver, &modo, "C:\\TC20\\BIN" );


outtextxy(10,10,mensaje);
anchura = textwidth( mensaje );
altura = textheight( mensaje );
getch();
closegraph();
printf( "El mensaje: \"%s\" tiene de anchura: %d y altura: %d\n", mensaje,
anchura, altura );
printf( "Pulsa una tecla para continuar...\n" );
getch();
}

Juan Carlos Gutiérrez Barquero____________________________________________ 63


Bismarck Salvador Traña López__________________________________________UNI

Estructuras del modo gráfico.

Estructura arccoordstype

struct arccoordstype {
int x, y;
int xstart, ystart;
int xend, yend;
};

Los miembros x e y definen el centro del arco. Los miembros xstart e ystart definen las
coordenadas x e y del punto de comienzo del arco. Similarmente, los miembros xend e yend
definen las coordenadas x e y del punto de final del arco.

Esta estructura se usa como parámetro en la función getarccoords, que se usa para recoger las
coordenadas del centro, y los puntos del comienzo y final de la última llamada con éxito a la
función arc.

Ejemplo:

#include <graphics.h>
#include <conio.h>
void main() {
int driver = EGA,modo = EGAHI,radio;
struct arccoordstype info_arco;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
for( radio=25; radio<=100; radio+=25 ) {
arc( 300, 150, 45, 315, radio );
getarccoords( &info_arco );
moveto( info_arco.xstart, info_arco.ystart );
lineto( info_arco.xend, info_arco.yend );
}
getch(); /* Pausa */
closegraph();
}

Estructura fillsettingstype

struct fillsettingstype {
int pattern;
int color;
};

Esta estructura se usa para obtener la información de tramas de relleno, mediante getfillsettings.
El campo pattern es la trama y el campo color es el color de relleno de la trama. Existen trece
valores ya definidos para tramas.

Juan Carlos Gutiérrez Barquero____________________________________________ 64


Bismarck Salvador Traña López__________________________________________UNI

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int gdriver = EGA,gmodo = EGAHI;
struct fillsettingstype info;
initgraph( &gdriver, &gmodo, "C:\\tc20\\BIN" );
getfillsettings( &info );
bar( 50, 50, 350, 300 );
getch(); /* Pausa */
closegraph();
printf( "Trama de relleno: %d\tColor de relleno: %d\n",
info.pattern, info.color );
getch();
}

Estructura linesettingstype.

struct linesettingstype {
int linestyle;
unsigned upattern;
int thickness;
}

Esta estructura se usa para obtener la información actual para las líneas mediante la función
getlinesettings. El campo linestyle es el estilo de la línea recta. El campo upattern es la trama
de la línea del usuario solamente cuando el campo linestyle es igual a USERBIT_LINE, ó 4.
Cuando esto sea el caso, el miembro upattern contiene una trama de línea definido por el
usuario de 16 bits. Un bit 1 en esta trama indica que el píxel correspondiente será asignado el
color actual. Un bit 0 indica que el píxel correspondiente no será alterado. El campo thickness
es el grosor de la línea.

Existen varios valores para los diferentes estilos y grosores de líneas rectas.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int gdriver = EGA;
int gmodo = EGAHI;
struct linesettingstype info;
initgraph( &gdriver, &gmodo, "C:\\tc20\\BIN" );
setlinestyle( DOTTED_LINE, 0xFF33, THICK_WIDTH );
circle( 350, 250, 50 );

Juan Carlos Gutiérrez Barquero____________________________________________ 65


Bismarck Salvador Traña López__________________________________________UNI

getlinesettings( &info );
getch(); /* Pausa */
closegraph();
printf( "Líneas rectas.\nEstilo: %d\tTrama: %X\tGrosor: %d\n",info.linestyle, info.upattern,
info.thickness );
getch();
}

Estructura palettetype

#define MAXCOLORS 15
struct palettetype {
unsigned char size;
signed char colors[MAXCOLORS+1];
};

Esta estructura se usa para obtener una los datos que definen la paleta según cada dispositivo.
El campo size indica el tamaño de la paleta. El campo colors contiene los valores numéricos
que representan los colores que ofrece el dispositivo en su paleta de colores.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h&t;
void main() {
int driver = EGA,modo = EGAHI,i;
struct palettetype *palette = NULL;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
palette = getpalettetype();
circle( 300, 150, 50 );
getch(); /* Pausa */
closegraph();
printf( "Paleta\n\nTamaño: %d\nColores: %d",
palette->size, palette->colors[0] );
for( i=1; i<palette->size; i++ )
printf( ", %d", palette->colors[i] );
printf( "\n" );
getch();
}

Estructura textsettingstype

struct textsettingstype {
int font;
int direction;
int charsize;
int horiz;
int vert;
};

Juan Carlos Gutiérrez Barquero____________________________________________ 66


Bismarck Salvador Traña López__________________________________________UNI

Esta estructura se usa para obtener información acerca de la fuente gráfica actual mediante la
función getextsettings. Esta estructura contiene información de la fuente actual en uso, la
orientación del texto, el tamaño del carácter, y la justificación horizontal y vertical.
Existen varios valores para describir el tipo, la orientación, y justificación de fuentes.

Ejemplo:

#include <graphics.h>
#include <stdio.h>
void main() {
int driver = EGA,modo = EGAHI;
struct textsettingstype info;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
gettextsettings( &info );
closegraph();
printf( "Texto\n\nFuente: %d\tSentido: %d\tTamaño: %d\n"
"Justificación:\nHorizontal: %d, Vertical: %d\n",
info.font, info.direction, info.charsize, info.horiz, info.vert);
getch();
}

Estructura viewporttype

struct viewporttype {
int left, top;
int right, bottom;
int clip;
};
Esta estructura se usa para obtener información acerca del área gráfica actual mediante la
función getviewsettings. Esta estructura contiene información acerca de las esquinas superior
izquierda e inferior derecha, también como el banderín de recorte del área gráfica.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver =IBM8514LO,modo=IBM8514HI;
struct viewporttype info;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
getviewsettings( &info );
closegraph();
printf( "Pantalla\n\nIzquierda: %d\tSuperior: %d\tDerecha: %d\t"
"Inferior: %d\tBanderín: %d\n",
info.left, info.top, info.right, info.bottom, info.clip);
getch();
}

Juan Carlos Gutiérrez Barquero____________________________________________ 67


Bismarck Salvador Traña López__________________________________________UNI

Estructura time

struct viewporttype {
int left, top;
int right, bottom;
int clip;
};
Esta estructura se usa para obtener información acerca del área gráfica actual mediante la
función getviewsettings. Esta estructura contiene información acerca de las esquinas superior
izquierda e inferior derecha, también como el banderín de recorte del área gráfica.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
void main() {
int driver =IBM8514LO,modo=IBM8514HI;
struct viewporttype info;
initgraph( &driver, &modo, "C:\\tc20\\BIN" );
getviewsettings( &info );
closegraph();
printf( "Pantalla\n\nIzquierda: %d\tSuperior: %d\tDerecha: %d\t"
"Inferior: %d\tBanderín: %d\n",
info.left, info.top, info.right, info.bottom, info.clip);
getch();
}

 Funciones de Rasterop.

Las funciones del rasterop, son auellas que realizar la accion de mover una imagen ya
sea transportando una imagen o copiandola.

Las funciones pertenecientes al rasterop son:

1. imagesize
2. malloc
3. getimage
4. putimage

Las cuatro complementan la operación de mover una imagen almacenándola en una


variable temporal de tipo buffer.

Procederemos a explicar cada una de ellas para realizar buestros ejercicios de


movimiento.

Juan Carlos Gutiérrez Barquero____________________________________________ 68


Bismarck Salvador Traña López__________________________________________UNI

Función imagesize

unsigned far imagesize(int izquierda,int superior, int derecha, int inferior);

Esta función es usada para determinar el tamaño del búfer necesitado para almacenar
una imagen con la función getimage. Los argumentos izquierda y superior definen las
coordenadas x e y de la esquina superior izquierda de la imagen rectangular.
Similarmente, los argumentos derecha y inferior definen las coordenadas x e y de la
esquina inferior derecha de la imagen rectangular.

La función imagesize retorna el número actual de bytes necesarios, si el tamaño requerido


es menor que 64 Kb menos 1 byte. Si esto no es el caso, el valor retornado es 0xFFFF, ó
-1.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdlib.h>
void main() {
int driver = EGA;
int modo = EGAHI;
void *imagen;
int imagentam;
initgraph( &driver, &modo, “C:\\tc20\\BIN” );
setfillstyle( SLASH_FILL, 5 );
bar( 50, 50, 350, 300 );
imagentam = imagesize( 50, 50, 100, 100 );
closegraph();
getch();
}

/*En este ejercicio solo calculamos el tamaño de la imagen*/

Función malloc

void *malloc(size_t size)

Función definida en conio.h. El tamaño está en bytes. Regresa un puntero(indicador)al


bloque recientemente asignado, o Nulo (NULL) si no existe suficiente espacio en nuevo
bloque. Si el tamaño==0, este regresa un Nulo(NULL) .

Talvez no comprenda lo que quiera dar a entender pero ala hora del ejercicio lo
entenderán.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdlib.h>
void main() {

Juan Carlos Gutiérrez Barquero____________________________________________ 69


Bismarck Salvador Traña López__________________________________________UNI

int driver = EGA;


int modo = EGAHI;
void *imagen;
int imagentam;
initgraph( &driver, &modo, “C:\\tc20\\BIN” );
setfillstyle( SLASH_FILL, 5 );
bar( 50, 50, 350, 300 );
imagentam = imagesize( 50, 50, 100, 100 );
imagen = malloc(imagesize( 50, 50, 100, 100 ));
free( imagen );
closegraph();
getch();
}

En este ejemplo calculamos en tamaño de la imagen y el número de bytes necesarios


para contenerla

Función getimage

void far getimage(int izquierda, int superior,int derecha, int inferior, void far *imagen);

Esta función es usada para guardar una porción rectangular de la pantalla para un uso
posterior. La esquina superior izquierda del área rectangular que ha de ser guardada está
definida por los argumentos izquierda y superior. Estos argumentos representan las
coordenades x e y de la esquina superior izquierda, respectivamente. Los argumentos
derecha e inferior definen la esquina inferior derecha de la imagen rectangular. Estos
argumentos definen las coordenades x e y de la esquina inferior derecha,
respectivamente. El argumento *image apunta al búfer de memoria donde la imagen está
guardada.

La función getimage no retorna ningún valor, directamente.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdlib.h>
void main() {
int driver = EGA;
int modo = EGAHI;
void *imagen;
int imagentam;
initgraph( &driver, &modo, “C:\\tc20\\BIN” );
setfillstyle( SLASH_FILL, 5 );
bar( 50, 50, 350, 300 );
imagentam = imagesize( 50, 50, 100, 100 );
imagen = malloc( imagentam );
getimage( 50, 50, 100, 100, imagen );
free( imagen );
getch();
}

Juan Carlos Gutiérrez Barquero____________________________________________ 70


Bismarck Salvador Traña López__________________________________________UNI

/*En este ejemplo solo conseguimos el tamaño de la imagen y guardamos la imagen en


un buffer*/

Función putimage

void far putimage(int izquierda , int superior, void far *image, int acción);

Esta función coloca una imagen que fue previamente guardada con la función getimage
en la pantalla. La esquina superior izquierda donde será colocada la imagen está definida
por los argumentos izquierda y superior. Estos argumentos representan las coordenadas
x e y de la esquina superior izquierda, respectivamente. El argumento *image apunta al
búfer de memoria donde la imagen está guardada. La imagen se coloca en la pantalla con
la acción definida en el argumento acción. Los valores y consonantes usados por el
argumento accion se describen a continuación ya que pudieron haberlos olvidado:

Constante Valor Significado


COPY_PUT 0 Sobrescribir los píxeles existentes
XOR_PUT 1 Operación OR Exclusivo con los píxeles
OR_PUT 2 Operación OR Inclusivo con los píxeles
AND_PUT 3 Operación AND con los píxels
NOT_PUT 4 Invertir la imagen

La función putimage no retorna ningún valor.

Ejemplo:

#include <graphics.h>
#include <conio.h>
#include <stdlib.h>
void main() {
int driver = EGA,modo = EGAHI, imagentam;
void *imagen;
initgraph( &driver, &modo, “C:\\tc20\\BIN” );
setfillstyle( SLASH_FILL, 5 );
bar( 50, 50, 350, 300 );
imagentam = imagesize( 50, 50, 100, 100 );
imagen = malloc( imagentam );
getimage( 50, 50, 100, 100, imagen );
putimage( 400, 50, imagen, COPY_PUT );
getch();
putimage( 400, 110, imagen, COPY_PUT );
getch(); /* Pausa */
closegraph();
free( imagen );
getch();
}

Este es el pequeño ejemplo del rasterop

Juan Carlos Gutiérrez Barquero____________________________________________ 71


Bismarck Salvador Traña López__________________________________________UNI

Otros ejemplo de Rasterop.

1. Un círculo que realiza un movimiento iliptico.

# include <conio.h>
# include <graphics.h>
# include <math.h>
# include <dos.h>
void inicializar(void);
void cuerpo(void);
void main(void)
{
inicializar();
cuerpo();
}
void inicializar(void)
{
int drive=DETECT,modo;
initgraph(&drive,&modo,"c:\\tc20\\bin");
}

void cuerpo()
{
double x,y;
while(!kbhit())
{
x=-180;
while(x<=180 && !kbhit())
{
y=sqrt(15000*(1-((x*x)/32400)));
circle(x+310,240-y,20);
delay(15000);
x+=1;
cleardevice();
}
x=180;
while(x>=-180 && !kbhit())
{
y=-1*sqrt(15000*(1-((x*x)/32400)));
circle(x+310,240-y,20);
delay(15000);
x-=1;
cleardevice();
}
}

Juan Carlos Gutiérrez Barquero____________________________________________ 72


Bismarck Salvador Traña López__________________________________________UNI

getch();
}

Realice un programa que muestre el movimiento de la tierra alrededor de la tierra y que


mustre los meses del año conforme valla avanzando la image.

# include <conio.h>
# include <stdio.h>
# include <stdlib.h>
# include <graphics.h>
# include <math.h>
# include <dos.h>
# include <process.h>
void inicializar(void);

void main()
{
float y2,y1,x,x2;
void far *buffer;
system("cls");
inicializar();
buffer=malloc(imagesize(0,20,20,20));
do
{
outtextxy(150,50,"MOVIMIENTO DE ROTACION DE LA TIERRA al rededor del sol");
x2=-300;
while(x2<=300 && !kbhit())
{
putpixel(random(639),random(480),WHITE);
putpixel(random(639),random(480),YELLOW);
if((x2>=-300)&&(x2<-200))
{
rectangle(480,440,600,460);
setfillstyle(1,BLUE);
floodfill(485,450,WHITE);
outtextxy(500,450,"Enero");
}
else
{
if((x2>=-200)&&(x2<-100))
{
setcolor(BLUE);outtextxy(500,450,"Enero");
setcolor(RED);
rectangle(480,440,600,460);
setfillstyle(1,GREEN);
floodfill(485,450,BLUE);
outtextxy(500,450,"Febrero");
}
else
{

Juan Carlos Gutiérrez Barquero____________________________________________ 73


Bismarck Salvador Traña López__________________________________________UNI

if((x2>=-100)&&(x2<0))
{
setcolor(YELLOW);
rectangle(480,440,600,460);
setfillstyle(1,BLUE);
floodfill(485,450,YELLOW);
outtextxy(500,450,"Marzo");
}
else
{
if((x2>=0)&&(x2<100))
{
setcolor(GREEN);
rectangle(480,440,600,460);
setfillstyle(1,BLUE);
floodfill(485,450,GREEN);
outtextxy(500,450,"Abril");
}
else
{
if((x2>=100)&&(x2<200))
{
setcolor(CYAN);
rectangle(480,440,600,460);
setfillstyle(1,BLUE);
floodfill(485,450,CYAN);
outtextxy(500,450,"Mayo");
}
else
{
if((x2>=200)&&(x2<299))
{
setcolor(MAGENTA);
rectangle(480,440,600,460);
setfillstyle(1,BLUE);
floodfill(485,450,MAGENTA);
outtextxy(500,450,"Junio");
}
}
}
}
}
}
setcolor(WHITE);
y2=sqrt((1-((x2*x2)/90000.00))*23850.00);
circle(310+x2,240-y2,10);
getimage(310+x2-10,240-y2-10,310+x2+10,240-y2+10,buffer);
delay(20000);
putimage(310+x2-10,240-y2-10,buffer,XOR_PUT);
circle(310,240,30);
setfillstyle(1,YELLOW);

Juan Carlos Gutiérrez Barquero____________________________________________ 74


Bismarck Salvador Traña López__________________________________________UNI

floodfill(310,240,WHITE);
setcolor(WHITE);
x2+=2;
}
free(buffer);
delay(30000);
x=300;
while(x>=-300 && !kbhit())
{
putpixel(random(639),random(480),WHITE);
putpixel(random(639),random(480),YELLOW);
if((x<=300)&&(x>200))
{
setcolor(RED);
rectangle(480,440,600,460);
setfillstyle(1,BLUE);
floodfill(485,450,RED);
outtextxy(500,450,"Julio");
}
else
{
if((x2<=200)&&(x>100))
{
setcolor(LIGHTGRAY);
rectangle(480,440,600,460);
setfillstyle(1,BLUE);
floodfill(485,450,LIGHTGRAY);
outtextxy(501,450,"Agosto");
}
else
{
if((x<=100)&&(x>0))
{
setcolor(YELLOW);
rectangle(480,440,600,460);
setfillstyle(1,BLUE);
floodfill(485,450,YELLOW);
outtextxy(500,450,"Septiembre");
}
else
{
if((x<=0)&&(x>-100))
{
setcolor(GREEN);
rectangle(480,440,600,460);
setfillstyle(1,BLUE);
floodfill(485,450,GREEN);
outtextxy(500,450,"Octubre");
}
else
{

Juan Carlos Gutiérrez Barquero____________________________________________ 75


Bismarck Salvador Traña López__________________________________________UNI

if((x<=-100)&&(x>-200))
{
setcolor(CYAN);
rectangle(480,440,600,460);
setfillstyle(1,BLUE);
floodfill(485,450,CYAN);
outtextxy(500,450,"Noviembre");
}
else
{
if((x<=-200)&&(x>-300))
{
setcolor(MAGENTA);
rectangle(480,440,600,460);
setfillstyle(1,BLUE);
floodfill(485,450,MAGENTA);
outtextxy(500,450,"Diciembre");
}
}
}
}
}
}

setcolor(WHITE);
y1=(sqrt((1-((x*x)/90000.00))*23850.00))*-1;

circle(310+x,240-y1,10);
getimage(310+x-10,240-y1-10,310+x+10,240-y1+10,buffer);
delay(20000);
putimage(310+x-10,240-y1-10,buffer,XOR_PUT);
circle(310,240,30);
setfillstyle(1,YELLOW);
floodfill(310,240,WHITE);
setcolor(WHITE);
x-=2;
}
free(buffer);
delay(2000);
}while(!kbhit());
}

/***********************INICIALIZAR***************************/

void inicializar(void)
{
int drive=DETECT,modo;
initgraph(&drive,&modo,"c:\\tc20\\bin");
}

Juan Carlos Gutiérrez Barquero____________________________________________ 76


Bismarck Salvador Traña López__________________________________________UNI

Existen otras maneras mas sencillas de hacerlo, que te parece si te doy una idea???
Declara un arreglo unidimensional donde escribiras los meses y hay un for que inicie en 1
y termine en 12 dentro de ese for llama a una funcion que es bottom_fond donde
dibujaras un rectangulo que se pintará siempre de azul pero su color frontera seran las i
recomendación (pasarle parámetros a la funcion para ahorrar códigodentro de ese misno
for llama al arreglo bidimensional imprimiendo en las coordenadas del rectangulo y los
demas cálculos son los mismos.

Funciones mas utilizadas en <graphics.h> de <stdlib.h>


La función atoi

int atoi(const char *s);

El macro atoi() convierte una cadena a su valor entero. Retorna el valor convertido de la
cadena leída. Si el valor no puede ser convertido el valor de retorno es 0. Observación: es
usada para convertir cadenas que contengas valores numéricas a fin de que estos valores
puedan ser computados.

Ejemplo

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
void main(void)
{
char *string="545";
long int valor_entero;
clrscr();
valor_entero=atoi(string);
printf("La cadena es %s, el valor entero correspondiente es %d\n",string,valor_entero);
getch();
}

La función itoa

int itoa(int value, char *string, int radix);

La función itoa convierte un entero a cadena, recibe los siguientes parámetros:


int value = es el valor que vas a convertir
char *string = es una arreglo de caracteres o un puntero que apunte a char, aquí se
colocará el valor entero convertido.
int radix = quiere decir el sistema en el que está el valor entero, ejemplo para enteros
decimales debe ser 10, para enteros hexadecimale debe ser 16, para octales debe ser 8.
Esta función es comúnmente usada con outtextxy(), en el siguiente ejemplo deseo
mostrar en pantalla gráfica el valor de una variable entera para lo cual tengo que
convertirla a cadena.

Juan Carlos Gutiérrez Barquero____________________________________________ 77


Bismarck Salvador Traña López__________________________________________UNI

Ejemplo

#include <conio.h>
#include <stdlib.h>
#include <graphics.h>
void main(void)
{
char *stringx,*stringy;
int x=150,y=150;
int adaptador=DETECT,modo;
initgraph(&adaptador,&modo,"c:\\tc20\\bin");
cleardevice();
itoa(x,stringx,10);
itoa(y,stringy,10);
outtextxy(150,130,"LOS VALORES DE X e Y SON:");
outtextxy(150,150,"x="); outtextxy(170,150,stringx);
outtextxy(150,160,"y="); outtextxy(170,160,stringy);
getch();
}

Funciones rand y random

int rand(void); int random(int valor_final);

Las funciones rand() y random() son similares, ambas devuelve un valor seudo-aleatorio,
la primera no recibe parámetros y el rango del valores está entre 0 y RAND_MAX este
usualmente es 32767, el rango de valores para random() esta entre 0 y (valor_final – 1).

El ejemplo siguiente simulara una cantidad de estrellas:

#include <graphics.h>
#include <conio.h>
#include <stdlib.h>
void main(void)
{
int adaptador=DETECT,modo;
initgraph(&adaptador,&modo,"c:\\tc20\\bin");
while(!kbhit())
{
putpixel(rand(),rand(),random(16));
}
}

Otra manera de realizar programas en modo grafico con gran efectiidad es utilizando la
librería mouse.h; esta función no esta definida dentro de la ayuda de tc20 pero esta dentro
del archivo BGI o include veremos cada una de las funciones asi como también como las
funciones contenidas dentro de ella.

Juan Carlos Gutiérrez Barquero____________________________________________ 78


Bismarck Salvador Traña López__________________________________________UNI

Funciones de la librería Mouse.h


Todos las librerias aqui explicadas aparecen en la carpeta include de tc20.paracomenzar
tenemos:

La función mver

Void mver (void)

La función mver muestra el puntero del ratón no es necesario enviar ningún parámetro.

La función mver no retorna ningún valor.

Ejemplo

#include<graphics.h>
#include<conio.h>
#include<mouse.h>
void main(){
int adaptador=DETECT,modo;
initgraph(&adaptador,&modo,"c:\\tc20\\BIN");
do{
mver();
}while(!kbhit());
closegraph();
getch();
}

La función mocultar

void mocultar (void)

Esta función oculta el puntero del ratón no recibe ni retorna ningún valor.

Ejemplo:

#include<graphics.h>
#include<conio.h>
#include<mouse.h>
void main(){
int adaptador=DETECT,modo;
initgraph(&adaptador,&modo,"c:\\tc20\\BIN");
do{
mver();
mocultar()
}while(!kbhit());

closegraph();
getch();
} Al utilizar estas dos funciones obtenemos un efecto del ratón muy vistoso.

Juan Carlos Gutiérrez Barquero____________________________________________ 79


Bismarck Salvador Traña López__________________________________________UNI

La función mtest.

int mtest (void)

Esta función sirve para testear el ratón en esta no es necesario enviar ningún parametro
retorna: 0 si no esta instalado el ratón, y n si el número de botones del ratón.

La funcion mtest retorna un entero

Ejemplo:

#include<graphics.h>
#include<conio.h>
#include<mouse.h>
void main(){
int adaptador=DETECT,modo;
initgraph(&adaptador,&modo,"c:\\tc20\\BIN");
do{
mver();
mtest();
}while(!kbhit());
closegraph();
getch();
}

La función mxpos

int mxpos (int modo)

Dice la posición de la coordenada x en la cual se encuentra el ratón. Es necesario


enviarunvalor para especificar el modo en cual sera usado.

1 es para modo gráfico.


8 es para modo texto

La función mxpos regresa la posición horizontal de un entero que seria la posición del
ratón en x.

Ejemplo

#include <graphics.h>
#include <conio.h>
#include <mouse.h>
#include <stdlib.h>
void main(void)
{
int adaptador=DETECT,modo;
int x,y;
char *strx, *stry;
initgraph(&adaptador,&modo,"c:\\tc20\\bin");
setbkcolor(BLUE);

Juan Carlos Gutiérrez Barquero____________________________________________ 80


Bismarck Salvador Traña López__________________________________________UNI

while(!kbhit())
{
setfillstyle(SOLID_FILL,BLUE);
mver();
x=mxpos(1);
y=mypos(1);
itoa(x,strx,10);
itoa(y,stry,10);
outtextxy(100,100,strx);
outtextxy(100,120,stry);
if(x!=mxpos(1)||y!=mypos(1))
{
setcolor(BLUE);
outtextxy(100,100,strx);
outtextxy(100,120,stry);
setcolor(WHITE);}
}
}

La función mypos

int mypos (int modo)

Dice la posición de la coordenada y en la cual se encuentra el ratón. Es necesario


enviarunvalor para especificar el modo en cual sera usado.

1 es para modo gráfico.


8 es para modo texto

La función mypos regresa la posición horizontal de un entero que seria la posición del
ratón en y.

Ejemplo

#include <graphics.h>
#include <conio.h>
#include <mouse.h>
#include <stdlib.h>
void main(void)
{
int adaptador=DETECT,modo;
int x,y;
char *strx, *stry;
initgraph(&adaptador,&modo,"c:\\tc20\\bin");
setbkcolor(BLUE);
while(!kbhit())
{
setfillstyle(SOLID_FILL,BLUE);
mver();
x=mxpos(1);
y=mypos(1);

Juan Carlos Gutiérrez Barquero____________________________________________ 81


Bismarck Salvador Traña López__________________________________________UNI

itoa(x,strx,10);
itoa(y,stry,10);
outtextxy(100,100,strx);
outtextxy(100,120,stry);
if(x!=mxpos(1)||y!=mypos(1))
{
setcolor(BLUE);
outtextxy(100,100,strx);
outtextxy(100,120,stry);
setcolor(WHITE);}
}
}

La función msituar

void msituar(int modo, int x, int y)

Esta función permite situar al curcor en una posición específica para su utilización es
necesario enviar tres parámetros modo, x,y.

Modo: 1 para modo gráfico.


8 para modo texto.

X: posición horizontal.
Y: posición vertical.

La función no retorna ningún valor.

Ejemplo

#include<graphics.h>
#include<conio.h>
#include<mouse.h>
void main(){
int adaptador=DETECT,modo;
initgraph(&adaptador,&modo,"c:\\tc20\\BIN");
msituar(1,310,240); /*Coloca el cursor en el centro de la pantalla*/
do{
mver();
}while(!kbhit());

closegraph();
getch();
}

Juan Carlos Gutiérrez Barquero____________________________________________ 82


Bismarck Salvador Traña López__________________________________________UNI

La función mclick

int mclick(void)

Nos dice si se ha pulsado un boton del Mouse, no es necesario enviar un parámetro.

Retorna 0 si no es presionado ningún botón.


1 si se presionó el botón izquierdo del Mouse.
2. si se presionó el botón derecho del Mouse.

Ejemplo

#include<graphics.h>
#include<stdlib.h>
#include<conio.h>
#include<mouse.h>
void main(){
int adaptador=DETECT,modo,x,y;
initgraph(&adaptador,&modo,"c:\\tc20\\BIN");
do{
mver();
x=mxpos(1);
y=mypos(1);
if(mclick()==1)
{
setcolor(GREEN);
outtextxy(x+30,y,"Diste un click izquierdo");
}
if(mclick()==2)
{
setcolor(RED);
outtextxy(x+30,y,"Diste un click derecho");
}
delay(10000);
setcolor(BLACK);
outtextxy(x+30,y,"Diste un click izquierdo");
outtextxy(x+30,y,"Diste un click derecho");
}while(!kbhit());

closegraph();
getch();
}

La función mlimit

void mlimit(int modo, int x1, int y1, int x2, int y2)

Esta función limita el espacio de movilización del cursor (ratón), en esta función es
necesario enviar cinco parámetros modo,x1,y1,x2,y2.

Juan Carlos Gutiérrez Barquero____________________________________________ 83


Bismarck Salvador Traña López__________________________________________UNI

Modo: 1 para modo gráfico.


8 para modo texto.

X1: límite de arriba.


Y1: límite ablajo.
X2: limite de abajo
Y2: límite de la derecha

Estas coordenadas formarán un rectángulo el cual sera el limite hasta donde podra llegar
el ratón.

Ejemplo

#include<graphics.h>
#include<conio.h>
#include<mouse.h>
void main(){
int adaptador=DETECT,modo;
initgraph(&adaptador,&modo,"c:\\tc20\\BIN");
do{ outtextxy(100,50,"Dibuj‚ el rect ngulo para que observaran el limite");
rectangle(100,100,500,400);
mlimit(1,100,100,500,400);
mver();
}while(!kbhit());

closegraph();
getch();
}

La función minlimit

int minlimit(int modo,int x1,int y1,int x2,int y2)

Esta función verifica si el ratón se encuentra en un área determinada para la


implementación de ésta función es necesario enviar cinco parámetros.

Modo: 1 para modo gráfico.


8 para modo texto.

X1: límite de arriba.


Y1: límite ablajo.
X2: limite de abajo
Y2: límite de la derecha

Retorna 1 si esta en esa area en caso contrario regresará 0 (cero).

Juan Carlos Gutiérrez Barquero____________________________________________ 84


Bismarck Salvador Traña López__________________________________________UNI

Ejemplo

#include<graphics.h>
#include<conio.h>
#include<mouse.h>
void main(){
int adaptador=DETECT,modo;
initgraph(&adaptador,&modo,"c:\\tc20\\BIN");
setbkcolor(BLUE);
do{ setcolor(WHITE);
outtextxy(210,105,"pasa el mouse por encima del bot¢n y veras el efecto");
mver();
rectangle(100,100,200,120);
if(minlimit(1,100,100,200,120)==1)
{
outtextxy(310,240,"Haz pulsado el rat¢n por encima del bot¢n");
delay(30000);
setcolor(BLACK);
outtextxy(310,240,"Haz pulsado el rat¢n por encima del bot¢n");
}
}while(!kbhit());

closegraph();
getch();
}

Ejemplos de la de la VGA.

/*Este programa te muestra las figuras geometricas*/

# include<stdio.h>
# include <dos.h>
# include <stdlib.h>
# include<graphics.h>
# include<stdlib.h>
# include<conio.h>
void inicializar(void);
void cuadro(void);
void main(void)
{
inicializar();

cuadro();
restorecrtmode();
}

void inicializar(void)
{

int adaptor=DETECT,modo,codigo;

Juan Carlos Gutiérrez Barquero____________________________________________ 85


Bismarck Salvador Traña López__________________________________________UNI

printf("desea seguir viendo los graficos");


initgraph(&adaptor,&modo,"c:\\tc20\\bin");
if((codigo=graphresult())!=0)
{
printf("%s",grapherrormsg(codigo));
exit(1);
}
}

void cuadro(void)
{
int paginas=1;
int i=155,j=120; /*i=izquierda arriba eje x jes igual eje y*/
int q=465,w=120; /* t= derecha arriba eje x k es igual eje y*/
int t=155,k=360; /*q= izquierda abajo eje x w es igual eje y */
int a=465,s=360; /*a= derecha abajo eje x s igual eje y*/
int pentagono[]={55,340, 105,410, 205,410, 255,340, 155,270, 55,340};
int hexagono[]={365,360, 415,410, 515,410, 565,360, 515,310, 415,310, 365,360};

while(paginas<=4)
{
system("cls");
setbkcolor(WHITE);
setlinestyle(SOLID_LINE,15,THICK_WIDTH);
setcolor(8);
rectangle(1,1,getmaxx()-1,getmaxy()-1);
line(310,0,310,480);
line(0,240,640,240);
setcolor(9);
setlinestyle(SOLID_LINE,15,NORM_WIDTH);
if(paginas>=1&&paginas<=2)
{
settextstyle(SMALL_FONT,HORIZ_DIR,4);
outtextxy(400,460,"Presione enter para ver las sig. graficas");
}
else
{
settextstyle(SMALL_FONT,HORIZ_DIR,4);
outtextxy(370,460,"Fin de presentacion Presione enter para Salir");
}
if(paginas==1)
{
/***************triangulo1**************************/
outtextxy(i-60,j-90,"Triangulo Rectangulo");
outtextxy(i-67,j-20,"h");
outtextxy(i,j+40,"b");
outtextxy(i-5,j+70,"S=(b x h)/2");
outtextxy(i-60,j-90,"Tri ngulo rectangulo");
line(i-60,j-60,i-60,j+40);
line(i-60,j+40,i+100,j+40);
line(i+100,j+40,i-60,j-60);

Juan Carlos Gutiérrez Barquero____________________________________________ 86


Bismarck Salvador Traña López__________________________________________UNI

/******************triangulo2****************************/
/* q eje x w eje y*/

outtextxy(q-100,w-90,"Triangulo Rectangulo 3 lados diferentes");


outtextxy(q-57,w-20,"h");
outtextxy(q-20,w+40,"a");
outtextxy(q+10,w-30,"b");
outtextxy(q-80,w-30,"c");
settextstyle(SMALL_FONT,HORIZ_DIR,5);
outtextxy(q-45,w+60,"S=-\/ p(p-a)(p-b)(p-c)'");
line(q-15,w+60,q+110,w+60);
outtextxy(q-30,w+75,"P=(a+b+c)/2");
settextstyle(SMALL_FONT,HORIZ_DIR,4);
/*outtextxy(q-5,w+70,"(b x h)/2");*/
outtextxy(i-60,j-90,"Tri ngulo rectangulo");
outtextxy(i-60,j-90,"Tri ngulo rectangulo");
outtextxy(i-60,j-90,"Tri ngulo rectangulo");
setlinestyle(DOTTED_LINE,3,NORM_WIDTH);
line(q-50,w-60,q-50,w+40);
setlinestyle(SOLID_LINE,15,NORM_WIDTH);
line(q-50,w+40,q+80,w+40);
line(q+80,w+40,q-50,w-60);
line(q-50,w+40,q-100,w+40);
line(q-100,w+40,q-50,w-60);
/*******************triangulo escaleno*****************************/
/*t es eje x k es eje y*/
outtextxy(t-60,k-90,"Triangulo Escaleno");
outtextxy(t-67,k-20,"h");
outtextxy(t,k+40,"b");
outtextxy(t-5,k+70,"S=(b x h)/2");
line(t-60,k-60,t-25,k+40); /*line que corta al triangulo o cat opu*/
line(t-25,k+40,t+100,k+40); /*cat ady.*/
line(t+100,k+40,t-60,k-60); /*hipoten.*/
setlinestyle(DOTTED_LINE,8,NORM_WIDTH);
line(a+20,s-60,a+20,s+40); /*Triangulo isosceles*/
line(t-60,k-60,t-60,k+40);
line(t-60,k+40,t+100,k+40);
setlinestyle(SOLID_LINE,21,NORM_WIDTH);
/*********************triangulo isosceles***************************/
outtextxy(a-40,s-90,"Triangulo Isosceles");
outtextxy(a+10,s-20,"h");
outtextxy(a+10,s+40,"b");
outtextxy(a-5,s+70,"S=(b x h)/2");
line(a+20,s-60,a-60,s+40); /*cat opt.*/
line(a-60,s+40,a+100,s+40); /*cat ady*/
line(a+100,s+40,a+20,s-60); /*hip*/
getch();
}
if(paginas==2)

Juan Carlos Gutiérrez Barquero____________________________________________ 87


Bismarck Salvador Traña López__________________________________________UNI

{
/********************Rectangulo******************/
line(i-45,j-25,i+65,j-25);
line(i-60,j+25,i+50,j+25);
line(i-45,j-25,i-60,j+25);
line(i+65,j-25,i+50,j+25);
setlinestyle(DOTTED_LINE,8,NORM_WIDTH);
line(i-45,j-25,i-45,j+25); /*altura*/

outtextxy(i-25,j-90,"Rectangulo");
outtextxy(i-52,j,"h");
outtextxy(i,j+30,"b");
outtextxy(i-35,j+60,"S=b x h");
setlinestyle(SOLID_LINE,8,NORM_WIDTH);
/*****************************Rombo***********************/
line(t-90,k,t+90,k);
line(t,k-60,t,k+60);

line(t-90,k,t,k-60);

line(t,k-60,t+90,k);
line(t,k+60,t+90,k);
line(t-90,k,t,k+60);
setlinestyle(DOTTED_LINE,24,NORM_WIDTH);
rectangle(t-90,k,t+90,k+70);
line(t,k-60,t+120,k-60);
line(t+120,k-60,t+120,k+60);
line(t+120,k+60,t,k+60);
line(q-60,w-45,q-60,w+45);
outtextxy(t-25,k-90,"Rombo");
outtextxy(t-45,k,"D");
outtextxy(i+3,k+30,"d");
outtextxy(t-35,k+75,"S=(D * d)/2");
setlinestyle(SOLID_LINE,8,NORM_WIDTH);
/*****************************Trapecio******************************/
line(q-60,w-45,q+60,w-45);
line(q-85,w+45,q+90,w+45);
line(q-85,w+45,q-60,w-45);
line(q+90,w+45,q+60,w-45);
outtextxy(q-25,w-90,"Trapecio");
outtextxy(q-52,w,"h");
outtextxy(q,w+50,"B");
outtextxy(q,w-60,"b");
outtextxy(q-35,w+60,"S=((B + b)* h)/2");
/*************************Pol¡gono*****************************/
line(a-60,s,a+100,s);
line(a+100,s,a+70,s+45);
line(a+70,s+45,a-60,s);
line(a+100,s,a+40,s-60);
line(a+40,s-60,a-60,s);
line(a+40,s-60,a-40,s-40);

Juan Carlos Gutiérrez Barquero____________________________________________ 88


Bismarck Salvador Traña López__________________________________________UNI

line(a-60,s,a-40,s-40);
outtextxy(a-25,s-90,"Poligono");
outtextxy(a-30,s-40,"S1");
outtextxy(a+25,s-30,"S2");
outtextxy(a+30,s+10,"S3");
outtextxy(a-35,s+60,"S= S1 + S2 + S3");
getch();
}

if(paginas==3)
{
/*********************tri ngulo equilatero*********************/
line(i-60,j+55,i+20,j-85); /*cat*/
line(i+100,j+55,i+20,j-85); /*cat*/
line(i-60,j+55,i+100,j+55); /*base*/
outtextxy(i-25,j-100,"Triangulo equilatero");
setlinestyle(DOTTED_LINE,24,NORM_WIDTH);
line(i+20,j-85,i+20,j+55);
setlinestyle(SOLID_LINE,8,NORM_WIDTH);
outtextxy(i+25,i-30,"h");
outtextxy(i+30,i+20,"b");
outtextxy(i-35,i+55,"S= (b * h)/2");
/*******************************cuadrado***********************/
rectangle(q-50,w-50,q+50,w+50);
outtextxy(q-25,w-100,"Cuadrado");
outtextxy(q,w+50,"b");
outtextxy(q-35,w+65,"S= b * b");
/****************************pent gono***********************/
fillpoly(5,pentagono);
outtextxy(t-25,k-110,"Pent gono");
line(t,k-20,t,k+50);
outtextxy(t+10,k+10,"a");
outtextxy(t-35,k+65,"S=(P * a)/2");
/*************Exagono o poligono regular*******************/

fillpoly(6,hexagono);

outtextxy(a-65,s-100,"Hex gono o poligono regular");


/*setfillstyle(LINE_FILL,GREEN);
floodfill(370,460,BLUE);*/
line(a,s,a,s+50);
outtextxy(a+10,s+10,"a");
outtextxy(a-35,s+65,"S=(P * a)/2");
getch();
}
if(paginas>4)
exit(1);
paginas=paginas+1;
}
clrscr();}
Este programa te simula un paisaje nocturno con algunas efectos de estrellas.

Juan Carlos Gutiérrez Barquero____________________________________________ 89


Bismarck Salvador Traña López__________________________________________UNI

# include <conio.h>
# include <dos.h>
# include <process.h>
# include <stdlib.h>
# include <graphics.h>
# define WHITE 15
void marco(int *,int *,int *,int *,int *);
void estrellas(int *,int *);
void ambiente(void);
void ambientedos(void);
void montana(void);
void cometas(void);
void main()
{
/*INICIALIZAR EL MODO GRAFICO DE MANERA RAPIDA*/

int a=DETECT,b;
int x1=0,y1=0,x2=640,y2=480,color=6;
int y=0, x=0;
initgraph(&a,&b,"c:\\tc20\\bin");
system("cls");
ambiente();
ambientedos();

marco(&x1,&y1,&x2,&y2,&color);
estrellas(&x,&y);
montana();
cometas();
getch();
}

void ambiente(void)
{
int x=50,y=480;
setbkcolor(BLUE);
setcolor(LIGHTGRAY);

rectangle(0,0,639,479);
moveto(x,y);
setcolor(LIGHTGRAY);
lineto(100,450);
lineto(150,400);
lineto(210,370);
lineto(0,370);
setfillstyle(SOLID_FILL,GREEN);
floodfill(1,470,LIGHTGRAY);
}

void ambientedos(void)
{

Juan Carlos Gutiérrez Barquero____________________________________________ 90


Bismarck Salvador Traña López__________________________________________UNI

setcolor(BROWN);
moveto(105,370);
lineto(125,350);
lineto(155,365);
lineto(155,360);
lineto(125,345);
lineto(55,345);
lineto(22,370);
lineto(105,370);
setfillstyle(6,BROWN);
floodfill(50,350,BROWN);

setcolor(BROWN);
rectangle(30,365,110,400);
setfillstyle(SOLID_FILL,BROWN);
floodfill(50,385,BROWN);

line(150,362,150,382.5); /*linea lateral | */


line(110,400,150,382.5); /*union de la linea lateral con la casa*/
setfillstyle(SOLID_FILL,BROWN);
floodfill(112,385,BROWN);

setcolor(RED);
line(130,367,140,364); /*ventana arriba --*/
line(130,377,140,374); /*ventana abajo __*/
line(130,367,130,377); /*lado derecha | */
line(140,364,140,374); /*lado izquierdo | */
setfillstyle(SOLID_FILL,MAGENTA);
floodfill(135,368,RED);

line(112,398,112,378); /*puerta arriba*/


line(112,378,120,374); /*puerta lado izquierdo*/
line(120,374,120.5,395); /*puerta lado derecho*/
line(112,397,120.5,394); /*puerta abajo*/
setfillstyle(SOLID_FILL,MAGENTA);
floodfill(114,380,RED);
line(110,365,110,400);

setcolor(YELLOW);
arc(310,300,90,270,30);
ellipse(310,300,90,270,05,30);
setfillstyle(SOLID_FILL,YELLOW);
floodfill(300,300,YELLOW);
}

void marco(int *x1,int *y1,int *x2,int *y2,int *color)


{
setbkcolor(BLUE);

Juan Carlos Gutiérrez Barquero____________________________________________ 91


Bismarck Salvador Traña López__________________________________________UNI

setcolor(*color);
rectangle(*x1,*y1,*x2,*y2);
}

void estrellas(int *x,int *y)


{
int i,px=*x,py=*y,color,t;

setcolor(YELLOW);
arc(310,180,90,270,30);
ellipse(310,180,90,270,05,30);
setfillstyle(SOLID_FILL,YELLOW);
floodfill(300,180,YELLOW);
color=getcolor();
t=1;
for(i=0;i<=400;i++)
{
montana();
setcolor(GREEN);
putpixel(px,py,color);
putpixel(px+10,py+50,WHITE);
px=(rand()%640);
py=(rand()%200);
if(i>=230)
continue;
else
{
putpixel(310,(480-t)-12,RED);
putpixel(310,(480-t)-9,YELLOW);
putpixel(310,(480-t),BLUE);

putpixel(t+1,100,RED);
putpixel(t-5,100,YELLOW);
putpixel(t-11,100,BLUE);

putpixel(t+1,50,RED);
putpixel(t-5,50,YELLOW);
putpixel(t-11,50,BLUE);
t+=3;
}
delay(4000);

}
}
void montana()
{
setcolor(LIGHTGRAY);
line(0,250,494,250); /*linea del lago superior */
line(480,250,560,200); /*monta¤a / */
line(560,200,639,250); /* monta¤a \ */
line(480,250,639,250); /* monta¤a ___ */

Juan Carlos Gutiérrez Barquero____________________________________________ 92


Bismarck Salvador Traña López__________________________________________UNI

setfillstyle(SOLID_FILL,LIGHTGREEN);
floodfill(590,230,LIGHTGRAY);
}

void cometas(void)
{
int i,t;
do
{
t=1;
for(i=0;i<=450;i++)
{
if(i>=230)
continue;
else
{
putpixel(t+3,65,RED);
putpixel(t-3,65,YELLOW);
putpixel(t-9,65,BLUE);

putpixel(t+13,80,RED);
putpixel(t+7,80,YELLOW);
putpixel(t+1,80,BLUE);

putpixel(t+13,100,RED);
putpixel(t+7,100,YELLOW);
putpixel(t+1,100,BLUE);

if((t+1)>=292 && (t+1)<=317)


{
putpixel(t+1,180,RED);
putpixel(t-5,180,YELLOW);
putpixel(t-11,180,YELLOW);
}
else
{
putpixel(t+1,180,RED);
putpixel(t-5,180,YELLOW);
putpixel(t-11,180,BLUE);
}
t+=3;
}
delay(2000);
}
t=0;
}while(!kbhit());
}

Este programa dibuja con el Mouse.

#include <mouse.h>

Juan Carlos Gutiérrez Barquero____________________________________________ 93


Bismarck Salvador Traña López__________________________________________UNI

#include <stdlib.h>
#include <stdio.h>
#include <graphics.h>
#include <conio.h>
#define ESC 27
void main()
{
int a=DETECT,b;
int posx,posy,h,z=0;
initgraph(&a,&b,"c:\\tc20\\bin");
setcolor(YELLOW);
outtextxy(10,10,"a) click izq sostenido para dibujar ");
outtextxy(10,20,"b) click izq dibuja una linea desde la pos. anterior hasta la nueva pos.");
outtextxy(10,30,"c) presionar una vez click der indica que se escogera un nuevo punto
donde");
outtextxy(10,40," empezara a dibujar el click debe ser fuera del dibujo en caso contrario
");
outtextxy(10,50," se borrara");
outtextxy(10,60,"d) click der sostenido para borrar");
outtextxy(10,70,"e) presiona una tecla para salir o click si no has empezado ha dibujar");
outtextxy(10,420,"Hecho por:");
settextstyle(4,0,1);
outtextxy(10,430," Bismarck Salvador Tra¤a L¢pez");
outtextxy(10+textwidth("Hecho por: "),450,"Juan Carlos Guti‚rrez Barquero");
while(mclick()!=1)
{
setcolor(WHITE);
rectangle(100,100,getmaxx()-100,getmaxy()-100);
mver();
posx=mxpos(1);
posy=mypos(1);
}
while(!kbhit())
{
setcolor(WHITE);
mlimit(1,100,100,getmaxx()-100,getmaxy()-100);
rectangle(100,100,getmaxx()-100,getmaxy()-100);
mver();
mocultar();
if(mclick()==1)
{
if(z==1)
{
z=0;
posx=mxpos(1);
posy=mypos(1);
}
line(posx,posy,mxpos(1),mypos(1));
posx=mxpos(1);
posy=mypos(1);
}

Juan Carlos Gutiérrez Barquero____________________________________________ 94


Bismarck Salvador Traña López__________________________________________UNI

else if(mclick()==2)
{
z=1;
setcolor(BLACK);
for(h=0;h<=20;h++)
circle(mxpos(1),mypos(1),h);
floodfill(mxpos(1),mypos(1),BLACK);
}
mlimit(1,100,100,getmaxx()-100,getmaxy()-100);
}
clrscr();
}

Este programa muestra el movimiento de una pelota.

# include<stdio.h>
# include <dos.h>
# include <stdlib.h>
# include<graphics.h>
# include<conio.h>
void inicializar(void);
void juego_visual(void);
void fondo_de_la_pantalla(void);
void mover(void);
void main(void)
{
inicializar();
juego_visual();
cleardevice();
fondo_de_la_pantalla();
mover();
getch();
restorecrtmode();
}

void inicializar(void)
{

int adaptor=DETECT,modo,codigo;
printf("desea seguir viendo los graficos");
initgraph(&adaptor,&modo,"c:\\tc20\\bin");
if((codigo=graphresult())!=0)
{
printf("%s",grapherrormsg(codigo));
exit(1);
}
}

void juego_visual(void)

Juan Carlos Gutiérrez Barquero____________________________________________ 95


Bismarck Salvador Traña López__________________________________________UNI

{
int i;
setbkcolor(BLUE);
setcolor(YELLOW);
outtextxy(5,5,"Cargando programa de movimiento grafico");
rectangle(21,20,450,30);
sleep(1);
for(i=11;i<=430;i++)
{
bar(10+i,20,20+i,30);
delay(5000);
if(i==430)
{
moveto(480,400);
outtext("programa Cargado");
}

}
sleep(1);
}

void mover(void)
{
void far *buffer;
int i;
setcolor(WHITE);
buffer=malloc(imagesize(127,98,172,143));

/*rectangle(127,98,172,143);*/

i=100;
while(!kbhit())
{
ala: circle(127+i,180,20);
getimage(107+i,160,147+i,200,buffer);
sleep(1);
putimage(107+i,160,buffer,XOR_PUT);
i+=10;
if(i==300)
{
i=100;
goto ala;
}
}
free( buffer);
}

void fondo_de_la_pantalla(void)
{
setbkcolor(BLUE);
setcolor(LIGHTBLUE);

Juan Carlos Gutiérrez Barquero____________________________________________ 96


Bismarck Salvador Traña López__________________________________________UNI

rectangle(1,1,getmaxx()-1,getmaxy()-1);
setcolor(LIGHTGRAY);

rectangle(6,5,634,20);
setfillstyle(SOLID_FILL,LIGHTGRAY);
floodfill(10,7,LIGHTGRAY);
setcolor(BLUE);
rectangle(5,5,100,20);
setcolor(RED);
outtextxy(8,11,"N");
setcolor(BLACK);
outtextxy(17,11,"uevo Juego");
setcolor(BLUE);
rectangle(110,5,160,20);
setcolor(RED);
outtextxy(116,11,"S");
setcolor(BLACK);
outtextxy(124,11,"alir");
}

Modo 13h 320x200().

 Introducción.

Cuando aparecieron los primeros computadores existía una memoria de solo 640 Kb
(memoria convencional) y el procesador que dominaba en aquella época era el 8086 y
8088 el cual podía direccionar hasta 1 Mb de memoria, ya que el microprocesador era de
16 bits. Añadieron a este diferentes áreas de memoria, para la BIOS, el video, cartuchos
ROM, etc.

Hoy en día la configuración de todo PC está organizada en bloques de 64 kb.

Organización de la memoria de un PC

A000: corresponde a la memoria de la pantalla en los modos gráficos de las tarjetas EGA
y VGA.

B000: Es para las tarjetas de video monocromo MDA y Hercules. También sirve para la
tarjeta CGA. Se utiliza como alojamiento de los modos alfanuméricos.

C000: Se depositan algunas rutinas BIOS, que no forman parte del núcleo original de
esta.

D000: Para cartuchos ROM. No se suele utilizar.

E000: Igual que D000.

F000: Aquí se guardan todas la rutinas (funciones) de la ROM-BIOS.

Juan Carlos Gutiérrez Barquero____________________________________________ 97


Bismarck Salvador Traña López__________________________________________UNI

El Procesador .

Para hacer la programación del sistema, hay que conocer algunos conceptos que juegan
un papel importante, términos como por ejemplo los registros, a los que hay que
conocer si se quieren manejar las interrupciones, ya sean a nivel DOS o BIOS.

Los registros .

Los registros son muy importantes para el ordenador, sirven para ayudar al
funcionamiento de las instrucciones. Una definición un poco más acertada, sería decir
que los registros son como variables internas con las que el computador realiza
operaciones, y lleva el control de los programas.

El tamaño de estos registros en un 286 es de 16 bits, pero a partir del 386 estos registros
son de 32 bits. Cada uno de estos registros se divide en dos partes de 8 bits cada una
(esto es así, por que el DOS trabaja en modo Real, y no puede direccionar más de 16
bits). Por ejemplo, el registro AX, se divide en AH y AL (H de High y L de Low). Podemos
distinguir cinco tipos de registros diferentes:

Registros Generales: Hay cuatro: AX, BX, CX, y DX. Son unos registros de 16 bits que
se usan como almacenamiento temporal, para realizar operaciones aritméticas, cada uno
de estos se divide en dos partes, la parte alta (AH), y la baja (AL), donde H significa High
y L de Low. Tienen un propósito general para el programador.

Registros de dirección (Puntero / Indice): SP, BP, SI y DI. Los utilizamos para formar
direcciones de 20 bits, junto con los registros de segmento CS, DS, SS, y ES.
Registros de segmento: CS, DS, SS y ES. Con ellos referenciamos direcciones de
memoria.

Registro de Puntero de Instrucción: IP (Instruction Pointer). Este lo usa el PC para


“acordarse” en que punto se ha quedado a partir de la base CS. Va cambiando su valor
cada vez que saltamos a un punto del programa.

Registro de Bandera (Flag): Sirve para saber el estado y tener el control del procesador.
Con este registro podremos saber, si por ejemplo después de una suma, el valor excede
de 65535 y por tanto no cabe en 16 bits. Estos bits, que pueden valer 0 o 1, indican varias
cosas según su valor.

Las interrupciones .

Las interrupciones, tal y como indica su nombre, tienen como función interrumpir en medio
de un procedimiento, ejecutar un trozo de código y continuar con lo que se estaba
haciendo. De esta manera la CPU se ahorra de ir preguntado todo el rato a los diferentes
periféricos si “necesitan” su ayuda (polling). Hay de varios tipos, las que son ejecutadas
por el hardware, las del Sistema Operativo y las iniciadas por el sistema (BIOS). Dentro
de estas hay las enmascarable, y las no enmascarables (NMI).

El B.I.O.S ( Basic Input Output System) tiene una serie de interrupciones, para que
cuando utilicemos alguna de ellas sepamos donde se encuentra. Para eso utilizaremos

Juan Carlos Gutiérrez Barquero____________________________________________ 98


Bismarck Salvador Traña López__________________________________________UNI

una tabla que nos servirá de “índice” para así obtener la dirección de la rutina. Esta tabla
se aloja en la parte más baja de la memoria, es decir, en 0000:0000, y tiene una longitud
de 256 entrada de 4bytes cada una; 2 bytes de dirección (segmento) y 2 bytes de
desplazamiento (offset), o sea, 1 kb en total.

El Bios dispone de las funciones principales que necesita un programa para la


comunicación con el hardware del PC y los dispositivos conectados a él.
Hay rutinas de todas clases. Su función más importante, es que permite usar a los
programas diferentes periféricos independientemente de las características del hardware.
Esto nos ahorra mucho tiempo a la hora de programar para diferentes periféricos. O sea,
que el Bios es un sistema de comunicación estándar.

A continuación describiré todas las interrupciones de la BIOS y su finalidad de la 00H a la


1BH, pero aunque hay más, no serán comentadas ya que es de uso de la BIOS.

INTERRUPCION 00H: División entre cero.

INTERRUPCION 01H: Un solo paso.

INTERRUPCION 02H: Interrupción no enmascarable.

INTERRUPCION 03H: Punto de ruptura.

INTERRUPCION 04H: Desbordamiento.

INTERRUPCION 05H: Imprime pantalla.

INTERRUPCION 08H: Sistema del cronómetro.

INTERRUPCION 09H: Interrupción del teclado.

INTERRUPCION 0BH y INTERRUPCION 0CH: Estas dos son para el Control de


Dispositivo Serial.

INTERRUPCION 0DH y INTERRUPCION 0FH: Estas son para el Control del Dispositivo
Paralelo.

INTERRUPCION 0EH: Control disco flexible.

INTERRUPCION 10H: Servicios de video.

INTERRUPCION 11H: Obtener equipo instalado. Determinación equipo.

INTERRUPCION 13H: Obtención tamaño memoria.

INTERRUPCION 14H: Comunicaciones i/o.

INTERRUPCION 15H: Servicios sistema.

INTERRUPCION 16H: Entrada teclado.

Juan Carlos Gutiérrez Barquero____________________________________________ 99


Bismarck Salvador Traña López__________________________________________UNI

INTERRUPCION 17H: Salida impresora.

INTERRUPCION 18H: Entrada basic de rom.

INTERRUPCION 19H: Cargador de arranque.

INTERRUPCION 1AH: Lee y pone hora.

INTERRUPCION 1BH: Tomar control en interrupción del teclado.

Estas son las interrupciones del BIOS, pero hay más, las del sistema operativo, MS-DOS
en nuestro caso. Cada interrupción se divide en funciones, que son los diferentes
servicios a los que tenemos acceso. Para llamar a una interrupción utilizamos los
registros, donde en AX indicaremos por norma general el número de interrupción, y en los
demás registros los parámetros.

La Programación del Sistema

A la hora de programar una aplicación, la forma de tratar la información, ordenarla, y


representarla es siempre igual para todos los computadores, los algoritmos utilizados no
varían, los podemos describir de forma muy general. Pero lo que sí varía y que depende
del sistema, es la forma en la que se debe tratar la información que entra en un programa
y la forma en la que sale, esto sí es realmente la programación de sistemas, en la que se
deben conocer como acceder al teclado, pantalla, impresora, archivos, etc.

Una de las cosas mas importantes en este tipo de programación es el acceso al


hardware. Debemos saber por donde nos movemos si queremos que nuestra aplicación
funcione correctamente.

Aunque la programación de sistemas esté tan fuertemente ligada al hardware, no


significa que tengamos que rompernos la cabeza para poner un "¡Hola!" en pantalla,
tenemos unas ayudas que nos simplifican mucho la vida. Esto es las funciones de el BIOS
y del Sistemas Operativo. Estas rutinas (interrupciones) nos ahorrará mucho trabajo, ya
que cada uno de estos servicios, conlleva más tiempo mediante la programación directa
al hardware.

Resumiendo, tenemos que las diferentes capas a la que una aplicación puede acceder
son tres:

Para decidirnos por uno de los tres, se tendrá que pensar en el objetivo de la aplicación
que se está desarrollando: si es un juego se tendrá que ir directo al hardware del
computador, por que si se hace mediante rutinas BIOS, el juego quedará demasiado
lento. Esto no quiere decir que las rutinas de la BIOS sean lentas de por si, lo que
pasa es que son “universales” y llevan muchos tipos de comprobaciones. Por otro lado, si
lo que se quiere es hacer una aplicación en modo texto, simplemente usaremos las
funciones que nos pone a disposición nuestro lenguaje de programación favorito.

 Capitulo I. Inicialización del modo gráfico.

Introducción.

Juan Carlos Gutiérrez Barquero____________________________________________ 100


Bismarck Salvador Traña López__________________________________________UNI

La pantalla de nuestro computador puede funcionar en dos modos, modo texto


(o alfanumérico) en el que se representan caracteres, y en modo gráfico, en el cual
además de caracteres se pueden representar imágenes.

Cuando hablamos de modos gráficos, la unidad representable es el pixel. Un pixel (un


punto) es la unidad mínima de información que podemos representar en pantalla. El
elemento que determina tanto la resolución como la cantidad de colores que podemos
representar, es el adaptador. Cada adaptador tiene unas características determinadas,
que hace que la forma de programar sea distinta para cada uno. La diferencia
fundamental es la manera de gestionar la memoria. La información de un pixel, se
almacena en código binario. Esta información hace referencia al color y atributos
especiales.

Los registros de las tarjetas constituyen el dispositivo de Entrada/Salida (E/S) de más bajo
nivel de los adaptadores gráficos. Es en ellos donde se almacena la información relativa a
como debe el computador representar los datos en la pantalla. Están conectados a
puertos de E/S que los hacen accesibles, permitiendo su modificación y lectura. Una
tarjeta de video esta formada por varios controladores hardware integrados en una misma
placa. Cada uno de estos controladores tiene a su vez una serie de registros asociados. A
la hora de programar las tarjetas gráficas, tendremos que acceder a estos registros, para
lo cual se emplea un índice de puerto o index port.

Un mismo controlador puede controlar más de un registro, por lo que la finalidad de este
índice es señalar cuál de todos los registros es el que se modificará. El modo de acceder
a cada controlador es distinto, la forma de leer o escribir en los registros será diferente en
cada caso.

La aparición de la tarjeta de video VGA (Video Graphic Array) como en el año 1987, hizo
que se convirtiera en un estándar de los computadores IBM y compatibles.

La VGA tiene una resolución máxima de 640x480 pixeles y puede mostrar


simultáneamente un máximo de 256 colores. Esta resolución es muy alta comparada con
las antiguas tarjetas CGA y EGA.

Lamentablemente para usar los 256 colores solo se puede usar una resolución de
320x200 pixeles. Este modo de vídeo es el conocido Modo 13h, en el cual se han
programado la mayoría de los juegos, demos, aplicaciones gráficas, etc. Este modo es
uno de los más usados en la programación gráfica, debido a su facilidad de uso y rapidez.

Las tarjetas VGA tienen normalmente un mínimo de 256 Kb, estando la memoria dividida
en 4 segmentos o planos. Existe un bit, que puede estar a 1 o 0. En el primer caso (bit a
1) hablamos de modos lineales, limitados a un plano. Cuando el bit está a 0, fuerza a usar
los cuatro planos, por lo que estamos en el caso de los modos denominados planares.
Este bit se denomina CHAIN-4, porque encadena los cuatro planos. La denominación
chained (modos lineales) y unchained (modos planares), hacen referencia al estado de
este bit, indicando que esta a 1 o 0, respectivamente. Los registros de estas tarjetas
pueden ser, de lectura, de escritura, o de lectura/escritura.
Las tarjetas SVGA tienen cada una de ellas su conjunto de registros. El inconveniente que
presentan estas tarjetas, es que aparecen problemas de compatibilidad. No estaremos
seguros de que todos nuestros programas que desarrollemos funcionen correctamente

Juan Carlos Gutiérrez Barquero____________________________________________ 101


Bismarck Salvador Traña López__________________________________________UNI

con todas las tarjetas. Este problema se puede evitar si nos limitamos a utilizar el conjunto
de registros de la VGA, con los que la SVGA son perfectamente compatibles.

Entrando al modo 13h .

El nombre 13h viene del parámetro que hay que pasarle a la interrupción 10h para iniciar
este modo. Esta ultima es un interrupción software que facilita al programador tomar el
control del hardware de un PC. La interrupción 10h esta encargado de dar los servicios de
video.

Ahora para iniciar el modo de video 13h debemos usar la función 00h de la interrupción
10h, y para esto se les pasa unos parámetros.

Qué significa todo esto, lo que sucede es que cada interrupción posee varias funciones y
a su vez cada función también puede tener sub-funciones. En este caso nosotros
queremos iniciar un nuevo modo de video, para esto utilizamos la función 00h de la
interrupción 10h, la cual esta encargada de activar modos de video específicos.

En este caso en el registro de 8 bits AL se le pasa el valor 00h y en el otro registro


(también de 8 bits) se coloca el modo de video a inicializar. A continuación podrás ver los
distintos modos de video, que soporta la tarjeta VGA.

Como podemos ver en la tabla el Modo 13h es uno de los que tiene menor resolución de
los modos gráficos, pero es el que tiene mayor número de colores. Nosotros utilizaremos
dos modos, el 13h y el 03h, este último los usaremos por la sencilla razón, de que cuando
se termina un programa gráfico, se debe regresar nuevamente al modo texto, en este
caso al modo 03h que es el que tienen por defecto todos los PC al ser encendidos. Este
modo texto es de 80x25 y con 16 colores.

Existen modos superiores al 13h, pero estos pertenecen a tarjetas Super VGA o
superiores.

El modo de video que utilizaremos se compone de 320 pixeles horizontales por 200
pixeles verticales, teniendo un total de 64000 pixeles. (320x200=64000)

Después de haber visto todo la teoria necesaria, podremos crear nuestra primera rutina
en lenguaje C para inicializar el modo 13h. Al iniciarse el modo 13h, los registros de la
VGA se programan automáticamente con las características propias de este modo.
Cuando se inicia este modo la memoria de video, en este caso los 64 Kb se limpian, es
decir todos tienen el color 0 (negro). Acuérdense de incluir la cabecera <dos.h> para
utilizar la función int86 y la union REGS.

La turina para incluir este modo sería:

void Modo13h(void)
{
union REGS r;
r.h.ah=0;
r.h.al=0x13;
int86(0x10, &r, &r);
}

Juan Carlos Gutiérrez Barquero____________________________________________ 102


Bismarck Salvador Traña López__________________________________________UNI

Lo que hemos hecho con esta función es simplemente llamar a la interrupción 10h con los
parámetros necesarios para iniciar el modo 13h.

Otra rutina que nos será de mucha utilidad será la que regresa al modo texto, esta función
es similar al anterior, la única diferencia es que al registro AL se le debe pasar el valor
03h.

Podríamos crear una función general para que realice los cambios de modos de video,
esto quedaría así:

void SetMode(char mode)


{
union REGS r;
r.h.ah=0;
r.h.al=mode;
int86(0x10, &r, &r);
}

Otra manera seria:

void SetMCGA(void)
{
_AX=0x0013 ;
geninterrupt(0x10) ;
}
Para volver al modo texto usamos la misma interrupción 10h, sólo que el servicio 03h :

void SetText(void)
{
_AX=0x0003 ;
geninterrupt(0x10) ;
}

y crear dos constantes con la directiva #define, es decir:

#define TEXTO 0x03


#define GRAFICO 0x13

de esta manera para iniciar el modo 13h, llamaríamos a la función SetMode de la


siguiente forma SetMode(GRAFICO); y para volver al modo texto simplemente:
SetMode(TEXTO);

Veamos de la primera forma:

#include <dos.h> para la funcion int86()


# include <stdio.h> para la funcion printf()
#include <conio.h> para la funcion getch()

Juan Carlos Gutiérrez Barquero____________________________________________ 103


Bismarck Salvador Traña López__________________________________________UNI

void Modo13h(void)
{
union REGS r;
r.h.ah=0;
r.h.al=0x13;
int86(0x10, &r, &r);
}

void ModoTexto(void)
{
union REGS r;
r.h.ah=0;
r.h.al=0x03;
int86(0x10, &r, &r);

void main(void)
{
Modo13h();
printf("Inicializacion correcta\npresione una tecla para volver\n al modo grafico");
getch();
ModoTexto();
printf("Regreso a modo VGA");
getch();
}

De la segunda manera seria:

#include <dos.h> para la funcion int86()


# include <stdio.h> para la funcion printf()
#include <conio.h> para la funcion getch()
unsigned char *DIR_VGA=(unsigned char *) MK_FP(0xA000, 0);

void Modo13h(void)
{
_AX=0x0013 ;
geninterrupt(0x10) ;
}

void ModoTexto(void)
{
_AX=0x0003 ;
geninterrupt(0x10) ;
}

void main(void)
{
Modo13h();
printf("Inicializacion correcta\npresione una tecla para volver\n al modo grafico");

Juan Carlos Gutiérrez Barquero____________________________________________ 104


Bismarck Salvador Traña López__________________________________________UNI

getch();
ModoTexto();
printf("Regreso a modo VGA");
getch();

Un Ejemplo:

Este es un ejemplo sencillo elaborado en el modo 13h (320*200) a 256 colores


que realiza el efecto del fuego. El programa fue compilado con Turbo C

Nota.- El programa debe ser compilado con alguno de estos modelos:


COMPACT,LARGE o HUGE para que pueda funcionar correctamente.

#include <dos.h> para MK_FP,poke,ouportb,geninterrupt


#include <stdlib.h> para random()
#include <conio.h> para kbhit()

typedef unsigned char byte;


byte far* Video= (byte far*)MK_FP(0xA000,0x000);

void Setrgbpalette(byte c,byte r,byte g,byte b)


{
outportb(0x3c8,c);
outportb(0x3c9,r);
outportb(0x3c9,g);
outportb(0x3c9,b);
}

void Modo13h(void)
{
_AL=0x13;
_AH=0;
geninterrupt(0x10);
}

void ModoTexto(void)
{
_AL=0x03;
_AH=0;
geninterrupt(0x10);
}

void main(void)
{
int x,y,c;
Modo13h();
for(x=1;x<=32;x++){

Juan Carlos Gutiérrez Barquero____________________________________________ 105


Bismarck Salvador Traña López__________________________________________UNI

Setrgbpalette(x,x*2-1,0,0);
Setrgbpalette(x+32,63,x*2-1,0);
Setrgbpalette(x+64,63,63,x*2-1);
Setrgbpalette(x+96,63,63,63);
}
do{
x=0;
do{
y=60;
do{
c=(Video[(y<<8)+(y<<6)+x]+Video[(y<<8)+(y<<6)+x+2]+
Video[(y<<8)+(y<<6)+x-2]+Video[(y+2)*320+x+2])/4;
if(c!=0) c--;
poke(0xA000,(y-2)*320+x,(c<<8)+c);
poke(0xA000,(y-1)*320+x,(c<<8)+c);
y+=2;
}while(y<202);
y-=2;
Video[(y<<8)+(y<<6)+x]=random(2)*160;
x+=2;
}while(x<=320);
}while(!kbhit());
ModoTexto();
}

Ya vimos un ejemplo en el modo13h y que les parecio el efecto del fuego bonito no pero
que dirian si les digo que hay otro metodo de inicializacion que puede darle mas realce a
sus programas,!No es broma ¡, si quieren saber un poco aquí les va:

Se lla modo XGA

Para poder ver estos ejemplos debes de tener intalada las librería que esta incluida en el
tc20 que te obsequiamos

Modo grafico con resolucion de 1024x768x256


(1024 pixeles horizontales, 768 verticales, 256 colores)

Antes que nada debes copiar el archivo Svga256.bgi ,que es un controlador para graficos,
a la carpeta bin de TC20.
Luego tienes que poner dentro del main la siguiente instrucción:
installuserdriver(“Svga256”,DetectVGA256);
“Svga256” es nombre del dispositivo debes omitir la extensión .bgi
DetectVGA256 es una función que debes declarar anteriormente como:
int huge DetectVGA256()
{
return 4;
}
y luego puedes llamar a la funcion initgraph() para empezar a trabajar en modo grafico,
para esta resolucion no estan disponibles las funciones setbkcolor(), ni cleardevice().
Aquí tienes el ejemplo completo:

Juan Carlos Gutiérrez Barquero____________________________________________ 106


Bismarck Salvador Traña López__________________________________________UNI

#include<conio.h>
#include <graphics.h>

int huge DetectVGA256()


{ /* 0 = 320x200x256 modo CGA 320(horizontal) con 200(vertical) pixeles(puntos) en
256colores
1 = 640x400x256 modo ATT400
2 = 640x480x256 modo VGA
3 = 800x600x256 modo SVGA
4 = 1024x768x256; modo XGA
Todos son los modos graficos estandar */
return 4;
}

void main()
{ int modo = DETECT, tarj,TIPOLETRA;
int color=0,x=180,y=50;

/*/Instalamos el driver SVGA.bgi para acceder a 256 colores*/


installuserdriver("Svga256",DetectVGA256);

/*Instalamos en nuestro proyecto el tipo de letra*/


TIPOLETRA = installuserfont("LITT.CHR");
/*Iniciamos el modo grafico*/
initgraph(&modo,&tarj,"");

setfillstyle(SOLID_FILL,WHITE); /*elegimos el color de relleno para


dibujar una barra */
bar(0 , 0, 1028, 768); /*y asi simulamos que hemos elegido un color
de fondo*/
/*No se pueden utilizar los comandos cleardevice ni setbkcolor.*/

setcolor(7);
while(color<256)
{ rectangle(x ,y ,x + 40, y + 40);
setfillstyle(SOLID_FILL,color);
bar(x + 1, y + 1, x + 39, y + 39);
x = x + 40;
color++;
if((color%16==0)&&(color!=0))
{ x = 180; y = y + 40;
}
}
settextstyle(TIPOLETRA,0,9); /*usamos el tipo de letra*/
setcolor(41); /*usamos el color numero 18, un gris oscuro*/
outtextxy(40,10,"Prueba de Inicializacion del modo XGA 1024x768
pixeles");
settextstyle(TIPOLETRA,0,6);
setcolor(BLACK);

Juan Carlos Gutiérrez Barquero____________________________________________ 107


Bismarck Salvador Traña López__________________________________________UNI

outtextxy(12,690,"Como ves ahora si puedes usar el color negro en tus


aplicaciones y usar una mayor resolucion: 1024x768");
settextstyle(TIPOLETRA,0,7);

setcolor(55);
outtextxy(425,740,"Huacho - Peru");
getch();
closegraph();
}
Ahora si quieren ver un programa funcionando a qui lo tienen

Programa que carga imágenes .bmp en modo 1024x768:

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <graphics.h>
#include <dos.h>
#include <mouse.h>

#define VAR 0
#define INVALIDO -1
#define FALLA 0
#define EXITO 1
#define KB_ESC 27
/* MACROS PARA LAS FUNCIONES GRAFICAS */
#define SEG_VIDEO 0xA000
#define M320x200 0x13
#define MTexto 0x3

struct cabecera {
char ident[2];
unsigned long int tam;
char reservado[4];
unsigned long int offset;
};

struct info_general {
unsigned long int tam_cabecera;
unsigned long int anchura; /* en pixels */
unsigned long int altura; /* en pixels */
int planos;
int tam_pixel; /* tama¤o en bits de cada pixel */
unsigned long int compresion;
unsigned long int tam_imagen; /* en bytes */
unsigned long int h_resolution;
unsigned long int v_resolution;
unsigned long int num_color; /* numero de colores utilizados */
unsigned long int color_imp; /* numeros de colores importantes */
};

Juan Carlos Gutiérrez Barquero____________________________________________ 108


Bismarck Salvador Traña López__________________________________________UNI

struct valor_rgb {
unsigned char azul;
unsigned char verde;
unsigned char rojo;
unsigned char reservado;
};

void modo_video(unsigned char modo);


void setpal(char col,char r,char g,char b);
/*void putpixel(unsigned int x, unsigned int y, unsigned char color);*/
/*-------------------------------------------------------------------------*/
char verifica(char *fname);
void mostrar_pal(void);
void carga(void);
void menu(void);
void presentacion(void);
void info_general(void);
void paleta(void);
void mostrar_imagen(void);
void cambiar_pal(struct valor_rgb *pal);
void despedida(void);

struct cabecera bmp_cab;


struct info_general bmp_info;
struct valor_rgb *rgb;
FILE *f;
char fname[50];

int main()
{
presentacion();
fclose(f);
free(rgb); /* liberamos la memoria utilizada */

return 0;
}

void carga(void)
{
register int i;

/* leemos la cabecera del archivo */


fread( &bmp_cab, sizeof(struct cabecera), 1, f);
/* leemos la informacion general del archivo */
fread( &bmp_info, sizeof(struct info_general), 1, f);

/*leemos todos los colores que existen en la imagen */


if (!bmp_info.num_color) /* si se usan todos los colores */

Juan Carlos Gutiérrez Barquero____________________________________________ 109


Bismarck Salvador Traña López__________________________________________UNI

bmp_info.num_color = 1 << bmp_info.tam_pixel; /* calculamos dependiendo del numero


de bits que ocupa cada pixel */

/* reservamos memoria dinamica para la tabla de colores RGB */


rgb = (struct valor_rgb *) malloc (bmp_info.num_color * sizeof(struct valor_rgb));

fread(rgb, bmp_info.num_color * sizeof(struct valor_rgb), 1, f);


}

void presentacion(void)
{
char control;

clrscr();

printf("%55s","---VISUALIZADOR DE IMAGENES BMP---");


printf("\n\n\n Introduzca el nombre y ruta de ser necesario");
printf("\n ARCHIVO: ");
gets(fname);
if (!fname[0])
exit(0);

control = verifica(fname);

if (control == FALLA)
{
printf("\n ERROR FATAL: el archivo no existe o esta protegido...");
exit(1);
}
if (control == INVALIDO)
{
printf("\n ERROR FATAL: el archivo no es un BMP valido... ");
exit(1);
}

/* llamamos al menu principal */


menu();
}

char verifica(char *fname)


{
f = fopen(fname,"rb");

if (!f)
return (FALLA);

carga(); /* leemos y almacenamos del archivo */

/* comprobamos que sea un archivo valido BMP */


if (bmp_cab.ident[0] == 'B' && bmp_cab.ident[1] == 'M')
return (EXITO);

Juan Carlos Gutiérrez Barquero____________________________________________ 110


Bismarck Salvador Traña López__________________________________________UNI

else
return (INVALIDO);

void menu(void)
{
char opcion;

for (;;)
{
clrscr();
printf("\n\n (1) INFORMACION GENERAL DEL ARCHIVO");
printf("\n\n (2) PALETA DE COLORES USADAS");
printf("\n\n (3) MOSTRAR PALETA");
printf("\n\n (4) VISUALIZAR IMAGEN");
printf("\n\n (5) SALIR");
printf("\n\n OPCION: ");
opcion = getche();

switch (opcion)
{
case '1': info_general(); break;
case '2': paleta(); break;
case '3': mostrar_pal(); break;
case '4': mostrar_imagen(); break;
case '5': despedida();
}
} /* termina bucle for(;;) */
}

void info_general(void)
{

clrscr();
printf("%35s%s>\n","<",fname);
printf("\n Tama¤o del archivo: %ld bytes",bmp_cab.tam);
printf("\n Offset del archivo: %ld",bmp_cab.offset);
printf("\n Tama¤o de la cabecera: %d bytes",bmp_info.tam_cabecera);
printf("\n Anchura: %d pixels",bmp_info.anchura);
printf("\n Altura: %d pixels",bmp_info.altura);
printf("\n Numero de planos: %d",bmp_info.planos);
printf("\n Numero de bits por pixel: %d bits",bmp_info.tam_pixel);
printf("\n Compresion: %d ",bmp_info.compresion);
printf("\n Tama¤o de la imagen: %ld bytes",bmp_info.tam_imagen);
printf("\n Resolucion horizontal: %d pixels/metros",bmp_info.h_resolution);
printf("\n Resolucion vertical: %d pixels/metros",bmp_info.v_resolution);
printf("\n Numero de colores utilizados: %d colores",bmp_info.num_color);
printf("\n Numero de colores importantes: %d colores",bmp_info.color_imp);

printf("\n\n\n PRESIONE CUALQUIER TECLA PARA CONTINUAR...");

Juan Carlos Gutiérrez Barquero____________________________________________ 111


Bismarck Salvador Traña López__________________________________________UNI

getch();
}

void cambiar_pal(struct valor_rgb *pal)


{
register int i;

for (i = 0; i < 256; i++)


setpal( i, pal[i-VAR].rojo / 4, pal[i-VAR].verde / 4, pal[i-VAR].azul / 4);
}

void paleta(void)
{
register int i,j;
char opcion;

clrscr();

printf("\n %10s %10s %10s %10s\n\n","Color","Rojo","Verde","Azul");


for (i = 0, j = 1; i <= (bmp_info.num_color); i++, j++)
{
if (j == 20 || i == (bmp_info.num_color) )
{
j = 0;
i--;
gotoxy(1,25);
printf(" Presione [ESC] para salir o cualquier tecla para continuar....");
opcion = getch();

if (opcion == KB_ESC)
break;
if (i+1 == (bmp_info.num_color))
break;

clrscr();
printf("\n %10s %10s %10s %10s\n\n","Color","Rojo","Verde","Azul");
continue;
}
printf("\n %10d %10d %10d %10d", i, rgb[i].rojo, rgb[i].verde, rgb[i].azul);
}
}

void mostrar_imagen(void)
{
register int x,y;
char *linea;
int resto;
unsigned long int posicion;
posicion = ftell(f); /* tomamos la posicion del puntero del archivo */

Juan Carlos Gutiérrez Barquero____________________________________________ 112


Bismarck Salvador Traña López__________________________________________UNI

modo_video(M320x200);
mver();
cambiar_pal(rgb);

/* reservamos memoria suficiente para albergar una linea de pixeles */


linea = (char *) malloc (bmp_info.anchura);

/* calculamos la cantidad de bytes necesario que sumado con la anchura


sea un multiplo de cuatro */
resto = (4 * ((bmp_info.anchura + 3) >> 2)) - bmp_info.anchura;

for (y = bmp_info.altura - 1; y >= 0; y--)


{
fread(linea, bmp_info.anchura, 1, f);
for (x = 0; x < bmp_info.anchura; x++)
putpixel(x,y,linea[x]);
fseek(f, resto, SEEK_CUR);
}
getch();

fseek(f, posicion, SEEK_SET); /* restauramos la posicion inicial del puntero */

modo_video(MTexto);
}

void mostrar_pal(void)
{
register int i,j;

modo_video(M320x200);

cambiar_pal(rgb);

for (i = 0; i < 256; i++)


for (j = 0; j <= 50; j++)
putpixel(i,j,i);
getch();

modo_video(MTexto);
}

void despedida(void)
{
clrscr();
gotoxy(20,13);
printf("ESPERO QUE HAYA DISFRUTADO DEL PROGRAMA...");
getch();
exit(0);
}

Juan Carlos Gutiérrez Barquero____________________________________________ 113


Bismarck Salvador Traña López__________________________________________UNI

/* ----FUNCIONES GRAFICAS ESCRITAS EN ENSAMBLADOR PARA SU MAYOR


RAPIDEZ---- */

int huge DetectVGA256()


{
return 4;
}
void modo_video()
{
int a=DETECT,b;
installuserdriver("Svga256",DetectVGA256);
initgraph(&a,&b,"c:\\tc20\\bin");
/* union REGS r;
r.h.ah=0;
r.h.al=0x13;
int86(0x10,&r,&r);*/
}

/*void putpixel(unsigned int x, unsigned int y, unsigned char color)


{
unsigned char *vga=(unsigned char *) MK_FP(0xA000, 0);
asm {
push ax
push bx
push di
push es
mov ax,SEG_VIDEO
mov es,ax
mov bx,[y]
mov di,bx
shl bx,8
shl di,6
add di,bx
add di,[x]
mov bl,[color]
mov es:[di],bl
pop es
pop di
pop bx
pop ax
}

memset(vga+x+(y*320),color,1);
}*/

void setpal(char col,char r,char g,char b)


{

Juan Carlos Gutiérrez Barquero____________________________________________ 114


Bismarck Salvador Traña López__________________________________________UNI

/*asm {
mov dx,03c8h;
mov al,col;
out dx,al;
mov dx,03c9h;
mov al,r;
out dx,al;
mov al,g;
out dx,al;
mov al,b;
out dx,al;
} */

outportb(0x3c8,col);
outportb(0x3c9,r);
outportb(0x3c9,g);
outportb(0x3c9,b);
}

Ahora un Ejemplo de la utilización de las estructuras este programa se llama reloj


analogo muestra la hora del sistema.

#include <graphics.h>
#include <stdio.h>
#include <dos.h>
#include <math.h>
#include <conio.h>
#include <stdio.h>
void main(void)
{
struct time tiempo;
int a=DETECT,b;
int radio=180;
int seg,min,w,h;
float angulmin, anguloseg, angulhora,hora;
char mensaje[20];
int xseg, yseg, xmin, ymin, xhora, yhora;
initgraph(&a,&b,"c:\\tc20\\bin");
circle(310,240,238);
h=0;
for(w=0;w<=60;w++){
if(h%5==0)
setcolor(RED);
else
setcolor(WHITE);

outtextxy(310+sin(6*h*(3.141592/180))*225,240-cos(h*(3.141592/180)*6)*225,"*");
h+=1;
}

Juan Carlos Gutiérrez Barquero____________________________________________ 115


Bismarck Salvador Traña López__________________________________________UNI

settextstyle(1,0,2);
outtextxy(300,20,"12");
outtextxy(520,230,"3");
outtextxy(310,433,"6");
outtextxy(100,230,"9");

do{
gettime(&tiempo);
seg=tiempo.ti_sec;
min=tiempo.ti_min;
hora=tiempo.ti_hour;
if(hora>12)
hora-=12;
hora+=(min/60);

anguloseg=((seg-15)/(3*3.14159));
angulmin=((min-15)/(3*3.14159));
angulhora=((hora*5-15)/(3*3.14159));
setcolor(WHITE);
circle(310,240,8);
rectangle(240,390,360,420);
sprintf(mensaje,"%2.0f: %2.0d: %2.0d",hora,tiempo.ti_min,tiempo.ti_sec);
outtextxy(250,390,mensaje);

xseg = 310 + radio * cos(anguloseg);


yseg = 240 + radio * sin(anguloseg);
xmin = 310 + 120 * cos(angulmin);
ymin = 240 + 120 * sin(angulmin);
xhora= 310 + 90 * cos(angulhora);
yhora= 240 + 90 * sin(angulhora);
setcolor(WHITE);
line(310,240,xseg,yseg);
setcolor(GREEN);
line(310,240,xmin,ymin);
setcolor(RED);
line(310,240,xhora,yhora);

sleep(1);

setcolor(BLACK);
line(310,240,xseg,yseg);
line(310,240,xmin,ymin);
line(310,240,xhora,yhora);
outtextxy(250,390,mensaje);

} while(!kbhit());
}

Juan Carlos Gutiérrez Barquero____________________________________________ 116


Bismarck Salvador Traña López__________________________________________UNI

Ahora aremos un efecto que talvez les guste y lo estudien con detenimiento ya que
merece mucha atención.

Primero guarda estas lineas de codigo en un programa de C con el nombre TECLA.C

#define ARRIBA 72
#define ABAJO 80
#define DERECHA 77
#define IZQUIERDA 75
#define ESC 27

#define ENTER 13

Ahora en otro programa copia este codigo y te sorprenderas de ver lo que hace

#include <graphics.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <dos.h>
#include <conio.h>
#include "tecla.c" //es para el manejo del teclado

float A,B,TAM=15,fi= -M_PI/7,teta= -M_PI/7,pro_X,pro_Y;


/*
ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
º Definicion de funciones º
º º
ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
*/
void IniciarGraficos();
void xyz();
void ProyectaPunto(float ,float ,float);
void Objeto();
void instrucciones();
/*****************************************************************/
/*
ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ProyectaPunto ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
º Funcion: Localiza un punto en la pantalla 3D º
º º
ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
*/
void ProyectaPunto(float x, float y, float z)
{
pro_X = y*cos(teta) + x*sin(teta);
pro_Y = z*cos(fi) + x*cos(teta)*sin(fi) - y*sin(fi)*sin(teta);
}
/*
ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ IniciarGraficos ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
º Funcion: Inicializa el modo grafico º
º º

Juan Carlos Gutiérrez Barquero____________________________________________ 117


Bismarck Salvador Traña López__________________________________________UNI

ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
*/
void IniciarGraficos()
{
int Tarjeta=VGA,Modo=VGAHI;
initgraph(&Tarjeta,&Modo,"");
if(graphresult()!=grOk){
printf("\nSi perdiste el archivo EGAVGA.BGI no podes correr el programa :(
asi que buscalo el C:\\tc\\bgi");
getch();
exit(0);
}
}
/*
ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ Main ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
º Funcion: Main. º
º º
ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
*/
void main()
{
IniciarGraficos();
A=getmaxx()/2,B=getmaxy()/2;

xyz();
Objeto();

closegraph();
}
/*
ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ xyz ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
º Funcion: Dibuja Lineas de Guia en las coordenadas X,Y,Z º
º º
ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
*/
void xyz()
{
float X0,Y0,X,Y,z,tam=3;
int x,y;
setcolor(8);
ProyectaPunto(0,0,0);X0 = pro_X,Y0 = pro_Y;
ProyectaPunto(tam,0,0);X = pro_X,Y = pro_Y;
line(A + X0*TAM,B - Y0*TAM,A + X*TAM,B - Y*TAM);
x=A + X*TAM,y=B - Y*TAM;
setcolor(8);

outtextxy(x,y,"x");
setcolor(8);

ProyectaPunto(0,tam,0);X = pro_X,Y = pro_Y;


line(A + X0*TAM,B - Y0*TAM,A + X*TAM,B - Y*TAM);

Juan Carlos Gutiérrez Barquero____________________________________________ 118


Bismarck Salvador Traña López__________________________________________UNI

x= A + X*TAM,y=B - Y*TAM;
setcolor(8);
outtextxy(x,y,"y");
setcolor(8);
ProyectaPunto(0,0,tam);X = pro_X,Y = pro_Y;
line(A + X0*TAM,B - Y0*TAM,A + X*TAM,B - Y*TAM);
setcolor(8);
x=A + X*TAM,y=B - Y*TAM;
outtextxy(x,y,"z");
ProyectaPunto(0,10,0);X=pro_X,Y = pro_Y;
x=A + X*TAM,y=B - Y*TAM;
setcolor(RED);
outtextxy(x,y,"JDZx");
setcolor(8);

}
/*
ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ Objeto ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
º Funcion: Dibuja un objeto 3D º
º º
ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
*/
void Objeto()
{
float angulo=-M_PI/100.0;
float AA[8],BB[8],CC[12],estrellas[2],EE[8],FF[8],
xa1=3,ya1=-3,za1=-3,xa2=-3,ya2=-3,za2=-3,
xa3=-3,ya3=-3,za3=3,xa4=3,ya4=-3,za4=3,

xb1=3,yb1=3,zb1=-3,xb2=-3,yb2=3,zb2=-3,
xb3=-3,yb3=3,zb3=3,xb4=3,yb4=3,zb4=3;
float x,y;
unsigned char tecla;
int fo;
do{
ProyectaPunto(xa1,ya1,za1);AA[0]=pro_X,AA[1]=pro_Y;
ProyectaPunto(xa2,ya2,za2);AA[2] = pro_X,AA[3] = pro_Y;
ProyectaPunto(xa3,ya3,za3);AA[4] = pro_X,AA[5] = pro_Y;
ProyectaPunto(xa4,ya4,za4);AA[6] = pro_X,AA[7] = pro_Y;
/*-------------------------------------------*/
ProyectaPunto(xb1,yb1,zb1);BB[0]=pro_X,BB[1]=pro_Y;
ProyectaPunto(xb2,yb2,zb2);BB[2] = pro_X,BB[3] = pro_Y;
ProyectaPunto(xb3,yb3,zb3);BB[4] = pro_X,BB[5] = pro_Y;
ProyectaPunto(xb4,yb4,zb4);BB[6] = pro_X,BB[7] = pro_Y;
ProyectaPunto(5,8,0);CC[0]=pro_X,CC[1]=pro_Y;
ProyectaPunto(-4,-8,0);CC[4]=pro_X,CC[5]=pro_Y;
ProyectaPunto(5,-5,3);CC[6]=pro_X,CC[7]=pro_Y;
ProyectaPunto(10,5,-1);CC[8]=pro_X,CC[9]=pro_Y;
ProyectaPunto(0,0,0);CC[10]=pro_X,CC[11]=pro_Y;

setcolor(1);

Juan Carlos Gutiérrez Barquero____________________________________________ 119


Bismarck Salvador Traña López__________________________________________UNI

circle(A + CC[0]*TAM,B - CC[1]*TAM,TAM*0.52);


setcolor(2);
circle(A + CC[4]*TAM,B - CC[5]*TAM,TAM*0.25);
setcolor(3);
circle(A + CC[6]*TAM,B - CC[7]*TAM,TAM*0.8);
setcolor(4);
circle(A + CC[8]*TAM,B - CC[9]*TAM,TAM*2.5);
setcolor(YELLOW);
circle(A + CC[10]*TAM,B - CC[11]*TAM,TAM*2.2);
/*-----------------------*/
ProyectaPunto(5,0,2);estrellas[0] = pro_X,estrellas[1] = pro_Y;
putpixel(A + estrellas[0]*TAM,B - estrellas[1]*TAM,7);

ProyectaPunto(-15-0.2,5,-2);estrellas[0] = pro_X,estrellas[1] = pro_Y;


setcolor(8);
circle(A + estrellas[0]*TAM,B - estrellas[1]*TAM,TAM*0.2);

ProyectaPunto(-10,-3,-2);estrellas[0] = pro_X,estrellas[1] = pro_Y;


putpixel(A + estrellas[0]*TAM,B - estrellas[1]*TAM,7);
ProyectaPunto(-10,-3,-2);estrellas[0] = pro_X,estrellas[1] = pro_Y;
putpixel(B + estrellas[1]*TAM,A - estrellas[1]*TAM,7);
ProyectaPunto(-5,3,-2);estrellas[0] = pro_X,estrellas[1] = pro_Y;
putpixel(A + estrellas[0]*TAM,B - estrellas[1]*TAM,7);
ProyectaPunto(10,-3,-2);estrellas[0] = pro_X,estrellas[1] = pro_Y;
putpixel(A + estrellas[0]*TAM,B - estrellas[1]*TAM,7);

xyz();

line(A + AA[0]*TAM,B - AA[1]*TAM,A + AA[2]*TAM,B - AA[3]*TAM);


line(A + AA[4]*TAM,B - AA[5]*TAM,A + AA[6]*TAM,B - AA[7]*TAM);
line(A + AA[0]*TAM,B - AA[1]*TAM,A + AA[6]*TAM,B - AA[7]*TAM);
line(A + AA[2]*TAM,B - AA[3]*TAM,A + AA[4]*TAM,B - AA[5]*TAM);

line(A + AA[0]*TAM,B - AA[1]*TAM,A + BB[0]*TAM,B - BB[1]*TAM);


line(A + AA[2]*TAM,B - AA[3]*TAM,A + BB[2]*TAM,B - BB[3]*TAM);
line(A + AA[4]*TAM,B - AA[5]*TAM,A + BB[4]*TAM,B - BB[5]*TAM);
line(A + AA[6]*TAM,B - AA[7]*TAM,A + BB[6]*TAM,B - BB[7]*TAM);

line(A + BB[0]*TAM,B - BB[1]*TAM,A + BB[2]*TAM,B - BB[3]*TAM);


line(A + BB[2]*TAM,B - BB[3]*TAM,A + BB[4]*TAM,B - BB[5]*TAM);
line(A + BB[4]*TAM,B - BB[5]*TAM,A + BB[6]*TAM,B - BB[7]*TAM);
line(A + BB[6]*TAM,B - BB[7]*TAM,A + BB[0]*TAM,B - BB[1]*TAM);

tecla=IZQUIERDA;
if(kbhit())
{
tecla=getch();
}
setfillstyle(1,0);

Juan Carlos Gutiérrez Barquero____________________________________________ 120


Bismarck Salvador Traña López__________________________________________UNI

switch(tecla)
{
case DERECHA:teta-=angulo;break;
case IZQUIERDA:teta+=angulo;break;
case ABAJO:fi+=angulo;break;
case ARRIBA:fi-=angulo;break;
case 122:if(TAM>0)TAM-=0.2;break;
case 120:if(TAM<30)TAM+=0.2;break;

}/*endcase*/

if(!kbhit()){delay(50);}
bar(0,0,getmaxx(),getmaxy());
}while(tecla!=ESC);
}

/*Ahora un juego de la serpientes este es un programa que encontre en Internet, no se


quien es el autor, le hice algunos cambios */

#include<stdlib.h>
# include <dos.h>
#include<stdio.h>
#include<conio.h>
#include<graphics.h>

#define clear; clrscr();


#define colorfondo 0
#define colorcabeza 4
#define c 1

#define bordear 40
#define bordeab 460 /*acotamos la parte valida de pantalla*/
#define bordeiz 20
#define bordede 620

void Inicio(void);
void Salir(void);
void Juego(void);
void Avanza(void);
void Crece(void);
void Libera(void);
void Pinta_serpiente(void);
void Pinta_rapido(void);
int Choque(void);
void Pinta_pantalla(void);
int Es_punto(int x,int y);
void Lee_record(void);
void Escribe_record(void);

Juan Carlos Gutiérrez Barquero____________________________________________ 121


Bismarck Salvador Traña López__________________________________________UNI

struct bola{
int x;
int y; /*La serp no es mas que una lista doble de puntos con su x y su y*/
struct bola *sig,*ant;
};

int m,v,vel,tarj,modo,puntos,max,punt[9]={90,80,70,60,50,40,30,20,10};
int longitud=0,radio=5,xa,ya,superado=0; /*No hay mas remedio que usar todas estas
globales*/

struct bola *cabeza,*indice,*ultimo; /*Para la lista*/

void main(void)
{
detectgraph(&tarj,&modo);/*comprueba el hw y detecta la tarjeta a usar*/
Inicio();
Juego();
}

void Inicio(void)
{
int i;
clear;

textcolor(14);
for (i=1; i<81; i++)
{
gotoxy(i,9);
cprintf ("Ä");
}

textcolor(1);
gotoxy(34,1); cprintf (" Û Û");
gotoxy(34,2); cprintf (" Û ÛÛ ÛÛ Û");
gotoxy(34,3); cprintf (" ÛÛÛÛÛ ÛÛÛÛÛ");
gotoxy(34,4); cprintf ("ÛÛ ÛÛÛ ÛÛÛ ÛÛ");
gotoxy(34,5); cprintf ("ÛÛ ÛÛÛÛÛÛÛ ÛÛ");
gotoxy(33,6); cprintf ("ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ");
gotoxy(33,7); cprintf ("ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ");
gotoxy(34,8); cprintf ("ÛÛÛÛÛÛÛÛÛÛÛÛÛ");
gotoxy(38,9); cprintf ("Û");gotoxy(42,9); cprintf("Û");

textcolor(4);
gotoxy(40,1); cprintf("Û");gotoxy(40,2); cprintf("Û");
gotoxy(40,3); cprintf("Û");gotoxy(40,4); cprintf("Û");
gotoxy(36,4); cprintf("Û");gotoxy(44,4); cprintf("Û");
gotoxy(36,5); cprintf("Û");gotoxy(44,5); cprintf("Û");
gotoxy(39,7); cprintf("O");gotoxy(41,7); cprintf("O");

Juan Carlos Gutiérrez Barquero____________________________________________ 122


Bismarck Salvador Traña López__________________________________________UNI

textcolor(9);
gotoxy(31,10); cprintf ("ÛÛÛ Û Û ÛÛÛ ÛÛÛ ÛÛÛ");
gotoxy(31,11); cprintf ("Û Û Û Û Û Û Û Û");
gotoxy(31,12); cprintf ("Û ÛÛÛ ÛÛÛ Û Û ÛÛÛ");
gotoxy(31,13); cprintf ("Û Û Û Û Û Û Û Û");
gotoxy(31,14); cprintf ("ÛÛÛ Û Û Û Û ÛÛÛ ÛÛÛ");

textcolor(15);
gotoxy(29,16); cprintf ("Dise¤ado por Abdul Ruiz");
gotoxy(31,17); cprintf ("CopyRight (c) 2002");
getch();
gotoxy(29,19); cprintf("Espera, por favor...");
delay(5500);
clear;
cprintf("TECLAS:\n\n\r");
cprintf(" -(Ar) 5(Ar)\n\r");
cprintf("MOVIMIENTOS o en el t.num.\n\r");
cprintf(" <Ä(Izq) (Ab) Ä>(Der) 1(Izq) 2(Ab) 3(Der)\n\r");
cprintf("Pausa: P\n\r");
cprintf("Salir: S\n\n\r");
cprintf("Si utilizas el teclado numerico, asegurate de que este activado\n\n\n\r");
cprintf("Pulsa una tecla para empezar");
getch();
sleep(5);

clear;
cprintf ("Dime el nivel de dificultad 1(Minima)...9(Maxima): ");
do{ scanf("%d",&m); }while(m<1 || m>9);

switch(m)
{
case 1:v=9;break;case 2:v=8;break;case 3:v=7;break;case 4:v=6;break;case
5:v=5;break;
case 6:v=4;break;case 7:v=3;break;case 8:v=2;break;case 9:v=1;break;
}
}

void Juego(void)
{
int obj=0;/*Para saber si hay algo de comida en la pantalla*/
int mov=radio*2;
char t,a;/*La tecla pulsada y la anterior*/
char pito=7;
int x2,y2;

initgraph(&tarj,&modo,"c:\\tc20\\bin");/*iniciamos modo grafico*/


setbkcolor(colorfondo);/*configuramos los colores del juego*/
setcolor(colorfondo);
setfillstyle(1,c);

Juan Carlos Gutiérrez Barquero____________________________________________ 123


Bismarck Salvador Traña López__________________________________________UNI

Lee_record();

t=77;/*ponemos una tecla (para que empiece yendo hacia la derecha)*/

superado=0;

vel=v*300;

Crece(); /*creamos la cabeza*/


cabeza->x=bordeiz+radio; /*la colocamos*/
cabeza->y=bordeab-radio;
Crece(); /*ponemos otras dos bolitas de cuerpo*/
Crece();

Pinta_pantalla();
gotoxy(35,15);
printf("ADELANTE");
delay(10000);
cleardevice();

do
{
/*si no hay objeto, cogemos una pos aleatoria*/
if(obj==0)
{
do{
do{
x2=(((rand()%((bordede/(radio*2))-1)+1))*radio*2)-radio;
}while(x2>bordede-radio || x2<bordeiz+radio);

do{
y2=(((rand()%((bordeab/(radio*2))-1)+1))*radio*2)-radio;
}while(y2>bordeab-radio || y2<bordear+radio);
}while(Es_punto(x2,y2));
obj=1;

/*si se pulsa una tecla*/


if(kbhit())
{
a=t; /*se guarda la anterior*/
t=getch(); /* y se coge*/
}

if(Choque())
{
printf("%c",pito); /*si se choca, pierde*/
Salir();
}

Juan Carlos Gutiérrez Barquero____________________________________________ 124


Bismarck Salvador Traña López__________________________________________UNI

Pinta_pantalla();

if(obj==1) /*si hay objeto, lo pintamos de otro color*/


{
setfillstyle(1,12);
fillellipse(x2,y2,radio-2,radio-2);
setfillstyle(1,c);
}

switch(t) /*evalua la tecla*/


{
case 72:
case '5':
Avanza();
cabeza->y=cabeza->y-mov;

break; /*arriba */

case 75:
case '1':
Avanza();
cabeza->x=cabeza->x-mov;

break; /*izquierda */

case 77:
case '3':
Avanza();
cabeza->x=cabeza->x+mov;

break; /*derecha */

case 80:
case '2':
Avanza();
cabeza->y=cabeza->y+mov;

break; /*abajo*/

case 's':
case 'S':
/*si quiere salir, liberamos mem, cerramos el modo grafico y exit*/
Libera();
closegraph();
Escribe_record();
exit(2);

break;

case 'p':/*pausa*/

Juan Carlos Gutiérrez Barquero____________________________________________ 125


Bismarck Salvador Traña López__________________________________________UNI

cleardevice();
Pinta_serpiente();
gotoxy(37,15);
printf("Pausa");
t=getch();
cleardevice();
Pinta_serpiente();
break;

/*si la tecla pulsada no hace nada, volvemos a poner la enterior*/


default: t=a;
}

/*si se pasa por encima del objeto, se borra y se crece*/


if(cabeza->x==x2 && cabeza->y==y2)
{
puntos+=punt[v-1];
obj=0;
printf("%c",pito);
switch(m)
{
case 1: Crece(); break;

case 2:
case 3: Crece();Crece();break;

case 4:
case 5: Crece();Crece();Crece();break;

case 6:
case 7:
case 8: Crece();Crece();Crece();Crece();Crece();break;
case 9:
Crece();Crece();Crece();Crece();Crece();Crece();break;
}
}

/*cuantas mas bolas tenga que dibujar el ordenador, mas se*/


/*ralentiza el juego, esto lo contrarestamos disminuyendo el delay*/
/*cada 30 bolitas(lo he calculado a ojo)

if((longitud)/(i*30)<0)
{
vel-=15;
i++;
}*/

delay(vel);

if(puntos>max)
{

Juan Carlos Gutiérrez Barquero____________________________________________ 126


Bismarck Salvador Traña López__________________________________________UNI

max=puntos;
superado=1;
}

}while(1);
}

void Salir(void)
{
char opc;

/*no hace falta comentar nada*/

if(superado!=0)
{
Escribe_record();
gotoxy(11,10);
printf(" Puntuacion maxima conseguida, eres el mejor, felicidades");
}

gotoxy(11,15);
printf("Estas muerto, lo siento (R)Reintentar,(I)Reiniciar,(S)Salir");

do{
opc=toupper(getch()); }while(opc!='R' && opc!='I' && opc!='S');

puntos=0;

switch (opc)
{
case 'R':
Libera();
Juego();
break;
case 'I':
closegraph();
Libera();
Inicio();
Juego();
break;
case 'S':
closegraph();
Libera();
exit(1);
break;
}
}

void Avanza(void)
{
indice=ultimo;

Juan Carlos Gutiérrez Barquero____________________________________________ 127


Bismarck Salvador Traña López__________________________________________UNI

xa=ultimo->x;
ya=ultimo->y;

/*nos quedamos con la posicion de la cola*/

/*corremos cada bola a la pos de la bola anterior*/


while(indice!=cabeza)
{
indice->x=indice->ant->x;
indice->y=indice->ant->y;
indice=indice->ant;
}
}

void Pinta_serpiente(void)
{
int i=0;
indice=cabeza;

setfillstyle(1,colorcabeza);
fillellipse(indice->x,indice->y,radio,radio);
setfillstyle(1,c);
i++;
indice=indice->sig;

/*sin comentarios*/

while(i<longitud)
{
fillellipse(indice->x,indice->y,radio,radio);
i++;
indice=indice->sig;

/*Como hemos comprobado en anteriores versiones, al pintar la serpiente entera*/


/*, si esta media mucho, se ralentizaba el juego mogollon, y sabemos que, al pintar*/
/*una bola y moverla, se queda su estela pintada, no?, co¤o pues pintemos solo*/
/*la primera, segunda y ultima bolita, y asi, mida lo que mida la serpiente,*/
/*solo pintaremos 3 bolas, y no se ralentiza casi nada.*/

void Pinta_rapido(void)
{
setfillstyle(1,colorcabeza);
fillellipse(cabeza->x,cabeza->y,radio,radio);
setfillstyle(1,c);
fillellipse(cabeza->sig->x,cabeza->sig->y,radio,radio);
fillellipse(ultimo->x,ultimo->y,radio,radio);

Juan Carlos Gutiérrez Barquero____________________________________________ 128


Bismarck Salvador Traña López__________________________________________UNI

int Choque(void)
{
indice=cabeza->sig;

/*si la cabeza esta en la pos de alguna bola o sale de los limites,


devolvemos un 1*/

while(indice!=NULL)
{
if(cabeza->x==indice->x && cabeza->y==indice->y) return 1;

if((cabeza->y<bordear+radio)||(cabeza->x<bordeiz+radio)||(cabeza-
>x>bordede-radio)||(cabeza->y>bordeab-radio)) return 1;

indice=indice->sig;
}

return 0;
}

void Crece(void)
{
struct bola *este;

este=malloc(sizeof(struct bola));

/*pues eso, se a¤ade una struct bola al final de la lista*/

if(cabeza==NULL)
{
ultimo=cabeza=este;
cabeza->ant=NULL;
ultimo->sig=NULL;
cabeza->x=bordeiz+radio;
cabeza->y=bordeab-radio;
}
else
{
ultimo->sig=este;
este->ant=ultimo;
ultimo=este;
ultimo->sig=NULL;
este->x=-100;
este->y=-100;
}
indice=ultimo;
longitud++;
}

Juan Carlos Gutiérrez Barquero____________________________________________ 129


Bismarck Salvador Traña López__________________________________________UNI

void Libera(void)
{
struct bola *aux;

indice=cabeza;

/*no hace falta comentario*/

cabeza=ultimo=NULL;

while(indice!=ultimo)
{
aux=indice;
indice=indice->sig;
free(aux);
}

aux=indice;
free(aux);
longitud=0;
}

void Pinta_pantalla(void)
{
gotoxy(1,1);
printf(" Serpiente v1 Desarrollado por Abdul Ruiz Chaos\n\r");
gotoxy(10,2);
printf("Nivel : %1d Punt.Max. : %4d Puntuacion :%4d Longitud
:%4d",m,max,puntos,longitud);

Pinta_rapido(); /* dibujamos la serp */

/*"borramos" la ultima bola*/


/*este metodo es mas util, porque usando un cleardevice(), la
pantalla vibraba mucho*/

setfillstyle(1,colorfondo);
fillellipse(xa,ya,radio,radio);
setfillstyle(1,c);

/* dibujamos los bordes de la pantalla */


setcolor(15);
rectangle(bordeiz-1,bordear-1,bordede+1,bordeab+1);
setcolor(0);
}

int Es_punto(int x,int y)


{
indice=cabeza->sig;

Juan Carlos Gutiérrez Barquero____________________________________________ 130


Bismarck Salvador Traña López__________________________________________UNI

while(indice!=NULL)
{
if(x==indice->x && y==indice->y) return 1;

indice=indice->sig;
}

return 0;
}

void Lee_record(void)
{
FILE *arch;

if((arch=fopen("chaos.ars","rb"))!=NULL)
{
fread(&max,1,sizeof(int),arch);
if(puntos>max)
{
max=puntos;
}
fclose(arch);
}
else
{
max=puntos;
}
}

void Escribe_record(void)
{
FILE *arch;

arch=fopen("chaos.ars","wb");
fwrite(&max,1,sizeof(int),arch);
fclose(arch);
}

Juan Carlos Gutiérrez Barquero____________________________________________ 131


Bismarck Salvador Traña López__________________________________________UNI

1 Introducción a la Geometría Computacional

1.1. Historia de la Geometría Computacional

La Geometría fue estudiada desde la antigua Grecia y Egipto. Constituían una


herramienta muy importante para resolver problemas prácticos. El primer
algoritmo que pueda considerarse completo es la construcción Euclídea, porque
fue el primero que satisfizo todos los requerimientos: es correcto, no ambiguo
y tiene un final. Este algoritmo además da una colección de instrucciones y
reglas, posee un conjunto de primitivas asociadas.

Ya en 1902 se describió un conjunto de pasos para contar como realizar sobre


papel una serie de primitivas Euclídeas. Se hablaba por entonces ya del
término complejidad, no computacional pero sí de dificultad para medir la
complicación de su realización. Se puede decir que fue una aproximación a lo
que entendemos hoy por .complejidad temporal.. No se hablaba del problema
en función del conjunto de entrada, pero sí intentaban reducirse el número de
pasos a realizar para resolver un problema. Poco a poco se fueron dando
cuenta de que todo problema posee un esfuerzo mínimo de realización, lo que
nosotros llamamos .complejidad del algoritmo..

Ya en 1672 se demostró que cualquier construcción que pudiera realizarse sólo


con regla y compás podría hacerse usando sólo compás. El concepto de línea
se amplió pasando a ser el lugar geométrico compuesto por la unión de los
puntos de corte de dos arcos realizados con un compás.

La Geometría ha tenido momentos de especial realce, por ejemplo el


crecimiento del análisis real, la Geometría métrica y la teoría de la convexidad,
han supuesto las herramientas matem áticas para el diseño de algoritmos
rápidos. Por un lado la distancia es un tema esencial en conceptos geométricos
y la teoría de la convexidad es analizada para obtener propiedades globales.

La Geometría Algorítmica está basada en caracterizaciones de objetos


geométricos en términos de propiedades de subconjuntos finitos. Un
tratamiento algorítmico implica eliminar el término infinito. Hoy día la
Geometría Computacional se utiliza como herramienta básica, no sólo en
disciplinas inmediatamente relacionadas con la Geometría. A partir de finales
de los años 60 y 70, el incremento del uso de la computadoras en
investigación y desarrollo, propicia usar estas máquinas para resolver
problemas clásicos que siempre fueron resueltos a base de reglas y compás.

Pero la memoria de las máquinas y el tiempo de ejecución de éstas, la mayoría


de las veces compartido, exigió de pronto el conseguir métodos que no sólo
funcionaran, sino que intentaran optimizar al máximo dos recuerdos muy
importantes, sobre todo por aquel entonces: la memoria y el tiempo de CPU.
Hoy por hoy a pesar de utilizar máquinas más potentes, también se plantea en

Juan Carlos Gutiérrez Barquero____________________________________________ 132


Bismarck Salvador Traña López__________________________________________UNI

muchas ocasiones incrementar el número de datos a procesar, por lo que hoy


sigue siendo un problema a resolver.

Algunas aplicaciones donde la Geometría Computacional es necesaria es la


Informática Gráfica, Robótica, Sistemas de Información Geográficos, etc. El
término nace en 1975 por Shamos y desde entonces los trabajos y los
estudios en esta disciplina han ido creciendo de forma espectacular.

1.2. Eficiencia de los algoritmos

Existen distintas formas de medir algoritmos o de clasificarlos. Un algoritmo


fácil de entender, codificar y depurar es preferible a otro que realice lo mismo
pero que no cumpla estas carácter ísticas. Algo similar ocurre con respecto a la
eficiencia; un algoritmo es eficiente cuando usa adecuadamente los recursos
del ordenador y lo hace con la mayor rapidez posible. Los recursos son siempre
referidos al tiempo de ejecución y al tiempo de CPU que necesita para
ejecutarse.

Para optar por escribir un tipo de algoritmo que cumpla con las
especificaciones anteriores tambi én es necesario saber cuantas veces va a
ejecutarse dicho algoritmo y con qué cantidad de datos. Así, un algoritmo poco
eficiente con pocos datos es preferible utilizarlo a otro que necesite gran
cantidad de tiempo en diseñarse. Por el contrario es beneficioso en muchas
ocasiones realizar un buen diseño a pesar de utilizar gran cantidad de esfuerzo
y emplear estructuras de datos complejas, si se consigue finalmente un
método de resolución que realice buenos tiempos de ejecución para entradas
grandes de datos. Por otra parte, cuando nuestro algoritmo pretende resolver
un problema clásico, como puede ser en Geometría Computacional, se da por
supuesto que tiene que ser eficiente con grantes cantidades de datos y además
hay que suponer que dicho algoritmo va a ser ampliamente utilizado.

Por tanto, nuestro propósito es no sólo entender y utilizar algoritmos que


funcionen en Geometría Computacional sino que también sean eficientes. Para
medir dicha eficiencia, hay que tener en cuenta ciertos parámetros:

 Dependencia de los datos de entrada del programa.


 Calidad del código que genera el compilador en el programa objeto.
 Las instrucciones y arquitectura de las computadoras.
 Tiempo de ejecución de dicho programa.

Como entrar en detalles de compiladores diferentes y de distintas arquitecturas


es muy amplio y además es absolutamente cambiante, basta decir que cuanto
más rápido mejor. Lo que haremos será estudiar los algoritmos en función de
la entrada, básicamente de su tamaño, y además en la complejidad o tiempo
que emplea el algoritmo para su ejecución. Obviamente un algoritmo, o
programa puesto que se está ejecutando, va a ser más rápido si tiene que
procesar diez datos que si tiene que realizar lo mismo con una entrada de
tamaño cien.

Juan Carlos Gutiérrez Barquero____________________________________________ 133


Bismarck Salvador Traña López__________________________________________UNI

Vamos a denominar T(n) al tiempo de ejecución de un programa con tamaño


n. Para no especi ficar el tiempo en segundos, puesto que depende
excesivamente de la máquina empleada, se usará una función expresada en
términos de n. Por ejemplo, T(n) = cn2, siendo c una constante.

En muchas ocasiones es necesario referirse al tiempo de ejecución esperado


del algoritmo en el peor de los casos para una entrada de tamaño n. De esta
forma se puede acotar el problema con una cota superior que nos permita
conocer el margen en el que nos podemos mover. El caso medio también
puede ser interesante en casos donde se conozca que el peor caso se produce
con una probabilidad muy pequeña.

Cuando decimos que el tiempo de ejecución T(n) de un programa es O(n2)


significa que existen constantes enteras positivas c tales que T(n) fi cn2. Por
tanto es lo que llamamos cota superior que nos identifica el peor caso. En el
caso genérico decimos que T(n) es O( f (n)). También existen cotas inferiores
para hablar del tiempo de ejecución. Es una cota inferior o lo que es lo mismo:
el algoritmo se ejecutará al menos en el tiempo que la función g(n) indica. Se
dice que T(n) es (g(n)) cuando existe una constante c tal que T(n) fi cg(n).

Cuanto menor sea la representación gráfica de la función f (n), más rápido se


ejecutará el algoritmo para un conjunto de entrada de tamaño dado. Pero esto
no siempre es verdadero para todo n, es decir, en ocasiones el tiempo de
ejecución se refiere a grandes cantidades de información y por tanto, n tiende
a infinito. Así O(n2) es mejor que O(n3); O(n) es mejor que O(n2) pero peor
que O(nlogn). Por ejemplo un programa con tiempo O(100n) es más lento para
n menor que 20 que otro que se ejecute en tiempo O(5n2). En algoritmos
típicos de Geometría Computacional lo que se busca sobre todo son
ejecuciones en tiempo lineal O(n), o en O(nlog n).

A menos que un algoritmo tenga una velocidad de crecimiento baja, O(n) o


O(nlogn), un crecimiento modesto en la rapidez de la computadora no infiuirá
gran cosa en el tamaño del problema.

A pesar de todo lo expuesto anteriormente, si un algoritmo va a ser utilizado


pocas veces, el costo de escritura de una algoritmo eficiente puede que no
merezca la pena. Tampoco es necesario si siempre va a recibir un conjunto de
entrada pequeño. En algunas ocasiones un algoritmo puede permitirse el lujo
de ser algo más lento si a cambio es más didáctico. Por otro lado, si la
eficiencia a nivel de tiempo en CPU implica un alto coste en cantidad de
almacenamiento o porque se encuentre en dispositivos de memoria masiva.
Por último, en algoritmos numéricos es tan importante la precisión de los
cálculos como la eficiencia de éstos.

Para calcular la eficiencia de un algoritmo hay que tener en cuenta ciertos


casos:

Juan Carlos Gutiérrez Barquero____________________________________________ 134


Bismarck Salvador Traña López__________________________________________UNI

 Si T1(n) y T2(n) son tiempos de ejecución de dos fragmentos de


programa, el tiempo totalserá de T1(n)+T2(n). Sin embargo, se
considera únicamente el más lento de los dos y eltiempo de ejecución
vendrá dado por tanto por O(max( f (n);g(n)) , siendo O( f (n)) eltiempo
para T1(n) y O(g(n)) para T2(n). También existen simplificaciones, por
ejemplo,O(c f (n)) se considera siempre O( f (n)).

 Una asignación u operación aritmética suele considerarse con tiempo


O(1).

 En las proposiciones condicionales, la parte de la condición suele


considerarse tiempoconstante si no necesita realizar llamadas a otras
funciones. El tiempo global de esta instrucción se toma de nuevo como
el mayor de las dos partes que la forman, parte then o parte else.

 En un ciclo se suele determinar por el número de iteraciones


multiplicadas por el orden del conjunto de instrucciones del cuerpo del
bucle. Si tenemos dos bucles de n ciclos, uno dentro de otro, se
considera tiempo cuadrático (suponiendo que el cuerpo del bucle tuviera
un tiempo contante).
Cuando se hacen llamadas a procedimientos no recursivos, se suman todos los
tiempos de ejecuci ón según las pautas anteriores, pero en procesos recursivos
se trabaja de forma distinta. Supongamos la siguiente función:

Para n > 2, tenemos que T(n) = c +T(n –1). El término T(n –1) se sustituye
por n –1:
T(n–1) = c+T(n–2), sustituyendo tenemos que T(n) = c+c+T(n–2). Siguiendo
este
proceso hasta el término i entonces T(n) = ic+T(n–i). Cuando i = n–1, T(n) =
nc+T(1) =
ncfi= O(n).

En el siguiente ejemplo tenemos a un algoritmo recursivo. Para el caso


general, n >1, el proceso divide el problema en dos mitades de
aproximadamente el mismo tamaño. Cuando éste proceso de división termina
necesita un tiempo lineal para unir los resultados de las dos mitades. Este
método es muy habitual en los problemas resueltos con el método Divide y
Vencerás.

Juan Carlos Gutiérrez Barquero____________________________________________ 135


Bismarck Salvador Traña López__________________________________________UNI

1.3. Operaciones básicas en Geometría Computacional

En este apartado entraremos en contacto con métodos para la resolución de


algunos de los problemas más elementales en Geometría. Nos servirá
igualmente para recordar conceptos a nivel computacional. Los algoritmos
aparecerán mayormente escritos en lenguaje Pascal, a no ser que alguno de
ellos aparezca, no por razones prácticas sino didácticas.

1.3.1. Introducción

A nivel de tratamiento de la información, en Geometría Computacional es clara


la preferencia por los tipos de datos enteros, lo que se denomina aritmética
entera. La razón es obvia, el ordenador debe transformar todo dato de la recta
real en un tipo de dato de una computadora que tiene un espacio finito de
almacenamiento. Los datos enteros sí garantizan su transformación íntegra a
un tipo de dato almacenado en memoria, siempre que éste sea menor que una
valor dado. Sin embargo, los datos reales o en coma flotante pierden casi
siempre cierta precisión al ser guardados. Cuando una variable de tipo real
nace en la ejecución de un programa y a su vez es utilizada como entrada para
crear otras nuevas, este proceso repetido puede hacer que el dato se aleje del
valor correcto. Por esta razón, y siempre que sea posible, es preferible hacer
uso de la aritmética entera. Por ejemplo, para saber si dos segmentos
intersecan basta utilizar aritmética entera, sin embargo si queremos saber
exactamente el lugar de intersección, sí era necesario
realizar algún cálculo en coma flotante.

1.3.2. Algunos tipos de datos simples

Los datos geométricos, a la hora de ser procesados por un ordenador, son


reemplazados por lo que denominamos tipos de datos. Muchos conceptos
típicamente matemáticos se denominan de forma análoga cuando se
representan internamente, así usamos el concepto de variable o de constante.
Los tipos de datos identifican el tipo de cada una de estas posibles variables o
constantes. Los tipos de datos que cualquier lenguaje de programación soporta
cubren el espectro más habitual, sin embargo, los de tipo geométrico deben
ser definidos explícitamente por el programador.

En este curso utilizaremos varios de estos tipos de datos. Por ejemplo, el tipo
de dato TipoPunto, TipoPoligono, etc. En las clases de prácticas se definirán
muchos de ellos, ahora identificaremos los anteriores en Pascal para poder
trabajar con ellos a lo largo de este tema:

Juan Carlos Gutiérrez Barquero____________________________________________ 136


Bismarck Salvador Traña López__________________________________________UNI

/************REPRESENTACION DE UN PUNTO*********************/
# define x 0;
# define y 0;
typedef enum{ FALSE, TRUE}bool;
# define DIM 2 /*Dimensión del punto*/
typedef int tpointi(DIM); /*Declaración de un arreglo de puntos*/

/*********REPRESENTACION DE UN PUNTO***********************/

# define PMAX 100


Typedef tpointi tpolygoni[PMAX]

Por simplicidad computacional, se emplea un array con dos posiciones para un


punto, identifi cando la posición 0 para guardar el valor de la x y la posición 1
para el de la y. Cuando se dice que un polígono es un array de puntos,
realmente tenemos una matriz bidimensional con un total de dos columnas,
igualmente fácil de manejar computacionalmente.

Las técnicas de tricoloreado dos tecnicas utilizadas para la representación de


los guardas y otras entidades diferentes de las triangulaciones y vasadas en las
diagonales si desean información de estas acudan al libro geometry
computacional in C.

1.3.3. Cálculo del área

Una de las operaciones más elementales en Geometría es el cálculo del área de


un triángulo, sea del tipo que sea. El método para realizar este cálculo no es
otro que la resolución de un determinante 3x3, aunque no es objetivo de esta
asignatura el cálculo matemático correspondiente.

El algoritmo escrito en pseudocódigo aparece a continuación. Por razones


argumentadas anteriormente, se opta por trabajar siempre con aritmética
entera. La función AreaTriangulo2 evitará la división por dos y calculará el
doble del área del triángulo, por lo hay que tener en cuenta que para obtener
el valor real del área será necesario hacer la división correspondiente.

Ahora vemos el código:

Int Area2(tPointi a, tPointi c, tPointi c)


{
return : = ( ( a [ 0 ] *b[1])–(a [ 1 ] * b [ 0 ]) +
( b [ 0 ] * c [1])–(b [ 1 ] * c [ 0 ]) +
( c [ 0 ] * a[1])–(c [ 1 ] * b [ 0 ]) ) ;
}

El resultado de este cálculo será positivo siempre que los vértices abc sean
recorridos en orden inverso a las agujas del reloj. En caso contrario el valor
será igualmente válido y medirá el área, pero tendrá el signo negativo.

Juan Carlos Gutiérrez Barquero____________________________________________ 137


Bismarck Salvador Traña López__________________________________________UNI

El cálculo del área de un polígono convexo es bien sencillo, basta con calcular
el área de todos los triángulos que se pueden formar lanzando diagonales
desde cualquier vértice, hasta el resto de los vértices del polígono convexo.

Si el polígono no es convexo, el área puede calcularse sumando las áreas de


los triángulos resultantes o incluso restando los de aquellos que no tengan
superficie, como en el siguiente ejemplo:

El área del polígono puede calcularse de dos modos:

 Area (abd) + area (bcd)


 Area(abc) – area(adc)

Este mismo principio puede extenderse a cualquier polígono, indistintamente


del tipo y número de vértices que tuviera. Veamos el siguiente ejemplo: se ha
elegido un punto cualquiera p, interior o exterior al polígono y se han trazado
diagonales a todos sus vértices. Los vértices de éste han sido enumerados del
o al n–1 en orden inverso a las agujas del reloj. Este dato es muy importante,
porque va a ser siempre el mecanismo utilizado para numerar los vértices
cuando trabajemos con polígonos. El número de triángulos obtenidos será de
n–1, siempre partiendo del punto p y siguiendo con dos vértices sucesivos del
modo siguiente:

Juan Carlos Gutiérrez Barquero____________________________________________ 138


Bismarck Salvador Traña López__________________________________________UNI

El triángulo p01 se traza en sentido inverso a las agujas del reloj, lo que
implicará siempre que el área de dicho triángulo es positiva. Se ha señalado
utilizando un símbolo + cuando el cálculo se realiza en el sentido antihorario y
con – cuando el área es negativa. Cuando el recorrido de los vértices del
triángulo siempre en la forma pvivi+1, sea en sentido horario, por ejemplo el
triángulo p67, el área correspondiente se resta. El resultado final, como
podemos observar en la figura, es que todas las áreas de los triángulos no
contenidas en el interior del polígonos son sumadas y restadas una vez con
resultado igual a cero, es decir, el resultado finalmente es el deseado.

Por tanto, podemos hacer uso de la función AreaTriangulo2 tantas veces como
triángulos se vayan formando con el proceso anterior, sumando unas veces las
áreas y otras restándose automáticamente según sea la salida de dicha
función. Como de cada triángulo se calcula el doble de su área, finalmente será
necesario dividir por dos el resultado final.

Como el punto P puede ser cualquier punto, sea interior o no al polígono,


podemos optar por tomar al punto del polígono s[0], o lo que es lo mismo
computacionalmente, a s[0]. El resultado lo podemos escribir a continuación:

Veamos el código:

int AreaPolygoni(tpointi s, int n)


{
float suma;
int i;
suma=0;
for(i=1;i<n-1;i++)
suma=suma+Area2(s[0],s[i],s[i+1]);
return suma;
}

1.3.4. Intersección de segmentos

Una de las operaciones elementales en Geometría Computacional es la


intersección de segmentos, cálculo válido para otros muchos problemas. Sin

Juan Carlos Gutiérrez Barquero____________________________________________ 139


Bismarck Salvador Traña López__________________________________________UNI

embargo, y siguiendo con la dinámica de trabajar siempre que sea posible con
aritmética de enteros, el algoritmo de intersección de segmentos va utilizar
otra serie de subrutinas necesarias.

Si recordamos una de las propiedades de la función del cálculo del área del
triángulo, el resultado es positivo si la lectura ordenada de los vértices nos da
un recorrido antihorario. Esta propiedad puede ser útil para determinar si un
punto c está a la izquierda del segmento ab. Observemos la siguiente figura:

El triángulo imaginario abc se dibuja en orden inverso a la agujas del reloj, lo


que automáticamente nos está indicando que el punto c está a la izquierda de
ab. Es necesario observar que no es lo mismo el segmento ab que el segmento
ba computacionalmente hablando. El área del triángulo bac es negativa porque
el punto c está a la derecha del segmento ba. El subprograma correspondiente
únicamente necesita realizar una llamada a la función AreaTriangulo2
anteriormente vista. Aprovechamos un cálculo en principio de distinta índole,
para determinar la posición de un punto con respecto a un segmento.

Veamos el codigo:

bool Left(tpointi a, tpointi b, tpointi c)


{
Return Area2(a,b,c)fi0; /*Si areamayor que cero retorna TRUE */
}

No es difícil entender que este mismo principio sirve para determinar si el


punto c está justamente a la derecha (la función anterior nos devuelve un
valor f alse). o si el área del triángulo abc es igual a cero, lo que indica que los
tres puntos son colineales:

Juan Carlos Gutiérrez Barquero____________________________________________ 140


Bismarck Salvador Traña López__________________________________________UNI

Veamos el codigo en C:

bool Collineal(tpointi a, tpoint b, tpoint c)


{
return Area2(a,b,c)==0;
}

Debemos insistir en que el código anterior da por supuesto que todos los
cálculos se realizan únicamente utilizando aritmética de enteros. No quiere
decir ésto que no pueda migrarse dicho código a operaciones en coma fiotante,
pero en ese caso jamás debería realizarse una operación como esta i f a = 0
::::, puesto que no siempre el valor que nosotros consideramos como cero (o
cualquier otro valor concreto) lo entienda así un tipo de dato en coma fiotante.
Para solventar este problema es más conveniente establecer un margen de
error permitido, y operar así: i f abs(a) < 0;001 :::, o lo que es lo mismo, si el
valor absoluto de la variable real a es menor que un valor próximo a cero.

Para saber si dos segmentos ab y cd intersectan, deberíamos saber si:

1. no son colineales
2. si d está a la izquierda de ab y c está a la derecha (o al revés) y además
3. si a está a la derecha de cd y b lo está a la izquierda (o al revés)

Nos podemos dar cuenta en la figura de que si no se cumple algunas de estas


premisas no tenemos garantía de que ambos segmentos intersecten.

El código debe tener en cuenta todos los casos citados anteriormente. La


operación válida para implementar el .al revés., es la operación XOR(a;b) cuya
tabla de verdad da el valor verdadero cuando a y b son distintos.

Juan Carlos Gutiérrez Barquero____________________________________________ 141


Bismarck Salvador Traña López__________________________________________UNI

bool Intersect(tpointi a, tpoint b, tpointi c, tpointi d)


{
If (IntersectProp(a,b,c,d))
Return TRUE; /*Hay intersección propia*/
else
If(Between (a,b,c) || Between (a,b,d) || Between (c,d,a) || Between (c,d,b)
Return TRUE; /*Hay interseccion impropia*/
else
return FALSE; /*No hay interseccion*/
}

El inconveniente del método es la redundancia. Ahora realizaremos una nueva


versión del algoritmo utilizando la primitiva Entre, que será capaz de saber si
un punto c está o no en el segmento ab. Para ellos veremos si la coordenada x
de c cae en el intervalo determinado por la coordenada x de a y b. Lo mismo
debe hacerse para la ordenada.

Veamos el codigo en C:

boll Between (tpoint a, tpointi b, tpoint c)


{
If(!Collineal(a,b,c))
Return FALSE;
If(a[x]!=b[x])
return ( (a[x]<=c[x]) && (c[x]<=b[x]) || (a[x]>=c[x] ) &&
(c[x]>=b[x]) );
else
return ( (a[y]<=c[y]) && (c[y]<=b[y]) || (a[y]>=c[y] ) &&
(c[y]>=b[y]) );
}

Esta versión del algoritmo además cuenta con al ventaja de poder trabajar con
las intersecciones de forma propia o impropia. Una intersección impropia
ocurre cuando un extremo de uno de los segmentos está justo en en alguna
posición del otro. La siguiente función en Pascal detecta primero si existe la
posibilidad de colinealidad con la función Entre. Posteriormente hace el mismo
proceso que IntersectaSegmento.

Veamos el código:

bool IntersecImprop(tpoint a, tpoint b, tpointi c, tpointi d)


{
If( Between (a,b,c) || Between (a,b,d) || Between (c,d,a) || Between (c,d,b) )
return TRUE;
else
return(Left(a,b,c) || Left(a,b,d) && Left(c,d,a) || Left(c,d,b) )
}

Juan Carlos Gutiérrez Barquero____________________________________________ 142


Bismarck Salvador Traña López__________________________________________UNI

Ahora veremos la function propia en C:

bool IntersecProp (tpoint a tpoint b, tpointi c, tpointi d)


{
If( Collineal(a,b,c) || Collineal (a,b,d) || Collineal (c,d,a) || Collineal (c,d,b) )
return FALSE;
return (Xor(Left (a,b,c), Left (a,b,d)) && Xor (Left(c,d,a),Left (c,d,b)));
}

bool Xor (bool x, bool y)


{
return !x^!y;
}

1.3.5. Cálculo de diagonales

El cálculo de una diagonal tiene como objetivo la triangulación de polígonos.


Una diagonal es aquel segmento que une dos vértices del polígono de forma
que no intersecte con ningún otro eje de dicho polígono y además sea interno
al polígono. Para saber si existe o no intersección puede utilizarse el proceso
anterior, pero para saber si es o no interno necesitamos realizar un
procesamiento adicional. Este cálculo puede realizarse en
tiempo constante porque si una diagonal es interior a un polígono P en su
vecindario (vértices próximos), también lo es para todo el polígono, no siendo
necesario un procesamiento completo del polígono P.

Diremos que un segmento s es interno a P si y sólo si es interno al cono cuyo


vértice es vi y cuyos lados pasan a través de vi–1 y vi+1. Existen dos casos, el
caso convexo en el que el resto del polígono está en la parte de menor ángulo
y el caso cóncavo en cuyo caso el ángulo es mayor que . La siguiente función
determina si el vector que une los vértices i y j del polígono P es interior o no
al polígono.

Veamos el codigo:

Esta función verifica si un vertice es convexo

boll InCone(int i, int n, tpoligoni P)


{
int i1,inl;
i1=(i+1)%n;
inl=(i+n-1)%n;
if(LeftOn(P[inl],P[i],P[i1])) /**/
return (Left(P[i],P[j],P[inl]) && Left (P[j],P[i],P[i1]));
else
return (!(LeftOn[P[i],P[i1]P[i1]) && LeftOn(P[j],P[i+,P[inl]));
}

Juan Carlos Gutiérrez Barquero____________________________________________ 143


Bismarck Salvador Traña López__________________________________________UNI

bool LeftOn (tpointi a, tpoint b,tpoint c)


{
return Area2(a,b,c)fi0;
}

Vemos en la figura siguiente los dos casos comentados anteriormente. El caso


convexo ocurre cuando el vértice vi+1 está a la izquierda del eje trazado por
vi–1vi. En este caso, lo vemos en la figura de la izquierda, para que se trate de
un eje interno debe ocurrir que vi–1 esté a la izquierda de viv j y que vi+1 lo
esté a la izquierda. El caso cóncavo aparece a la derecha.

Por tanto, una diagonal es aquel segmento interior que está dentro del
polígono. Pero el segmento i j es una diagonal si no intersecta con ninguna
otro de las aristas del polígono. Este cálculo necesita forzosamente un tiempo
de ejecución lineal. La función DiagonalfiIE se encargará de realizar este
chequeo, y que será invocado por la función Diagonal cuyo código resulta
bastante evidente: el segmento i j es una diagonal del polígono P de tamaño n
si es interior y además no intersecta con ninguna arista.

Veamos el código:

Es/*ta function verifica la possible diagonal entre dos vertices.*/

bool Diagonal (int i, int j,int n,tpolygoni P)


{
return(InCode(i,j,n,P) && Diagonaie(i,j,n,P)());
}

/*Esta function verifica si una diagonal se intersecta con una arista del
poligono */

bool Diagonalie (int i, int j, int n, tpoligono p)


{
Int k,k1;
for(k=0; k<n; k++)
{
K1=(k+1)%n;
if( !( ( k==i) || ( K1==i ) || ( k==j) || ( k1==j ) ) )

Juan Carlos Gutiérrez Barquero____________________________________________ 144


Bismarck Salvador Traña López__________________________________________UNI

if ( Intersect (p[i], p[j], p[k], p[k1]))


return FALSE;
}
return TRUE;
}

/*Esta function remueve el triangulo formado por la diagonal y la arista


asociada ala diagonal, para lo cual reasigna las etiquetas del polígono.*/

void ClipEar1 (int i, int n, tpoligoni p, int labels [ ])


{
Int k;
for(k=I; k<n-1 ; k++)
{
point assign (p[k+1]);
labels[k] = labels [k+1];
}
}

/*Esta funccion obtiene los vértices de la possible diagonal */

void TriRecurse(int n, tpoligoni P, int labels [])


{
int i,i1,i2;
if(n>3)
for(i=0; i<n;i++)
{
i1=(i+1)%n;
i2=(i+2)%n;
if (Diagonal(i,i2,n,p))
{
printf("%d %d lºn",labels[i2]);
ClipEar1(i1,n,p,labels);
TriRecurse(n-1,P,labels);
break;
} /*Cierre del if*/
} /*Cierre del for*/
} /**Cierre de la funcion/

1.3.6. Un ejemplo completo: triangulación de un polígono

La triangulación de polígonos es un cálculo muy importante en muchas


aplicaciones de Geometr ía Computacional. Permite, aparte de otras muchas
aplicaciones, dividir un polígono con posible cierta complejidad, en un conjunto
de triángulos fáciles de procesar.

Veremos que el siguiente método puede ser perfectamente válido para lograr
la triangulación de cualquier polígono P de tamaño n. Sin embargo, y a pesar

Juan Carlos Gutiérrez Barquero____________________________________________ 145


Bismarck Salvador Traña López__________________________________________UNI

de su simpleza, su utilización no es aconsejable y por ello no se ha


implementado en un leguaje de programación. Vemos que es un método
recursivo porque se llama a sí mismo. La llamada recursiva se realiza cuando
se localiza una diagonal, y como en ese momento el número de vértices a
procesar es uno menos, puesto que el vértice i1 queda aislado al encontrarse
la diagonal ii2. La función EliminaVertice se encargaría de este operación,
dejando cada vez que se llama un polígono más pequeño.

Esta función triangula el poligono P de n vertices.

void Trinagulate1 (int n, tpoligoni)


{
tpoligoni.pt;
int labels[PMax];
clrscr();
for(i=0;i<n;i++)
{
PointAssign(pt[i]<P[i]);
labels[i];
}
printf("Diagonales de la triangulacion;\n");
TriRecurse(n,pt,Labels);
}

/*Esta función asigna al punto "a" el valor del punto "b".*/

void PointAssign(tpoint a, tpoint b)


{
int i;
for(i=0;i<DIM;i++)
a[i]=b[i];
}
La razón por la que este algoritmo no es desde luego nada interesente es por
su tiempo de ejecución. El algoritmo trabaja en O(n3) porque realiza un total
n–3 llamadas recursivas y cada una de ellas, en el peor de los casos, necesita
un tiempo cuadrático (la función diagonal es lineal, así como la ejecución
completa del bucle).

Como veremos a lo largo de este curso, para valores de n elevados, este


algoritmo resulta
Evidentemente lento, y en Geometría Computacional todos los problemas
cuyas soluciones inmediatas sean susceptibles de mejora, son estudiados.
Como veremos en sucesivos capítulos, cualquier polígono puede ser
triangulado en tiempo O(nlog n).

Los algoritmos geométricos, al igual que los desarrollados para otras


disciplinas, deberán combinar adecuadamente junto con su conjunto de

Juan Carlos Gutiérrez Barquero____________________________________________ 146


Bismarck Salvador Traña López__________________________________________UNI

sentencias, a las estructuras de datos correctas para conseguir el objetivo


final, obtener programas eficientes.

1.4.1. Introducción a las estructuras de datos

El estudio de las estructuras de datos y los métodos para la organización de


dichos datos es uno de los aspectos clásicos más importantes para construir
algoritmos eficientes. Los programas trabajan con información que permanece
en memoria principal o en algún dispositivo de memoria secundaria. La
organización que se le da a dichos datos es lo que denominamos estructuras
de datos.

La mayoría de las estructuras de datos pueden verse como contenedores que


mantienen información de un determinado tipo con un cierto orden. El acceso y
manejo de los datos en un contenedor se realiza a través de un localizador.
Dependiendo de la estructura del contenedor, el localizador proporcionará un
modo de acceso a los datos, por ejemplo, un índice de un array o un puntero.
Una estructura de datos además de proporcionar un modo de acceso, también
deber llevar asociado un conjunto de operadores, como insertar un nuevo
dato, actualizar uno ya existente o eliminar algún otro. Las características de
una estructura de datos va en relación al orden de ejecución de sus
operaciones y a los requerimientos de espacio. En cualquier caso, la estructura
de datos más idónea dependerá siempre del problema que se pretenda
resolver.

Un tipo de dato es una colección de objetos a la que se le da una


representación matemática. Un tipo de dato abstracto o TDA es un tipo de
dato, más un conjunto de operaciones que lo manejan. Las estructuras de
datos pueden considerarse como implementaciones concretas de tipos de
datos abstractos. Los TDAs (notación anglosajona) han evolucionado hasta
convertirse en clases de objetos. Lo que inicialmente se concibió como un
concepto matemático que se adaptó a la sintaxis de los lenguajes de
programación tradicionales, se ha ido convirtiendo en una nueva filosofía de
programación con los nuevos lenguajes orientados a objetos.

Existen multitudes de estructuras de datos ampliamente documentadas en la


bibliografía. En este apartado repasaremos algunas de las estructuras de datos
usadas en el curso.

Dependiendo de la organización interna y del modo de acceso a los datos,


existen dos tipos fundamentales de contenedores, los contenedores
secuenciales o secuencias y los asociativos. Una secuencia es un contenedor
que almacena los elementos con un orden lineal determinado, realizando el
acceso a la información indicando la posición donde se localiza el dato. Por el
contrario un contenedor asociativo, independientemente de su organización
interna, permite el acceso por clave.

Contenedores secuenciales

Juan Carlos Gutiérrez Barquero____________________________________________ 147


Bismarck Salvador Traña López__________________________________________UNI

Las secuencias más usuales son las listas enlazadas y los arrays. Las primeras
forman una sucesión de nodos o elementos informativos unidos unos a otros
mediante punteros. El tiempo de acceso es lineal, aunque normalmente el
acceso a los puntos extremos es constante. Los arrays son contenedores
implícitos con acceso directo. Esta ventaja de acceso en tiempo constante se
ve mermada por poseer menor fiexibilidad. Por ejemplo, la operación de
inserción de un elemento en posiciones intermedias es de orden lineal. Sobre
cualquiera de los contenedores secuenciales anteriormente citados puede
construirse estructuras de datos muy conocidas y empleadas, las pilas (LIFO) o
las colas (fiFO). Cualquiera de ellas implementada sobre una lista puede
considerarse como una estructura de datos dinámica. Esta consideración
también puede realizarse sobre arrays dinámicos.

Contenedores asociativos

Los contenedores asociativos poseen una filosofía de manejo y una estructura


interna totalmente diferente a las secuencias. La diferencia fundamental es que
el acceso a la información se realiza siempre utilizando una clave y no una
posición, que puede identificar o no de forma unívoca al dato. Las operaciones
fundamentales son de inserción, actualización, borrado y búsqueda por dicha
clave.

La organización interna de los datos depende del tipo de contenedor asocitivo,


por árbol o por tabla hash. Los primeros mantienen un árbol binario de
búsqueda o Inorden, manteniendo una relación de orden entre nodos padre e
hijos. Normalmente, todo nodo padre es mayor que su hijo izquierdo pero
menor o igual que su hijo derecho. El tiempo de acceso en este tipo de
estructuras permite eliminar en cada consulta la mitad del grueso de la
información, siempre que todas las ramas del árbol permanezcan a la misma
altura. Estas estructuras de datos garantizan búsquedas en O(logn).

Las estructuras de datos consideradas de carácter general son ampliamente


utilizadas en Geometría Computacional, pero existen otros aspectos como son
la naturaleza espacial de los objetos geométricos o sus propiedades
estructurales que requieren del manejo de estructuras de datos más
específicas para construir algoritmos eficientes.
La elección de un tipo de estructura de datos o de otro va a depender del tipo
de objetivo que se persiga: captura de información estructural, acceso a los
datos o actualización eficiente de éstos, optimización del espacio requerido o
del número de operaciones de E/S. En otras ocasiones, las estructuras de
datos se eligen según la asociación de objetos geométricos que representen.

1.4. Algunos métodos de programación utilizados en C.

La Geometría Computacional, en contraste con la disciplina clásica basada en


proporcionar teoremas matemáticos, enfatiza los aspectos computacionales de

Juan Carlos Gutiérrez Barquero____________________________________________ 148


Bismarck Salvador Traña López__________________________________________UNI

los problemas y atiende a las propiedades geométricas para encontrar


algoritmos eficientes.

Que un problema geométrico tenga solución dentro de la Geometría


Computacional no basta en muchas ocasiones, se desea además que la
solución se encuentre en un tiempo óptimo. Existen una serie de técnicas y
paradigmas de programación que vienen siendo muy habituales no sólo en el
campo de la Geometría Computacional. Algunos de estos métodos como el
divide y vencerás ya habían proporcionado la solución óptima a problemas
como la ordenación, en otras ocasiones los métodos de resolución están
íntimamente relacionados con las capacidades de la propia Geometría.
A continuación daremos un repaso a los paradigmas de programación más
empleados dentro de la Geometría Computacional.

1.5.1. Método incremental

Éste puede ser considerado como uno de los métodos más simples e intuitivos,
también conocido como el método iterativo. La idea sobre la que trabaja este
método consiste en construir la solución de modo iterativo, considerando que
la entrada se produce de modo incremental. Para cada una de las nuevas
entradas, el problema se resuelve convenientemente.

Supongamos que se pretende procesar una nube de n puntos en el plano S,


para resolver cualquier problema Prob(S) bajo esta técnica. En muchas
ocasiones S deberá estar ordenada bajo algún criterio con coste O(nlogn). El
algoritmo trabaja siempre de forma similar. Inicialmente Prob se resuelve de
forma trivial para fs0; s1; :::; sk–1g, los primeros k puntos de S.
Posteriormente y uno a uno, se resolverá Prob(Si) para el resto de si 2 S; i =
fk; :::;n–1g tal y como indica el Algoritmo 1, obtiendo finalmente Prob(S).

Si en un problema de tamaño n, el coste de insertar un nuevo dato es O(n),


entonces el tiempo global del algoritmo será de O(n2). Pero si añadir un nuevo
elemento a la solución parcial del problema tiene coste constante o
logarítmico, el algoritmo final se procesará en O(nlogn), la misma cota
impuesta por el algoritmo de ordenación.

Algoritmo 1 Incremental-generalizado.

Incremental(RC,p)
1. Si p está dentro de RC
2. entonces devolver RC
3. sino
4. (Pt1,Pt2) := puntosTangentes(RC,p)
5. Eliminar aristas en RC de Pt1 a Pt2
6. Conectar Pt1 con p y p con Pt2

Juan Carlos Gutiérrez Barquero____________________________________________ 149


Bismarck Salvador Traña López__________________________________________UNI

Ahora veremos los puntos tangentes:

1.5.1.1. Línea de barrido

Esta estrategia es una particularización del método incremental,


considerándose válida para problemas en dos dimensiones cuando el conjunto
de entrada se produce en un cierto orden. La solución de la mayoría de estos
problemas puede ser incremental, de hecho, la implementación de este
paradigma sigue las mismas premisas que las dadas para el método
incremental citado anteriormente.

El método supone que una línea horizontal (o vertical) recorre el plano de


arriba hacia abajo (o en sentido contrario). Cada vez que dicha línea encuentra
un ítem (un punto, una línea, etc) lo procesa modificando el conjunto de
estructuras que datos que participen en la construcción de dicho algoritmo. La
validez de esta técnica está basada en la observación de que la parte que
queda por encima o por debajo de la línea de barrido se ha construido
correctamente.

Por ejemplo en el problema de intersección de segmentos, cuando la línea


encuentra un nuevo segmento, se realizará una inserción en alguna estructura
de datos capaz de mantenerlos ordenados de izquierda a derecha (en el caso
de que la línea de barrido sea horizontal). Normalmente se escogen
estructuras de datos con tiempos de actualización y de acceso logarítmico. Si
el procesamiento de dichos objetos geométricos es constante, como es el caso
de la intersección de dos segmentos, pueden trabarse con tiempos del orden
de O(nlog n).

Juan Carlos Gutiérrez Barquero____________________________________________ 150


Bismarck Salvador Traña López__________________________________________UNI

Esta técnica es empleada para diferentes problemas como la intersección de


segmentos, partición de polígonos en subdivisiones monótonas, intersección de
semiplanos, el diagrama de Voronoi y triangulación de Delaunay, los métodos
de Jarvis y Graham para el cálculo de la envolvente convexa, la técnica de
localización de los puntos más lejanos, y otros más. Existen técnicas análogas
para dimensiones mayores.

Algoritmo 1

El algoritmo de la línea de barrido rotatoria (Rotational Sweep Line):

Entrada: Un conjunto de obstáculos poligonales S y un conjunto de puntos Q.


Salida: Un grafo de visibilidad G = (V, E).

Sea W la unión de los vértices de S y los puntos de Q, sea E el conjunto de


aristas, y sea T un árbol de búsqueda equilibrado.

1.- Para cada vértice w de V:


2.- Ordenar todos los vértices de W en sentido horario según la semi-línea P,
y, en caso de empate, ordenar según distancia al punto p.
3.- Ordenar las aristas que intersectan con P e insertarlas en el árbol
equilibrado T.
4.- Para cada vértice w de la lista ordenada W:
5.- Si w es visible desde p, entonces añadir la arista e=(p, w) al grafo G.
6.- Insertar en el árbol los obstáculos que incidan en w y que estén en el
lado horario de P.
7.- Eliminar del árbol los obstáculos que incidan en w y que estén en el lado
antihorario de P.

1.5.2. Divide y Vencerás

Juan Carlos Gutiérrez Barquero____________________________________________ 151


Bismarck Salvador Traña López__________________________________________UNI

Esta técnica clásica de programación es válida para resolver de forma eficiente


problemas Geom. étricos. Normalmente se basa en la sucesiva división en
partes iguales del problema original, hasta conseguir que éste posea un
tamaño lo suficientemente pequeño como para poder resolverse fácilmente.
Llegado a un punto en que la división deja de tener sentido, los problemas se
van resolviendo recursivamente y combinando sus soluciones para obtener la
solución final. El método más eficiente de ordenación, el quicksort, está basado
en este paradigma.

El Algoritmo 2 resume los pasos empleados para resolver el problema Prob(S)


partiendo del conjunto de datos S y realizando sucesivas divisiones por dos.
Para que el algoritmo se considere eficiente se espera que el Paso 3.a) que
divide el problema en subproblemas y el Paso 3.c) que combina las soluciones
parciales, se realice en tiempo lineal. El segundo ejemplo de ecuación de
recurrencia de la Sección 1.2 corresponde al método Divide y Vencerás.

Algoritmo 2

Paradigma de divide y vencerás

Juan Carlos Gutiérrez Barquero____________________________________________ 152


Bismarck Salvador Traña López__________________________________________UNI

Juan Carlos Gutiérrez Barquero____________________________________________ 153


Bismarck Salvador Traña López__________________________________________UNI

Es un ejemplote Voronoi.

Esta técnica es ampliamente


utilizada y sirve de base para
construir algoritmos aleatorios.
Puede emplearse para construir la
envolvente convexa, el diagrama de
Voronoi, etc.

2 La envolvente convexa y triangulación de polígonos

2.1. La envolvente convexa

2.1.1. Introducción

Juan Carlos Gutiérrez Barquero____________________________________________ 154


Bismarck Salvador Traña López__________________________________________UNI

El cálculo de la envolvente convexa de un conjunto de puntos en espacios


euclídeos ha sido sin duda uno de los problemas a la vez más básicos y más
ampliamente estudiados a lo largo de la historia de la Geometría
Computacional. Además, los campos en los que ha resultado un cálculo útil
sorprenden por lo aparentemente poco relacionados con las matemáticas en
general y la geometría en particular.

Un conjunto convexo del plano se define como el conjunto que verifica que el
segmento que une a dos cualesquiera de sus puntos está totalmente contenido
en él. Evidentemente, esta definición puede ser aplicada en cualquier espacio
euclídeo n-dimensional. La idea de conjunto convexo nos lleva inmediatamente
a la de envolvente convexa, considerando a ésta como el menor conjunto
convexo que contiene a dicho conjunto. Este concepto es fácil de entender en
cierto modo si consideramos a la envolvente convexa de un conjunto de puntos
en el plano, como la forma que adquiriría una goma elástica envolviendo a
todos los puntos del conjunto que están fijos sobre el plano. De nuevo esta
idea es extensible intuitivamente a otras dimensiones, y puede servir de
partida para construir algoritmos simples para el cálculo de la envolvente
convexa.

Pero exiten otras definiciones válidas: la envolvente convexa de un conjunto


de puntos en el plano es la unión de todos los triángulos determinados por
dicho conjunto de puntos.

El estudio de la envolvente convexa en dos dimensiones ha sido objeto de


especial interés. Existen una serie de características asociadas a los puntos que
forman parte de la envolvente convexa en el plano y que han servido
igualmente para definirla y construirla. Un punto q 2 S pertenece a la
envolvente convexa de S, q 2CH(S), si es posible trazar una recta pasando por
dicho punto de forma que todo el conjunto S0, S0 = S–fqg quede a un lado de
dicha recta.

Como se ha indicado con anterioridad, el cálculo de la envolvente convexa ha


sido aplicado ampliamente a disciplinas en ocasiones ajenas a la Geometría
Computacional y en espacios distintos de dos dimensiones. Tal es el caso del
reconocimiento de patrones, procesamiento de imágenes, etc.

Juan Carlos Gutiérrez Barquero____________________________________________ 155


Bismarck Salvador Traña López__________________________________________UNI

Los algoritmos propuestos para este problema son realmente numerosos. Sin
duda es uno de los planteamientos de la Geometría Computacional que más
soluciones ha conseguido. Desde incluso antes del nacimiento oficial de esta
disciplina se conoce el trabajo de Graham en el año 1972. Se han propuesto
estrategias siguiendo los paradigmas más empleados como el método
incremental y el divide y vencerás. Todos estos algoritmos construyen la
envolvente convexa en tiempo (nlog n). El método propuesto por Preparata y
Shamos en 1985 fue denominado Quickhull por su similitud con el método de
ordenación Quicksort. Otra propuesta diferente se dió a conocer en 1973 como
.La marcha de Jarvis.. Este método, que estudiaremos en este curso, posee un
tiempo de ejecución cuadrático, sin embargo supone un interesante ejemplo
basado en manejo de ángulos del cual partiremos para el tratamiento del
diagrama polar. Todos estos trabajos constituyen el conjunto clásico de
algoritmos para la resolución de la envolvente convexa, al que habría que
añadir otras técnicas posteriores.

La envolvente convexa posee una cota de (nlog n), es decir, no se puede


encontrar una algoritmo que trabaje en menor tiempo para todos los casos.

2.1.2. Un ejemplo de resolución

El problema de encontrar la envolvente convexa de un conjunto de puntos


puede ser interesante porque es equivalente a encontrar todo el conjunto de
puntos extremos de dicha nube. Por tanto, puede definirse como punto
extremo a cualquier punto de la envolvente convexa, y todo punto extremo
tiene la propiedad de que es posible trazar una línea recta a través de él de
modo que el resto de puntos queden a un lado.

También podemos decir que un punto es extremo si y sólo si está dentro de


algún posible triángulo cuyos vértices son cualesquiera de los puntos de la
nube original. Pero bajo esta definición, necesitaríamos procesar O(n3)
triángulos para cada uno de los n puntos, lo que nos proporciona un método en
triempo O(n4), lo cual no es eficiente teniendo en cuenta que se trata de un
problema (nlog n).

Pero la envolvente convexa posee aplicaciones fuera del ámbito de la


Geometría. Supongamos que una explotación petrolífera extrae diferentes tipos
de componentes mezclados en distinta propoción. Si el objetivo es conseguir
los componentes A y B pero en una nueva proporción, podemos hacer uso de
la envolvente convexa del modo siguiente. Supongamos que partimos de las
mezclas:

e1 = A10%B35%, e2 = A16%B20% y e3 = A7%B15% y queremos obtener e


f = A13%B22%. Para saber si es posible obtener este producto final e f basta
con considerar las anteriores mezclas como puntos del plano, obteniendo el
siguiente esquema:

Juan Carlos Gutiérrez Barquero____________________________________________ 156


Bismarck Salvador Traña López__________________________________________UNI

Todo punto que caiga dentro de la envolvente convexa de todas las muestras
representadas por puntos en el plano, indica que puede ser producida. Si por
contra, cayera fuera de dicha envolvente, la combinación final no podría
producirse. Este problema sería un problema 3D si cada mezcla tuviera tres
componentes, la solución sería la contrucción de la envolvente convexa
tridimensional.

2.1.3 Algoritmo trivial 1.


 Determinación de puntos extremos

1. Para cada i hacer


2. Para cada j  i hacer
3. Para cada k  i  j hacer
4. Para cada h  i  k  j hacer
5. Si Ph está a la izqda de (Pi,Pj) y
6. Ph está a la izqda de (Pj,Pk) y
7. Ph está a la izqda de (Pk,Pi)
8. entonces Ph no es extremo

 Complejidad: O(n4 )

2.1.4 Algoritmo trivial 2.

 Determinación de aristas extremas



1. Para cada i hacer
2. Para cada j  i hacer
3. Para cada k  i  j hacer
4. Si Pk no está a la izqda de (Pi,Pj)
5. entonces (Pi,Pj) no es extremo

 Complejidad: O(n3 )

2.1.5 Gift Wrapping

1. Encontrar punto p más pequeño en coord. y, sea i[0] su índice


2. i := i[0]
3. Repetir
4. Para cada j  i hacer

Juan Carlos Gutiérrez Barquero____________________________________________ 157


Bismarck Salvador Traña López__________________________________________UNI

5. Calcular el ángulo en sentido antihorario entre


Pj y la arista anterior de la EC
6. Sea k el índice del punto con ángulo menor
7. Marcar (Pi,Pk) como una arista de la EC
8. i := k
9. Hasta que i = i0

2.1.6 Quick Hull

QuickHull(a,b,S)
1. Si S={a,b} entonces devolver (a,b)
2. sino
3. c := índice del punto con máxima distancia a (a,b)
4. A := puntos a la izquierda de (a,c)
5. B := puntos a la izquierda de (c,b)
6. devolver
concatenar(QuickHull(a,c,A),QuickHull(c,b,B)

ConvexHull(S)
1. a := punto más a la derecha de S;
2. b := punto más a la izquierda de S
3. devolver concatenar(QuickHull(a,b,izquierda(a,b,S)),
QuickHull(b,a,izquierda(b,a,S)))

La idea básica del quickhull (envolvente rápida) es que para la mayoría de las
configuraciones de puntos se puede en cada paso descartar muchos puntos a
la hora de construir la envolvente convexa.

El primer paso del quickhull es encontrar los cuatro puntos extremos en las
direcciones norte, sur, este y oeste y formar el cuadrilátero que ellos definen

Juan Carlos Gutiérrez Barquero____________________________________________ 158


Bismarck Salvador Traña López__________________________________________UNI

(este cuadrilátero puede ser degenerado), ahora todos los puntos en el interior
de dicho cuadrilátero no son extremos, con lo cual pueden ser descartados.

Así nos quedan puntos repartidos en cuatro regiones no conectadas entre si,
trataremos cada una de estas regiones independientemente. En cada una de
ellas encontramos el punto más alejado a la recta que define dicha región
obteniendo así cuatro nuevos puntos y un polígono de a lo más ocho lados que
divididirá a los puntos que no hemos eliminado en ocho regiones que tratamos
individualmente siguiendo la misma regla anterior.

En nuestro ejemplo en el paso siguiente se obtine ya la envolvente convexa.

Juan Carlos Gutiérrez Barquero____________________________________________ 159


Bismarck Salvador Traña López__________________________________________UNI

El QuickHull suele calcular la envolvente convexa en un tiempo mucho más


corto que si lo hacemos a través del diagrma de Voronoi. Sin embargo, un
análisis del algoritmo nos lleva a que su complejidad en tiempo es cuadrática.
Por lo tanto, centrémosno ahora en encontrar un algoritmo óptimo.

Ejemplo:

2.1.4. Graham's scan

Este es sin duda uno de los algoritmos más importantes para el cálculo de la
envolvente convexa, no únicamente por su simpleza sino también por su
rapidez.

El proceso realiza primero una ordenación angular de los puntos. El punto


tomado de referencia para tal fin debe ser un punto de la envolvente convexa,
por ejemplo el de menor ordenada y al que llamaremos p0. El resto de puntos
quedan ordenados de izquierda a derecha obteniendo la secuencia p1, p2,…,
pn–1.

El proceso seguido por Graham toma sucesivamente triángulos elegidos según


el orden conseguido tras la ordenación. Siempre que el triángulo formado abc,
posea una superficie positiva, el vértice central, es decir, el vértice b será
tenido en cuenta como probable punto de la envolvente. Pero cuando dicho
área es negativa, b es descartado definitivamente.

Para comprender dicho algoritmo, será necesario repasar el concepto de pila.


Una pila es un tipo de dato abstacto (tipo de dato más operaciones para su

Juan Carlos Gutiérrez Barquero____________________________________________ 160


Bismarck Salvador Traña López__________________________________________UNI

manejo) muy restrictivo a la hora de manejar los datos que almacena. Se


construye utilizando una estructura de datos lineal, array o lista enlazada,
sobre la que sólo se pueden introducir (push) o sacar elementos (pop) por uno
de sus extremos, lo que se denomina cima o tope de la pila.

Algoritmo Scan de Graham

1. Encontrar el punto inferior más a la derecha (P0)


2. Ordenar todos los puntos angularmente alrededor de P0;
aquellos puntos que estén en el mismo ángulo colocar primero el
más cercano a P0: etiquetarlos P1,...,Pn-1
3. Pila S := (Pn-1,P0)
4. t := tope de S (P0)
5. i := 1
6. Mientras que i<n hacer
7. Si Pi está estrictamente a la izqda de (Pt-1,Pt)
8. entonces Push(S,i); i := i+1
9. sino Pop(S)

Ejemplo:

El orden de ejecución de este algoritmo viene dado por la necesidad de


ordenar los puntos angularmente. Cualquier ordenación necesita al menos un
tiemo O(nlogn). A este tiempo se le debe añadir el del proceso ideado por
Graham. Para averiguar este tiempo basta con percatarse que en cada paso un
punto es tenido en cuenta o es descartado. En el peor de los casos, cuando la
envolvente convexa es un triángulo, necesitamos del orden de 2n–3
operaciones.

El Scan de Graham construye una lista que al final será la de los puntos
extremos ordenados. Supondremos que no existen tres puntos alineados
(aunque esta condición puede ser eliminada). Comenzamos encontrando el
primer punto con el orden lexicográfico, llamemosle p0. A continuación
ordenamos los puntos por el ángulo que forman con p0.

Juan Carlos Gutiérrez Barquero____________________________________________ 161


Bismarck Salvador Traña López__________________________________________UNI

Suponemos que partimos de la lista ordenada así obtenida. En cada momento,


tendremos tres puntos, llamémosles Inicial, Medio y Final. Nos preguntamos si
el ángulo IMF es positivo (en el sentido contraro de las agujas del reloj o
negativo. Si es positivo, la lista la dejamos como estaba y el anterior Medio pas
a ser Inicial y el final pas a ser Medio y escogemos el siguiente en la lista para
ser el nuevo Final. Si es negativo, eliminamos Medio de la lista, el anterior
Inicial pasa a ser Medio, el Final sigue como Final y escogemos el anterior de la
lista para ser el nuevo Incial. La lista resultante después de recorrer todos los
puntos será la de los extremos de la envolvente convexa.

Teorema 5.1: El Scan de Graham calcula la envolvente convexa de n puntos


en el plano en tiempo óptimo O(n log n).

2.1.4. La marcha de Jarvis

Juan Carlos Gutiérrez Barquero____________________________________________ 162


Bismarck Salvador Traña López__________________________________________UNI

La marcha de Jarvis está basada en el siguiente resultado, que sirve de


definición para la envolvente convexa:

El segmento l, definido por dos puntos de la nube de puntos, es un eje de la


envolvente convexa si todos los puntos de la envolvente convexa están a un
lado de ésta.

Siguiendo este criterio podríamos descubrir para cada punto los n2


combinaciones posibles, lo que nos da un fácil pero costoso O(n3) algoritmo.

Sin embargo, Jarvis pudo bajar la cota al comprobar que si pi pj es una arista
perteneciente a la envolvente convexa, la siguiente arista la encontramos
partiendo de p j en tiempo lineal. Veamos el siguiente ejemplo:

Partiendo de s6, el punto de menor ordenada, alcanzamos a s5 tras realizar un


barrido angular en sentido antihorario desde la horizontal. El siguiente punto
de la envolvente se puede localizar partiendo de s5 y haciendo un nuevo
barrido angular, localizando así a s1.

Es fácil de intuir que este proceso tardará en ejecutarse un tiempo


proporcional al número de puntos en la envolvente y al tamaño de la nube.
Cada barrido angular necesita un tiempo de proceso del orden de O(n) pasos.
Si la envolvente final posee k puntos, el tiempo final de ejecución es del orden
de O(nk). En el mejor de los casos el comportamiento puede ser lineal, por
ejemplo si la envolvente es un triángulo. El peor de los casos, sin embargo, se
da cuando todos los puntos están en la envolvente convexa, convirtiéndose en
un método cuadrático.

2.1.5. Algoritmo Incremental

La ventaja de unos métodos con respecto a otros a veces viene dada por su
velocidad computacional y otras veces por su fácil extensión a tres
dimensiones, lo que habitualmente llamamos problemas 3D. El método
incremental para el cálculo de la envolvente convexa es uno de estos casos.

En todo método incremental se supone que se ha resuelto el problema para un


tamaño n y que en cada paso se añade un nuevo punto dando solución a un
problema de tamaño n+1.

Para el cálculo de la envolvente convexa ordenamos inicialmente los puntos del


conjunto P de izquierda a derecha, por ejemplo, obteniendo el conjunto p0;
p1; :::; pn–1. Tomamos los tres primeros puntos tras esta ordenación
sabiendo que el polígono que forman p0; p1; p2, un triángulo, es convexo. El
siguiente paso intenta añadir el siguiente punto, p3, de modo que se tras
finalizar este paso tengamos la envolvente convexa de los primerso cuatro
puntos p0; p1; p2; p3.

Juan Carlos Gutiérrez Barquero____________________________________________ 163


Bismarck Salvador Traña López__________________________________________UNI

Para añadir cada nuevo punto pi+1 basta con lanzar tangentes desde este
punto hasta el polígono convexo obtenido en el paso anterior. Localizaremos
dos tangentes, una superior y otra inferior, como vemos en la siguiente figura:

 Siempre ocurre que el punto más bajo de tangencia es tal que pi, es tal
que q está a laizquierda de pi–1 pi pero a la derecha de pi pi+1.
 El punto pj será el punto de mayor tangencia si q está a la derecha de p
j–1 pj pero a laizquierda de pj pj+1.

El procedimiento PuntosTangentes calcula los puntos resultantes de lanzar


tangentes desde el punto q al polígono P de tamaño n obteniendo como
resultado los vértices pt1 y pt2. El algoritmo determina que el vértice p[i] es
un punto tangente si recorriendo cada arista del polígono P dicho vértice
implica un cambio de resultado tras ejecutar la función IzquierdaOSobre.

Pero obtener los dos puntos tangentes no implica de ningún modo que el
proceso ha finalizado. El punto q será parte ahora del nuevo polígono convexo,
así como lo serán los puntos pt1 y pt2. Sin embargo, es posible que algunos
otros vértices del polígono convexo desaparezcan. En el ejemplo de la figura,
la secuencia de vértices i+1; :::; j–1 claramente desaparece al añadir el nuevo
punto. El resultado final sería el polígono
P = p0; p1; :::; pi_1; pi;q; pj ; pj+1; :::; pn_1.

El orden de ejecución del algoritmo es la suma de los siguientes términos:

1. la operación de ordenación con coste O(nlogn)

2. Operación que en el peor de los casos es lineal.

Juan Carlos Gutiérrez Barquero____________________________________________ 164


Bismarck Salvador Traña López__________________________________________UNI

Por tanto, y aunque el algoritmo es susceptible de añadir mejoras, necesita en


el peor de los casos un tiempo O(n2).

2.1.6. Método Divide y Vencerás

Los métodos divide y vencerás siempre trabajan de modo similar. En este


caso, partimos de una nube de puntos ordenada de izquierda a derecha que se
divide en dos mitades A y B de aproximadamente el mismo tamaño. Para cada
una de estas mitades, continuamos el proceso de división hasta que finalmente
el tamaño de la nube de puntos es tan pequeño que puede resolverse el
problema fácilmente. Este caso concreto es cuando tenemos tres puntos.
Cuando el problema no pueda dividirse más, el siguiente paso es unir dos a
dos los polígonos convexos resultantes de la etapa anterior, en un nuevo
polígono convexo. El proceso finaliza obteniendo la envolvente convexa de
todos los puntos.

Sin embargo, la etapa de unión de dos polígonos convexos en un único


polígono convexo no es inmediata. Necesitamos calcular dos tangentes que
unan dichas envolventes, una tangente superior y otra inferior.

El mecanismo para conseguir, por ejemplo, la tangente inferior, podemos


observarlo en la siguiente figura. Se comenzaría suponiendo que el segmento
que une el punto más a la derecha de A y el punto más a la izquierda de B es
una tangente válida. Como esta suposición es falsa para este ejemplo,
decidimos bajar vértices sucesivamente hasta que por fin para el polígono A se
localiza una envolvente. En el ejemplo, A2-B5 ya es una tangente para A, sin
embargo no lo es para B. El siguiente paso es hacer exactamente lo mismo
pero bajando sucesivos vértices de B. El proceso termina cuando para los dos
polígonos se encuentra una tangente.

Para saber si ab es tangente a un polígono observemos los siguientes gráficos:


queda como ejercicio la implementación de las funciones TangenteMenor y
TangenteMayor.

Juan Carlos Gutiérrez Barquero____________________________________________ 165


Bismarck Salvador Traña López__________________________________________UNI

El tiempo de ejecución de un algoritmo divide y vencerás depende en gran


medida del comportamiento de la función de mezcla de cada pareja de
diagramas polares. Cuando ésto se realiza en tiempo lineal, como es este caso,
sabemos que contamos con un algoritmo en tiempo O(nlog n).

2.2. Triangulación de polígonos

En el Tema 1 hicimos referencia por primera vez a la triangulación de


polígonos. En esa ocasión se hizo incapié de la necesidad de encontrar un
algoritmo eficiente para realizar este cálculo, puesto que el dado en dicho tema
posee un tiempo de ejecución poco eficiente.

En esta sección conseguiremos triangular cualquier tipo de polígono en tiempo


óptimo O(nlogn), para ello serán necesarios dos pasos diferentes: la partición
de cualquier polígono en particiones monótonas y la triangulación de cada una
de estas porciones.

2.2.1. Polígonos monótonos

Un polígono monótono con respecto a la vertical es aquel que si se recorre con


una línea horizontal bajando a lo largo del total del polígono, ocurre que sólo
encontramos como máximo dos intersecciones del polígono con dicha recta. Si
además no consideramos la existencia de aristas horizontales, entonces
hablamos de polígono extrictamente monótono.

Intuitivamente hablando, un polígono monótono será aquel que no posee


cúspides interiores, considerándo al vértice vi como cúspide si vi–1 y vi+1
están ambos arriba o abajo.

La ventaja de triangular un polígono monótono es más o menos intuitiva: al


bajar una línea horizontal imaginaria a lo largo de todo el triángulo y
encontrarse con el vértice vi, se sabe que su diagonal irá a un punto visible
desde dicho punto. La idea es ir bajando paulatinamente vértice a vértice, bien
por la izquierda, bien por la derecha, dibujando diagonales según el siguiente
algoritmo.

ALGORITMO TriangulacionMonotona
ENTRADA: El Ìpolgono S de Òtamao n
SALIDA : El conjunto de Diagonales que t r i a n g u l a n e l Ìpolgono

Juan Carlos Gutiérrez Barquero____________________________________________ 166


Bismarck Salvador Traña López__________________________________________UNI

INICIO
Ordenar (S, n , p )
ListaCrea ( l )
l i s t aCr e a ( Diagonales )
ListaMeteFinal ( l , p [ 0 ] )
ListaMeteFinal ( l , p [ 1 ] )
PARA j <_ 2 HASTA n_1 REPETIR
SI Adyacente ( L i s t a I n i c i o ( l , p [ j ] ) AND NOT Adyacente ( L i s t a F i
nal(l),p[j])
ENTONCES
v<_ L i s t aSa c a I ni c io ( l )
MIENTRAS (NOT ListaVacia ( l ) ) REPETIR
v <_ L i s t aSa c a I ni c i o ( l )
ListaMeteFinal ( Diagonales , v , p [ j ] )
FIN_MIENTRAS
ListaMeteFinal ( l , v )
ListaMeteFinal ( l , p [ j ] )
SINO
SI Adyacente ( L i s t a F i n a l ( l ) , p [ j ] ) AND NOT Adyacente ( L i s t a I n
icio(l),p[j])
ENTONCES
v<_ListaSacaFinal ( l )
x<_F i n a l L i s t a ( l )
MIENTRAS ( Angulo ( p [ j ] , v , x ) < Pi ) REPETIR
ListaMeteFinal ( Diagonales , x , p [ j ] )
v <_ ListaFinalSaca ( l )
SI NOT ListaVacia ( l )
ENTONCES x<_L i s t a F i n a l ( l )
FIN_MIENTRAS
ListaMeteFinal ( l , v )
ListaMeteFinal ( l , p [ j ] )
SINO
v<_L i s t aSa c a I n i c io ( l )
v<_L i s t aSa c a I n i c io ( l )
MIENTRAS NOT ( ListaVacia ( l ) )
v<_L i s t aSa c a I ni c i o ( l )
ListaMeteFinal ( Diagonales , p [ j ] , v )
FIN_MIENTRAS
FIN_SI
FIN_SI
FIN_PARA

En la siguiente figura podemos observar el conjunto de diagonales obtenidas


tras la ejecución de este algoritmo para el polígono monótono de ejemplo.

Juan Carlos Gutiérrez Barquero____________________________________________ 167


Bismarck Salvador Traña López__________________________________________UNI

Veamos como funciona el algoritmo en sus primeros pasos para este ejemplo.
Al ordenar los puntos de mayor a menor obtenemos la numeración que vemos
en la figura. Antes de entrar en el bucle, la lista l = s0; s1. Al entrar por
primera vez en el bucle con j = 2, observamos que s2 es adyacente al final de
la lista l (2a sentencia condicional) pero el algoritmo no construye aún
diagonales porque el Angulo(s[2],1,0)> por lo que la lista acaba siendo l
=s0; s1; s2. Las siguientes iteraciones son iguales, hasta que para j = 4 se
construyen las diagonales 1–4;2–4;3–4. La idea básica del algortimo es
construir diagonales con el comienzo de la lista l o con el final de ésta ,
teniendo en cuenta que sólo se unen puntos visibles entre sí (ángulo < ) y
que no son adyacentes.

El tiempo de ejecución de este algoritmo es O(nlog n). El proceso de


ordenación de los vértices ya posee este orden de ejecución, por lo que dicho
algoritmo no podría ejecutarse en menor tiempo. Además habría que añadir un
tiempo lineal, puesto que todas las operaciones del algoritmo, operaciones con
el final y principio de la lista, el cálculo del ángulo, etc., se ejecutan en tiempo
constante. Tan sólo nos puede plantear un problema los bucles MIENTRAS,
pero sin embargo aseguramos el tiempo lineal del proceso, puesto que todo
vértice es procesado como mucho dos veces, uno cuando entra en la lista y
otra vez cuando sale de ésta.

2.2.2. Partición de un polígono en componentes monótonas

La triangulación de polígonos monótonos desde luego deja de tener sentido si


no es posible particionar cualquier tipo de polígono en particiones monótonas.
En este apartado completamos el proceso de triangulación eficiente de
polígonos dando un algoritmo capaz de encontrar diagonales tales que dividan
dicho polígono en particiones monótonas.

Juan Carlos Gutiérrez Barquero____________________________________________ 168


Bismarck Salvador Traña López__________________________________________UNI

Consideramos a un polígono y-monótono si para cada horizontal h, la


intersección del polígono con h es conexa, esto es, es un punto o el vacío. La
definición para x-monótono es análoga.

Antes de ver el funcionamiento del polígono determinaremos los cinco tipos de


vértices que podemos encontrar:

Vértice comienzo: el vértice vi es de comienzo si vi–1 y vi+1 poseen menor


ordenada y su ángulo interior es menor que .

Vértice final: el vértice vi es de final si vi–1 y vi+1 poseen mayor ordenada y


su ángulo interior es menor que .

Vértice Partición: el vértice vi es de partición si vi–1 y vi+1 poseen menor


ordenada ysu ángulo interior es mayor que .

Vértice Mezcla: el vértice vi es de mezcla si vi–1 y vi+1 poseen mayor


ordenada y suángulo interior es mayor que .

Juan Carlos Gutiérrez Barquero____________________________________________ 169


Bismarck Salvador Traña López__________________________________________UNI

Vértice Regular: cualquier otro vértice no considerado anteriormente.

Utilizaremos la técnica de .línea de barrido. para encontrar las distintas


particiones monótonas. La idea será localizar los vértices que rompen la
monotonía, los de partición y los de mezcla. Desde ellos se lanzarán
diagonales, el destino de éstos será también un objetivo primordial del
algoritmo.

Las estructuras de datos implicadas son las siguientes:

P: será un array conteniendo los vértices del polígono, ordenados como es


habitual en sentido antihorario.
Q: La línea imaginaria bajando por el plano se encontraría los puntos de P de
mayor a menor ordenada. Para no duplicar los puntos, y sobre todo por
ventajas computacionales, Q será un vector de tamaño n con los índices
de P identificando los puntos ordenados de mayor a menor ordenada.
T: Se trata de alguna estructura de datos con tiempo de acceso logarítmico.
Almacenará los vértices ei, siendo ei = vi –vi+1, con excepción de en–1 =
vn–1v0. El conjunto de ejes se almacenan por la coordenada x, tanto de
izquierda a derecha.
Ayu: Es un array que almacena en todo momento el vértice candidato para
unir a los vértices de partición y mezcla. Los valores podrán ir
cambiando hasta que se localice el valor correcto.
D: Mantiene el resultado, es decir, el conjunto de diagonales conseguidas por
el algoritmo.
Puede ser un array o una lista.

Juan Carlos Gutiérrez Barquero____________________________________________ 170


Bismarck Salvador Traña López__________________________________________UNI

Algoritmo de triangulación

PROCEDIMIENTO HacerMonotono (VAR P : TipoPol igono ; VAR D: TipoLi s ta )


ENTRADA: El Ìpolgono P de Òtamao n
SALIDA : El conjunto D de diagonales
INICIO
Ordenar_Y ( P, n ,Q)
I n i c i l i z a r L i s t a ( T)
I n i c i a l i z a r L i s t a (D)
Para k<_ 0 hasta n_1 Repet i r
i <_Q( k )
34
2 La envolvente convexa y triangulaciÛn de polÌgonos
EN CASO DE QUE TipoVer t i ce (P, i ) SEA
0 : ( _ Comienzo _)
ListaInser tarOrdenado (T, i ,P)
Ayu ( i ) <_ i
1 : ( _ Final _)
Si TipoVer t i ce (P , Ayu ( i _1))=2
Entonces I n s e r t a r L i s t a (D, { i_Ayu ( i _1)})
L i s t aEl imi n a r ( T , i _1)
2 : ( _ Mezcla_)
Si TipoVer t i ce ( P, Ayu ( i _1))=2
Entonces L i s t a I n s e r t a r (D, { i_Ayu ( i _1)})
L i s t aEl imi n a r ( T , i _1)

Juan Carlos Gutiérrez Barquero____________________________________________ 171


Bismarck Salvador Traña López__________________________________________UNI

j <_ ListaBuscar Izdo ( T , i )


Si TipoVer t i ce ( P, Ayu ( j ) )=2
Entonces L i s t a I n s e r t a r (D, { i_Ayu ( j ) } )
Ayu ( j ) <_ i
3 : ( _ ÛPa r t i c i n )
j <_ ListaBuscar Izdo ( T , i )
L i s t a I n s e r t a r (D, { i_Ayu ( j ) } )
Ayu ( j )<_ i
ListaInser tarOrdenado ( T , i ,P)
Ayu ( i )<_i
4 : ( _ Regular_)
Si Inter iorDecha ( P, i )
Entonces
Si TipoVer t ice (P , Ayu ( i _1))=2
Entonces L i s t a I n s e r t a r (D, { i_Ayu ( i _1)})
L i s t aBo r r a r (T, i _1)
ListaInser tarOrdenado (T, i ,P)
Ayu ( i )<_i
Sino
j <_ListaBuscar Izdo (T, i )
Si TipoVer t ice (P, Ayu ( j ) ) = 2
Entonces L i s t a I n s e r t a r (D, { i_Ayu ( j ) }
Ayu ( j )<_ i
FIN_SI
FIN_SI
FIN_CASE
FIN

El tiempo de ejecución del algoritmo es óptimo. La ordenación de los vértices


puede realizarse en tiempo O(nlog n). Cada una de las n iteraciones de bucle
se ejecuta en tiempo logarítmico, el correspondiente a las operaciones de
manejo de la estructura de datos T.

Juan Carlos Gutiérrez Barquero____________________________________________ 172


Bismarck Salvador Traña López__________________________________________UNI

3 Intersecciones

La detección de intersecciones constituye una de las primitivas geométricas


fundamentales con muy diversas aplicaciones. En realidad virtual o diseño de
circuitos VLSI el manejo de intersecciones es fundamental. Pero existen otras
áreas de interés más próximas a la Topografía como son los Sistemas de
Información Geográfica. En muchas ocasiones para hacer los mapas más
manejables y legibles, la información que contienen se divide en distintos tipos
de mapas, cada uno especializado en carreteras, ciudades, ríos y montañas,
densidad de población, etc. La información geométrica subyacente en cada uno
de estos mapas también puede considerarse diferente, así una carretera puede
ser representada mediante una polilínea y un núcleo urbano puede serlo
mediante conjunto de polígonos. Sin embargo, cuando dos de estos mapas
necesitan ser manejados al mismo tiempo (por ejemplo para conocer las
carreteras que llegan a una ciudad), es necesario el manejo de intersecciones
entre iguales o diversos objetos geométricos.

Los problemas que necesitan del procesamiento de intersecciones pueden ser


de distinta índole. En algunas ocasiones no es necesario determinar con
exactitud el punto de corte entre dos elementos geométricos, únicamente
conocer si existe dicha intersección. También podemos encontrar una
diferencia sustancial entre manejar un par de líneas o segmentos o hacerlo con
un conjunto de ellos. De hecho, la realidad a veces necesita simular la forma
de cualquier línea mediante una polilínea o conjunto de segmentos unidos a
modo de cadena. La naturaleza de los objetos también puede ser conveniente
conocerla a priori, puesto que la intersección de polígonos convexos cuenta con
un orden de ejecución menor que si se trata de polígonos generales.

3.1. Intersección de polígonos convexos

El tiempo de ejecución para conocer la intersección de dos polígonos


cualesquiera es O(nm), siendo n y m el número de ejes de los dos polígonos.
El resultado de calcular la intersección de dos polígonos cualesquiera podemos
verlo en la siguiente figura:

Juan Carlos Gutiérrez Barquero____________________________________________ 173


Bismarck Salvador Traña López__________________________________________UNI

Sin embargo, realizar esta operación con polígonos convexos puede hacerse en
tiempo O(n+m). Existe una propiedad importante para la intersección de
polígonos convexos: la intersección es también un polígono convexo.

Denominaremos a A y B a los ejes que en cada momento se están ejecutando.


En cada iteración se avanza el eje A o el B (siempre en orden inverso a las
agujas del reloj) dependiendo de ciertas reglas de avance. El objetivo es que
dichos avances se vayan sincronizando en cuanto a su velocidad para
encontrar todas las intersecciones.

Denominamos a como el punto de la cabecera del vector A, y b el punto de la


cabecera de B. Las reglas de avance harán por ejemplo, que si B apunta hacia
la línea que contiene a A, entonces avanzamos B para buscar la intersección
con A.

Denominaremos a H(A) como el semiplano cerrado a la izquierda de A y a H(B)


como el semiplano cerrado a la izquierda de H(B). Ocurre que AxB > 0 cuando
el giro más corto de girar A hacia B es en sentido inverso a las agujas del reloj.
Como por regla general los dos vectores no coincidirán en su inicio,
imaginaremos sin embargo que así ocurre para localizar el giro más corto. En
el siguiente ejemplo, AxB > 0 puesto que observamos que hacemos un giro en
sentido de las agujas del reloj para ir de A hacia B.

Las reglas de avance quedan resumidas en la siguiente tabla:


AXB Condiciones del semiplano Regla de avance

El método anteriormente descrito aparece a continuación. También aparece el


pseudocódigo de otros subalgoritmos llamados por el algoritmo principal.

Juan Carlos Gutiérrez Barquero____________________________________________ 174


Bismarck Salvador Traña López__________________________________________UNI

PROCEDIMIENTO IntersectaPol igonosConvexos (VAR P,Q: TipoPol igono ; n


,m: Entero )
ENTRADA: P,Q de ñtamao n y m
SALIDA :
VARIABLES:
CONSTANTES: Origen : ( 0 , 0 )
INICIO
a<–0
b<–0
Dentro <– –1
REPETIR
a1 <– (a+n–1) mod n
b1 <– (b+m–1) mod m
Subvector (P[ a ] , P[ a1 ] , A)
Subvector (Q[ b ] , Q[ b1 ] , B)
ProductoX <– Area2 ( Origen , A, B)
bHA <– IzdaSobre (P[ a1 ] ,P[ a ] ,Q[ b ] )
aHB <– IzdaSobre (Q[ b1 ] ,Q[ b ] ,P[ a ] )
SI ( I n t e r s e c t a P[ a1 ] ,P[ a ] ,Q[ b1 ] ,Q[ b ] ,P)
ENTONCES SI Dentro = –1
ENTONCES AvanA <– 0
AvanB <– 0
fiNfiSI
Dentro <– TestDentro ( aHB)
fiNfiSI
SI ( ProductoX fi 0)
ENTONCES SI ( bHA)
ENTONCES a <– Avanza ( a , AvanA , n , Dentro , P[ a ] )
SINO b <– Avanza ( b , AvanB , m, NOT Dentro , Q[ b ] )
SINO SI ( aHB)
ENTONCES b <– Avanza ( b , AvanB , m, NOT Dentro , Q[ b ] )
SINO a <– Avanza ( a , AvanA , n , Dentro , P[ a ] )
fiNfiSI
HASTA ( AvanA fi n AND AvanB fi m)
SI Dentro = –1
ENTONCES ESCRIBIR "No ha habido ó i n t e r s e c c i n " ;
fiN
FUNCION TestDentro (H: Entero ) : óLgico
INICIO
SI H < > 0
ENTONCES TestDentro <– 1
SINO TestDentro <– 0
fiN
FUCION Avanza (VAR ab , Avd : Entero ; n : Entero ; Dentro : óLgico ; v :
TipoPunto )
INICIO
SI Dentro
ENTONCES Pi n t a r ( v )

Juan Carlos Gutiérrez Barquero____________________________________________ 175


Bismarck Salvador Traña López__________________________________________UNI

fiNfiSI
Adv <– Adv + 1
ab <– (ab +1) mod n
fiN

PROCEDIMIENTO Subvector (VAR a , b , c : TipoPunto )


INICIO
PARA i <– 0 HASTA 1 INCR + 1 REPETIR
c [ i ] <– a [ i ] – b [ i ]
fiNfiPARA
fiN

En el siguiente ejemplo observamos un ejemplo de polígonos convexos.

En la siguiente tabla observamos el resultado tras aplicar el algoritmo. En la


última columna observamos el avance de los ejes. Podemos comprobar que el
recorrido conjunto de ambos ejes permite localizar todas y cada una de las
intersecciones existentes.

Juan Carlos Gutiérrez Barquero____________________________________________ 176


Bismarck Salvador Traña López__________________________________________UNI

3.2. Intersección de segmentos

La intersección de segmentos es un cálculo muy importante por las


repercusiones que tiene a nivel de intersecciones en el mundo real. Podemos
simular una carretera como un conjunt de segmentos. Para saber donde se
podría construir un puente podríamos computar el conjunto de intersecciones
entre el mapa de carreteras y el mapa de ríos. Las operaciones realizadas a
bajo nivel en la aplicación informática que lleve a cabo este cálculo serán las
de intersecciones de segmentos.

Puede realizarse siempre un algoritmo de fuerza bruta para realizar este


cálculo utilizando para ello un tiempo O(n2) si supuestamente empleamos un
número similar de ríos y de carreteras. Como es de imaginar este algoritmo
procesaría todos los ríos con todas las carreteras, y siendo por tanto poco
eficiente. Probablemente el número de intersecciones sea mucho menor al de
potenciales puntos de intersección, por lo que podría buscarse un método
alternativo que tuviera por principio el procesar únicamente posibles
intersecciones en segmentos que estén cerca, evitando así cálculos
innecesarios.
La estrategia a seguir puede ser la de bajar una línea horizontal L de arriba
hacia abajo por el plano de modo que se vaya tropezando tanto con un tipo de
segmentos como del otro. Cada vez que L se encuentra con un segmento lo
almacena en una lista ordenada de izquierda a derecha, y así sucesivamente
con todos y cada uno de los segmentos localizados. Para cada nueva
intersección y borrado en dicha lista se pregunta si la nueva situación provoca
una intersección, siempre entre segmentos vecinos de la lista.

Juan Carlos Gutiérrez Barquero____________________________________________ 177


Bismarck Salvador Traña López__________________________________________UNI

Emplear las estructuras de datos correctas en este método es fundamental


para la eficiencia de éste.

P: Es un array que contiene el conjunto de segmentos iniciales dispuesto en


principio en cualquier tipo de orden. El array tiene un tamaño de nx4
puntos, siendo cada fila los cuatro puntos que forman un segmento en el
orden {x0; y0; x1; y1}, siendo siempre el punto (x0; y0) el extremo de
mayor ordenada.
Q: Es la denominada cola de eventos que almacena el conjunto de vértices y
de puntos intersección. Mantendrá los puntos extremos ordenados de
mayor a menor ordenada. Cuando en dicha lista se inserten dos puntos
con la misma ordenada, se considerará primero el de menor abscisa.
Será preferentemente una lista a un array porque deberá permitir
inserciones.
T: Estructura de datos que simula el comportamiento de la línea imaginaria L.
Mantendrá ordenados de izquierda a derecha los segmentos que en todo
momento vaya atravesando, eliminado los ya procesados e insertándolos
cuando se comienzan a atravesar. Esta estructura de datos deberá
permitir inserciones y borrados en tiempo logarítmico 1.
I: Cualquier tipo de estructura de datos que almacene las intersecciones
localizadas.

La función ManejarEvento es la encargada de procesar cada uno de los nuevos


extremos de la cola de eventos Q. Esta función sería llamada por el algoritmo
EncontrarIntersecccion tal y como vemos a continuación.

ALGORITMO Encontrar Intersección

Juan Carlos Gutiérrez Barquero____________________________________________ 178


Bismarck Salvador Traña López__________________________________________UNI

Juan Carlos Gutiérrez Barquero____________________________________________ 179


Bismarck Salvador Traña López__________________________________________UNI

Sin embargo, la función de intersección de segmentos desarrollada en el


primer tema no es válida para conocer el punto de intersección entre dos
segmentos, sólo para saber si dicha intersección se produce.

Juan Carlos Gutiérrez Barquero____________________________________________ 180


Bismarck Salvador Traña López__________________________________________UNI

En el siguiente ejemplo vemos un conjunto de segmentos en los cuales


podemos encontrar un conjunto de intersecciones. Hay que tener en cuenta
que el algoritmo también detecta las intersecciones de los segmentos por sus
extremos, ya que hay que tener en cuenta que los segmentos pueden llegar
desordenados y sin relación unos con otros.

4 Localización de puntos y diagramas de Voronoi

4.1. Punto en polígono

El problema denominado punto en polígono se presenta cada vez que se


pincha el ratón en pantalla. Si el polígono P sobre el que trabajamos es
convexo, el problema puede solucionarse en tiempo O(logn). Pero el método
más interesante ocurre cuando P no es convexo.

Para este caso utilizamos el siguiente método: si queremos saber si el punto q


está dentro o fuera del polígono P trazamos una línea en cualquier dirección,
por ejemplo utilizando la recta r igual a y = 0, x fi 0 y entonces contamos el
número de intersecciones que se producen. El punto q está dentro de P si el
número de intersecciones es impar.

Para hacer más fácil el cálculo y utilizar una recta r válida para cualquier caso,
hacemos coincidir el punto q con el origen de coordenadas de forma que la r
pueda ser simplemente y = 0; para x >= 0.

Juan Carlos Gutiérrez Barquero____________________________________________ 181


Bismarck Salvador Traña López__________________________________________UNI

Para conocer el lugar justo de intersección utilizaremos la siguiente fórmula:

trasladamos la idea anterior al siguiente algoritmo en pseudocódigo que realiza


el test de inclusión de punto en polígono. El primer conjunto de bucles hace el
cambio de origen de coordenadas.

En el siguiente se detectan las intersecciones con la recta y = 0, pero sólo se


tienen en
cuenta los cortes que se producen para x > 0.

El tiempo de ejección de este test de inclusión es lineal, como claramente


indica la búsqueda exhaustiva de intersecciones de una recta con todas las
aristas de un polígono.

Juan Carlos Gutiérrez Barquero____________________________________________ 182


Bismarck Salvador Traña López__________________________________________UNI

DIAGRAMA DE VORONOI

En este capítulo estudiamos el diagrama Voronoi, una estructura geométrica


segunda en importancia sólo detrás del buque convexo. En cierto sentido un
diagrama de Voronoi registra todo lo que uno alguna vez querría saber acerca
de proximidad para un grupo de puntos (u objetos más generales). Y a menudo
uno quiere saber detalles acerca de la proximidad: ¿Quién está más cercano a
quién? ¿Quién está más lejos? Etcétera. El concepto tiene más de un siglo de
antigüedad, discutido en 1850 por Dirichlet y en 1908 por Voronoi.
Comenzaremos con una serie de ejemplos para motivar el debate y entonces, la
zambullida en los detalles de la estructura enriquecedora del diagrama Voronoi
(en las Secciones 5.2 y 5.3). Hay que familiarizarse con estos detalles antes de
que los algoritmos puedan ser apreciados (en la Sección 5.4). Finalmente
revelaremos la bella conexión entre diagramas Voronoi y buques convexos en la
Sección 5.7. Este capítulo incluye sólo dos pedazos pequeños de código, para
construir lo dual del diagrama Voronoi (la triangulación Delaunay), en la Sección
5.7.4.

APLICACIONES PRELIMINARES

1. Las torres de observación de fuego

Imagine un bosque vasto conteniendo un número de torres. Cada


guardabosque es responsable de extinguir cualquier fuego más cercano para
su torre que para cualquier otra torre. El grupo de todos los árboles para los
cuales un guardabosque particular es responsable constituye el “Polígono de
Voronoi” asociado con su torre. El diagrama de Voronoi traza las líneas entre
estas áreas de responsabilidad: Los lugares en el bosque que son
equidistantes de dos o más torres. (Una vistazo a la Figura 5.5 puede ayudar
a la intuición).

Juan Carlos Gutiérrez Barquero____________________________________________ 183


Bismarck Salvador Traña López__________________________________________UNI

2. Las torres en llamas

Imagine ahora la situación perversa donde todos los guardabosques


encienden sus torres simultáneamente, y el bosque se quema a una velocidad
uniforme. El fuego se dispersará en círculos centrado en cada torre. Los
puntos en los cuales el fuego se apaga es porque ha alcanzado previamente
árboles consumidos son esas puntos equidistantes de dos o más torres, los
cuáles son exactamente los puntos en el diagrama Voronoi.
3. La agrupación de vecinos mas cercanos

Una técnica frecuentemente utilizada en el campo de reconocimiento de


patrones es trazar un mapa de un grupo de objetos blancos en un rasgo del
espacio reduciendo los objetos a puntos cuyas coordenadas son las medidas
de rasgo. El ejemplo de cinco medidas de la sastrería de la Sección 4.6.1
puede ser visto como una definición o una característica del espacio. La
identidad de un objeto de afiliación desconocida entonces le puede ser
asignada al objeto próximo de blanco en el rasgo del espacio.
Un ejemplo hará esto más claro. Suponga que un depósito de partes incluye
dos tipos de nueces A y B, una con diámetros interior y exterior de 2 y 3
centímetros respectivamente, y B con el espacio de diámetros 3 y 4 cm., el
rasgo es el cuadrante positivo del plano Euclidiano de dos dimensiones,
positivo porque ningún radio puede ser negativo A traza al grano (2,3), y B
al punto (3,4).
Suponga, que un sistema de visión enfoca la atención en una nuez x en el
depósito y se mide su radio interior y exterior para tener 2.8 y 3.7 cm.
Conociendo que hay inexactitudes de medida, y que sólo las nueces de tipo
A y B están en el depósito, ¿que tipo de nuez es x? Es más probable que sea
una nuez de tipo B, porque su distancia para B en el rasgo del espacio 0.36,
considerando su distancia para A es 1.06. Vea la Figura 5.1. En otras
palabras, la vecina más próxima de x es B, porque la x está en el polígono
de Voronoi de B.
Si hay muchos tipos de nueces, la tarea de identificación es localizar la nuez
desconocida x en el diagrama de Voronoi de las nueces blancos. Cómo puede
estar esto hecho eficazmente se discutirá en la Sección 5.5.1.

Juan Carlos Gutiérrez Barquero____________________________________________ 184


Bismarck Salvador Traña López__________________________________________UNI

4. La facilidad de Localización

Suponga que a usted le gustaría localizar una tienda de comestibles nueva en


un área con varias tiendas de comestibles existentes, irreconciliables.
¿Asumiendo densidad de población uniforme, dónde debería estar la tienda
nueva localizada para optimizar sus ventas? Un método natural que satisfaga
esta restricción vaga es localizar la tienda nueva tan lejos de las viejas como
sea posible. Aun esto es un poco vago; Más precisamente podríamos escoger
una localización cuya distancia para la tienda próxima es tan grande como sea
posible. Esto es equivalente a localizar la tienda nueva en el centro del círculo
vacío más grande, el circulo más grande cuyo interior no contiene otras
tiendas. La distancia para la tienda mas próxima es entonces el radio de este
círculo.
Mostraremos en 5.5.3 Sección que mientras central de círculo vacío más
grande debe mentir en el diagrama Voronoi.
5. Planeando el camino

Imagine un ambiente desordenado a través del cual un robot debe planear un


camino. Para minimizar el riesgo de colisión, al robot le gustaría quedarse tan
lejos de todos los obstáculos como sea posible. Si restringimos la pregunta a
dos dimensiones, y si el robot es circular, entonces el robot debería quedarse
todo el tiempo en el diagrama de Voronoi de los obstáculos. Si los obstáculos
son puntos (digamos polos delgados), entonces éste es el diagrama
convencional de Voronoi. Si los obstáculos son polígonos u otras formas,
entonces una versión generalizada del punto del diagrama Voronoi determina
el camino apropiado.
Revisaremos este ejemplo en el Capítulo 8 (la Sección 8.5.2).
6. La cristalografía

Juan Carlos Gutiérrez Barquero____________________________________________ 185


Bismarck Salvador Traña López__________________________________________UNI

Asuma un número de granos de cristal crecen uniformes, a velocidad


constante. ¿Cuál será la apariencia del cristal cuando el crecimiento ya no es
posible? Debería estar claro ahora que esto es análogo al incendio de bosques,
y que cada grano crecerá para un polígono de Voronoi, con regiones
adyacentes del grano encontrándose a lo largo del diagrama de Voronoi. Los
diagramas de Voronoi por mucho tiempo se han usado para simular
crecimiento del cristal.
La lista de aplicaciones podría seguir sin parar, y veremos otros en la Sección
5.5. Pero es hora de definir el diagrama formalmente.

DEFINICIONES Y PROPIEDADES BASICAS

Sea P = {p1, p2,…, pn} un grupo de puntos en el plano Euclidiano de dos


dimensiones. Éstos son llamados los sitios. Divida en partes el plano asignando
cada punto en el plano a su sitio más próximo. Todos esos puntos asignados a
pi forman la región de Voronoi V (pi). V (pi) consiste de todos los puntos al menos
tan cercanos a pi en lo que se refiere a cualquier otro sitio:

V (pi) = {x: | pi - x pj - x j }.
Note que hemos definido este grupo para ser cerrado. Algunas puntos no tienen
un único sitio próximo, o vecino próximo. El grupo de todos los puntos que tienen
más de un vecino cercano forman el diagrama de Voronoi V (P) para el grupo de
sitios.
Más tarde definiremos los diagramas de Voronoi para grupos de objetos más
generales que puntos. Primero miramos diagramas con justamente algunos
sitios antes de detallar sus propiedades para n mayor.

Dos sitios
Considere solo dos sitios, p1 y p2. Sea B (p1, p2) = B12 la bisectriz perpendicular
del segmento p1p2. Entonces cada punto x en B12 es equidistante de p1 y p2. Esto
puede verse dibujando el triángulo (p1, p2, x) como se muestra en la Figura 5.2.
Por el teorema del lado - ángulo - lado de Euclides, | p1x | = | p2x |.

Juan Carlos Gutiérrez Barquero____________________________________________ 186


Bismarck Salvador Traña López__________________________________________UNI

Tres sitios
Para tres sitios, es claro que fuera del triángulo (p1, p2, p3), el diagrama contenga
la bisectriz B12, B23, B31. Lo que no esta claro es que ocurre en los alrededores
del triángulo. Otra vez de Euclides las bisectrices perpendiculares de los tres
lados del triángulo todos atraviesan un punto, el circuncentro, el centro del único
círculo que pasa a través de los vértices del triángulo. Así el diagrama Voronoi
para tres puntos debe aparecer como en la Figura 5.3. (Sin embargo, el
circuncentro de un triángulo no está siempre dentro del triángulo como se
muestra).

Semiplanos
La generalización más allá de tres puntos quizá todavía no esta clara, pero es
ciertamente claro que la bisectriz Bij desempeñará un papel. Sea H el semiplano
cerrado con límite Bij y conteniendo pi. Entonces H puede ser visto como todos
los puntos que están cercanos a pi que a pj. Ahora ordene el regreso de que V
(pi) es el grupo de todos los puntos más cercano a pi que a cualquier otro sitio:
En otras palabras, los puntos más cercanos a pi que a p1, y más cercano a pi que

Juan Carlos Gutiérrez Barquero____________________________________________ 187


Bismarck Salvador Traña López__________________________________________UNI

a p2, y más cercano a pi que a p3, etcétera. Esto muestra que podemos escribir
esta ecuación o V(pi):
V (pi) = 
i j
H (pi, pj) (ecuación 5.2)

Donde la notación significa que la intersección debe estar ocupada sobre todo i
y j como i  j. Note que esa conjunción inglesa “y” ha sido trasladada a
intersección de conjunto.

Figura 5.4 (a) Diagrama de Voronoi para cuatro puntos cocirculares; (b)
diagrama después de mover los puntos hacia la izquierda.
La ecuación (5.2) inmediatamente nos da una propiedad importante de los
diagramas de Voronoi: Las regiones de Voronoi son convexas, pues la
intersección de cualquier número de Semiplanos es un grupo convexo. Cuando
las regiones están rodeadas, son polígonos convexos. Los bordes de las regiones
de Voronoi son llamados bordes de Voronoi, y los vértices son llamados vértices
de Voronoi. Note que un punto en el interior de un borde de Voronoi tiene dos
sitios próximos, y un vértice de Voronoi tiene al menos tres sitios próximos.

Cuatro sitios
El diagrama de cuatro puntos formando las esquinas de un rectángulo es
mostrado en la Figura 5.4 (a). Note que el vértice de Voronoi es de grado cuatro.
Ahora suponga que un sitio es movido ligeramente, como en la Figura 5.4 (b).
Hay un sentido en el cual este diagrama es normal, y uno en la Figura 5.4 (a)
que es anormal o “degenerado”. Es degenerado por que hay cuatro puntos
cocirculares. A menudo lo encontraremos útil para excluir este tipo de
degeneración.

Muchos sitios
Un diagrama típico con muchos sitios es mostrado en la figura 5.5. Un vértice
de Voronoi no es mostrado en esta figura: Los dos rayos casi horizontales

Juan Carlos Gutiérrez Barquero____________________________________________ 188


Bismarck Salvador Traña López__________________________________________UNI

dejando el diagrama para la izquierda no son muy paralelos y se intersecan en


un vértice de Voronoi cerca de 70 centímetros a la izquierda de la figura.
Tamaño del diagrama
Aunque hay exactamente n regiones de Voronoi para n sitios, el tamaño
combinatorio total del diagrama concebible podría ser cuadrático en n, pues
cualquier región particular de Voronoi puede tener  (n) bordes de Voronoi
(Ejercicio 5.3.3 [4]). Sin embargo, nosotros ahora mostramos que de hecho este
no es el caso, que el tamaño total del diagrama es  (n).
Asumamos pues por simplicidad que ninguna de los cuatro puntos son
cocirculares, y por consiguiente cada vértice de Voronoi es de grado tres.
Construya la gráfica dual G (Sección 4.4) para un diagrama Voronoi V (P) como
sigue: Los nodos de G son sitios de V (P), y dos nodos están conectados por un
arco si sus polígonos correspondientes de Voronoi comparten un borde de
Voronoi (comparten un borde de longitud positiva).
Ahora observe que éste es un grafo planar: Podemos incrustar cada nodo en su
sitio, y todo el incidente de arcos para el nodo puede ser angularmente
clasificado lo mismo los bordes del polígono. Además todas las caras de G son
triángulos, siendo propio del vértice de Voronoi de grado tres. Este reclamo será
hecho más claro en un momento (la Figura 5.6).
Pero previamente mostramos que la fórmula de Euler significa que un grafo
planar triangulado con n vértices tiene 3n-6 de bordes y 2n-4 caras; Vea la
Sección 4.1.1 Teorema 4.4.5. Porque las caras de G son propias de los vértices
de Voronoi, y porque los bordes de G son propios de los bordes de Voronoi
(desde que cada arco de G le cruza un borde de Voronoi), hemos mostrado que
el número de vértices de Voronoi, bordes, y las caras es  (n).

Si ahora removemos, entonces la suposición que ninguno de los cuatro puntos


son cocirculares, la gráfica es todavía planar, pero no necesariamente
triangulado. Por ejemplo, lo dual del diagrama mostrado en Figura 5.4 (a) es un
cuadrilátero. Pero tales gráficas poco trianguladas tienen menos conexiones
entre dos vértices de un gráfico y caras, así es que los  (n) saltos continúan
teniendo aplicación.
Una consecuencia del salto del borde 3n-6 es que el número común de bordes
de un polígono de Voronoi no puede ser más de seis (el Ejercicio 5.3.3 [5]).

Triangulación de Delaunay

En 1934 Delaunay probó que cuándo la gráfica dual se traza con líneas rectas,
produce una triangulación planar de los sitios de Voronoi P (si ninguno de los
cuatro sitios son cocirculares), ahora llamado la triangulación de Delaunay D (P).
Figura 5.6 muestra la triangulación de Delaunay para el diagrama de Voronoi en
Figura 5.5, y la figura 5.7 muestra que la triangulación de Delaunay
superpuesta en el diagrama correspondiente de Voronoi. Note que no es
inmediatamente que usando líneas rectas en el duales evitaria cruzarse adentro

Juan Carlos Gutiérrez Barquero____________________________________________ 189


Bismarck Salvador Traña López__________________________________________UNI

del dual; El segmento dual entre dos sitios necesariamente no cruza el borde
Voronoi compartido entre ahora, pero más bien esperaremos hasta que hayamos
recogido más propiedades de la triangulación Voronoi de diagramas y Delaunay,
cuándo la prueba será fácil.

Las propiedades de triangulación de Delaunay

Debido que la triangulación de Delaunay y el diagrama de Voronoi son


estructuras duales, cada uno contiene la misma “información” en algún sentido,
solamente representó en una forma más bien diferente. Para ganar un agarre
en estas estructuras complicadas, es importante para tener una comprensión
cabal de las relaciones entre una triangulación de Delaunay y su diagrama
correspondiente de Voronoi. Listamos sin prueba varias propiedades de
Delaunay y seguiremos con una lista más substantiva de propiedades de
Voronoi. Sólo la propiedad D6 y D7 no han sido mencionados anteriormente. Fija
un grupo de sitios P.
D1. D (P) es la línea recta dual de V (P). Esto es por definición.
D2. D (P) es una triangulación si ninguno de los cuatro puntos de P son
cocirculares: Cada cara es un triángulo. Éste es el teorema de Delaunay. Las
caras de D (P) son llamados triángulos de Delaunay.
D3. Cada cara (triángulo) de D (P) es propia de un vértice de V (P).
D4. Cada borde de D (P) es propio de un borde de V (P).
D5. Cada nodo de D (P) es propio de una región de V (P).
D6. El límite de D (P) es el buque convexo de los sitios.
D7. El interior de cada (triángulo) cara de D (P) no contiene sitios.
Las propiedades D6 y D7 aquí son lo más interesante; Pueden ser verificados
en Figura 5.6 y 5.7.

Elementos del diagrama de Voronoi

Podemos considerar los siguientes elementos en el diagrama de Voronoi:

pi punto generador.

eje es el conjunto de segmentos, semirectas o lÌneas in_nitas que son frontera


de una región de Voronoi a otra.

vértice un punto que comparte m·s de dos polÌgonoos de Voronoi.

adyacentes dos regiones se dice que son adyacentes si comparten algÿn eje.
Los conceptos anteriores son extensibles a tres dimensiones, teniendo en
cuenta que ahora las regiones de Voronoi serÌan poliedros y los ejes serÌan
polÌgonos.

Las propiedades de los Diagramas de Voronoi

Juan Carlos Gutiérrez Barquero____________________________________________ 190


Bismarck Salvador Traña López__________________________________________UNI

V1. Cada región de Voronoi V (pi) es convexa.


V2. V (pi) es ilimitada si pi está en el buque convexo del grupo del punto.
V3. Si la v es un vértice de Voronoi en la unión de V (p1), V (p2), y V (p3),
entonces la v es el centro del círculo C (v) determinado por p1, p2, y p3. (Este
reclamo generaliza a los vértices de Voronoi de cualquier grado).
V4. C (v) es el circuncirculo para el triángulo de Delaunay correspondiente a v.
V5. El interior de C (v) no contiene sitios.
V6. Si pj es el vecino próximo a pi, entonces (pi, pj) es un borde de D (P).
V7. Si hay algún círculo a través pi y pj que no contiene otros sitios, entonces
(pi, pj) es un borde de D (P). El inverso también tiene aplicación: Pues cada
borde de Delaunay, hay algún círculo vacío.
La propiedad V7, la menos intuitiva, es una caracterización importante de los
bordes de Delaunay y será usada en varias pruebas más tarde. Ésta es la única
propiedad que probaremos formalmente.
La prueba. En una dirección es fácil: Si ab es un borde de Delaunay, entonces V
(a) y V (b) comparten un borde de longitud positiva e  V (P). Ponga a un círculo
C (x) con centro en x, con radio igual a la distancia de a o b. Este círculo está
obviamente vacío de otros sitios, pero si no fuera así, el sitio c estaría sobre o
dentro del círculo, x estaría en V (c) igualmente, pero conocemos que x esta
solamente V (a) y V (b).

La implicación inversa es más sutil. Suponga que hay un círculo vacío C (x) a
través de a y b, con centro en x. Tenemos la intención de poner a prueba que
ab  D (P). Porque la x es equidistante de a y b, x está en las regiones de
Voronoi de ambos a y b mientras ningún otro punto interfiera con “El sentido de
la hermandad próximo”. Pero ninguno es, porque el círculo está vacío. Por
consiguiente, x  V(a)  V (b) (la retentiva que definimos como regiones de
Voronoi son grupos cerrados). Porque ninguno de los puntos estan sobre el límite

Juan Carlos Gutiérrez Barquero____________________________________________ 191


Bismarck Salvador Traña López__________________________________________UNI

de C (x) de otra manera que a y b (por la hipótesis), allí deban ser libertad para
contonearse la x un poco y mantener vacío C (x). En particular, podemos mover
x a lo largo de Bab, la bisectriz entre a y b, y podemos mantener vacío al
conservar el círculo a través de a y b. Ver la Figura 5.8. Por eso la x está en una
longitud positiva del borde de Voronoi (un subconjunto de Bab ) compartido entre
V (a) y V (b). Y por consiguiente ab EMBED Equation.3  D (P).
Dejamos la prueba de las otras propiedades para la intuición, los ejercicios, y
para la Sección. 5.7.2
Ejercicios

1. El polígono regular [fácil]. Describa la triangulación del diagrama de


Voronoi y la triangulación de Delaunay para los vértices de un polígono regular.
2. Las regiones ilimitadas. Ponga a prueba la propiedad V2: V (pi) es ilimitada
del si pi está en el buque convexo del grupo del punto. No se haga cargo de
la propiedad correspondiente de Delaunay D6, pero de otra manera cualquier
propiedad Delaunay o Voronoi puede ser empleada en la prueba.
3. El vecino más cercano. Ponga a prueba propiedad V6: Si el pj es un vecino
próximo a pi, entonces (pi, pj) es un borde de D (P). Cualquier propiedad
Delaunay o Voronoi puede ser empleada en la prueba.
4. El vértice de Delaunay de alto-grado. Diseñe un grupo de puntos, con n
arbitrario, y sin cuatro puntos cocirculares, algo semejante aquél que el
vértice de la triangulación Delaunay tiene grado n-1.
5. Numero promedio de bordes del polígono de Voronoi, pruebe que el promedio
de todas las regiones Voronoi para cualquier grupo de n puntos, no excede 6.
6. Las triangulaciones de Pitteway. Una triangulación de un grupo de P de puntos
es llamada una triangulación Pitteway si, para cada triángulo T = (a, b, c),
cada punto en T tiene uno de a, b, o c como su vecino mas cercano entre los
puntos de P.
a. Muestre por ejemplo que no toda triangulación de Delaunay es una
triangulación de Pitteway.
b. Caracterizar aquellas triangulaciones de Delaunay que son triangulaciones
de Pitteway.

Los ALGORITMOS
Las muchas aplicaciones del diagrama de Voronoi y su belleza inherente les han
instado a los investigadores a que inventen una colección variada de algoritmos
para computarlo. En esta Sección 5.7.2 examinaremos cuatro algoritmos, cada
uno superficialmente, para ver que la Sección 5.7.2 que el diagrama Voronoi
puede ser computado usando nuestro código del buque convexo.

La intersección de los Semiplanos


Podríamos construir cada región de Voronoi separadamente, por la intersección
de n-1 semiplanos según la ecuación (5.2). Construyendo la intersección de n
semiplanos es dual para la tarea de construir el buque convexo de n puntos en
dos dimensiones y puede ser llevado a cabo con algoritmos similares en el

Juan Carlos Gutiérrez Barquero____________________________________________ 192


Bismarck Salvador Traña López__________________________________________UNI

tiempo O(n log n) tiempo (Ejercicio 6.5.3 [5]). Hacer esto para cada sitio costaría
a O (n2 log n).

Construcción incremental
Suponga que el diagrama de Voronoi V para k puntos están ya construidos, y
ahora nos gustaría construir el diagrama V’ después de añadir uno o mas puntos
p. Suponga que p cae dentro de los círculos asociados con varios vértices de
Voronoi, dice C (v1),…, C (vm). Entonces estos vértices de V no pueden ser
vértices de V’, pues violan la condición de que los círculos del vértice de Voronoi
deben estar vacíos de sitios (V5, Sección 5.3.2). Resulta que éstos son los únicos
vértices de V que no están arrastrados sobre V’ También resulta que estos
vértices están todos localizados en un área del diagrama. Estas observaciones
vagas pueden ser hechas precisas, y forjan uno de los algoritmos más limpios
para construir el diagrama de Voronoi. El algoritmo gasta O (n) tiempo por
inserción de punto, para una complejidad total de O (n2). A pesar de esta
complejidad cuadrática, éste ha sido el método más popular de construir el
diagrama; Ver Field (1986) para los detalles de implementación.
El algoritmo incremental ha sido revitalizado recientemente con aleatorización,
el cual tocaremos en la Sección 5.7.4.

Divide y vencerás
El diagrama de Voronoi puede estar construido con un complejo algoritmo divide
y conquista en el tiempo O (n log n), primera parte detallada por Sahmos y Hoey
(1975). Fue este papel que introdujo el diagrama de Voronoi para la comunidad
de ciencia de la computación. Esta vez la complejidad es asintóticamente óptima,
pero el algoritmo es bastante difícil para implementar. De cualquier forma puede
estar hecho con atención meticulosa para las estructuras de datos.

Juan Carlos Gutiérrez Barquero____________________________________________ 193


Bismarck Salvador Traña López__________________________________________UNI

Omitiremos este algoritmo históricamente importante para enfocar la atención


en algunos desarrollos recientes emocionantes.

El algoritmo de Fortune

Hasta la mediados de los 80, la mayoría de implementaciones para computar el


diagrama de Voronoi le usaron a la O (n2) algoritmo incremental, aceptando su
desempeño más lento para evitar las complejidades de la codificación divide y
conquista. Pero en 1985, Fortune (1987) inventó un algoritmo del barrido de
plano listo que es tan simple como los algoritmos incrementales pero tiene
complejidad del peor caso de O (n log n). Nosotros ahora esbozaremos la idea
principal detrás de este algoritmo.
Los algoritmos Plane-sweep(barrido de plano) (Sección 2.2.4 ) pasan una línea
de barrido sobre el plano, dejando en cualquier hora el problema solucionado
para la porción del plano ya barrido y no resuelta para la porción todavía no
alcanzada. Un algoritmo Plane-sweep para construir el diagrama de Voronoi
construiría el diagrama detrás de la línea. A primera vista, esto parece realmente
mentira, como los bordes Voronoi de una región de Voronoi V (p) serían
encontrados por la línea de barrido antes de que la L encuentre la p del sitio
responsable para la región. Fortune superó esta imposibilidad aparente por una
idea extraordinariamente lista.

Los conos

Imagine los sitios en el plano xy del sistema de coordenadas tridimensional.


Erecto sobre cada sitio p un cono cuya cima está en p, y cuyos lados se inclinan

Juan Carlos Gutiérrez Barquero____________________________________________ 194


Bismarck Salvador Traña López__________________________________________UNI

a 45º. Si la tercera dimensión es mirada como el tiempo, entonces el cono sobre


p representa un círculo dilatándose sobre p a una velocidad única: Después de
t unidades tiempo, su radio es t.
Ahora considere dos conos cercanos, sobre los sitios p1 y p2. Se intersecan en
una curva en el espacio. Recordando la vista de círculos en expansión del
diagrama de Voronoi, debería ser esperado que esta curva yazca enteramente
en un plano vertical, el plano ortogonal para la bisectriz de p1p2. Vea la Figura
5.9. Así que aunque la intersección está curvada en tres dimensiones, se
proyecta para una línea recta en el plano xy.
Es solamente un paso pequeño de aquí a que si los conos sobre todos los sitios
son opacos, y son mirados de z = -  , ¡lo que se ve es precisamente el diagrama
Voronoi!

El corte del cono

Estamos ahora preparados para describir la idea de Fortune. Su algoritmo barre


los conos con un plano sesgado, sesgado a 45º para el plano xy. La L de la línea
de barrido es la intersección con el plano xy. Demos por supuesto que la L es
paralela al eje vertical y que su coordenada de la x es l(ele). Vea Figura 5.10.
Imagínate que  , al igual que los conos, es opaco, y otra vez considera la vista
de z = -  .
Para la x > l, lado de L, solamente  es visible desde abajo: Hace un viraje
debajo del plano xy y así también obscurece los sitios y los conos. Esto
representa la porción del plano todavía no ha sido barrido. Para la x < l , lado
de L, el diagrama de Voronoi es visible hasta la intersección de  con la
“frontera” derecha (x positiva) de los conos. La intersección de  con cualquier
cono es una parábola (una propiedad básica de secciones cónicas), y así la
intersección de  con su frontera derecha proyecta al plano xy (y así aparece z
= -  ) como un “frente parabólico”, una curva compuesta de piezas de
parábolas. Vea Figura 5.11. Dos parábolas se unen en un lugar donde 
encuentra dos conos. De nuestro debate de la intersección de dos conos arriba,
esto debe estar en un borde de Voronoi.

Frente parabólico
Ahora finalmente podemos ver cómo solucionó Fortune el problema del problema
de la línea de barrido encontrando bordes de Voronoi antes de los sitios
generadores: ¡Porque su plano de barrido se inclina en los mismos bordes como
los lados de los conos, L encuentra un sitio p exactamente cuando  primero
golpea el cono para p! Por lo tanto no es ése el caso que el diagrama de Voronoi
que está en todo tiempo construido para la izquierda de L, pero está en todo
tiempo construido bajo  , lo cual quiere decir que esta construido a la izquierda
de L arriba para la parte delantera parabólica, lo cual queda atrás de L un poco.
El Lo que es mantenido en todo tiempo por el algoritmo es la parte delantera
parabólica, cuyas uniones trazan el diagrama de Voronoi sobre el tiempo, Desde
que todas estas enroscaduchas permanecen en los bordes de Voronoi. Aunque

Juan Carlos Gutiérrez Barquero____________________________________________ 195


Bismarck Salvador Traña López__________________________________________UNI

nos acabamos de ninguna manera con la descripción del algoritmo, no haremos


el intento de detallarlo mas alla de esto.

Finalmente, debería estar claro que el algoritmo sólo necesite almacenar la parte
delantera parabólica, que es de tamaño O (n) y es a menudo O( n ) .Esto es
una ventaja significante del algoritmo de Fortune cuando la n es grande: El
almacenamiento necesitado es a menudo mucho más pequeño que el tamaño
de diagrama. Y n es a menudo grande, quizá 106 (Sugihara e Iri 1992), para
diagramas basados en datos recogidos por ejemplo en los sistemas de
información geográficos.

5.4.5 Ejercicios

1. D V (p) (p) Diseñar un algoritmo para la computar del diagrama de


Voronoi, dado la triangulación Delaunay. Trate de lograr complejidad de 0 (n)
tiempo.

2. Diagramas de Voronoi unidimensionales. Un Diagrama unidimensional de


Voronoi para un grupo de puntos P = { P1, …, Pn } en una línea (diga el eje de
la x) es un grupo de puntos V (P) = { x1, …, xn-1 } algo semejante que el x1 es
el punto medio de pi de Pi + 1.

Suponga que usted da un conjunto X = { x1, …, xn-1 }. Diseñe criterios que te


permitirán determinar si es o no X es un diagrama de unidimensional de Voronoi
de un grupo de puntos, y si es así. Determine P. ¿Con qué rapidez es el algoritmo
implementado?

3. Los diagramas dinámicos Voronoi. La imagen un grupo de puntos


moviendo en el plano, cada uno con una velocidad fija y dirección. Sea V (t) el
diagrama de Voronoi de puntos en el tiempo t. Este un problema no resuelto,
obtener saltos apretados en el número de diagramas distintos
combinatoriamente que pueden resultar todo el tiempo. Aquí le pregunto a usted
que establezca lo mejor conoce salto inferior:  (n2). En otras palabras,
encuentre un grupo de n moviendo puntos algo tal que V (t) cambie su estructura
combinatoria cn2 para alguna constante C.
Ningún a mismo capaz de encontrar un ejemplo en el cual haya más n2 cambios
pero el mejor salto alto esta alrededor de O(n3).
4. La triangulación arbitraria. Diseñe un algoritmo para encontrar que una
triangulación arbitraria de un grupo de punto P: Una colección de diagonales
incidente para cada punto de P que divide H (P) en triángulos. La ausencia del
requisito que la triangulación sea Delauny permite libertad considerable en el
diseño.

5. Algoritmo Lanzando. Investigue el siguiente algoritmo pensado para construir


a D (P): Comience con una triangulación arbitraria de P. Entonces repito el

Juan Carlos Gutiérrez Barquero____________________________________________ 196


Bismarck Salvador Traña López__________________________________________UNI

siguiente procedimiento hasta que D (P) es logrado. Identifique dos triángulos


adyacentes abc y cbd compartiendo la diagonal bc, tal que el cuadrilátero abcd
sea convexo. Si d está dentro del circuncirculo de abc, entonces suprima cb y
agregue ad. ¿Trabajará esto?

5.5 Aplicaciones en Detalle.

Ahora Discutiremos cinco aplicaciones del diagrama de Voronoi, en el detalle


accidentado: Los vecinos próximos, las triangulaciones “gordas”, los círculos
más grandes vacíos, los árboles de extensión mínimos, y viajando a través de
caminos del vendedor.

5.5.1 Los vecinos más cercanos.

Una aplicación del diagrama Voronoi para agrupamiento de los vecinos más
cercanos mencionado en Sección 5.1 Este problema puede verse como un
problema de consulta: ¿Cuál es el vecino mas cercano a un punto buscado? Otra
versión es la problema del vecino más cercano: Encuentras el vecino mas
cercano para cada punto en un grupo dado. Esto tiene un número de aplicación
en una variedad de campos, incluyendo biología, la ecología, la geografía, y
Física.

Defina la relación entre el vecino mas cercano y un grupo P de puntos como


sigue: La b es una vecina próxima de a si | a – b |  minc  a| a – c |, donde c 
P. Podemos escribir esta relación a  b: Un vecino cercano de a es b. Note que
la definición no es simétrica con respectos a las tareas que a a y b les toca,
sugiriendo que la relación no sea así misma simétrica. Y de hecho éste es
ciertamente el caso: Si a  b, no es necesaria que b  a; Ver a Figure 5.12
También nota que un punto puede tener varios vecinos igualmente cercanos
(ejemplo, punto d en la figura).

El vecino cercano pregunta

Dado un grupo fijo de puntos P, construir el diagrama de Voronoi en el tiempo


O (n log n). Ahora para un punto de consulta q, encontrando un vecino cercano
de q para encontrar en cuál región de Voronoi cae, pues los sitios de esas
regiones de Voronoi son precisamente sus vecinos cercanos. El problema de
hallar un punto dentro de una partición es llamada localización del punto. El
problema ha sido estudiado con exceso y será discutido en el Capítulo 7 (Sección
7.11). Veremos que en este ejemplo, el tiempo O (log n) es suficiente para cada
consulta.

Todos los Vecino Cercanos

Defina al Nearest Neigbor Graph ((NNG) Graficos de vecinos mas cercanos) para
asociar un nodo con cada punto de P y un arco entre ellos si un punto es un
vecino cercano del otro. Hemos definido esto para ser un gráfico orientado,

Juan Carlos Gutiérrez Barquero____________________________________________ 197


Bismarck Salvador Traña López__________________________________________UNI

aunque la relación no es simétrica, adecuadamente podría ser directa. Pero no


necesitaremos la versión directa aquí.

Una forma sucinta a captar la esencia del algoritmo del vecino cercano
eficiente es a través del siguiente lema.

Lema 5.5.1 NNG  D (P)

Dejo la prueba para el ejercicio 5.5.6 [2] y [3 ]

Un algoritmo de fuerza bruta para encontrar a los vecinos cercanos para cada
punto en un grupo requeriría O (n2), pero el lema de arriba nos deja registrar
solo los bordes O (n) de la triangulación de Delauny y por lo tanto logra O (n
log n).

Vecino natural: Otra forma de conseguir el valor que tendrÌa un punto


cualquier p serÌa el
de conocer todos aquellos vecinos naturales de p. Averiguar esta informaciÛn
no es m·s
que resolver el problema de los vecinos m·s cercanos a un punto utilizando el
diagrama
de Voronoi. Aplicaciones en este sentido son la identi_caciÛn de ·tomos
vecinos en el
espacio, molÈculas o partÌculas en estructuras cristalinas y amorfas, etc. Ahora
el punto
de estudio p puede estar in_uenciado por dicho conjunto de vecinos, los cuales
tambiÈn
puede llevar asociados distintos pesos de in_uencia

1.5.2 La triangulación Maximizando el Ángulo Mínimo.

Juan Carlos Gutiérrez Barquero____________________________________________ 198


Bismarck Salvador Traña López__________________________________________UNI

Analizar las propiedades estructurales de forma complicada es a menudo


realizado por una técnica llamada “análisis finito del elemento.” Esto es usado,
por ejemplo, por fabricantes del automóvil a modelar cuerpos del coche (Field
1986). El dominio a ser estudiado está subdividido en una malla de “elementos
finitos, ” y entonces las ecuaciones diferenciales relevantes modelando la
dinámica estructural sea solucionado por discretización sobre la partición. La
estabilidad de los procedimientos numéricos usados depende de la calidad las
buenas particiones y esto pasa por que la triangulación de Delaunay son
especialmente buenas particiones. Nosotros ahora discutiremos el sentido en el
cual la triangulación Delauny es buena.

Una triangulación de un grupo de puntos S es la generalización del objeto del


cual la triangulación de Delauny es un ejemplo particular. Un grupo de
segmentos cuyos puntos finales están en S, Que sólo interseca a cada quien en
puntos finales, y cuál divide en partes el buque convexo de S en triángulos. Para
los propósitos del análisis finito del elemento, las triangulaciones con triángulos
“ gordos ” son más convenientes. Una forma para hacer esto precisa más es
evitar triángulos con ángulos pequeños. Así es natural buscar una triangulación
que tiene el ángulo menor más grande, esto es, para maximizar el ángulo de
más pequeño sobre todas las triangulaciones. ¡Esto ocurre para hacer
precisamente la triangulación de Delauny! De hecho, una declaración algo más
fuerte puede ser hecha, cuál nosotros ahora describimos después de introducir
alguna notación.

Sea T una triangulación de un grupo de puntos S, y sea su secuencia de angulos(


 1,  2, …,  3t), una lista de los ángulos de los triángulos, clasificados del
más pequeño al más grande, con t el número de triángulos en T. El número t
es uno constante para cada S (Ejercicio 5.5.6[4 ] ). Podemos definir una relación
entre dos triangulaciones del mismo grupo del punto, T y T ', eso trata de
capturar “ la gordura ” de los triángulos. Diga que T  T’ (T es más gorda que
T’) si la secuencia de ángulo de T es lexicográficamente mayor que la secuencia
de ángulo de T ': Ya sea  1 >  ’1, o  1 =  ’1 y  2 >  ’2 o  1 =  ’1 y  2=  ’2 y
 3 >  ’3,, etcétera. Edelsbrunnerr (1987, p. 302) probó este teorema:

Theorem5.5.2..La triangulación de Delaunay T=D (P) es lo máximo con


respecto a la relación de la gordura - ángulo: T  T’ para cualquier otra
triangulación T ' de P.

En particular esto dice que la triangulación Delaunay maximiza el ángulo


menor.

1.5.3 El círculo Vacío más grande

Mencionamos en sección 5.1 el problema de encontrar el círculo vacío más


grande entre un conjunto S de sitios: El centro de tal círculo es una buena
localización para una tienda nueva. Otra aplicación es mencionada por Toussaint
(1983a): Localice un reactor nuclear tan lejos de una colección de ciudades-

Juan Carlos Gutiérrez Barquero____________________________________________ 199


Bismarck Salvador Traña López__________________________________________UNI

sitios como sea posible. Ahora examinamos problema del círculo vacío más
grande en detalle.

El problema hace sentir un poco menos alguna restricción sobre la localización


del centro del círculo, para hay siempre círculos arbitrariamente vacíos grandes
fuera de cualquier grupo de puntos finito. Así nosotros redactamos el problema
de esta forma:

El círculo Vacío más grande. Encuentre que un círculo vacío más grande cuyo
centro está en el buque convexo (cerrado) de un grupo de n sitios S, vacíos en
que no contiene sitios en su interior, y más grande que no hay otro semejante
con estrictamente radio mayor.

Sea a f (p) el radio del círculo más grande y vacío puesto en el centro en P del
punto. Entonces andamos buscando un máximo de esta función sobre toda p en
el buque de S, H=H(S). Pero hay un número aparentemente infinito de
candidatos apuntando a este máximo. Un tema común en la geometría
computacional es reducir un grupo infinito del candidato para una lista finita
pequeña, y entonces encontrar estos eficazmente. Seguimos esto en este
escenario en esta sección, comenzar por discutir informalmente que solamente
ciertos puntos p son candidatos verdaderos para un máximo de f.

Concéntrese Dentro del cierre.

Imagine inflar un círculo desde un punto p en H. El radio en el cual este circulo


golpea y por consiguiente incluye algún sitio de S = { S1, … Sn } es el valor de
f (P). asumamos temporalmente a esta subdivisión que la p es estrictamente
interior para H. Si el radio f (P), el círculo incluye justamente un sitio S1,
entonces debería ser claro que f (p) no puede ser un máximo de la función del
radio. Pues si p es movido para P ' a lo largo del rayo s1p (el rayo de s1 a través
de p) lejos de s1, entonces f (P ') es mayor, como se muestra en Figure 5.13
(los segundos pisos). Por eso la p no pudo haber sido un máximo local de f, pues
hay un punto P ' en cualquier barrio de p donde f es mayor. Nota que esa
suposición de que p es estrictamente interior para el buque garantiza que hay
una p ' como esa también en H.

Asumamos que en el radio f (P), El circulo incluye exactamente dos sitios S1 y


S2. Otra vez f (P) no puede estar en un máximo: Si la p es movida para P ' a lo
largo de la bisectriz de s1s2 (fuera de s1 s2), entonces f (P ') es otra vez mayor,
como se muestra en Figure 5.13 (baje círculos). Otra forma de ver esto por la
intersección de dos tales conos de sitios en el centro (Figure5 5.9) discutido en
la sección 4.4.4. la curva de intersección de dos conos represe la distancia de
los sitios para puntos en la bisectriz. Desde que la curva es una hipérbola
ascendente, ningún punto interior de la bisectriz es un máximo local: La distancia
aumenta de una dirección o otra.

Es única cuando el círculo incluye tres sitios que f (P) pudo estar en el máximo.
Si lo tres sitios “entrecruzados” el centro p, en el sentido que hizo girar más que

Juan Carlos Gutiérrez Barquero____________________________________________ 200


Bismarck Salvador Traña López__________________________________________UNI

lo que un semicírculo (como en Figure 5.3), entonces el movimiento de p en


cualquier dirección resulta en moverse p más cercano para algún sitio, y así
disminuir a f (P). Nosotros ahora hemos establecido este hecho:

Lema 5.5.3 Si el centro p de un círculo vacío más grande es estrictamente


interior para el buque de la sitio H (S), entonces la p debe ser coincidente con
un vértice de Voronoi.

Note que no es necesariamente cierto que cada vértice de Voronoi represente


un máximo local de f (p) (el Ejercicio 5.5.6[5 ] ).

Centro dentro del convexo

Ahora consideremos que el circulo de centro p está en el buque H =H(S). La


razón que nuestras anteriores discusiones no ejercen es esa moviéndose p para
p ' el movimiento fuera del buque, y nuestra especificación problemática
restringieron centros para el buque. Nosotros ahora discutimos aún más
informalmente que por encima de que un círculo máximo debe incluir dos sitios.

Juan Carlos Gutiérrez Barquero____________________________________________ 201


Bismarck Salvador Traña López__________________________________________UNI

Supongo que f (p) es un máximo con p en H y el círculo incluye justamente s1


de sitios. Primero, que no puede ser esa p está en un vértice de H, pues los
vértices de H es todos los sitios mismos, y esto implicaría a esa f (P) = 0. Así es
que p está en el interior de un borde de H. Entonces moviendo p en uno u otro
caso a lo largo de h debe aumentar su forma de distancia s1, cortado en rodajas
por un plano vertical (la Figura 5.9).

Si, sin embargo, el círculo centró en p contiene s1 de dos sitios y s2, entonces
es posible que la dirección a lo largo de la bisectriz de los sitios que aumenta
sus distancias sea la dirección que va fuera del buque. Así adecuadamente podría
ser que f (P) está en el máximo. Hemos mostrado este hecho:

Lema5.5.4 si el centro p de un circulo vació mas grande permanece en el


buque de sitios H (S), Entonces p debe permanecer sobre un borde Voronoi.

Juan Carlos Gutiérrez Barquero____________________________________________ 202


Bismarck Salvador Traña López__________________________________________UNI

El algoritmo

Nosotros ahora hemos establecido nuestro meta: Ha encontrado un grupo finito


de punto que son centros potenciales de círculos vacíos más grandes: Los
vértices de Voronoi y las intersecciones entre bordes de Voronoi y el buque de
sitios. Esto sugiere el algoritmo 5.1, debido a Toussaint (1983a).

Note que no cualquier vértice de Voronoi está necesariamente dentro del buque
(la Figura 5.14), que necesita comprobar que v  H dentro del algoritmo. Una
implementación ingenua de este algoritmo requeriría el tiempo cuadrático en n,
pero localizar un vértice Voronoi en H e intersecar un borde de Voronoi con e
puede estar consumado en el tiempo O (log n), y estas eficiencias conducen a
un algoritmo O (n log n) en conjunto.

Dejamos Detallamos para ejercitar a 5.5.6[6.

El algoritmo: círculo Vacío MÁS GRANDE


Computar el diagrama Voronoi V (s) de la S de sitios.
Computar el buque convexo H=H (S).
Para v del vértice Voronoi haga
Si la v está dentro de H: V  H entonces
El radio de cómputo si el grupo cerrado de gente giró alrededor de
v y actualización llega al límite.
Para cada e del borde Voronoi haga
El cómputo p =e   H, la intersección del con el límite del buque.
El radio de cómputo de círculo giró alrededor de actualización de la P
llegue al límite.
El regreso llegue al límite.

Juan Carlos Gutiérrez Barquero____________________________________________ 203


Bismarck Salvador Traña López__________________________________________UNI

Lo mínimo
que se extiende a lo largo un
árbol.

Lo mínimo que se extiende a lo


largo un árbol. (MST)(A minimum spanning tree) de un grupo de puntos es la
longitud del arbol que se extiende a lo largo de todos los puntos: es decir en
árbol más pequeño cuyos nodos son precisamente esos en el grupo. Cuando el
largo de un borde es medido por la longitud usual del segmento Euclideano
conectando sus puntos finales, el árbol es a menudo llamado el Euclideano
mínimo extendiéndose a lo largo de árbol, abreviado a EMST. Aquí sólo
considerarán las longitudes Euclideano y así también dejarán caer al modificador
redundante. Un ejemplo es mostrado en Figure 5.15. MST tienen muchas
aplicaciones. Pues el ejemplo es la topología de la red que minimiza la longitud
total del alambre que usualmente minimiza ambos costo y el tiempo que se
demora.

Algoritmo de Kruskal

Aquí consideraremos el problema de computar el MST de un grupo de puntos en


el plano. Veamos primero el problema más general de computar el MST para
una gráfica G. Aunque es de ninguna manera obvio, una estrategia ávida sin
discernimiento es encontrar al MST, basada en la intuición simple que un árbol
más pequeño debería estar cercano de los bordes más pequeños. Esto sugiere
que puede estar tal árbol construido arriba del incrementalmente añadiendo
borde más pequeño, todavía ningún explorado, lo cual también mantiene un
arbolado (alifaticidad). Este algoritmo es conocido como el algoritmo de Kruskal
y se publico en 1956.

Sea T el árbol incrementalmente construido, y sea la notación T + e quiere decir


que el árbol T une el borde e. El algoritmo Kruskal se muestra en Algoritmo 5.2.
No nos detendremos para probar este algoritmo,¿ esta bien?. pero sólo demanda
que su complejidad se domina por el primer paso ordenando. Esto requiere (E
log E) de tiempo, dónde la E es el número de bordes en la gráfica.

Juan Carlos Gutiérrez Barquero____________________________________________ 204


Bismarck Salvador Traña López__________________________________________UNI
El algoritmo: El ALGORITMO DE KRUSKAL
Ponga en cortocircuito bordes de G por el largo por
ahí: E1, e2
Inicializar T para que este vacío
Mientras la T no avanza a rastras haga
Si T + e1 es acíclica
Entonces T + T + ei
Ii + 1
El algoritmo 5.2 el algoritmo de Kruskal.

MST  D (p)

Para el MST de puntos en el plano, hay ( n ) bordes, así es que la complejidad


2

del paso de clasificación es O (n log n) si es cargada fuera en la gráfica


2

completa. Para llamar a este registro de los bordes de la triangulación de


Delaunay de proximidad en algún sentido, es razonable esperar que sólo los
bordes de Delaunay alguna vez necesiten ser usados para construir un MST. Y
afortunadamente esto es cierto, como se muestra en el siguiente teorema.

Teorema 5.5.5 Lo mínimo que se extiende a lo largo un árbol es un


subconjunto de triangulación Delaunay: MST D (p).

Prueba. Queremos mostrar que si ab  MST, entonces ab  D. Implica that ab


 MST y suponga lo contrario que ab  MST. Entonces tratamos de derivar una
contradicción enseñando lo que se supuso MST no es mínimo.

Recuerde que si ab  D, entonces hay un círculo vacío a través de a y b


(Propiedad V7 y Teorema 5.3.1) .en caso contrario ab  D, ningún círculo a
través de a y b puede estar vacíos. En particular, el círculo de diámetro ab debe
de tener un sitio dentro de este.

Así se supone que C está en este círculo, como se muestra en figura 5.16.
Entonces
| ac | < | ab |, y |bc|< |ab|; Estas desigualdades tienen aplicación aun si C está
en el círculo, puesto que la C es distinta de a y b. La extracción de ab
desconectará el árbol en dos árboles internos, con a en primera parte, Ta, y b
en el otro, Tb. Suponga sin la pérdida de generalidad que e esta en Ta. Elimine
ab y añada el borde bc para hacer un árbol nuevo,
T’ = Ta bc +Tb. Este árbol es mas corto, así el único único usado ab, no podría
serminimo. Hemos alcanzado una contradicción negando que ab está en D, así
es que debe ser ese ab D.

Juan Carlos Gutiérrez Barquero____________________________________________ 205


Bismarck Salvador Traña López__________________________________________UNI

Esto entonces produce una mejora en el primer paso de algoritmo de Kruskal:


Primero encuentre la triangulación de Delaunay en el tiempo O (n Long n), y
entonces clasifique sólo esos bordes O (n), en el tiempo O (n logn). Resulta lo
demás de algoritmo de Kruskal puede ser implementado para poner a funcionar
en O (n log n), para que la complejidad total por encontrar el MST para el grupo
de n untos en el plano es O (n log n).

5.5.5. El Problema del Vendedor de viajes.

Uno de los problemas más estudiados en la informática es el problema del


vendedor de viajes: Encuentre el más pequeño camino cerrado que visita cada
punto en un grupo dado. Tal camino se llamado El camino Vendedor de
viajes(TSP); imagine los puntos como ciudades que el vendedor debe visitar
en el orden arbitrario antes de devolver casa. Este problema tiene tremenda
importancia práctica, no sólo para esa aplicación si no porque muchas otras
problemáticas pueden reducirse a él. Se ha probado que desafortunadamente,
el problema es duro NP, un término técnico que ningún algoritmo polinómico
conoce para resolverlo (Garey y Johnson 1979); Ni parece probablemente al
momento de escribir la presente que a ser encontrada. Las combinaciones de
significado práctico y la inflexibilidad han conducido a una búsqueda para los
algoritmos de heurísticas efectivos y de aproximación. Uno de los algoritmos de
la aproximación más simples es basado en la triangulación de Delaunay, Vía lo
mínimo que Se extiende a lo largo el árbol.

La idea es más bien algo simple, pero no obstante hace un trabajo razonable.
Encuentra al MST para el grupo de puntos, y simplemente lo lleva cabo y lo allá
por la manera ilustrada en Figure 5.17. Debería estar claro que la excursión
construida por aquí tiene exactamente dos veces el largo del MST, desde que
cada borde del árbol se cruza una vez en cada dirección.

Juan Carlos Gutiérrez Barquero____________________________________________ 206


Bismarck Salvador Traña López__________________________________________UNI

Ahora obtenemos un límite dentrote que esta mal este doble-MST recorrido
podría ser. Sea M la longitud de Lo mínimo que se extiende a lo largo un
árbol y M2 la longitud de a doble-MST; claro M2 = 2M. Sea T la longitud de
El camino de un Vendedor de viajes r y T1 la longitud de un TSP con un
borde removido. Note que T1 se extiende a lo largo del árbol.

Las siguientes desigualidades son inmediatas:

T1 < T,
M  T1,
M < T,
M2 < 2T.

Esto entones logra un límite superior constante en la calidad del recorrido: Lo


doble-MST no está peor que dos veces la longitud de TSP.

Juan Carlos Gutiérrez Barquero____________________________________________ 207


Bismarck Salvador Traña López__________________________________________UNI

Este resultado puede ser mejorado con heurísticas diversas. Esbozaré sólo el
más simple tan heurístico, lo cual se basa en la determinación comprensible para
no volver a visitar un sitio dos veces. El recorrido del camino doble-MST de
entrada, con la modificación que si el próximo sitio ya se ha visitado hasta ahora
por el camino, salte el sitio y considera conectarse al próximo uno a lo largo del
doble-MST recorrido. Esto hace el efecto de tomar una ruta más directa para
recorrer MST . Si ponemos en un índice los sitios por el orden en que ellos se
visitan a lo largo del recorrido del MST, a algunos sitios que Si podría conectar
para Sj por un segmento de la línea recta o un atajo al recorrido, considerando
que el recorrido doble-MST sigue un camino encorvado Si, Si+1 , …, Sj-1,Sj..
Heurístico puede ser único acorta el camino. Un ejemplo es mostrado 5.18. Note
que el camino fue acortado.

Desafortunadamente este heuristico no garantiza un desempeño mejorado, pero


una variación leve Conocido como las gamas “Heurísticas Christofides”. Usa un
grupo de segmentos llamados un “El mínimo de Euclideano emparejando” como
un guía para atajos y puede garantizar un longitud del camino no más de (3/2)
T, eso es, no más de 50 % más largo que lo óptimo. Más heurísticos sofisticaron
generalmente encuentran un camino dentro de un poco porcentaje óptimo
(Bentley 1992), aunque este desempeño no está garantizado en su estado actual
para el algoritmo citado anteriormente. Un resiente descanso teórico es sin
dudarlo el esquema de aproximación de Polinomio-tiempo para el TSP, acerca
de lo mejor, puede ser esperanza para un problema completo a NP. Éste es un
método de llegar dentro (1 +  ) de optativo para cualquier  > 0, en un tiempo
O (np), dónde la p son proporcionales para 1/  . Vea a Arora (1996) y Mitchell
(1996).

5.5.6 Los ejercicios

1. El grado de NING. ¿Cuál es grado mínimo de salida de un nodo de un Grafico


de vecino mas cercano(NING) (la Sección 5.5.1) de n puntos en dos 2
dimensiones? ¿Cuál es el grado máximo de un nodo? Demuestran ejemplos que
justifiquen su respuesta, e intento probar que son máximos.

2. NNG y D [Fácil] . Encuentran un ejemplos que muestre ese NNG pueden ser
un subconjunto correcto de D (p).

3. NND D. Ponga a prueba a Lema5.5.1: Si la b es una vecina próxima de a.


Entonces ab D (p).

4. Los Números de triángulos en una estrangulación. Comprobar que el número


de triángulos t en cualquier triangulación de algún grupo de puntos reparados
S esat una constante toda triangulación de S tiene la misma t.

5. El vértice de Voronoi no un máximo local. Construya un grupo de puntos que


tiene un vértice de Voronoi estrictamente p dentro del buque, algo semejante

Juan Carlos Gutiérrez Barquero____________________________________________ 208


Bismarck Salvador Traña López__________________________________________UNI

que f (p) no es un máximo local, donde la f es función del radio definido en la


sección 5.5.3

6. El algoritmo del círculo vacío. El detalle (en el código pseudo) cómo llevar a
cabo el algoritmo del círculo vacío (Algoritmo 5.1) con el fin de que su
complejidad de tiempo es O (n log n).

7. El Gráfico del Barrio relativo (RNG) de juego de puntos P1,…, Pn es un gráfico


cuyos nodos corresponden a los puntos, y con Pi de dos nodos y Pj conectado
por un arco ellos son para que no acerca de cualquier otro punto, eso es, si

|Pi + Pj|  max { | Pi + Pm |,| Pi + Pm | }.


m i . j

(Vea Jaromezyk + y Toussaint (1992).) Esta ecuación determina una región “


prohibida ” dentro de la cual ningún punto Pm puede mentir si Pi y Pj son
adyacentes en el RNG, no a diferencia del Teorema 5.3.1 Esta región, llamada
Lune ( Pi, Pj), son la intersección de dos discos abiertos centradas en Pi y Pj,
ambos de radio | Pi – Pj |.

a. Diseñe un algoritmo “de fuerza bruta” para construir al RNG. No se


preocupe por eficiencia. ¿Qué es su complejidad de tiempo?

b. Ponga a prueba a ese RNG: Cada borde del RNG es también borde de la
triangulación de Delauny. (Compárese con Teorema 5.5.5).

c. Use (b) diseñar un algoritmo más rápido.

8. El tamaño de triangulación de Delaunay en tres dimensiones. Tenemos que


mostrar cual es el tamaño de la triangulación de Delaunay adentro de dos
dimensiones es lineal, O (n). Mostrar que esto no se mantiene en tres
dimensiones: El tamaño de D (p) puede ser cuadrático. Defina a D (p) en tres
dimensiones exactamente, análogamente y para la versión de Dos dimensiones
(bidimensional): Es el dual de V (p), el cual es el lugar de puntos geométricos
que no tienen un único vecino cercano.

Sea P un punto determinada consistente en dos partes:

a El n/2 puntos uniformemente distribuidos alrededor de un círculo en el


plano x y centrados en el origen, y
b El n/2 puntos uniformemente distribuidos en el origen del eje z
simétrico al origen..

Sostenga la opinión que el tamaño de D (p) es  (n2).

Juan Carlos Gutiérrez Barquero____________________________________________ 209


Bismarck Salvador Traña López__________________________________________UNI

9. El tamaño del gráfico del barrio relativo (RNG) en dimensiones del árbol.
Ejercicio[7] anteriormente establecido ese RNG D (P) en dos dimensiones, y esta
relación contiene dimensiones arbitrarias. Se ha demostrado el tamaño que
tiene el RNG en dimensiones del árbol es O (n3/2) (Jaromezyk y Toussaint
1992), así es que es más pequeña que la triangulación de Delaunay. Pero parece
que este salto superior es débil: Jaromezyk y Kowaluk (1991) suponen que el
tamaño es O (n). Confirmar esta conjetura es un problema.

10. MST  RNG Pruebe que cada borde de un MST es un borde del RNG.
(Compárese con Teorema 5.5.5)

11. Los puntos más remotos de Voronoi. Defina el diagrama de puntos más
remotos de Voronoi F (P) asociado cada punto del plano para el sitio que es su
“vecino más remoto”, el sitio que está más lejos (fuera). Los puntos con el vecino
mas cercano de una region de voronoi mas remota; Los puntos con dos vecinos
mas lejanos limitan con forma de bordes F (P). Vea Figura 5.19.

a. Que es F (P) para dos sitios?


b. ¿Cuál es F (P) para tres sitios?

c. Derive algunas propiedades estructurales de diagrama más remoto


Voronoi, parecido al Delaunay y Voronoi en Sección 5.3.1 y 5.3.2. Uso Figure
5.19 y 5 5.2.3. Uso 5.19 para ayudar a formar a hipótesis

Juan Carlos Gutiérrez Barquero____________________________________________ 210


Bismarck Salvador Traña López__________________________________________UNI

12. L Mínimo que se expande a lo largo de un Círculo. Muestre cómo el círculo


más remoto del radio – el diagrama del punto de Voronoi puede usarse para
computar lo más pequeño – que rodea un grupo dado del punto. Asuma que F
(P) está disponible.

Ejes Medios

El diagrama de Voronoi puede ser generalizado en varias direcciones, y


algunas de estas generalizaciones tienen importante significado práctico. En esta
sección tocamos una generalización, una de las más simples: aceptando un
grupo de sitios que son un grupo infinitos de puntos, en particular el límite
continúo de un polígono.

En la sección 5.20 definimos el diagrama de Voronoi como el grupo de


puntos cuyo sitio más cercano no es único. Estos puntos son equidistantemente
mejor cerrados en dos o más sitios. Define los ejes medios de un polígono P,
que están dentro de un grupo de puntos internos P que un punto bien cerrado
entre los puntos de  P. Una definición muy similar puede ser usada para una
colección de puntos arbitrarios, pero aquí examinaremos solo el caso donde los
puntos forman el límite de un polígono.

El eje medio de un rectángulo se muestra en la figura 5.20; cada punto


sobre el segmento horizontal dentro del rectángulo está equidistante de los
puntos verticales de arriba abajo y están por encima y por debajo de los lados
del rectángulo. Cada punto sobre un segmento diagonal esta equidistante de dos
lados adyacentes del rectángulo y los dos puntos finales del segmento horizontal
están equidistante de tres lados del rectángulo.

Juan Carlos Gutiérrez Barquero____________________________________________ 211


Bismarck Salvador Traña López__________________________________________UNI

Un ejemplo mas completo se muestra en la figura 5.21, ocho vértices en


un polígono convexo. Uno puede suponer a partir de este ejemplo que el eje
medio del polígono convexo P está un árbol cuyos permisos son los ejes de P.
Esto es un porcentaje verdadero y está realmente lleno de polígonos no
convexos. Todo punto de los ejes medios está en el centro de un circulo que toca
el límite de los puntos más lejanos y así es como los vértices de Voronoi están
en los centros de los círculos tocando tres sitios, los vértices de los ejes medios
están en el centro de los círculos tocando tres puntos distintos del limite como
se muestra en la figura 5.22.

A veces los ejes medios de P están definidos como el sitio central de los
círculos máximos: los círculos dentro de P estos no son los mismos encerrados
en algunos otros sitos dentro de P. El proceso de transformación dentro de los
ejes medios son en ocasiones llamados “Transformación de Bosques Quemados”
por si una imagen del polígono P tal como un césped seco que alumbra con un
fuego a los bordes de P, todo esto causará que queme todo dentro de el a una
velocidad uniforme y los ejes medios sean el grupo de puntos extintos donde el
fuego conozca al fuego de otra dirección. La conexión entre esta analogía y la
quema del bosque discutido en la sección 5.1 debería ser evidente.

Los ejes medios fueron introducidos por Blum en 1967 para el estudio en
forma biológica, el observo de manera semejante a un esqueleto (eje) las
amenazas bajo el medio (mediana) de alguna forma. Esta es menos aparente
para un polígono convexo que para un no convexo y formas planas que era el
principal interés de Blum, uno puede caracterizar la forma de cierta sección de
la estructura de estos ejes medios y esto va incluido un interés considerable
entre los investigadores en el reconocimiento de patrones y visiones de
computadoras, por ejemplo bookstein lo usa para caracterizar las diferencias
entre los huesos de la mandíbula normales y los deformados. Puede ser usada
para calcular una compensación interior de un polígono, todos de cuyas fronteras
son compensaciones interiores por una distancia fija. Expandidas o externas
compensaciones cuentan en la versión exterior del eje medio. Calcular
compensaciones es un importante problema en la manufacturación, donde la
tolerancia de ingeniería es llevada naturalmente a formas de compensación.

Juan Carlos Gutiérrez Barquero____________________________________________ 212


Bismarck Salvador Traña López__________________________________________UNI

El eje medio de un polígono de n vértices puede ser construido en 0 (n


log n) time; asintoticamente menor pero más prácticos de logaritmos están
disponibles. Para polígonos convexos, 0 (n) veces es suficiente.

Ejercicios

 Eje medio de un polígono no convexo. Mostrar por ejemplo que el eje


medio de un polígono no convexo puede contener segmentos curvados.
¿Qué puedes decir acerca de la forma funcional de estas curvas?

Juan Carlos Gutiérrez Barquero____________________________________________ 213


Bismarck Salvador Traña López__________________________________________UNI

 Eje medio y el diagrama de Voronoi o hay alguna relación entre el eje


medio de un polígono convexo P y el diagrama de Voronoi de los vectores
de P? Supone algunos aspectos de esta relación y pruébalos y construye
contra ejemplos.

Eje medio de un polígono. Describe lo que debe parecer el eje medio de un


polígono.

Estructura recta. Aichholzer, Alberts, Aurenhammer & Gärtner, introdujo


una estructura que es similar al eje medio, pero compuesta de segmentos rectos
incluso para polígonos no convexos. Mover cada borde de un paralelo al mismo
interior a una velocidad constante con bordes adyacentes encogiendo y
creciendo así que los vértices viajan a lo largo del ángulo bisector. Cuando un
borde se encoge hasta la longitud cero, sus bordes vecinos se hacen adyacentes.
Cuando un vértice reflejo choca hacia adentro de un borde, el poligono se agrieta
y el proceso de encogimiento de las piezas continúa.

Trabaja manualmente la estructura recta con unas formas letras


conectadas del alfabeto: T, E, X. Forma algunas suposiciones acerca de las
propiedades de la estructura recta.

Conexión con el Convexo Hulls

El 1986 Ellels Brunner & Seidel decubrió una bella conexión entre la
triangulación de Delaunay y el convex hulls en una de las más altas dimensiones:
primero explicaré entre el convex hull bidimensional y la triangulación
unidimensional de Delaunay (que son admitimademente triviales) y entonces
generaliza la triangulación bidimensional de Delaunay y el tridimensional convex
hull.

Entonces esta conexión nos dará un método fácil para calcular la


triangulación de Delaunay y del diagrama de Voronoi, vía tridimensional hulls
triangulación unidimensional de Delaunay. Empezaremos en una dimensión
donde la matemática es transparente sea
P= {x1,…,xn} un conjunto de puntos sobre el eje x. Claramente la triangulación
unidimensional de Delaunay es simple el camino conecta x1 a x2 a… xn , pero
veremos que esto es una proyección sobre el eje x de un conjunto de puntos
bidimensionales con coordenadas (xi, x2i). Estos puntos pueden ser vistos como
una proyección de la ascendencia de x en la parábola z= x2. Es trivialmente
verdadero que el connuex hull de estos puntos bidimensionales proyectado hacia
abajo a la triangulación unidimensional de Delaunay, tanto como el borde más
alto del hull es descartado, pero hay mucho más aquí que esta observación trivial
que puede ser aclarada con la consideración de las tangentes a la parábola.

Juan Carlos Gutiérrez Barquero____________________________________________ 214


Bismarck Salvador Traña López__________________________________________UNI

La pendiente de la parábola z= x2 en el punto x= a es 2a (porque dz/dx=


2x). De esta manera la ecuación de la tangente a la parábola en el punto (a, a2)
es

z-a2= 2a (x-a)
z= 2a x-a2

En preparación para estudiar el mismo proceso en tres dimensiones, ahora


investigaremos la intersección entre esta tangente y la parábola cuando la
tangente es trasladada verticalmente una distancia r2. Cuando la tangente es
aumentada por esta cantidad su ecuación se convierte en

z=2a x-a2 + r2

Entonces la tangente aumentada intersecta la parábola en + r lejos de a,


el punto original de tangencia. Nota que x= a + r puede ser pensado como la
ecuación de un circulo unidimensional de radio r centrado en a. Esto es ilustrado
en la figura 5.23 con a= 5 y r= 3 así que el disco es el segmento [2,8].

Triangulación Bidimensional de Delaunay

Repetimos el mismo análisis en dos dimensiones.

El paraboloide es z= x2 + y2, (Ver la figura 5.24) toma los sitios/puntos


dados en el plano y los proyecta hacia arriba hasta que toca el paraboloide, eso
es, trazar cada punto como sigue:

(xi, yi) | (xi, yi, x2i + y2i)

Toma el convex hull de este conjunto de puntos tridimensionales, ver la


figura 5.25. Ahora descarta las caras más altas de este hull. Todas esas caras

Juan Carlos Gutiérrez Barquero____________________________________________ 215


Bismarck Salvador Traña López__________________________________________UNI

que son exteriores punteando los puntos superiores normales, en el sentido de


times un producto positivo el resultado es una “condra” más baja. Proyectar esto
al plano xy. El reclamo es que esta es una triangulacion de Delaunay. Ver la
figura 5.26. Ahora establecemos esta fenomenal conexión formalmente. La
ecuación del plano tangente encima del punto (a, b) es

z=2a x + 2b y – (a2 + b2)


(Esta es una analogía directa a la ecuación z= 2a x – a2: dz/dx= 2x and
dz/dy= 2y)

Ahora cambiamos este plano superior por r2, exactamente como


cambiamos la línea tangente en la subsección previa

z= 2a x +2b y – (a2 + b2)+ r2


(x-a)2 + (y-b2)= r2

Juan Carlos Gutiérrez Barquero____________________________________________ 216


Bismarck Salvador Traña López__________________________________________UNI

El plano cambiado intercepta el paraboloide en una curva (una elipse) que


proyecta a un círculo. Esto es ilustrado en la figura 5.27 y 5.28.

Ahora revisemos desde un punto de vista para dirigirnos s la triangulación


Delaunay. Considere el plano  a través de tres puntos en el paraboloide  = (
pi, pj, pk) que forma una cara del buque de convexo en tres dimensiones. Este
plano parte de la paraboloide. Si traducimos verticalmente hacia abajo, entonces
en algún punto cesará de intersecar el paraboloide. Digamos que el último punto
que toca es (a, b, a2 b2). Entonces podemos observar como un cambio
ascendente de este plano contiguo; Llame a la cantidad de cambio r2. Ahora
debería estar claro que el análisis previo tiene aplicaciones.

Desde  esta en una cara inferior del buque, todo las otras puntos del
paraboloide están arriba. Desde que están arriba  , están más que el r2 arriba,
cuál es el r2 debajo. Por eso estos puntos se proyectan fuera del círculo de radio
r en el plano x y. Por eso el círculo determinado adentro de los planos x y está
vacío en todos los otros sitios. Por eso forma un triángulo Delaunay. Por eso
cada cara triangular inferior del buque convexo es propia de un triángulo
Delaunay. ¡Por eso la proyección del “fondo” del buque convexo se proyecta para
la triangulación Delaunay! Otra vez evacue a Figure 5.26.

Déjeme explicar esta compenetración importante otra vez de otra manera.


Comience con el plano t contiguo para el paraboloide por encima de p = (a, b).
Su punto de contacto se proyecta hacia abajo para p. Ahora muévase t arriba.
La proyección de su intersección con el paraboloide es un círculo en expansión
puesto en el centro en p. Cuando los t golpes una punto q en el paraboloide que
esta por encima de un sitio, las intercepciones del círculo en expansión en el
sitio en el plano que es la proyección de q. Así el círculo está vacío t hasta  los
límites exteriores, cuándo atraviese los tres sitios cuya proyección forma la cara
 del buque del triángulo soportada por  .

Juan Carlos Gutiérrez Barquero____________________________________________ 217


Bismarck Salvador Traña López__________________________________________UNI

Un corolario útil para el debate citado anteriormente es éste:

Corolario 5.7.1 Cuatro puntos (xi, yi), i = 1, 2, 3, 4, consisten en una mentira


del círculo si (xi, yi, xi2 yi2) en un plano.

La coplanaridad de estas puntos puede ser comprobada viendo si el volumen


del tetraedro que determinan (la Ecuación 1.15 y 4.6) es cero.

La implicación

Teorema 5.7.2 La triangulación de Delaunay de un grupo de puntos en dos


dimensiones es precisamente la proyección para el Plano x y del buque
convexo inferior de lo transformado apunta en tres dimensiones, transformado
por mapeo arriba para el paraboloide z = x2 y2.

Desde que el buque convexo en tres dimensiones puede ser computado en el


tiempo O (la Sección 4.2.2) (n log n), esto significa que la triangulación de
Delaunay puede ser computada en el mismo tiempo límite. Una vez que la
triangulación Delaunay está en tus manos, es relativamente fácil computar el
diagrama Voronoi (el Ejercicio 5.7.5[2 ] ). Esto conduce a otro algoritmo O (n
log n) para construir el diagrama de Voronoi.

Como se podría esperar, esta relación entre diagramas Voronoi y podría


abombar buques que yo una dimensión más alta mantengo en dimensiones
arbitrarias. Así ambos el diagrama Voronoi y la triangulación Delaunay en tres
dimensiones pueden forjarse de un buque convexo en cuatro dimensiones. De
hecho, puede ser que el más uso común de código del buque del 4D es para
construir mallas sólidas de Delaunay tetraédrico. En general, el diagrama de
Voronoi dual para un grupo de puntos de d-dimensional es la proyección del
buque “ inferior ” de puntos en d + 1 dimensión.

La implementación de la Triangulación de Delaunay: El código O (n4)

Teorema 5.7.2 el código conciso permite increíblemente computar la


triangulación de Delaunay, si uno es indiferente sobre la complejidad de tiempo
¡En particular, si O (n4) es aceptable (y raramente lo es), entonces la
triangulación de Delaunay puede ser computada con menos esas treinta líneas
de código de la C! Esto es presentado en código 5.1 en parte como la curiosidad,
pero también a enfatiza cómo la comprensión profunda de geometría puede
conducir al código limpio.

main()
{

Juan Carlos Gutiérrez Barquero____________________________________________ 218


Bismarck Salvador Traña López__________________________________________UNI

int x[MAX],y[MAX],z[MAX]; /*Intreda de puntos x,y,z*/


int n; /*Numero dep puntos introducidos*/
int i,j,k,m; /*induce de cuatro puntos*/
int flag; /*localizacion normal de (i,j,k)*/

/*Intrda de puntos y computar z=x^2+y^2*/

scanf("%d",&n);
for(i=0; i<n; i++)
{
scanf("%d %d",&x[i],&y[i]);
z[i]=x[i]*x[i]+y[i]*y[i];
}

/*por cada tres (i,j,k)*/


for(i=0;i<=n-2;i++)
for(j=0;j<=n-2;j++)
for(k=0;k<=n-2;k++)
if(j!=k)
/*computar la triangulacion normal*/
xn=(y[j]-y[i]*z[k]-z[i])-(y[k]-y[i]*z[j]-a[i]);
yn=(x[k]-x[i]*z[j]-z[u])-(x[j]-x[i]*z[k]-z[i]);
zn=(x[j]-x[i]*y[k]-y[i])-(x[k]-x[i]*y[j]-y[i]);

/*solo examina las caras del boton de la paraboloide zn <0*/


if(flag==(zn<0))
/*para otro punto m*/
for(m=0;m<n;m++)
{
/*revizar si m se acerca (i,j,k)*/
flag=flag&&
((x[m]-x[i]*xn+
(y[m]-y[i]*yn+
(z[m]-z[i]*zn<=0;
if(flag)
printf("%d\t%d\t%d\n",i,j,k);+
}

La estructura O (n4) del código es evidente yo los cuatro de para lazos anidados.
Para cada triple de puntos (i, j, k), el programa revisa y ve si todos los otros
puntos m están o por encima del plano conteniendo i, j, y k. Si es así, (i, j, k)
es salida como triángulo Delaunay. (La símil arridad para el algoritmo del buque
de dos dimensiones en Algoritmo 3.2 debería ser evidente).

Juan Carlos Gutiérrez Barquero____________________________________________ 219


Bismarck Salvador Traña López__________________________________________UNI

Triang. de Delaunay dual.

La prueba de por encima de plano es realizada chocheando la orientación


exterior normal para el triángulo, (xn, yn, zn), con un vector de punto para
apuntar m.

Aunque es interesante para ver tal código sucinto computar un propósito guste
la triangulación Delaunay, es impráctica para varias razones:

1. El código devuelve todos los triángulos dentro de una cara de


triangulación Delaunay cuyo límite consta de cuatro o más puntos del
cocircular. Así una mueca cuadrada resulta en salida cuatro de triángulo,
representando las dos triangulaciones del cuadrado. Obtener más salida útil
requeriría el postprocesamiento.

2. Hay ineficiencias obvias en el código (por ejemplo, la m-loop podría


quebrarse cuando un punto es descubierto debajo del plano del I, j, k). Estos
podrían repararse fácilmente al precio de alargarle un poco, pero…

3. La dependencia de tiempo del n4 es inaceptable. Pues dos prueban


carreras con n = 100 y n = 200 puntos, el tiempo de computar fue 12 y
239 segundos respectivamente, exhibiendo un más de 24 incremento de
16 pliegues para el doble número de puntos. Esto indica 1,000 tan n
tomaba varios días.

Triangulación de Delaunay 3D cáscara: O (el n2) el Código

Es una tarea fácil convertir el código cuadrático desarrollado en Capítulo 4


para construir el buque del 3D en el código cuadrático para construir la
triangulación Delaunay. Toda la complejidad está en el código del buque
(código de casi 1,000 líneas de ) acerca del razonamiento que entró en
Teorema 5.7.2. Las modificaciones adicionales son relativamente menores.

Juan Carlos Gutiérrez Barquero____________________________________________ 220


Bismarck Salvador Traña López__________________________________________UNI

Primer, que la rutina ReadVertices (código 4.10) debería leer en x, y y


computar z= x2 +y2. En segundo lugar, después del buque entero se forja,
un procedimiento LowerFaces se siente llamado al lazo sobre todas las fases
e identifique cuáles es en la carena (código 5.2). Como en código secreto
5.1, este está consumado computando las coordenadas de la z de un vector
normal para cada f de la cara. Esta computar, encarnado en Normz, es
justamente un cambio leve para Collinear (Código 4.12). Si Normz (f) < z,
entonces la cara está en la carena, y su proyección encima del Plano x y es
un triángulo de Delaunay.

Ilustramos con un ejemplo de n = 10 puntos cuyas coordenadas son


ostentadas en la tabla 5.1. Note que las coordenadas de la z son infundidas
arriba en la magnitud cuadrando. Esta parte pequeña que el ejemplo detiene
adecuadamente dentro del rango seguro de la computar de volumen discutió
comparación para el código del buque del 3D. La salida Postscript del código
es mostrada en Figure 5.29.

Como esperada, esta implementación O (n2) es mucho más rápida que el


código O (n4): En la misma n = 100 y n = 200 ejemplos que mientras código
más lento usó 12 y 239 segundos, el código más rápido usaron 0.2 y 1.3
segundos respectivamente. Además, la aceleración del randomizadas discutió
en Sección 4.5 vueltas así de en una O (n log n) esperado algoritmo de
tiempo.

5.7.5 Ejercicio 1.

El rango de dt2.c [programación] . Encuentro un grupo del punto cuyas


coordenadas son tan pequeñas como sea posible, y para las cuales el código
del dt2.c (código 5.2) devuelve un resultado incorrecto debido al
derramamiento del cálculo de volumen.

void LowerFaces(void)
{
tFace f=faces;
int Flower=0; /*numero total*/
do{
if(Normz(f)<0){
Flower++;
f->vertex[0]->Vnum,
f->vertex[1]->Vnum,
f->vertex[2]->Vnum);
}

f=f->next;
}while(f!=faces);
printf("%d lower faces identificada\n",Flower);
}

Juan Carlos Gutiérrez Barquero____________________________________________ 221


Bismarck Salvador Traña López__________________________________________UNI

int Normz (tFaces f)


{
tvertex a, b, c;
a=f->vertex[0];
b=f->vertex[1];
c=f->vertex[2];
return
(b->v[x]-a->v[x])*(c->v[x]-a->v[y])-
(b->v[y]-a->v[y])-(c->v[x]-a->v[x]);
}

2. D à V (P) (P) [programación] . Modifique el código del dt2.c para computar


el diagrama de Voronoi de la triangulación de Delaunay. (Vea ejercicio 5.5.6
[1]). Hay que repetidamente construir círculos a través de tres puntos dadas
a, b, c. Las coordenadas de la p central = (p0, p1) pueden ser computadas
como sigue:

A = b0 – a0,

B= b1 – a1,

C = c0 – a0,

D = c1 – a1,

E = A(a0 + b0 ) + B(a1 + b1 ),

F = C(a0 + b0 ) + D(a1 + b1 ),

G = 2(A(c1 – b1 ) – B(c0 – b0 )),

p0 = (DE – BF)/G,

p1 = (AF – CE)/G.

Tabla 5.1 Coordenadas de sitios de Delaunay, Incluyendo z= x2 +y2

I X Y X2+y2
0 31 -76 6737
1 -13 21 610
2 -63 -83 10858
3 -5 -66 4381
4 87 -94 16405
5 40 71 6641
6 23 -46 2645
7 64 -80 10496
8 0 -57 3249

Juan Carlos Gutiérrez Barquero____________________________________________ 222


Bismarck Salvador Traña López__________________________________________UNI

9 -14 2 200

5.8 Conexión a arreglos

Tenemos que mostrar que la triangulación de Delaunay puede ser derivada de


la transformación paraboloide e indican que es fácil de obtener el diagrama de
Voronoi si es posible obtener el diagrama de Voronoi directamente de la
transformación del paraboloide. Aunque todo esto se entiende que tienes que
esperar al siguiente capitulo (Sección 6.7) y mostraremos la conexión que se
realiza utilizando las ecuaciones mas relevante.

5.8.1 Diagrama de Voronoi unidimensional


Considerando dos tangentes examinadas en la sección 5.7.1 (ecuación 5.4)
siendo x=a y otro siendo x=b:
z=2ax-a2

z=2bx – b2

Donde se intersecan resolviendo las ecuaciones simultáneas la solución sería

2ax-a2 = 2bx – b2,


x(2a – 2b)=a2 – b2,
x=(a+b)(a-b)
2(a-b)

x= a+b
2
De esta manera la proyección de las intersecciones están adyacentes al
diagrama de Voronoi y al grupo de puntos

Juan Carlos Gutiérrez Barquero____________________________________________ 223


Bismarck Salvador Traña López__________________________________________UNI

5.8.2 Diagrama de Voronoi bidimensinal


Considere dos tangentes planas del paraboloide analizado en la Seccion (5.7.2)
(ecuación 5.8), donde estan a y b y otra donde esta c y b.

z = 2ax + 2by – a2 + b 2,
z = 2cx + 2dy – c2 + d2

¿donde están los intersectos? Resolviendo la ecuación simultanea


2ax + 2by – a2 + b 2 = 2cx + 2dy – c2 + d2
x(2a – 2c) + y(2b – 2d) = ( a2 - c 2) + (a2 + b 2 )

Esta ecuación es precisamente la bisectriz perpendicular del segmento de (a,b)


y (c,d) ver figura 5.31.

Si observamos las tangentes opacas planas de z=+infinito (con el paraboloide


transparente) debería ser visible en la primera intersección su primera
intersección es la bisectriz entre los lugares que generan las tangentes planas
la proyección de estas primeras tangentes es precisamente el diagrama de
voronoi!
También tenemos que recordar la situación al visualizar la proyección de
puntos dentro del paraboloide de z=-infinito vea la triangulación de Delaunay
la observación de las tangentes planas del paraboloide y estos puntos de
z=+infinito vea el diagrama de Voronoi.

Juan Carlos Gutiérrez Barquero____________________________________________ 224


Bismarck Salvador Traña López__________________________________________UNI

Bibliografía
[1] M. Berg, M. Kreveld, M. Overmars, O. Schwarzkopf. Computational
Geometry, Algorithms
and Applications. Springer, 1997.
[2] M. Kreveld, J. Nievergelt, T. Roos, P. Widmayer. Algorithmic Foundations of
Geographic
Information Systems. Springer, 1997.
[3] J. O'Rourke. Computational Geometry in C. Cambridge University Press,
1994.
[4] F. P. Preparata y M. I. Shamos. Computational geometry: an introduction.
Springer-Verlag,
New York, 1985.
73

Juan Carlos Gutiérrez Barquero____________________________________________ 225

You might also like