/*********************************************************************************/ /* */ /* 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 */ #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 */ #define B_DOMAIN 20 /* choice of domain shape */ #define CIRCLE_PATTERN 2 /* pattern of circles */ #define ABSORBING_CIRCLES 0 /* set to 1 for circular scatterers to be absorbing */ #define NMAXCIRCLES 1000 /* total number of circles (must be at least NCX*NCY for square grid) */ #define NCX 15 /* number of circles in x direction */ #define NCY 20 /* number of circles in y direction */ #define LAMBDA 0.75 /* parameter controlling shape of billiard */ #define MU 0.035 /* second parameter controlling shape of billiard */ #define FOCI 1 /* set to 1 to draw focal points of ellipse */ #define NPOLY 8 /* number of sides of polygon */ #define APOLY 0.25 /* 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 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 30000 /* number of particles */ #define NPARTMAX 100000 /* 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 SHOWTRAILS 0 /* set to 1 to keep trails of the particles */ #define NSTEPS 3000 /* number of frames of movie */ #define TIME 1000 /* time between movie frames, for fluidity of real-time simulation */ #define DPHI 0.000005 /* integration step */ #define NVID 150 /* number of iterations between images displayed on screen */ /* 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 NCOLORS 32 /* number of colors */ #define COLORSHIFT 0 /* hue of initial color */ #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.02 /* 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 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 */ #include "global_particles.c" #include "sub_part_billiard.c" /*********************/ /* animation part */ /*********************/ void init_boundary_config(double smin, double smax, double anglemin, double anglemax, double *configs[NPARTMAX]) /* initialize configuration: drop on the boundary, beta version */ /* WORKS FOR ELLIPSE, HAS TO BE ADAPTED TO GENERAL BILLIARD */ { 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 1) dalpha = (angle2 - angle1)/((double)(NPART-1)); else dalpha = 0.0; 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; /* test whether particle does not escape billiard */ if (active[i]) active[i] = xy_in_billiard(x1, y1); if (active[i]) { rgb_color_scheme(color[i], rgb); glColor3f(rgb[0], rgb[1], rgb[2]); glBegin(GL_LINE_STRIP); glVertex2d(SCALING_FACTOR*x1, SCALING_FACTOR*y1); glVertex2d(SCALING_FACTOR*x2, SCALING_FACTOR*y2); glEnd (); /* taking care of boundary conditions - only needed for periodic boundary conditions */ if (PERIODIC_BC) { if (SCALING_FACTOR*x2 > XMAX) { glBegin(GL_LINE_STRIP); glVertex2d(SCALING_FACTOR*(x1+XMIN-XMAX), SCALING_FACTOR*y1); glVertex2d(SCALING_FACTOR*(x2+XMIN-XMAX), SCALING_FACTOR*y2); glEnd (); } if (SCALING_FACTOR*x2 < XMIN) { glBegin(GL_LINE_STRIP); glVertex2d(SCALING_FACTOR*(x1-XMIN+XMAX), SCALING_FACTOR*y1); glVertex2d(SCALING_FACTOR*(x2-XMIN+XMAX), SCALING_FACTOR*y2); glEnd (); } if (SCALING_FACTOR*y2 > YMAX) { glBegin(GL_LINE_STRIP); glVertex2d(SCALING_FACTOR*x1, SCALING_FACTOR*(y1+YMIN-YMAX)); glVertex2d(SCALING_FACTOR*x2, SCALING_FACTOR*(y2+YMIN-YMAX)); glEnd (); } if (SCALING_FACTOR*y2 < YMIN) { glBegin(GL_LINE_STRIP); glVertex2d(SCALING_FACTOR*x1, SCALING_FACTOR*(y1+YMAX-YMIN)); glVertex2d(SCALING_FACTOR*x2, SCALING_FACTOR*(y2+YMAX-YMIN)); glEnd (); } } } /* draw trajectories, for debugging purpose */ if (DEBUG) { glLineWidth(1.0); glBegin(GL_LINES); glVertex2d(SCALING_FACTOR*configs[i][4], SCALING_FACTOR*configs[i][5]); glVertex2d(SCALING_FACTOR*configs[i][6], SCALING_FACTOR*configs[i][7]); glEnd (); glLineWidth(3.0); } if (configs[i][2] > configs[i][3] - DPHI) configs[i][2] -= configs[i][3]; } if (DRAW_BILLIARD) draw_billiard(); } void graph_movie(int time, int color[NPARTMAX], double *configs[NPARTMAX], int active[NPARTMAX]) /* compute next movie frame */ { int i, j, c; for (j=0; j=0) color[i]++; if (!RAINBOW_COLOR) { 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 animation() { double time, dt, alpha, r; double *configs[NPARTMAX]; int i, j, resamp = 1, s, i1, i2; int *color, *newcolor, *active; /* Since NPARTMAX can be big, it seemed wiser to use some memory allocation here */ color = malloc(sizeof(int)*(NPARTMAX)); newcolor = malloc(sizeof(int)*(NPARTMAX)); active = malloc(sizeof(int)*(NPARTMAX)); for (i=0; i