/* XBubble - path_init.c Copyright (C) 2002 Ivan Djelic Copyright (C) 2003 Martin Quinson This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "utils.h" #include "setting.h" #include "bubble.h" #include "cell.h" static int debug=1; /* To control the verbosity of the cache creation stuff */ /* xbubble_path_cache is what this program builds. Fake definition here to break dependency loop, to compile cell.c (which we need to load to get low level stuff, and which loads the result of this program by declaring 'extern int xbubble_path_cache') */ int xbubble_path_cache[2][ROWS][NB_ANGLES][2][TRAJ_MAX_LENGTH]; /* We use a cache to compute quickly were a launched ball will land */ typedef struct { Vector neighbor; Vector position; } LaunchPath; static int cellXY( CellArray ca, double x, double y ) { double rx, ry; int column, row, rest, a, b, c, e; e = ca->parity; rx = (x-(e+1.0)/4); ry = 2*ROW_HEIGHT*(y-0.5) + 1; a = (int) floor(rx+ry); b = (int) floor(ry-rx); c = (int) floor(2*x + (1-e)/2); row = (a+b)/3; rest = e*(row%2); column = (c+rest)>>1; return cellCL( ca, row, column ); } /* Algorithm for the target cell finding: (applied for each possible angle in target_cell_init() to fill the cache in) While the bubble did not reach the top: Compute the next segment the ball will cover, ie the position on which it will either stick on the ceiling (and end of the algorithm) or bump against a wall (before covering yet another segment Clip the searched area for colliding balls for efficiency For each ball in the covered area: if the trajectory of the ball touches the ball, store where, and after which distance on the segment Sort the array of reached position by lenght of the covered area Put it in the cache of paths. */ typedef struct { int neighbor; /* pos of the neighbor */ int position; /* where to go if neighbor is there */ double dist; /* Y distance covered on the segment to get there */ } possible_target_t; /** * possible_target_cmp: * * Helping function of cell_path_init() to qsort() between the different possible * target and get the closer one (which will be kept) */ int possible_target_cmp(const void *a, const void *b) { /* used to qsort */ return ((possible_target_t*)a)->dist < ((possible_target_t*)b)->dist ? -1 : (((possible_target_t*)a)->dist > ((possible_target_t*)b)->dist ? 1 : 0); } /** * compute_one_traj: * * FIXME: TODO * Compute one of the trajectories void compute_one_traj(int parity, int ceiling, int angle) { } */ /** * path_init: * * Computes the caches of trajectories path */ void path_init( void ) { int angle, i, ceiling,parity; double cdist= COLLISION_DIST*COLLISION_DIST; /* square of the collision distance */ CellArray ca; Vector neighbor, position; /* malloc the tables */ neighbor = vector_new(25); position = vector_new(25); ca=cell_array_new(1); /* for each parity */ for (parity=0;parity<2;parity++){ ca->parity *= -1; /* parity=0 => ca->parity=-1; p=1 => ca->p=1 */ printf(" /* parity=%d */ {\n", parity); for (ceiling=0; ceilingfirst_row=ceiling; printf(" /* ceiling=%d */ {\n",ceiling); for (angle=0; angle COLS - 0.5 ) { /* Too right: hits wall */ // xe = 2*COLS - 1.0 - xc; /* always stay inside board cells */ xe = COLS - 0.5; /* always stay inside board cells */ ye = yb + ( xe - xb )*vy/vx; } else { /* bubble hits ceiling: this segment is the last one */ bouncing = 0; xe = xc; ye = (0.5 + ceiling*ROW_HEIGHT); ceiling_pos=cellXY( ca, xe, ye ); } if (debug>1) fprintf(stderr,"[%.1f;%.1f -> %.1f;%.1f;vx=%f;vy=%f]\n",xb,yb,xe,ye,vx,vy); seg_len = ( ye - yb )/vy; /* segment length */ /* now scan cells and look for any colliding bubble */ /* try to minimize row range */ max_row = clip( (int) floor( yb / ROW_HEIGHT ) + 1, 0, ROWS-1 ); min_row = clip( (int) floor( ye / ROW_HEIGHT ) - 1, 0, ROWS-1 ); if (debug>1) fprintf(stderr,"Rows=%d-%d\n",min_row,max_row); /* scan from bottom to top */ for ( row = max_row; row >= min_row; row-- ) { cell = COLS*row; /* column range */ first_col = row_start( ca, row); /* 0 when parity=1; 1 when parity=-1 */ /* xc = intersection between row and bubble trajectory */ xc = xb + ( row*ROW_HEIGHT + 0.5 - yb )*vx/vy - ( 1.0 - first_col )/2; /* restrict cell scan to a small horizontal range : */ min_col = clip( (int) floor( xc - half_range +1 ), first_col, COLS-1 ); max_col = clip( (int) floor( xc + half_range ), first_col, COLS-1 ); if (debug>1) fprintf(stderr,"Row=%d; first_col=%d; half_range=%.1f;xc=%.1f Cols=%d-%d\n", row, first_col, half_range,xc,min_col,max_col); for ( col = min_col; col <= max_col; col++ ) { /* We have a potential collider in cell[col+cell], let's call it 'A'. We now check if A will intersect our bubble (let's call it 'M'). let's compute ( xa, ya ) = location of bubble A */ cell_center( ca, col + cell, &xa, &ya ); if (debug>1) fprintf(stderr,"%d (%.1f;%.1f)=>",col+cell,xa,ya); /* dot product: used to check if bubble M is approaching or leaving bubble A (hence the ( dp > 0 ) test ) */ dp = vx*( xa - xb ) + vy*( ya - yb ); if ( dp > 0 ) { /* Let v be the velocity vector of bubble M. Let theta = angle(v,MA). Then, let H be the orthogonal projection of A on (M,v): A + /| / | / | / | / | / |_ M +->..|.|.......... v H There will be a collision if AH <= 1. Thus, we compute AH using a vector product : vp = | v /\ MA | = |v.MA.sin(theta)| Since v and MA are in the same plane we have: vp = | vy*( xa - xb ) - vx*( ya - yb ) | AH = |MA.sin(theta)| = vp/v But instead of computing v = sqrt(v2), we compute vp2 = vp.vp = v2.AH.AH, such that : AH = sqrt( vp2 / v2 ) Now the test AH <= 1 becomes vp2 <= v2. */ double vp, vp2; vp = vy*( xa - xb ) - vx*( ya - yb ); vp2 = vp*vp; /* Strictly speaking, if AH <= 1 then bubbles will intersect. But we relax this condition for easier aiming: AH <= COLLISION_DIST */ if ( vp2 <= cdist*v2 ) { double dist; /* We have a collision. But where will M be when it touches A ? In point T = M + dist.v : ( T is on (M,v), between M and H and such that TA = COLLISION_DIST ) */ dist = ( dp - sqrt( v2*cdist - vp2 ) )/v2; if (debug>1) fprintf(stderr,"(d=%f) ",dist); if (dist >= 0 && dist <= seg_len) { xt = xb + vx*dist; yt = yb + vy*dist; // if (xt < 0.5) xt=1-xt; // if (xt > COLS - 0.5001) xt = 2*COLS - 1.0002 - xt; targets[target_count].neighbor=col+cell; targets[target_count].position=cellXY( ca, xt, yt ); targets[target_count].dist=dist; if (targets[target_count].position == -1) { if (debug>1) { fprintf(stderr,"Pos (%.1f;%.1f) => -1 because of %d\n",xt,yt,col+cell); } if (debug>2) abort(); } else { target_count++; } if (debug>1) fprintf(stderr,"%d\n",targets[target_count-1].position); } else {if (debug>1) fprintf (stderr,"dist=%.1f<0;\n",dist); } } else {if (debug>1) fprintf (stderr,"too far;\n");} } else { if (debug>1) fprintf (stderr,"dp=%.1f<0;{vx=%.1f,xa-xb=%.1f;vy=%.1f,ya-yb=%.1f}\n",dp,vx,xa-xb,vy,ya-yb); } } } /* sort out the targets found on this segment, and push them to the cache */ qsort(targets,target_count, sizeof(possible_target_t),&possible_target_cmp); for (i=0; isize >= TRAJ_MAX_LENGTH) fail("the constant TRAJ_MAX_LENGTH is too small (%d=<%d). Please edit setting.h", TRAJ_MAX_LENGTH,neighbor->size); printf(" /* neighbor */ {"); for (i=0;isize ? neighbor->element[i] : (i==neighbor->size ? OUT_OF_BOUNDS : 0 )), (isize ? position->element[i] : (i==position->size ? ceiling_pos : 0 )), (i