SPIRAL/S.C
2021-09-08 21:41:03 +02:00

361 lines
15 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Este programa forma parte del Curso de C
Copyright (C) 1991 Grupo Editorial Jackson
Todos los derechos reservados
*/
/* ESPIRAL: dibuja espirales poligonales aleatorias */
/* NOTA: tambin 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 idntico 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 prdida 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 trminos 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 trigonomtricas 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
dividindolo por 10.0 se subdivide el  ngulo de giro en dcimas 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 tcnica
trigonomtrica 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 idntico 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 prdida 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 trminos 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 trigonomtricas 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
dividindolo por 10.0 se subdivide el  ngulo de giro en dcimas 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 tcnica
trigonomtrica 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*/