YouTube-simulations/billiard_phase_space.c

1158 lines
37 KiB
C
Raw Normal View History

2023-09-13 21:56:27 +02:00
/*********************************************************************************/
/* */
/* Animation of particles in billiard */
/* */
/* N. Berglund, august 2022 */
/* */
/* Feel free to reuse, but if doing so it would be nice to drop a */
/* line to nils.berglund@univ-orleans.fr - Thanks! */
/* */
/* compile with */
/* gcc -o billiard_phase_space billiard_phase_space.c */
/* -O3 -L/usr/X11R6/lib -ltiff -lm -lGL -lGLU -lX11 -lXmu -lglut */
/* */
/* OMP acceleration may be more effective after executing */
/* export OMP_NUM_THREADS=2 in the shell before running the program */
/* */
/* To make a video, set MOVIE to 1 and create subfolder tif_part */
/* */
/* create movie using */
/* ffmpeg -i part.%05d.tif -vcodec libx264 part.mp4 */
/* */
/*********************************************************************************/
#include <math.h>
#include <string.h>
#include <GL/glut.h>
#include <GL/glu.h>
#include <unistd.h>
#include <sys/types.h>
#include <tiffio.h> /* Sam Leffler's libtiff library. */
#include <time.h>
2023-10-29 15:45:58 +01:00
#define MOVIE 0 /* set to 1 to generate movie */
2023-09-13 21:56:27 +02:00
#define SAVE_MEMORY 1 /* set to 1 to save memory when writing tiff images */
#define WINWIDTH 1280 /* window width */
#define WINHEIGHT 720 /* window height */
#define XMIN -2.0
#define XMAX 2.0 /* x interval */
#define YMIN -1.125
#define YMAX 1.125 /* y interval for 9/16 aspect ratio */
#define XPHASEMAX 0.0 /* max x coordinate of phase portrait */
#define PIXELIZE 1 /* set to 1 to pixelize phase portrait (beta) */
#define NGRID 200 /* size of grid to draw orbits with positive Lyapunov exponent */
#define SYMMETRIZE_S 0 /* set to 1 to symmetrize orbits wrt s */
#define SCALING_FACTOR 1.0 /* scaling factor of drawing, needed for flower billiards, otherwise set to 1.0 */
/* Choice of the billiard table, see global_particles.c */
2023-10-29 15:45:58 +01:00
#define B_DOMAIN 1 /* choice of domain shape */
2023-09-13 21:56:27 +02:00
#define CIRCLE_PATTERN 6 /* pattern of circles */
#define POLYLINE_PATTERN 4 /* pattern of polyline */
#define ABSORBING_CIRCLES 1 /* set to 1 for circular scatterers to be absorbing */
#define NMAXCIRCLES 50000 /* total number of circles (must be at least NCX*NCY for square grid) */
#define NMAXPOLY 50000 /* total number of sides of polygonal line */
#define NCX 9 /* number of circles in x direction */
#define NCY 20 /* number of circles in y direction */
#define NPOISSON 500 /* number of points for Poisson C_RAND_POISSON arrangement */
#define NGOLDENSPIRAL 2000 /* max number of points for C_GOLDEN_SPIRAL arrandement */
#define SDEPTH 2 /* Sierpinski gastket depth */
2023-10-29 15:45:58 +01:00
#define LAMBDAMIN 0.0 /* parameter controlling shape of domain */
#define LAMBDA 1.5 /* parameter controlling shape of domain */
2023-09-13 21:56:27 +02:00
#define MU 1.0 /* second parameter controlling shape of billiard */
#define FOCI 1 /* set to 1 to draw focal points of ellipse */
#define NPOLY 6 /* number of sides of polygon */
// #define NPOLY 3 /* number of sides of polygon */
#define APOLY -1.0 /* angle by which to turn polygon, in units of Pi/2 */
#define DRAW_BILLIARD 1 /* set to 1 to draw billiard */
#define DRAW_CONSTRUCTION_LINES 1 /* set to 1 to draw additional construction lines for billiard */
#define PERIODIC_BC 0 /* set to 1 to enforce periodic boundary conditions when drawing particles */
#define PENROSE_RATIO 1.0 /* parameter controlling the shape of small ellipses in Penrose room */
#define RESAMPLE 0 /* set to 1 if particles should be added when dispersion too large */
#define DEBUG 0 /* draw trajectories, for debugging purposes */
/* Simulation parameters */
#define NPART 1 /* number of particles */
#define NPARTMAX 100000 /* maximal number of particles after resampling */
#define TRAJ_LENGTH 10000 /* length of trajectory */
#define LMAX 0.01 /* minimal segment length triggering resampling */
#define DMIN 0.02 /* minimal distance to boundary for triggering resampling */
#define CYCLE 1 /* set to 1 for closed curve (start in all directions) */
#define SHOWTRAILS 0 /* set to 1 to keep trails of the particles */
#define SHOWZOOM 0 /* set to 1 to show zoom on specific area */
#define TEST_ACTIVE 1 /* set to 1 to test whether particle is in billiard */
#define PRINT_TRAJECTORY_LENGTH 0 /* set to 1 to print length of trajectory 0 */
#define PRINT_TRAJECTORY_PERIOD 0 /* set to 1 to print period of trajectory 0 */
#define DRAW_LENGTHS_PLOT 0 /* set to 1 to plot trajectory lengths */
2023-10-29 15:45:58 +01:00
#define LENGTHS_LOG_SCALE 0 /* set to 1 to use log scale for plot of lengths */
2023-09-13 21:56:27 +02:00
#define MAX_ANGLE 90.0 /* range of angles of trajectory */
#define NSTEPS 4000 /* number of frames of movie */
// #define NSTEPS 500 /* number of frames of movie */
#define TIME 2500 /* time between movie frames, for fluidity of real-time simulation */
#define DPHI 0.00001 /* integration step */
#define NVID 150 /* number of iterations between images displayed on screen */
#define SYMMETRIC_PARAMETER 0 /* set to 1 if parameters depend symmetrically on time */
/* Decreasing TIME accelerates the animation and the movie */
/* For constant speed of movie, TIME*DPHI should be kept constant */
/* However, increasing DPHI too much deterioriates quality of simulation */
/* NVID tells how often a picture is drawn in the animation, increase it for faster anim */
/* For a good quality movie, take for instance TIME = 400, DPHI = 0.00005, NVID = 100 */
/* Colors and other graphical parameters */
#define COLOR_PALETTE 11 /* Color palette, see list in global_pdes.c */
#define NCOLORS 64 /* number of colors */
#define CFACTOR 3 /* color step */
#define COLORSHIFT 0 /* hue of initial color */
#define LYAP_PLOT_COLOR 100.0 /* color hue of Lyapunov exponent plot */
#define RAINBOW_COLOR 0 /* set to 1 to use different colors for all particles */
#define FLOWER_COLOR 0 /* set to 1 to adapt initial colors to flower billiard (tracks vs core) */
#define NSEG 100 /* number of segments of boundary */
#define LENGTH 0.03 /* length of velocity vectors */
#define BILLIARD_WIDTH 2 /* width of billiard */
#define PARTICLE_WIDTH 2 /* width of particles */
#define FRONT_WIDTH 3 /* width of wave front */
#define BLACK 1 /* set to 1 for black background */
#define COLOR_OUTSIDE 0 /* set to 1 for colored outside */
#define OUTER_COLOR 270.0 /* color outside billiard */
#define PAINT_INT 0 /* set to 1 to paint interior in other color (for polygon/Reuleaux) */
#define PAINT_EXT 1 /* set to 1 to paint exterior */
#define PRINT_LAMBDA 1 /* set to 1 to print value of lambda */
#define PRINT_MU 0 /* set to 1 to print value of mu */
#define PRINT_PENROSE_RATIO 0 /* set to 1 to print value of the Penrose billiard ratio */
#define PRINT_LYAPUNOV 1 /* set to 1 to print mean Lyapunov exponent */
#define PLOT_LYAPUNOV 1 /* set to 1 to add plot of Lyapunov exponents */
#define LOGSCALEX_LYAP 0 /* set to 1 to use log scale on parameter axis of Lyapunov exponents */
#define LYAP_MAX 1.0 /* maximal Lyapunov exponent */
2023-10-29 15:45:58 +01:00
#define ADAPT_TO_SYMMETRY 0 /* set to 1 to show only one symmetric part of phase space */
2023-09-13 21:56:27 +02:00
#define SYMMETRY_FACTOR 3 /* proportion of phase space to be shown */
#define PAUSE 1000 /* number of frames after which to pause */
#define PSLEEP 1 /* sleep time during pause */
#define SLEEP1 1 /* initial sleeping time */
#define SLEEP2 1 /* final sleeping time */
#define END_FRAMES 100 /* number of frames at end of movie */
#include "global_particles.c"
#include "sub_part_billiard_phasespace.c"
/*********************/
/* animation part */
/*********************/
double print_parameters()
/* print billiard parameters */
{
double x, xtext;
char message[50];
if (PRINT_LAMBDA) switch(B_DOMAIN) {
case (D_ELLIPSE):
{
x = sqrt(1.0 - 1.0/(lambda*lambda));
sprintf(message, "Eccentricity %.3f", x);
printf("Eccentricity %.3f\n", x);
xtext = 0.75;
break;
}
case (D_STADIUM):
{
x = lambda;
sprintf(message, "Linear part %.3f", x);
printf("Linear part %.3f\n", x);
xtext = 0.75;
break;
}
case (D_REULEAUX):
{
x = -lambda;
sprintf(message, "Radius %.3f", x);
printf("Radius %.3f\n", x);
xtext = 0.75;
break;
}
case (D_ALT_REU):
{
x = lambda;
sprintf(message, "Radius %.3f", x);
printf("Radius %.3f\n", x);
xtext = 0.75;
break;
}
case (D_PARABOLAS):
{
x = lambda;
sprintf(message, "Focal distance %.3f", x);
printf("Focal distance %.3f\n", x);
xtext = 0.65;
break;
}
case (D_PENROSE):
{
x = lambda;
sprintf(message, "Aspect ratio %.3f", x);
printf("Aspect ratio %.3f\n", x);
xtext = 0.65;
break;
}
case (D_FLOWER):
{
x = lambda;
sprintf(message, "Parameter %.3f", x);
printf("Aspect ratio %.3f\n", x);
xtext = 0.65;
break;
}
default: sprintf(message, " ");
}
if (PRINT_MU) switch(B_DOMAIN) {
case (D_ANNULUS):
{
x = mu;
sprintf(message, "Distance to center %.3f", x);
printf("Distance to center %.3f\n", x);
xtext = 0.65;
break;
}
// case (D_PARABOLAS):
// {
// x = mu;
// sprintf(message, "Radius %.3f", x);
// printf("Radius %.3f\n", x);
// xtext = 0.65;
// break;
// }
default: sprintf(message, " ");
}
if (PRINT_PENROSE_RATIO) switch(B_DOMAIN) {
case (D_PENROSE):
{
x = penrose_ratio;
sprintf(message, "Aspect ratio %.3f", x);
printf("Aspect ratio %.3f\n", x);
xtext = 0.65;
break;
}
default: sprintf(message, " ");
}
glColor3f(1.0, 1.0, 1.0);
glLineWidth(1);
write_text(xtext, -0.25, message);
}
double print_lyap_exponent(double lyap)
/* print Lyapunov exponent */
{
char message[50];
sprintf(message, "Average Lyapunov exponent %.3f", lyap);
glColor3f(1.0, 1.0, 1.0);
glLineWidth(1);
write_text(0.45, -0.35, message);
}
double s_range(double lambda, double mu)
/* return range of s values (boundary parametrization of billiard) */
{
double psi0, omega, omega2, beta2, aa, bb, cc, ymax, width, l1, l2, co, so, axis1, axis2, phimax;
switch(B_DOMAIN) {
case (D_ELLIPSE): return(DPI);
case (D_STADIUM):
{
if (lambda > 0.0) return (DPI + 2.0*lambda);
else
{
psi0 = asin(-lambda/2);
return(DPI-4.0*psi0);
}
}
case (D_ANNULUS): return(2.0*DPI);
case (D_REULEAUX):
{
omega2 = PI/((double)NPOLY);
beta2 = asin(sin(omega2)/vabs(lambda));
return(beta2*2.0*(double)NPOLY);
}
case (D_ALT_REU):
{
omega2 = PI/((double)NPOLY);
beta2 = asin(sin(omega2)/vabs(lambda));
return(beta2*2.0*(double)NPOLY);
}
case (D_PARABOLAS):
{
omega2 = PI/((double)NPOLY);
aa = 0.25/mu;
bb = 1.0/tan(omega2);
cc = lambda + mu;
ymax = ( - bb + sqrt(bb*bb + 4.0*aa*cc))/(2.0*aa);
return((double)NPOLY*2.0*ymax);
}
case (D_PENROSE):
{
cc = sqrt(lambda*lambda - (1.0-mu)*(1.0-mu));
width = 0.1*mu;
l1 = mu - width;
l2 = lambda - cc;
// printf("l2 = %.3lg\n", l2);
if (SYMMETRIZE_S) return(2.0*(PI + 2.0*l1 + l2));
else return(4.0*(PI + 2.0*l1 + l2));
}
case (D_FLOWER):
{
compute_flower_parameters(&omega, &co, &so, &axis1, &axis2, &phimax);
return(2.0*(double)NPOLY*phimax);
}
default: return(1.0);
}
}
double lyapunov_exponent(double s, double u, double srange)
/* estimate Lyapunov exponent of orbit starting in (s, u) */
{
double s1, u1, ds, du, nlyap = 0.0, delta = 0.01, config[8], config2[8], delta2;
int i, n = 1000;
config[0] = s;
config[1] = u;
vbilliard(config);
s1 = s + delta;
if (s1 > srange) s1 -= srange;
u1 = u;
config2[0] = s1;
config2[1] = u1;
vbilliard(config2);
for (i=0; i<n; i++)
{
vbilliard(config);
vbilliard(config2);
ds = config2[0] - config[0];
du = config2[1] - config[1];
delta2 = module2(ds, du);
nlyap += log(delta2/delta);
config2[0] = config[0] + ds*delta/delta2;
config2[1] = config[1] + du*delta/delta2;
}
return(nlyap/(double)n);
}
void draw_one_trajectory(double s, double u, int length, int draw)
/* draw a trajectory with initial condition (s, u) and length iterations */
{
int i;
double config[8], x1, y1, x2, y2, xw, yw;
/* convert to window coordinates for use of GL_SCISSOR_TEST - should be improvable */
xw = (int)((XPHASEMAX - XMIN)/(XMAX - XMIN)*(double)WINWIDTH);
yw = (int)((-0.1 - YMIN)/(YMAX - YMIN)*(double)WINHEIGHT);
/* draw the trajectories */
glEnable(GL_SCISSOR_TEST);
glScissor(xw, yw, WINWIDTH, WINHEIGHT);
config[0] = s;
config[1] = u;
vbilliard(config);
glPushMatrix();
glTranslatef(1.0, 0.5, 0.0);
glScalef(0.5, 0.5, 1.0);
glBegin(GL_LINE_STRIP);
for (i=0; i<length; i++)
{
vbilliard(config);
x1 = scaling_factor*config[4];
y1 = scaling_factor*config[5];
if (x1 > XMIN) glVertex2d(x1, y1);
}
glEnd();
if (draw) draw_billiard();
glPopMatrix();
glDisable(GL_SCISSOR_TEST);
}
void draw_trajectory(double s, double u, int length, double range)
{
int i;
if (ADAPT_TO_SYMMETRY)
{
for (i=0; i<SYMMETRY_FACTOR; i++)
draw_one_trajectory(s + (double)i*range/(double)SYMMETRY_FACTOR, u, length, i == SYMMETRY_FACTOR-1);
}
else draw_one_trajectory(s, u, length, 1);
}
double adapt_to_symmetry(double x, double range)
{
static double factor = (double)SYMMETRY_FACTOR;
// printf("x = %.2lg\t", x);
x *= factor;
while (x > range) x -= range;
// printf("x = %.2lg\n", x);
return(x);
}
void draw_orbit(double s, double u, int length)
/* draw an orbit with initial condition (s, u) and length iterations */
{
int i;
double config[8], r, range, x;
config[0] = s;
config[1] = u;
vbilliard(config);
r = 0.001;
range = s_range(lambda, mu);
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, WINWIDTH/2, WINHEIGHT);
for (i=0; i<length; i++)
{
vbilliard(config);
x = config[0];
// while (x > range) x -= range;
// while (x < 0.0) x += range;
if (ADAPT_TO_SYMMETRY) x = adapt_to_symmetry(x, range);
// x = x*3.0 - 3.0*range*(double)((int)x/range);
draw_circle(XMIN + x*(XPHASEMAX-XMIN)/range, cos(config[1])*YMAX, r, NSEG);
}
glDisable(GL_SCISSOR_TEST);
}
void update_grid(double s, double u, int length, int *grid)
/* update pixelization grid */
{
int i, is, iu, n;
double config[8], range, rs, ru;
config[0] = s;
config[1] = u;
vbilliard(config);
range = s_range(lambda, mu);
rs = (double)NGRID/range;
ru = (double)NGRID/2.0;
for (i=0; i<length; i++)
{
vbilliard(config);
is = (int)(config[0]*rs);
iu = (int)((cos(config[1]) + 1.0)*ru);
n = iu*NGRID + is;
// printf("conf = (%.2lg, %.2lg), is = %i, iu = %i, n = %i\n", config[0], config[1], is, iu, n);
if ((n >= 0)&&(n < NGRID*NGRID)) grid[n]++;
}
}
double draw_phase_portrait(int ns, int nu, int length, int trajlength, double lyapmax)
/* draw several orbits */
{
int i, j, k, color, nlyap = 0;
double s, u, range, ds, du, rgb[3], lyap, x, y, dx, dy, cratio, total_lyap = 0.0, mean_lyap, x1;
int *grid;
double *lyap_exp;
grid = (int *)malloc(NGRID*NGRID*sizeof(int));
lyap_exp = (double *)malloc(ns*nu*sizeof(double));
for (i=0; i<NGRID*NGRID; i++) grid[i] = 0;
range = s_range(lambda, mu);
ds = 1.0/(double)ns;
du = 1.0/(double)nu;
/* TODO */
if (ADAPT_TO_SYMMETRY) ds *= 1.0/(double)SYMMETRY_FACTOR;
print_parameters();
/* compute Lyapunov exponents */
#pragma omp parallel for private(i,j)
for (i=0; i<ns; i++)
for (j=0; j<nu; j++)
{
s = ((double)i + 0.5)*ds*range;
u = acos(((double)j + 0.5)*du*2.0 - 1.0);
lyap = lyapunov_exponent(s, u, range);
if ((lyap > -1.0e20)&&(lyap < 1.0e20))
{
total_lyap += lyap;
nlyap++;
}
lyap_exp[i + ns*j] = lyap;
}
/* draw orbits */
#pragma omp parallel for private(i,j)
for (i=0; i<ns; i++)
for (j=0; j<nu; j++)
{
s = ((double)i + 0.5)*ds*range;
u = acos(((double)j + 0.5)*du*2.0 - 1.0);
lyap = lyap_exp[i + ns*j];
color = (j*CFACTOR)%NCOLORS;
rgb_color_scheme_lum(color, 0.5, rgb);
if (lyap > lyapmax)
{
cratio = lyapmax/lyap;
for (k=0; k<3; k++) rgb[k]*=cratio;
}
glColor3f(rgb[0], rgb[1], rgb[2]);
if ((PIXELIZE)&&(lyap >= lyapmax)) update_grid(s, u, 150*length, grid);
// if ((i%1 == 0)&&(j%4 == 0)) draw_trajectory(s, u, trajlength);
draw_orbit(s, u, length);
}
/* draw trajectories, large Lyapunov exponent */
#pragma omp parallel for private(i,j)
for (i=0; i<ns; i++)
for (j=0; j<nu; j++) if (lyap_exp[i + ns*j] > lyapmax)
{
s = ((double)i + 0.5)*ds*range;
u = acos(((double)j + 0.5)*du*2.0 - 1.0);
color = (j*CFACTOR)%NCOLORS;
rgb_color_scheme_lum(color, 0.5, rgb);
cratio = lyapmax/lyap_exp[i + ns*j];
if (cratio < 0.1) cratio = 0.1;
for (k=0; k<3; k++) rgb[k]*=cratio;
glColor3f(rgb[0], rgb[1], rgb[2]);
if ((i%2 == 0)&&(j%4 == 0))
{
draw_trajectory(s, u, trajlength, range);
// if (SYMMETRIZE_S)
// {
// glColor3f(rgb[0], rgb[1], rgb[2]);
// draw_trajectory(range + s, u, trajlength);
// }
}
}
/* draw trajectories, small Lyapunov exponent */
#pragma omp parallel for private(i,j)
for (i=0; i<ns; i++)
for (j=0; j<nu; j++) if (lyap_exp[i + ns*j] <= lyapmax)
{
s = ((double)i + 0.5)*ds*range;
u = acos(((double)j + 0.5)*du*2.0 - 1.0);
color = (j*CFACTOR)%NCOLORS;
rgb_color_scheme_lum(color, 0.5, rgb);
glColor3f(rgb[0], rgb[1], rgb[2]);
if ((i%2 == 0)&&(j%4 == 0))
{
draw_trajectory(s, u, trajlength, range);
// if (SYMMETRIZE_S)
// {
// glColor3f(rgb[0], rgb[1], rgb[2]);
// draw_trajectory(range + s, u, trajlength);
// }
}
}
/* draw pixelized background */
if (PIXELIZE)
{
dx = (XPHASEMAX - XMIN)/(double)NGRID;
dy = (YMAX - YMIN)/(double)NGRID;
glBegin(GL_QUADS);
#pragma omp parallel for private(i,j)
for (i=0; i<NGRID; i++)
for (j=0; j<NGRID; j++) if (grid[j*NGRID + i] > 0)
{
cratio = (double)grid[j*NGRID + i]/200.0;
if (cratio > 1.0) cratio = 1.0;
glColor3f(cratio, cratio, cratio);
x = XMIN + (double)i*dx;
x1 = x + dx;
if (ADAPT_TO_SYMMETRY)
{
x = adapt_to_symmetry(x, range);
x1 = adapt_to_symmetry(x1, range);
}
y = YMIN + (double)j*dy;
glVertex2d(x, y);
glVertex2d(x1, y);
glVertex2d(x1, y+dy);
glVertex2d(x, y+dy);
}
glEnd();
}
/* redraw some orbits */
#pragma omp parallel for private(i,j)
for (i=0; i<ns; i++)
for (j=0; j<nu; j++)
{
s = ((double)i + 0.5)*ds*range;
u = acos(((double)j + 0.5)*du*2.0 - 1.0);
lyap = lyap_exp[i + ns*j];
color = (j*CFACTOR)%NCOLORS;
rgb_color_scheme_lum(color, 0.5, rgb);
if (lyap > lyapmax)
{
cratio = lyapmax/lyap;
for (k=0; k<3; k++) rgb[k]*=cratio;
}
glColor3f(rgb[0], rgb[1], rgb[2]);
if ((!PIXELIZE)||(lyap < 2.0*lyapmax)) draw_orbit(s, u, length);
}
glColor3f(1.0, 1.0, 1.0);
glLineWidth(1);
draw_line(XPHASEMAX, YMIN, XPHASEMAX, YMAX);
free(grid);
free(lyap_exp);
if (nlyap > 0) mean_lyap = total_lyap/(double)(nlyap);
else mean_lyap = 0.0;
if (PRINT_LYAPUNOV) print_lyap_exponent(mean_lyap);
return(mean_lyap);
}
double lambda_schedule(double time)
{
switch (B_DOMAIN){
case (D_ELLIPSE): return(1.0 + 0.5*(LAMBDA-1.0)*(1.0 - cos(time*DPI)));
case (D_STADIUM):
{
if (time < 0.5) return(0.5*LAMBDA*(1.0 - cos(2.0*time*DPI)));
else return(-0.5*(1.0 - cos(2.0*(time-0.5)*DPI)));
}
// case (D_REULEAUX): return(-0.0001 - exp(0.5*log(vabs(LAMBDA))*(1.0 - cos(time*DPI))));
// case (D_REULEAUX): return(-1.00001 - 0.5*(LAMBDA - 1.0)*(1.0 - cos(time*DPI)));
case (D_REULEAUX):
{
if (LOGSCALEX_LYAP) return(-0.0001 - exp(0.5*log(vabs(LAMBDA))*(1.0 - cos(time*DPI))));
else return(-1.00001 - 0.5*(LAMBDA - 1.0)*(1.0 - cos(time*PI)));
}
case (D_ALT_REU):
{
if (LOGSCALEX_LYAP) return(0.0001 + exp(0.5*log(vabs(LAMBDA))*(1.0 - cos(time*DPI))));
else return(1.00001 + 0.5*(LAMBDA - 1.0)*(1.0 - cos(time*PI)));
}
case (D_PARABOLAS):
{
if (SYMMETRIC_PARAMETER) return((-1.0*cos(time*DPI)));
else return((-1.0*cos(time*PI)));
}
case (D_PENROSE):
{
if (PRINT_LAMBDA) return(0.5*(LAMBDA + LAMBDAMIN - (LAMBDA - LAMBDAMIN)*cos(time*DPI)));
else return(LAMBDA);
}
case (D_FLOWER):
{
return(LAMBDAMIN + 0.5*(LAMBDA-LAMBDAMIN)*(1.0 - cos(time*DPI)));
}
default: return(LAMBDA);
}
}
double mu_schedule(double time)
{
switch (B_DOMAIN){
case (D_ANNULUS): return(0.5*MU*(1.0 - cos(time*DPI)));
case (D_PARABOLAS):
{
if (SYMMETRIC_PARAMETER) return(MU*(1.0 + 1.0*cos(time*DPI)));
else return(MU*(1.0 + 1.0*cos(time*PI)));
}
default: return(MU);
}
}
double penrose_ratio_schedule(double time)
{
switch (B_DOMAIN){
case (D_PENROSE):
{
if (PRINT_PENROSE_RATIO) return(PENROSE_RATIO*0.5*(1.0 - 0.999*cos(time*DPI)));
else return(PENROSE_RATIO);
}
default: return(MU);
}
}
double plot_coord(double x, double xmin, double xmax)
{
return(xmin + x*(xmax - xmin));
}
double plot_coord_log(double x, double min, double xmin, double xmax)
{
return(xmin + (1.0 - log(x)/log(min))*(xmax - xmin));
}
void plot_lyapunov_exponents_linscale(int i, double *lyap_exponents)
/* add plot of lyapunov exponents */
{
int j, k, l, n1, n2, n3, n4, jmin, jmax;
char message[100];
static double xmin, xmax, ymin, ymax, xmid, ymid, dx, dy, plotxmin, plotxmax, plotymin, plotymax, lmin, lmax;
double pos[2], x1, y1, x2, y2, rgb[3], x, y, lambda0, t, xshift;
static int first = 1;
if (first)
{
xmin = 0.1;
xmax = XMAX - 0.1;
ymin = YMIN + 0.05;
ymax = YMIN + 0.75;
xmid = 0.5*(xmin + xmax);
ymid = 0.5*(ymin + ymax);
dx = 0.5*(xmax - xmin);
dy = 0.5*(ymax - ymin);
plotxmin = xmin + 0.1;
plotxmax = xmax - 0.1;
plotymin = ymin + 0.07;
plotymax = ymax - 0.15;
if (PRINT_LAMBDA)
{
lmin = vabs(lambda_schedule(0.0));
// lmax = lambda_schedule(0.5);
lmax = vabs(lambda_schedule(1.0));
}
else if (PRINT_PENROSE_RATIO)
{
lmin = penrose_ratio_schedule(0.0);
lmax = penrose_ratio_schedule(0.5);
}
printf("lmin = %.3lg, lmax = %.3lg\n", lmin, lmax);
first = 0;
}
glColor3f(1.0, 1.0, 1.0);
glLineWidth(2);
/* axes and labels */
draw_line(plotxmin, plotymin, plotxmax + 0.1, plotymin);
draw_line(plotxmin, plotymin, plotxmin, plotymax + 0.1);
switch (B_DOMAIN) {
case (D_PARABOLAS) :
{
sprintf(message, "Focal dist");
jmin = -10;
jmax = 11;
break;
}
case (D_REULEAUX) :
{
sprintf(message, "Radius");
jmin = 1;
jmax = (int)(LAMBDA) + 1;
break;
}
case (D_ALT_REU) :
{
sprintf(message, "Radius");
jmin = 1;
jmax = (int)(LAMBDA) + 1;
break;
}
case (D_PENROSE) :
{
sprintf(message, "Aspect ratio");
jmin = (int)(LAMBDAMIN*10.0);
jmax = (int)(LAMBDA*10.0) + 1;
// jmin = 0;
// jmax = 10;
break;
}
case (D_FLOWER) :
{
sprintf(message, "Parameter");
jmin = (int)(LAMBDAMIN*10.0);
jmax = (int)(LAMBDA*10.0) + 1;
// jmin = 0;
// jmax = 10;
break;
}
default:
{
sprintf(message, "Parameter");
jmin = 0;
jmax = 10;
}
}
write_text_fixedwidth(plotxmax - 0.15, plotymin + 0.075, message);
/* graduations */
for (j=jmin; j<jmax; j++)
{
// lambda0 = 0.1*(double)j;
lambda0 = (double)j;
x = (lambda0 - lmin)/(lmax - lmin);
// printf("Graduation %.1f x = %.3f\n", lambda0, x);
x1 = plot_coord(x, plotxmin, plotxmax); /* TO FIX */
// printf("Graduation %.1f at %.3f\n", lambda0, x1);
if (x1 < 0.0) x1 = -x1;
if (lambda0 >= 0.0) xshift = -0.04;
else xshift = -0.07;
if (j%10 == 0)
{
draw_line(x1, plotymin - 0.02, x1, plotymin + 0.02);
sprintf(message, "%.1f", lambda0);
write_text_fixedwidth(x1 + xshift, plotymin - 0.075, message);
}
else
{
draw_line(x1, plotymin - 0.01, x1, plotymin + 0.01);
if ((j+10)%2 == 0)
{
sprintf(message, "%.1f", lambda0);
write_text_fixedwidth(x1 + xshift, plotymin - 0.075, message);
}
}
}
sprintf(message, "Avrg Lyap");
write_text_fixedwidth(plotxmin - 0.05, plotymax + 0.15, message);
for (j=1; j<=(int)(10.0*LYAP_MAX); j++)
{
y = (double)j/(10.0*LYAP_MAX);
y1 = plot_coord(y, plotymin, plotymax);
draw_line(plotxmin - 0.025, y1, plotxmin + 0.025, y1);
sprintf(message, "%.1f", 0.1*(double)j);
write_text_fixedwidth(plotxmin - 0.14, y1 - 0.015, message);
}
/* plot */
hsl_to_rgb_palette(LYAP_PLOT_COLOR, 0.9, 0.5, rgb, COLOR_PALETTE);
glColor3f(rgb[0], rgb[1], rgb[2]);
x1 = plotxmin;
y1 = plotymin;
for (j=0; j<i; j++)
{
t = (double)j/(double)(NSTEPS-1);
if (PRINT_LAMBDA) lambda0 = vabs(lambda_schedule(t));
else if (PRINT_PENROSE_RATIO) lambda0 = penrose_ratio_schedule(t);
// switch (B_DOMAIN){
// case (D_PARABOLAS):
// {
// lambda0 = lambda_schedule(t);
// break;
// }
// case (D_PENROSE):
// {
// lambda0 = penrose_ratio_schedule(t);
// break;
// }
// }
// x = (-lambda0 - 1.0)/(LAMBDA - 1.0);
x = (lambda0 - lmin)/(lmax - lmin);
y = lyap_exponents[j]/LYAP_MAX;
// printf("lambda = %.3lg, x = %.3lg\n", lambda0, x);
x2 = plot_coord(x, plotxmin, plotxmax);
y2 = plot_coord(y, plotymin, plotymax);
draw_line(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
}
}
void plot_lyapunov_exponents_logscale(int i, double *lyap_exponents)
/* add plot of lyapunov exponents */
{
int j, k, l, n1, n2, n3, n4;
char message[100];
static double xmin, xmax, ymin, ymax, xmid, ymid, dx, dy, plotxmin, plotxmax, plotymin, plotymax;
double pos[2], x1, y1, x2, y2, rgb[3], x, y, lambda0, t;
static int first = 1;
if (first)
{
xmin = 0.1;
xmax = XMAX - 0.1;
ymin = YMIN + 0.05;
ymax = YMIN + 0.75;
xmid = 0.5*(xmin + xmax);
ymid = 0.5*(ymin + ymax);
dx = 0.5*(xmax - xmin);
dy = 0.5*(ymax - ymin);
plotxmin = xmin + 0.1;
plotxmax = xmax - 0.1;
plotymin = ymin + 0.07;
plotymax = ymax - 0.15;
first = 0;
}
glColor3f(1.0, 1.0, 1.0);
glLineWidth(2);
/* axes and labels */
draw_line(plotxmin, plotymin, plotxmax + 0.05, plotymin);
draw_line(plotxmin, plotymin, plotxmin, plotymax + 0.1);
sprintf(message, "Radius");
write_text_fixedwidth(plotxmax, plotymin - 0.075, message);
// n1 = (int)(log(LAMBDA)/log(10.0));
n1 = (int)(log(LAMBDA)/log(10.0)) + 1;
for (k=1; k<n1; k++)
{
n1 = (int)ipow(10, k);
j = n1;
lambda0 = (double)j;
if (lambda0 < LAMBDA)
{
x = lambda0/LAMBDA;
x1 = plot_coord_log(x, 1.0/LAMBDA, plotxmin, plotxmax);
draw_line(x1, plotymin - 0.02, x1, plotymin + 0.02);
sprintf(message, "%i", (int)lambda0);
write_text_fixedwidth(x1 - 0.015 - 0.015*(double)(k-1), plotymin - 0.075, message);
}
for (l=1; l<10; l++)
{
j = (n1/10)*l;
lambda0 = (double)j;
if (lambda0 < LAMBDA)
{
x = lambda0/LAMBDA;
x1 = plot_coord_log(x, 1.0/LAMBDA, plotxmin, plotxmax);
draw_line(x1, plotymin - 0.01, x1, plotymin + 0.01);
if ((l == 2)||(l == 5))
{
sprintf(message, "%i", (int)lambda0);
write_text_fixedwidth(x1 - 0.015 - 0.015*(double)(k-1), plotymin - 0.075, message);
}
}
}
}
sprintf(message, "Avrg Lyap");
write_text_fixedwidth(plotxmin - 0.05, plotymax + 0.15, message);
// for (j=1; j<=5; j++)
// {
// y = 0.2*(double)j;
for (j=1; j<=(int)(10.0*LYAP_MAX); j++)
{
y = (double)j/(10.0*LYAP_MAX);
y1 = plot_coord(y, plotymin, plotymax);
draw_line(plotxmin - 0.025, y1, plotxmin + 0.025, y1);
sprintf(message, "%.1f", 0.1*(double)j);
write_text_fixedwidth(plotxmin - 0.14, y1 - 0.015, message);
}
/* plot */
hsl_to_rgb_palette(LYAP_PLOT_COLOR, 0.9, 0.5, rgb, COLOR_PALETTE);
glColor3f(rgb[0], rgb[1], rgb[2]);
x1 = plotxmin;
y1 = plotymin;
for (j=0; j<i; j++)
{
t = (double)j/(double)(NSTEPS-1);
lambda0 = lambda_schedule(t);
// x = (-lambda0 - 1.0)/(LAMBDA - 1.0);
x = -lambda0/LAMBDA;
y = lyap_exponents[j]/LYAP_MAX;
x2 = plot_coord_log(x, 1.0/LAMBDA, plotxmin, plotxmax);
y2 = plot_coord(y, plotymin, plotymax);
draw_line(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
}
}
void plot_lyapunov_exponents(int i, double *lyap_exponents)
{
if (LOGSCALEX_LYAP) plot_lyapunov_exponents_logscale(i, lyap_exponents);
else plot_lyapunov_exponents_linscale(i, lyap_exponents);
}
void animation()
{
double time, dt, alpha, r, rgb[3], x, y, a, b, c, nsteps;
double *lyap_exponents;
int i, j, resamp = 1, s, i1, i2, period;
char message[50];
if (PLOT_LYAPUNOV) lyap_exponents = (double *)malloc(NSTEPS*sizeof(double));
if (SYMMETRIC_PARAMETER) nsteps = NSTEPS/2;
else nsteps = NSTEPS;
for (i=0; i<=nsteps; i++)
{
time = (double)i/(double)(NSTEPS-1);
blank();
lambda = lambda_schedule(time);
mu = mu_schedule(time);
penrose_ratio = penrose_ratio_schedule(time);
if (B_DOMAIN == D_FLOWER) scaling_factor = 0.7/lambda;
else scaling_factor = 1.0;
// lyap_exponents[i] = draw_phase_portrait(14, 20, 200, 20, 0.1);
// lyap_exponents[i] = draw_phase_portrait(10, 20, 100, 20, 0.2);
// lyap_exponents[i] = draw_phase_portrait(16, 20, 100, 20, 0.2);
lyap_exponents[i] = draw_phase_portrait(6, 20, 200, 20, 0.3);
if (PLOT_LYAPUNOV) plot_lyapunov_exponents(i, lyap_exponents);
glutSwapBuffers();
if (MOVIE)
{
save_frame();
if (SYMMETRIC_PARAMETER) save_frame_counter (NSTEPS + 1 - i);
/* it seems that saving too many files too fast can cause trouble with the file system */
/* so this is to make a pause from time to time - parameter PAUSE may need adjusting */
if (i % PAUSE == PAUSE - 1)
{
printf("Making a short pause\n");
sleep(PSLEEP);
s = system("mv part*.tif tif_part/");
}
}
if ((i==0)&&(SYMMETRIC_PARAMETER))
for (j=0; j<END_FRAMES; j++) save_frame_counter(NSTEPS + j + 1);
}
if ((MOVIE)&&(!SYMMETRIC_PARAMETER))
for (i=0; i<END_FRAMES; i++) save_frame_counter(NSTEPS + i + 1);
if (MOVIE) s = system("mv part*.tif tif_part/");
if (PLOT_LYAPUNOV) free(lyap_exponents);
}
void display(void)
{
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
glPushMatrix();
blank();
if (!SHOWTRAILS)
{
glutSwapBuffers();
blank();
glutSwapBuffers();
}
animation();
sleep(SLEEP2);
glPopMatrix();
glutDestroyWindow(glutGetWindow());
printf("Start local time and date: %s", asctime(timeinfo));
time(&rawtime);
timeinfo = localtime(&rawtime);
printf("Current local time and date: %s", asctime(timeinfo));
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
if (SHOWTRAILS) glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE | GLUT_DEPTH);
else glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
// glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(WINWIDTH,WINHEIGHT);
glutCreateWindow("Billiard animation");
init();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}