/*********************************************************************************/ /* */ /* 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. */ #include #include #define MOVIE 0 /* set to 1 to generate movie */ #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 WINWIDTH 1920 /* window width */ // #define WINHEIGHT 1000 /* window height */ // // #define XMIN -2.0 // #define XMAX 2.0 /* x interval */ // #define YMIN -1.041666667 // #define YMAX 1.041666667 /* 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 31 /* choice of domain shape */ #define CIRCLE_PATTERN 1 /* pattern of circles */ #define POLYLINE_PATTERN 15 /* pattern of polyline */ #define ABSORBING_CIRCLES 0 /* set to 1 for circular scatterers to be absorbing */ #define NMAXCIRCLES 100000 /* total number of circles (must be at least NCX*NCY for square grid) */ #define NMAXPOLY 100000 /* total number of sides of polygonal line */ // #define NCX 10 /* number of circles in x direction */ // #define NCY 10 /* number of circles in y direction */ #define NCX 30 /* 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 1 /* Sierpinski gastket depth */ #define LAMBDA 1.5 /* parameter controlling shape of domain */ #define MU 0.005 /* 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 APOLY 0.0 /* angle by which to turn polygon, in units of Pi/2 */ #define PENROSE_RATIO 2.5 /* parameter controlling the shape of small ellipses in Penrose room */ #define DRAW_BILLIARD 1 /* set to 1 to draw billiard */ #define DRAW_CONSTRUCTION_LINES 0 /* 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 1 /* number of particles */ #define NPART 20000 /* number of particles */ // #define NPART 10000 /* 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 HEATMAP 1 /* set to 1 to show heat map of particles */ #define DRAW_HEATMAP_PARTICLES 1 /* set to 1 to draw particles in heat map */ #define HEATMAP_MAX_PART_BY_CELL 0 /* to draw only limited number of particles in cell */ #define PLOT_HEATMAP_AVERAGE 0 /* set to 1 to plot average number of particles in heat map */ #define SHOWZOOM 0 /* set to 1 to show zoom on specific area */ #define PRINT_PARTICLE_NUMBER 0 /* set to 1 to print number of particles */ #define PRINT_LEFT_RIGHT_PARTICLE_NUMBER 1 /* set to 1 to print number of particles on left and right side */ #define PRINT_CIRCLE_PARTICLE_NUMBER 0 /* set to 1 to print number of particles outside circular maze */ #define PRINT_COLLISION_NUMBER 0 /* set to 1 to print number of collisions */ #define TEST_ACTIVE 1 /* set to 1 to test whether particle is in billiard */ #define TEST_INITIAL_COND 0 /* set to 1 to allow only initial conditions that pass a test */ #define NSTEPS 12300 /* number of frames of movie */ // #define NSTEPS 6500 /* number of frames of movie */ #define TIME 1500 /* time between movie frames, for fluidity of real-time simulation */ // #define TIME 750 /* time between movie frames, for fluidity of real-time simulation */ #define DPHI 0.00002 /* integration step */ // #define DPHI 0.00005 /* integration step */ // #define DPHI 0.00001 /* integration step */ #define NVID 25 /* number of iterations between images displayed on screen */ // #define NVID 100 /* number of iterations between images displayed on screen */ #define END_FRAMES 100 /* number of still frames at the end of the movie */ /* 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 17 /* Color palette, see list in global_pdes.c */ #define NCOLORS 1000 /* number of colors */ #define COLORSHIFT 0 /* hue of initial color */ #define COLOR_HUEMIN 0 /* minimal color hue */ #define COLOR_HUEMAX 150 /* maximal color hue */ #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.025 /* length of velocity vectors */ // #define LENGTH 0.04 /* 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 PAUSE 1000 /* number of frames after which to pause */ #define PSLEEP 2 /* sleep time during pause */ #define SLEEP1 1 /* initial sleeping time */ #define SLEEP2 1 /* final sleeping time */ #define NXMAZE 24 /* width of maze */ #define NYMAZE 20 /* height of maze */ #define MAZE_MAX_NGBH 6 /* max number of neighbours of maze cell */ #define RAND_SHIFT 11 /* seed of random number generator */ // #define RAND_SHIFT 0 /* seed of random number generator */ #define MAZE_XSHIFT 0.0 /* horizontal shift of maze */ #define MAZE_RANDOM_FACTOR 0.1 /* randomization factor for S_MAZE_RANDOM */ #define MAZE_CORNER_RADIUS 0.5 /* radius of tounded corners in maze */ #include "global_particles.c" #include "sub_maze.c" #include "sub_part_billiard.c" int ncollisions = 0; /*********************/ /* 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 1) dalpha = (angle2 - angle1)/((double)(particle2 - particle1-1)); else dalpha = 0.0; for (i=particle1; i 1.0) { yb = shifty + 0.5*(1.0 - y_target)/width; glBegin(GL_LINE_STRIP); glVertex2d(x1, yb); glVertex2d(x2, yb); glVertex2d(x2, yb + 0.02); glVertex2d(x1, yb + 0.02); glEnd(); } /* other boundaries not yet implemented */ /* draw target in zoom */ glLineWidth(BILLIARD_WIDTH*2); if (shooter) glColor3f(1.0, 0.0, 0.0); else glColor3f(0.0, 0.8, 0.2); tradius = zoomwidth*MU/width; draw_circle(shiftx, shifty, tradius, NSEG); // glLineWidth(PARTICLE_WIDTH*2); for (i=0; i 1.0)) { if (x1 > 0.0) xb = 1.0; else xb = -1.0; y2 = y1 + (xb - x1)*(y2 - y1)/(x2 - x1); x2 = xb; } else if ((vabs(x1) > 1.0)&&(vabs(x2) < 1.0)) { if (x2 > 0.0) xb = 1.0; else xb = -1.0; y1 = y2 + (xb - x2)*(y1 - y2)/(x1 - x2); x1 = xb; } if ((vabs(y1) < 1.0)&&(vabs(y2) > 1.0)) { if (y1 > 0.0) yb = 1.0; else yb = -1.0; x2 = x1 + (yb - y1)*(x2 - x1)/(y2 - y1); y2 = yb; } else if ((vabs(y1) > 1.0)&&(vabs(y2) < 1.0)) { if (y2 > 0.0) yb = 1.0; else yb = -1.0; x1 = x2 + (yb - y2)*(x1 - x2)/(y1 - y2); y1 = yb; } // if ((active[i])&&(vabs(x1) < 1.0)&&(vabs(y1) < 1.0)&&(vabs(x2) < 1.0)&&(vabs(y2) < 1.0)) if (((active[i])&&(vabs(x1) < 1.0)&&(vabs(y1) < 1.0))||((vabs(x2) < 1.0)&&(vabs(y2) < 1.0))) { rgb_color_scheme_minmax(color[i], rgb); glColor3f(rgb[0], rgb[1], rgb[2]); glBegin(GL_LINE_STRIP); glVertex2d(shiftx + zoomwidth*SCALING_FACTOR*x1, shifty + zoomwidth*SCALING_FACTOR*y1); glVertex2d(shiftx + zoomwidth*SCALING_FACTOR*x2, shifty + zoomwidth*SCALING_FACTOR*y2); glEnd (); } } } void draw_config_showtrails(int color[NPARTMAX], double *configs[NPARTMAX], int active[NPARTMAX]) /* draw the particles */ { int i; double x0, y0, x1, y1, x2, y2, cosphi, sinphi, rgb[3], len; glutSwapBuffers(); if (PAINT_INT) paint_billiard_interior(); glLineWidth(PARTICLE_WIDTH); glEnable(GL_LINE_SMOOTH); 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]; len = configs[i][2] + LENGTH; if (len > configs[i][3]) len = configs[i][3]; x0 = configs[i][4]; y0 = configs[i][5]; x1 = configs[i][4] + configs[i][2]*cosphi; y1 = configs[i][5] + configs[i][2]*sinphi; x2 = configs[i][4] + len*cosphi; y2 = configs[i][5] + len*sinphi; /* test whether particle does not escape billiard */ if ((TEST_ACTIVE)&&(active[i])) active[i] = xy_in_billiard(x1, y1); if (active[i]) { rgb_color_scheme_minmax(color[i], rgb); glColor3f(rgb[0], rgb[1], rgb[2]); glBegin(GL_LINE_STRIP); glVertex2d(SCALING_FACTOR*x0, SCALING_FACTOR*y0); glVertex2d(SCALING_FACTOR*x2, SCALING_FACTOR*y2); glEnd (); } // if (configs[i][2] > configs[i][3] - DPHI) // { // glBegin(GL_LINE_STRIP); // glVertex2d(SCALING_FACTOR*x0, SCALING_FACTOR*y0); // glVertex2d(SCALING_FACTOR*configs[i][6], SCALING_FACTOR*configs[i][7]); // glEnd (); // } } if (DRAW_BILLIARD) draw_billiard(); if (SHOWZOOM) switch (POLYLINE_PATTERN) { case (P_TOKA_PRIME): { draw_zoom(color, configs, active, x_target, y_target, 0.1, 1.65, 0.75, 0.3, 0); draw_zoom(color, configs, active, x_shooter, y_shooter, 0.1, -1.65, 0.75, 0.3, 1); break; } case (P_TOKA_NONSELF): { draw_zoom(color, configs, active, 0.0, 0.0, 0.1, 1.65, 0.75, 0.3, 0); break; } } // if (SHOWZOOM) draw_zoom(color, configs, active, 0.95, 0.0, 0.1); } void draw_config_heatmap(double *configs[NPARTMAX], int active[NPARTMAX], int heatmap_number[NXMAZE*NYMAZE+1], int heatmap_total[NXMAZE*NYMAZE+1], short int heatmap_visited[NXMAZE*NYMAZE+1], int draw_particles) /* draw a heat map of particle distribution (for mazes) */ { int i, j, n, part_number; double x, y, cosphi, sinphi, rgb[3], len, padding = 0.02; double *xtable, *ytable; short int *drawtable; static int time, first = 1; static double minprop; if (first) { time = 0; first = 0; minprop = 0.01; // if (PLOT_HEATMAP_AVERAGE) minprop = 0.005; // else minprop = 0.01; } time++; drawtable = malloc(sizeof(short int)*(NPARTMAX)); xtable = malloc(sizeof(double)*(NPARTMAX)); ytable = malloc(sizeof(double)*(NPARTMAX)); glutSwapBuffers(); blank(); if (PAINT_INT) paint_billiard_interior(); for (i=0; i configs[i][3] - padding) len = configs[i][3] - padding; if (len < 1.0e-10) len = 1.0e-10; // if (len < 0.0) len = 1.0e-10; x = configs[i][4] + len*cosphi; y = configs[i][5] + len*sinphi; xtable[i] = x; ytable[i] = y; /* test whether particle does not escape billiard */ if ((TEST_ACTIVE)&&(active[i])) active[i] = xy_in_billiard(x, y); if (active[i]) { n = find_maze_cell(x, y); if ((n > -1)&&(n < NXMAZE*NYMAZE+1)) { heatmap_number[n]++; heatmap_total[n]++; heatmap_visited[n] = 1; } if (HEATMAP_MAX_PART_BY_CELL > 0) { drawtable[i] = ((n == -2)||((n >= -1)&&(heatmap_number[n] <= HEATMAP_MAX_PART_BY_CELL))); } else drawtable[i] = (n >= -1); } // printf("Particle %i is in maze cell %i\n", i, n); } for (n=0; n= NCOLORS) color[i] -= NCOLORS; // } // if ((ABSORBING_CIRCLES)&&(c < 0)) active[i] = 0; // } // 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 ((TEST_ACTIVE)&&(active[i])) active[i] = xy_in_billiard(x1, y1); if (active[i]) { rgb_color_scheme_minmax(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(); if (SHOWZOOM) switch (POLYLINE_PATTERN) { case (P_TOKA_PRIME): { draw_zoom(color, configs, active, x_target, y_target, 0.1, 1.65, 0.75, 0.3, 0); draw_zoom(color, configs, active, x_shooter, y_shooter, 0.1, -1.65, 0.75, 0.3, 1); break; } case (P_TOKA_NONSELF): { draw_zoom(color, configs, active, 0.0, 0.0, 0.1, 0.82, 0.82, 0.25, 0); break; } } // if (SHOWZOOM) draw_zoom(color, configs, active, 0.95, 0.0, 0.1); } 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)&&(c>=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 print_part_number(double *configs[NPARTMAX], int active[NPARTMAX], double x, double y) { char message[50]; int i, n_active_particles = 0; double rgb[3]; /* count active particles, using the fact that absorbed particles have been given dummy coordinates */ for (i=0; i XMIN)&&(y1 < YMAX)&&(y1 > YMIN)) exits[i].left = 1; else if ((x1 > xmax)&&(x1 < XMAX)&&(y1 < YMAX)&&(y1 > YMIN)) { exits[i].right = 1; printf("Detected leaving particle %i at (%.2f, %2f)\n\n\n", i, x1, y1); } } for (i=0; i 1) sprintf(message, "%i particles", nleft); else sprintf(message, "%i particle", nleft); write_text(xl, yl, message); if (nright > 1) sprintf(message, "%i particles", nright); else sprintf(message, "%i particle", nright); write_text(xr, yr, message); } void print_circle_part_number(double *configs[NPARTMAX], int active[NPARTMAX], double xr, double yr) { char message[50]; int i, npart = 0; double rgb[3], x1, y1, cosphi, sinphi; /* count active particles, using the fact that absorbed particles have been given dummy coordinates */ for (i=0; i= DUMMY_ABSORBING) npart++; hsl_to_rgb(0.0, 0.0, 0.0, rgb); erase_area(xr, yr - 0.03, 0.4, 0.12, rgb); glColor3f(1.0, 1.0, 1.0); if (npart > 1) sprintf(message, "%i particles", npart); else sprintf(message, "%i particle", npart); write_text(xr, yr, message); } void print_collision_number(int ncollisions, double x, double y) { char message[50]; double rgb[3]; hsl_to_rgb(0.0, 0.0, 0.0, rgb); erase_area(x, y, 0.5, 0.1, rgb); glColor3f(1.0, 1.0, 1.0); sprintf(message, "%i collisions", ncollisions); write_text(x, y, message); } void animation() { double time, dt, alpha, r, rgb[3], alphamax; double *configs[NPARTMAX]; int i, j, resamp = 1, s, i1, i2, c, lengthmax; int *color, *newcolor, *active, *heatmap_number, *heatmap_total; short int *heatmap_visited; t_exit *exits; // t_circle *circles; /* experimental */ /* 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)); // circles = malloc(sizeof(t_circle)*(NMAXCIRCLES)); /* experimental */ if (HEATMAP) { heatmap_number = malloc(sizeof(int)*(NXMAZE*NYMAZE+1)); heatmap_total = malloc(sizeof(int)*(NXMAZE*NYMAZE+1)); heatmap_visited = malloc(sizeof(short int)*(NXMAZE*NYMAZE+1)); for (i=0; i= 0)&&(i<=1000)) // { // c = vbilliard(configs[0]); // i++; // } // if (i > 100) printf("Angle %.6lg, length %i\n", alpha, i); // if (i > lengthmax) // { // lengthmax = i; // alphamax = alpha; // } // } // printf("Angle %.6lg, max length %i\n", alphamax, lengthmax); // alphamax = 2.50949; // init_drop_config(x_shooter, y_shooter, alphamax, alphamax + DPI, configs); init_drop_config(-0.05, 0.05, 0.0, 0.3*PID, configs); // init_drop_config(-0.5, 0.0, 0.2, 0.4, configs); // init_drop_config(0.0, 0.05, 0.0, DPI, configs); // init_drop_config(-1.3, -0.1, 0.0, DPI, configs); // init_drop_config(1.4, 0.1, 0.0, DPI, configs); // init_drop_config(0.5, 0.5, -1.0, 1.0, configs); // init_sym_drop_config(-1.0, 0.5, -PID, PID, configs); // init_drop_config(-0.999, 0.0, -alpha, alpha, configs); // other possible initial conditions : // init_line_config(-1.3, -0.3, -1.2, -0.3, PID, configs); // init_line_config(0.0, 0.0, 0.5, 0.0, PID, configs); // init_line_config(0.0, 0.0, 0.0, -0.5, PI, configs); // init_line_config(-1.25, -0.5, -1.25, 0.5, 0.0*PID, configs); // init_line_config(-1.0, -0.3, -1.0, 0.3, 0.0, configs); // init_line_config(-0.7, -0.45, -0.7, 0.45, 0.0, configs); // init_line_config(-1.5, 0.1, -0.1, 1.0, -0.5*PID, configs); if (!SHOWTRAILS) blank(); glColor3f(0.0, 0.0, 0.0); if (DRAW_BILLIARD) draw_billiard(); if (PRINT_PARTICLE_NUMBER) print_part_number(configs, active, XMIN + 0.1, YMIN + 0.1); else if (PRINT_LEFT_RIGHT_PARTICLE_NUMBER) print_left_right_part_number(configs, active, XMIN + 0.1, YMIN + 0.05, XMAX - 0.45, YMIN + 0.05, exits); else if (PRINT_CIRCLE_PARTICLE_NUMBER) print_circle_part_number(configs, active, XMAX - 0.45, YMIN + 0.05); else if (PRINT_COLLISION_NUMBER) print_collision_number(ncollisions, XMIN + 0.1, YMIN + 0.1); glutSwapBuffers(); for (i=0; i