/* 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 #include #include #include #include /* 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 #include /*para definir NULL*/ #include #include #include #include /* 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*/