/*********************************************************************************/ /* */ /* Animation of particles in billiard */ /* */ /* N. Berglund, december 2012, april 2021 */ /* UPDATE 14 April 21 : graphics files go to subfolder, */ /* Switch MOVIE to decide whether to create a movie */ /* UPDATE 3 May 21 : new domains */ /* */ /* 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 particle_billiard particle_billiard.c */ /* -O3 -L/usr/X11R6/lib -ltiff -lm -lGL -lGLU -lX11 -lXmu -lglut */ /* */ /* To make a video, set MOVIE to 1 and create subfolder tif_part */ /* It may be possible to increase parameter PAUSE */ /* */ /* create movie using */ /* ffmpeg -i part.%05d.tif -vcodec libx264 part.mp4 */ /* */ /*********************************************************************************/ #include #include #include #include #include #include #include /* Sam Leffler's libtiff library. */ #define MOVIE 0 /* set to 1 to generate movie */ #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 */ /* Choice of the billiard table */ #define B_DOMAIN 3 /* choice of domain shape */ #define D_RECTANGLE 0 /* rectangular domain */ #define D_ELLIPSE 1 /* elliptical domain */ #define D_STADIUM 2 /* stadium-shaped domain */ #define D_SINAI 3 /* Sinai billiard */ #define D_DIAMOND 4 /* diamond-shaped billiard */ #define D_TRIANGLE 5 /* triangular billiard */ #define LAMBDA 0.5 /* parameter controlling shape of billiard */ // #define LAMBDA 1.73205080756888 /* sqrt(3) for triangle tiling plane */ #define FOCI 1 /* set to 1 to draw focal points of ellipse */ #define RESAMPLE 0 /* set to 1 if particles should be added when dispersion too large */ #define NPART 5000 /* number of particles */ #define NPARTMAX 50000 /* maximal number of particles after resampling */ #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 NSTEPS 5000 /* number of frames of movie */ #define TIME 1500 /* time between movie frames, for fluidity of real-time simulation */ #define DPHI 0.00001 /* integration step */ #define NVID 750 /* number of iterations between images displayed on screen */ #define NCOLORS 10 /* number of colors */ #define COLORSHIFT 0 /* hue of initial color */ #define NSEG 100 /* number of segments of boundary */ #define LENGTH 0.05 /* length of velocity vectors */ #define BLACK 1 /* set to 1 for black background */ /* 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 */ #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 1000 /* final sleeping time */ #define PI 3.141592654 #define DPI 6.283185307 #define PID 1.570796327 #include "sub_part_billiard.c" /*********************/ /* animation part */ /*********************/ void init_boundary_config(smin, smax, anglemin, anglemax, configs) /* initialize configuration: drop on the boundary, beta version */ /* WORKS FOR ELLIPSE, HAS TO BE ADAPTED TO GENERAL BILLIARD */ double smin, smax, anglemin, anglemax; double *configs[NPARTMAX]; { int i; double ds, da, s, angle, theta, alpha, pos[2]; if (anglemin <= 0.0) anglemin = PI/((double)NPART); if (anglemax >= PI) anglemax = PI*(1.0 - 1.0/((double)NPART)); ds = (smax - smin)/((double)NPART); da = (anglemax - anglemin)/((double)NPART); for (i=0; i= NCOLORS) color[i] -= NCOLORS; } configs[i][2] += DPHI; cosphi = (configs[i][6] - configs[i][4])/configs[i][3]; sinphi = (configs[i][7] - configs[i][5])/configs[i][3]; x1 = configs[i][4] + configs[i][2]*cosphi; y1 = configs[i][5] + configs[i][2]*sinphi; x2 = configs[i][4] + (configs[i][2] + LENGTH)*cosphi; y2 = configs[i][5] + (configs[i][2] + LENGTH)*sinphi; rgb_color_scheme(color[i], rgb); glColor3f(rgb[0], rgb[1], rgb[2]); glBegin(GL_LINE_STRIP); glVertex2d(x1, y1); glVertex2d(x2, y2); glEnd (); /* taking care of boundary conditions - only needed for periodic boundary conditions */ if (x2 > XMAX) { glBegin(GL_LINE_STRIP); glVertex2d(x1+XMIN-XMAX, y1); glVertex2d(x2+XMIN-XMAX, y2); glEnd (); } if (x2 < XMIN) { glBegin(GL_LINE_STRIP); glVertex2d(x1-XMIN+XMAX, y1); glVertex2d(x2-XMIN+XMAX, y2); glEnd (); } if (y2 > YMAX) { glBegin(GL_LINE_STRIP); glVertex2d(x1, y1+YMIN-YMAX); glVertex2d(x2, y2+YMIN-YMAX); glEnd (); } if (y2 < YMIN) { glBegin(GL_LINE_STRIP); glVertex2d(x1, y1+YMAX-YMIN); glVertex2d(x2, y2+YMAX-YMIN); glEnd (); } /* for debugging purpose */ // glLineWidth(1.0); // glBegin(GL_LINES); // glVertex2d(configs[i][4], configs[i][5]); // glVertex2d(configs[i][6], configs[i][7]); // glEnd (); // glLineWidth(3.0); if (configs[i][2] > configs[i][3] - DPHI) configs[i][2] -= configs[i][3]; } draw_billiard(LAMBDA); } void graph_movie(time, color, configs) /* compute next movie frame */ int time, color[NPARTMAX]; double *configs[NPARTMAX]; { int i, j, c; for (j=0; j=0) color[i]++; if (color[i] >= NCOLORS) color[i] -= NCOLORS; } configs[i][2] += DPHI; if (configs[i][2] > configs[i][3] - DPHI) { configs[i][2] -= configs[i][3]; } } } // draw_config(color, configs); } void graph_no_movie(time, color, configs) /* plot next image without making a movie */ int time, color[NPARTMAX]; double *configs[NPARTMAX]; { int i, j, c; for (j=0; j=0) color[i]++; if (color[i] >= NCOLORS) color[i] -= NCOLORS; } configs[i][2] += DPHI; if (configs[i][2] > configs[i][3] - DPHI) configs[i][2] -= configs[i][3]; } } } void animation() { double time, dt; double *configs[NPARTMAX]; int i, j, resamp = 1, s; int *color, *newcolor; /* Since NPARTMAX can be big, it seemed wiser to use some memory allocation here */ color = malloc(sizeof(int)*(NPARTMAX)); newcolor = malloc(sizeof(int)*(NPARTMAX)); for (i=0; i