361 lines
15 KiB
C
361 lines
15 KiB
C
/*
|
||
Este programa forma parte del Curso de C
|
||
Copyright (C) 1991 Grupo Editorial Jackson
|
||
Todos los derechos reservados
|
||
*/
|
||
|
||
|
||
/* ESPIRAL: dibuja espirales poligonales aleatorias */
|
||
|
||
|
||
/* NOTA: tambi‚n este fichero, como REBOTE.C, contiene dos programas
|
||
distintos, el primero escrito con las funciones gr ficas de Turbo C
|
||
2.0 y el segundo con las de Quick C 2.0. La opci¢n es autom tica
|
||
con una instrucci¢n #ifdef.
|
||
|
||
Este sistema comienza a resultar demasiado inc¢modo: en la lecci¢n
|
||
E5 se explica c¢mo crear una 'librer¡a' de funciones gr ficas
|
||
port til, parecida a la librer¡a Jconio, empleada para las funciones
|
||
de consola.
|
||
*/
|
||
|
||
|
||
/*========================= Versi¢n para Turbo C 2.0 ======================*/
|
||
|
||
#ifdef __TURBOC__
|
||
|
||
|
||
#include <stdlib.h>
|
||
#include <time.h>
|
||
#include <math.h>
|
||
#include <conio.h>
|
||
#include <graphics.h>
|
||
|
||
|
||
/* constantes y variables globales */
|
||
|
||
#define PAUSA 3 /*3 segundos de pausa entre espirales*/
|
||
|
||
int maxcol; /*l¡mite de colores*/
|
||
int centroX; /*coordenadas del centro de la pantalla*/
|
||
int centroY;
|
||
double cuadra; /*factor de cuadratura*/
|
||
double pi; /*n£mero pi*/
|
||
double grad; /*constante de conversi¢n grados/radianes*/
|
||
|
||
|
||
/* prototipos de funciones: */
|
||
|
||
void Espiral(void);
|
||
|
||
|
||
/********************************************************************
|
||
* main
|
||
********************************************************************/
|
||
|
||
main()
|
||
{
|
||
int driver = DETECT; /*elige autom ticamente*/
|
||
int modo;
|
||
int dimx,dimy; /*dimensiones pantalla (n£mero pixels)*/
|
||
time_t t;
|
||
|
||
initgraph(&driver,&modo,NULL); /*inicia gr ficos <1>*/
|
||
if (driver < 0) { /*comprueba si ok*/
|
||
cputs("\r\n- No se pueden utilizar los"
|
||
" gr ficos de este ordenador."); /*sale si no puede*/
|
||
exit(1);
|
||
}
|
||
|
||
maxcol = getmaxcolor(); /*color m s alto*/
|
||
dimx = getmaxx()+1; /*y l¡mites pantalla*/
|
||
dimy = getmaxy()+1;
|
||
centroX = dimx/2; /*calcula centro <2>*/
|
||
centroY = dimy/2;
|
||
cuadra = (dimx/(double)dimy)/(4.0/3.0); /*cuadratura <3>*/
|
||
pi = 4.0*atan(1.0); /*n£mero pi <4>*/
|
||
grad = pi/180.0; /*constante convers. <5>*/
|
||
|
||
srand((unsigned int)time(NULL)); /*inicia random*/
|
||
while (kbhit()) getch(); /*vac¡a buffer*/
|
||
|
||
while (! kbhit()) { /*sale con una tecla*/
|
||
Espiral(); /*dibuja una espiral*/
|
||
t = time(NULL)+PAUSA; /*espera o tecla <6>*/
|
||
while (! kbhit() && time(NULL) < t)
|
||
;
|
||
}
|
||
if (kbhit()) getch(); /*vac¡a posibles teclas*/
|
||
|
||
closegraph(); /*vuelve al modo texto*/
|
||
}
|
||
|
||
/* Notas sobre main:
|
||
<1> El paso al modo gr fico es id‚ntico al de REBOTE.
|
||
<2> Dado que el n£mero de pixels es par, no existe un centro exacto.
|
||
<3> El factor de cuadratura se utilizar para corregir la dimensi¢n
|
||
horizontal de forma que la figura aparezca con las mismas proporciones
|
||
tanto en vertical como en horizontal, independientemente de la forma
|
||
de los pixels en un determinado ordenador. Las operaciones con
|
||
(double) son necesarias para evitar que dimx/dimy sea una divisi¢n
|
||
entera con p‚rdida del resto.
|
||
<4> Aprovechando el hecho de que 45ø es ã/4 y que la tangente de 45ø es
|
||
1.0, se puede obtener el valor de pi sin tener que escribirlo.
|
||
<5> Es m s c¢modo disponer de una constante para convertir entre grados
|
||
sexagesimales (una vuelta = 360ø) y radianes (una vuelta = 2ã).
|
||
<6> El tiempo de espera establecido (PAUSA segundos) se interrumpe si el
|
||
usuario pulsa una tecla para terminar el programa. Si se desea una
|
||
mayor precisi¢n, se deber¡a utilizar clock en lugar de time.
|
||
*/
|
||
|
||
|
||
/********************************************************************
|
||
* Espiral: dibuja una espiral poligonal aleatoria.
|
||
********************************************************************/
|
||
|
||
void Espiral(void) /*<1>*/
|
||
{
|
||
double r; /*radio*/
|
||
double ia; /*incremento ngulo*/
|
||
double ir; /*incremento radio*/
|
||
double rlim; /*l¡mite radio*/
|
||
double s,c; /*seno y coseno actuales <2>*/
|
||
double ca; /*coseno anterior*/
|
||
double isin,icos; /*incremento seno y coseno*/
|
||
int x,y;
|
||
|
||
rlim = centroY*2.5; /*fija l¡mite*/
|
||
ia = (double)((rand()%3600)/10.0); /*extrae incrementos <3>*/
|
||
ir = 1.0+(double)((rand()%200)/100.0);
|
||
isin = sin(ia*grad);
|
||
icos = cos(ia*grad);
|
||
r = 0.0; /*parte del centro*/
|
||
s = sin(0.0); /*con ngulo 0 grados*/
|
||
c = cos(0.0);
|
||
|
||
setcolor(1+rand()%maxcol); /*y color, no negro <4>*/
|
||
cleardevice(); /*borra pantalla*/
|
||
moveto(centroX,centroY); /*va al centro*/
|
||
while (r < rlim) {
|
||
ca = c; /*copia coseno*/
|
||
c = c*icos-s*isin; /*nuevo coseno <5>*/
|
||
s = s*icos+ca*isin; /*nuevo seno*/
|
||
x = centroX+(int)(floor(r*c*cuadra+0.5)); /*nuevas coordenadas <6>*/
|
||
y = centroY+(int)(floor(r*s+0.5));
|
||
lineto(x,y); /*traza l¡nea*/
|
||
r += ir; /*incrementa radio*/
|
||
}
|
||
}
|
||
|
||
/* Notas sobre Espiral:
|
||
<1> Una espiral poligonal se obtiene girando a velocidad constante
|
||
alrededor del centro y aumentando progresivamente el radio. A
|
||
intervalos regulares (en t‚rminos de ngulo) se fija un punto y
|
||
se une con un segmento rectil¡neo al punto anterior.
|
||
<2> La funci¢n Espiral usa un truco para no tener que llamar a dos
|
||
funciones trigonom‚tricas relativamente lentas (seno y coseno) en
|
||
cada vuelta. Es posible calcular una sola vez, al principio, las dos
|
||
variables isin y icos, y utilizarlas con simples multiplicaciones
|
||
para calcular la posici¢n sucesiva a lo largo de una circunferencia.
|
||
<3> La rotaci¢n se obtiene aumentando en cada vuelta el ngulo actual un
|
||
determinado valor (en realidad utilizamos el truco descrito en la nota
|
||
2). Ejecutando rand()%3600 se obtiene un valor entre 0 y 3599, y
|
||
dividi‚ndolo por 10.0 se subdivide el ngulo de giro en d‚cimas de
|
||
grado: el valor de ia resulta entonces comprendido entre 0.0 y 359.9
|
||
grados. El valor de ir (incremento radio) estar comprendido, sin
|
||
embargo, entre 1.0 y 2.99.
|
||
<4> Extrae un color entre los disponibles, excluido el cero. En el caso
|
||
de un monitor monocromo, extrae siempre el blanco.
|
||
<5> Este es el atajo utilizado para evitar el c lculo en cada vuelta del
|
||
seno y el coseno del ngulo actual. No profundizamos en la t‚cnica
|
||
trigonom‚trica empleada.
|
||
<6> La coordenada horizontal se multiplica por cuadra (el factor de
|
||
cuadratura) para asegurar unas proporciones 'cuadradas'. Utilizamos
|
||
el sistema habitual para aproximar un double al entero m s cercano:
|
||
a¤adir 0.5 y aproximar con floor al entero inferior. La utilizaci¢n
|
||
de (int) no es estrictamente necesaria, pero muestra sin ninguna
|
||
duda que se ejecuta un truncamiento de la parte decimal pasando de
|
||
un double a un int (la parte decimal ya era cero, de todas formas,
|
||
por el efecto de floor).
|
||
*/
|
||
|
||
|
||
|
||
/*========================= Versi¢n para Quick C 2.0 ======================*/
|
||
|
||
#else
|
||
|
||
|
||
#include <stdlib.h>
|
||
#include <stddef.h> /*para definir NULL*/
|
||
#include <time.h>
|
||
#include <math.h>
|
||
#include <conio.h>
|
||
#include <graph.h>
|
||
|
||
|
||
/* constantes y variables globales */
|
||
|
||
#define PAUSA 3 /*3 segundos de pausa entre espirales*/
|
||
|
||
int maxcol; /*l¡mite de colores*/
|
||
int centroX; /*coordenadas del centro de la pantalla*/
|
||
int centroY;
|
||
double cuadra; /*factor de cuadratura*/
|
||
double pi; /*n£mero pi*/
|
||
double grad; /*constante de conversi¢n grados/radianes*/
|
||
|
||
|
||
/* prototipos de funciones: */
|
||
|
||
void Espiral(void);
|
||
|
||
|
||
/********************************************************************
|
||
* main
|
||
********************************************************************/
|
||
|
||
main()
|
||
{
|
||
int modos[] = { /*lista de los modos en orden de preferencia*/
|
||
_VRES16COLOR, /*VGA 640 x 480*/
|
||
_ERESCOLOR, /*EGA color 640 x 350*/
|
||
_ERESNOCOLOR, /*EGA monocroma 640 x 350*/
|
||
_ORESCOLOR, /*Olivetti 640 x 400*/
|
||
_HERCMONO, /*Hercules 720 x 348*/
|
||
_HRESBW, /*CGA 640 x 400*/
|
||
_DEFAULTMODE /*indica fin de lista*/
|
||
};
|
||
struct videoconfig v; /*datos del modo de v¡deo actual*/
|
||
int dimx,dimy; /*dimensiones de la pantalla (n£mero pixels)*/
|
||
time_t t;
|
||
int i;
|
||
|
||
i = 0; /*inicia gr ficos <1>*/
|
||
while (modos[i] != _DEFAULTMODE
|
||
&& _setvideomode(modos[i]) == 0) {
|
||
i++;
|
||
}
|
||
|
||
if (modos[i] == _DEFAULTMODE) { /*comprueba si ok*/
|
||
cputs("\r\n- No se pueden utilizar los"
|
||
" gr ficos de este ordenador."); /*sale si no puede*/
|
||
exit(1);
|
||
}
|
||
|
||
_getvideoconfig(&v); /*lee datos modo v¡deo:*/
|
||
maxcol = v.numcolors; /*color m ximo*/
|
||
dimx = v.numxpixels; /*l¡mites pantalla*/
|
||
dimy = v.numypixels;
|
||
centroX = dimx/2; /*calcula centro <2>*/
|
||
centroY = dimy/2;
|
||
cuadra = (dimx/(double)dimy)/(4.0/3.0); /*cuadratura <3>*/
|
||
pi = 4.0*atan(1.0); /*n£mero pi <4>*/
|
||
grad = pi/180.0; /*constante convers. <5>*/
|
||
|
||
srand((unsigned int)time(NULL)); /*inicia random*/
|
||
while (kbhit()) getch(); /*vac¡a buffer*/
|
||
|
||
while (! kbhit()) { /*sale con una tecla*/
|
||
Espiral(); /*dibuja una espiral*/
|
||
t = time(NULL)+PAUSA; /*espera o tecla <6>*/
|
||
while (! kbhit() && time(NULL) < t)
|
||
;
|
||
}
|
||
if (kbhit()) getch(); /*vac¡a posibles teclas*/
|
||
|
||
_setvideomode(_DEFAULTMODE); /*vuelve al modo texto*/
|
||
}
|
||
|
||
/* Notas sobre main:
|
||
<1> El paso al modo gr fico es id‚ntico al de REBOTE.
|
||
<2> Dado que el n£mero de pixels es par, no existe un centro exacto.
|
||
<3> El factor de cuadratura se utilizar para corregir la dimensi¢n
|
||
horizontal de forma que la figura aparezca con las mismas proporciones
|
||
tanto en vertical como en horizontal, independientemente de la forma
|
||
de los pixels en un determinado ordenador. Las operaciones con
|
||
(double) son necesarias para evitar que dimx/dimy sea una divisi¢n
|
||
entera con p‚rdida del resto.
|
||
<4> Aprovechando el hecho de que 45ø es ã/4 y que la tangente de 45ø es
|
||
1.0, se puede obtener el valor de pi sin tener que escribirlo.
|
||
<5> Es m s c¢modo disponer de una constante para convertir entre grados
|
||
sexagesimales (una vuelta = 360ø) y radianes (una vuelta = 2ã).
|
||
<6> El tiempo de espera establecido (PAUSA segundos) se interrumpe si el
|
||
usuario pulsa una tecla para terminar el programa. Si se desea una
|
||
mayor precisi¢n, se deber¡a utilizar clock en lugar de time.
|
||
*/
|
||
|
||
|
||
/********************************************************************
|
||
* Espiral: dibuja una espiral poligonal aleatoria.
|
||
********************************************************************/
|
||
|
||
void Espiral(void) /*<1>*/
|
||
{
|
||
double r; /*radio*/
|
||
double ia; /*incremento ngulo*/
|
||
double ir; /*incremento radio*/
|
||
double rlim; /*l¡mite radio*/
|
||
double s,c; /*seno y coseno actuales <2>*/
|
||
double ca; /*coseno anterior*/
|
||
double isin,icos; /*incremento seno y coseno*/
|
||
int x,y;
|
||
|
||
rlim = centroY*2.5; /*fija l¡mite*/
|
||
ia = (double)((rand()%3600)/10.0); /*extrae incrementos <3>*/
|
||
ir = 1.0+(double)((rand()%200)/100.0);
|
||
isin = sin(ia*grad);
|
||
icos = cos(ia*grad);
|
||
r = 0.0; /*parte del centro*/
|
||
s = sin(0.0); /*con ngulo 0 grados*/
|
||
c = cos(0.0);
|
||
|
||
_setcolor(1+rand()%maxcol); /*y color, no negro <4>*/
|
||
_clearscreen(_GCLEARSCREEN); /*borra pantalla*/
|
||
_moveto(centroX,centroY); /*va al centro*/
|
||
while (r < rlim) {
|
||
ca = c; /*copia coseno*/
|
||
c = c*icos-s*isin; /*nuevo coseno <5>*/
|
||
s = s*icos+ca*isin; /*nuevo seno*/
|
||
x = centroX+(int)(floor(r*c*cuadra+0.5)); /*nuevas coordenadas <6>*/
|
||
y = centroY+(int)(floor(r*s+0.5));
|
||
lineto(x,y); /*traza l¡nea*/
|
||
r += ir; /*incrementa radio*/
|
||
}
|
||
}
|
||
|
||
/* Notas sobre Espiral:
|
||
<1> Una espiral poligonal se obtiene girando a velocidad constante
|
||
alrededor del centro y aumentando progresivamente el radio. A
|
||
intervalos regulares (en t‚rminos de ngulo) se fija un punto y
|
||
se une con un segmento rectil¡neo al punto anterior.
|
||
<2> La funci¢n Espiral usa un truco para no tener que llamar a dos
|
||
funciones trigonom‚tricas relativamente lentas (seno y coseno) en
|
||
cada vuelta. Es posible calcular una sola vez, al principio, las dos
|
||
variables isin y icos, y utilizarlas con simples multiplicaciones
|
||
para calcular la posici¢n sucesiva a lo largo de una circunferencia.
|
||
<3> La rotaci¢n se obtiene aumentando en cada vuelta el ngulo actual un
|
||
determinado valor (utilizamos el truco descrito en la nota 2).
|
||
Ejecutando rand()%3600 se obtiene un valor entre 0 y 3599, y
|
||
dividi‚ndolo por 10.0 se subdivide el ngulo de giro en d‚cimas de
|
||
grado: el valor de ia resulta entonces comprendido entre 0.0 y 359.9
|
||
grados. El valor de ir (incremento radio) estar comprendido, sin
|
||
embargo, entre 1.0 y 2.99.
|
||
<4> Extrae un color entre los disponibles, excluido el cero. En el caso
|
||
de un monitor monocromo, extrae siempre el blanco.
|
||
<5> Este es el atajo utilizado para evitar calcular en cada vuelta el
|
||
seno y el coseno del ngulo actual. No profundizamos en la t‚cnica
|
||
trigonom‚trica empleada.
|
||
<6> La coordenada horizontal se multiplica por cuadra (el factor de
|
||
cuadratura) para asegurar unas proporciones 'cuadradas'. Utilizamos
|
||
el sistema habitual para aproximar un double al entero m s cercano:
|
||
a¤adir 0.5 y aproximar con floor al entero inferior. La utilizaci¢n
|
||
de (int) no es estrictamente necesaria, pero muestra sin ninguna
|
||
duda que se ejecuta un truncamiento de la parte decimal pasando de
|
||
un double a un int (la parte decimal ya era cero, de todas formas,
|
||
por el efecto de floor).
|
||
*/
|
||
|
||
|
||
#endif /*fin opci¢n Turbo C/Quick C*/
|
||
|