📄 ptinpoly.c
字号:
/* first, is point inside bounding rectangle? */ if ( ( ty = point[Y] ) < p_trap_set->miny || ty >= p_trap_set->maxy || ( tx = point[X] ) < p_trap_set->minx || tx >= p_trap_set->maxx ) { /* outside of box */ return( 0 ) ; } /* what bin are we in? */ b = ( ty - p_trap_set->miny ) * p_trap_set->inv_ydelta ; /* find if we're inside this bin's bounds */ if ( tx < (p_trap = &p_trap_set->trapz[b])->minx || tx > p_trap->maxx ) { /* outside of box */ return( 0 ) ; } /* now search bin for crossings */ pp_bin = p_trap->edge_set ; count = p_trap->count ; for ( j = 0 ; j < count ; j++, pp_bin++ ) { if ( tx < (*pp_bin)->minx ) { /* all remaining edges are to right of point, so test them */ do { if ( (*pp_bin)->full_cross ) { inside_flag = !inside_flag ; } else { id = (*pp_bin)->id ; if ( ( ty <= pgon[id][Y] ) != ( ty <= pgon[(id+1)%numverts][Y] ) ) { /* point crosses edge in Y, so must cross */ inside_flag = !inside_flag ; } } pp_bin++ ; } while ( ++j < count ) ; goto Exit; } else if ( tx < (*pp_bin)->maxx ) { /* edge is overlapping point in X, check it */ id = (*pp_bin)->id ; vtx0 = pgon[id] ; vtx1 = pgon[(id+1)%numverts] ; if ( (*pp_bin)->full_cross || ( ty <= vtx0[Y] ) != ( ty <= vtx1[Y] ) ) { /* edge crosses in Y, so have to do full crossings test */ if ( (vtx0[X] - (vtx0[Y] - ty )* ( vtx1[X]-vtx0[X])/(vtx1[Y]-vtx0[Y])) >= tx ) { inside_flag = !inside_flag ; } } } /* else edge is to left of point, ignore it */ } Exit: return( inside_flag ) ;}void TrapezoidCleanup( p_trap_set )pTrapezoidSet p_trap_set ;{int i, j, count ; for ( i = 0 ; i < p_trap_set->bins ; i++ ) { /* all of these should have bin sets, but check just in case */ if ( p_trap_set->trapz[i].edge_set ) { count = p_trap_set->trapz[i].count ; for ( j = 0 ; j < count ; j++ ) { if ( p_trap_set->trapz[i].edge_set[j] ) { free( p_trap_set->trapz[i].edge_set[j] ) ; } } free( p_trap_set->trapz[i].edge_set ) ; } } free( p_trap_set->trapz ) ;}/* ======= Grid algorithm ================================================= *//* Impose a grid upon the polygon and test only the local edges against the * point. * * Call setup with 2D polygon _pgon_ with _numverts_ number of vertices, * grid resolution _resolution_ and a pointer to a grid structure _p_gs_. * Call testing procedure with a pointer to this array and test point _point_, * returns 1 if inside, 0 if outside. * Call cleanup with pointer to grid structure to free space. *//* Strategy for setup: * Get bounds of polygon, allocate grid. * "Walk" each edge of the polygon and note which edges have been crossed * and what cells are entered (points on a grid edge are always considered * to be above that edge). Keep a record of the edges overlapping a cell. * For cells with edges, determine if any cell border has no edges passing * through it and so can be used for shooting a test ray. * Keep track of the parity of the x (horizontal) grid cell borders for * use in determining whether the grid corners are inside or outside. */void GridSetup( pgon, numverts, resolution, p_gs )double pgon[][2] ;int numverts ;int resolution ;pGridSet p_gs ;{double *vtx0, *vtx1, *vtxa, *vtxb, *p_gl ;int i, j, gc_clear_flags ;double vx0, vx1, vy0, vy1, gxdiff, gydiff, eps ;pGridCell p_gc, p_ngc ;double xdiff, ydiff, tmax, inv_x, inv_y, xdir, ydir, t_near, tx, ty ;double tgcx, tgcy ;int gcx, gcy, sign_x ;int y_flag, io_state ; p_gs->xres = p_gs->yres = resolution ; p_gs->tot_cells = p_gs->xres * p_gs->yres ; p_gs->glx = (double *)malloc( (p_gs->xres+1) * sizeof(double)); MALLOC_CHECK( p_gs->glx ) ; p_gs->gly = (double *)malloc( (p_gs->yres+1) * sizeof(double)); MALLOC_CHECK( p_gs->gly ) ; p_gs->gc = (pGridCell)malloc( p_gs->tot_cells * sizeof(GridCell)); MALLOC_CHECK( p_gs->gc ) ; p_gs->minx = p_gs->maxx = pgon[0][X] ; p_gs->miny = p_gs->maxy = pgon[0][Y] ; /* find bounds of polygon */ for ( i = 1 ; i < numverts ; i++ ) { vx0 = pgon[i][X] ; if ( p_gs->minx > vx0 ) { p_gs->minx = vx0 ; } else if ( p_gs->maxx < vx0 ) { p_gs->maxx = vx0 ; } vy0 = pgon[i][Y] ; if ( p_gs->miny > vy0 ) { p_gs->miny = vy0 ; } else if ( p_gs->maxy < vy0 ) { p_gs->maxy = vy0 ; } } /* add a little to the bounds to ensure everything falls inside area */ gxdiff = p_gs->maxx - p_gs->minx ; gydiff = p_gs->maxy - p_gs->miny ; p_gs->minx -= EPSILON * gxdiff ; p_gs->maxx += EPSILON * gxdiff ; p_gs->miny -= EPSILON * gydiff ; p_gs->maxy += EPSILON * gydiff ; /* avoid roundoff problems near corners by not getting too close to them */ eps = 1e-9 * ( gxdiff + gydiff ) ; /* use the new bounds to compute cell widths */ TryAgain: p_gs->xdelta = (p_gs->maxx-p_gs->minx) / (double)p_gs->xres ; p_gs->inv_xdelta = 1.0 / p_gs->xdelta ; p_gs->ydelta = (p_gs->maxy-p_gs->miny) / (double)p_gs->yres ; p_gs->inv_ydelta = 1.0 / p_gs->ydelta ; for ( i = 0, p_gl = p_gs->glx ; i < p_gs->xres ; i++ ) { *p_gl++ = p_gs->minx + i * p_gs->xdelta ; } /* make last grid corner precisely correct */ *p_gl = p_gs->maxx ; for ( i = 0, p_gl = p_gs->gly ; i < p_gs->yres ; i++ ) { *p_gl++ = p_gs->miny + i * p_gs->ydelta ; } *p_gl = p_gs->maxy ; for ( i = 0, p_gc = p_gs->gc ; i < p_gs->tot_cells ; i++, p_gc++ ) { p_gc->tot_edges = 0 ; p_gc->gc_flags = 0x0 ; p_gc->gr = NULL ; } /* loop through edges and insert into grid structure */ vtx0 = pgon[numverts-1] ; for ( i = 0 ; i < numverts ; i++ ) { vtx1 = pgon[i] ; if ( vtx0[Y] < vtx1[Y] ) { vtxa = vtx0 ; vtxb = vtx1 ; } else { vtxa = vtx1 ; vtxb = vtx0 ; } /* Set x variable for the direction of the ray */ xdiff = vtxb[X] - vtxa[X] ; ydiff = vtxb[Y] - vtxa[Y] ; tmax = sqrt( xdiff * xdiff + ydiff * ydiff ) ; /* if edge is of 0 length, ignore it (useless edge) */ if ( tmax == 0.0 ) goto NextEdge ; xdir = xdiff / tmax ; ydir = ydiff / tmax ; gcx = (int)(( vtxa[X] - p_gs->minx ) * p_gs->inv_xdelta) ; gcy = (int)(( vtxa[Y] - p_gs->miny ) * p_gs->inv_ydelta) ; /* get information about slopes of edge, etc */ if ( vtxa[X] == vtxb[X] ) { sign_x = 0 ; tx = HUGE ; } else { inv_x = tmax / xdiff ; tx = p_gs->xdelta * (double)gcx + p_gs->minx - vtxa[X] ; if ( vtxa[X] < vtxb[X] ) { sign_x = 1 ; tx += p_gs->xdelta ; tgcx = p_gs->xdelta * inv_x ; } else { sign_x = -1 ; tgcx = -p_gs->xdelta * inv_x ; } tx *= inv_x ; } if ( vtxa[Y] == vtxb[Y] ) { ty = HUGE ; } else { inv_y = tmax / ydiff ; ty = (p_gs->ydelta * (double)(gcy+1) + p_gs->miny - vtxa[Y]) * inv_y ; tgcy = p_gs->ydelta * inv_y ; } p_gc = &p_gs->gc[gcy*p_gs->xres+gcx] ; vx0 = vtxa[X] ; vy0 = vtxa[Y] ; t_near = 0.0 ; do { /* choose the next boundary, but don't move yet */ if ( tx <= ty ) { gcx += sign_x ; ty -= tx ; t_near += tx ; tx = tgcx ; /* note which edge is hit when leaving this cell */ if ( t_near < tmax ) { if ( sign_x > 0 ) { p_gc->gc_flags |= GC_R_EDGE_HIT ; vx1 = p_gs->glx[gcx] ; } else { p_gc->gc_flags |= GC_L_EDGE_HIT ; vx1 = p_gs->glx[gcx+1] ; } /* get new location */ vy1 = t_near * ydir + vtxa[Y] ; } else { /* end of edge, so get exact value */ vx1 = vtxb[X] ; vy1 = vtxb[Y] ; } y_flag = FALSE ; } else { gcy++ ; tx -= ty ; t_near += ty ; ty = tgcy ; /* note top edge is hit when leaving this cell */ if ( t_near < tmax ) { p_gc->gc_flags |= GC_T_EDGE_HIT ; /* this toggles the parity bit */ p_gc->gc_flags ^= GC_T_EDGE_PARITY ; /* get new location */ vx1 = t_near * xdir + vtxa[X] ; vy1 = p_gs->gly[gcy] ; } else { /* end of edge, so get exact value */ vx1 = vtxb[X] ; vy1 = vtxb[Y] ; } y_flag = TRUE ; } /* check for corner crossing, then mark the cell we're in */ if ( !AddGridRecAlloc( p_gc, vx0, vy0, vx1, vy1, eps ) ) { /* warning, danger - we have just crossed a corner. * There are all kinds of topological messiness we could * do to get around this case, but they're a headache. * The simplest recovery is just to change the extents a bit * and redo the meshing, so that hopefully no edges will * perfectly cross a corner. Since it's a preprocess, we * don't care too much about the time to do it. */ /* clean out all grid records */ for ( i = 0, p_gc = p_gs->gc ; i < p_gs->tot_cells ; i++, p_gc++ ) { if ( p_gc->gr ) { free( p_gc->gr ) ; } } /* make the bounding box ever so slightly larger, hopefully * changing the alignment of the corners. */ p_gs->minx -= EPSILON * gxdiff * 0.24 ; p_gs->miny -= EPSILON * gydiff * 0.10 ; /* yes, it's the dreaded goto - run in fear for your lives! */ goto TryAgain ; } if ( t_near < tmax ) { /* note how we're entering the next cell */ /* TBD: could be done faster by incrementing index in the * incrementing code, above */ p_gc = &p_gs->gc[gcy*p_gs->xres+gcx] ; if ( y_flag ) { p_gc->gc_flags |= GC_B_EDGE_HIT ; /* this toggles the parity bit */ p_gc->gc_flags ^= GC_B_EDGE_PARITY ; } else { p_gc->gc_flags |= ( sign_x > 0 ) ? GC_L_EDGE_HIT : GC_R_EDGE_HIT ; } } vx0 = vx1 ; vy0 = vy1 ; } /* have we gone further than the end of the edge? */ while ( t_near < tmax ) ; NextEdge: vtx0 = vtx1 ; } /* the grid is all set up, now set up the inside/outside value of each * corner. */ p_gc = p_gs->gc ; p_ngc = &p_gs->gc[p_gs->xres] ; /* we know the bottom and top rows are all outside, so no flag is set */ for ( i = 1; i < p_gs->yres ; i++ ) { /* start outside */ io_state = 0x0 ; for ( j = 0; j < p_gs->xres ; j++ ) { if ( io_state ) { /* change cell left corners to inside */ p_gc->gc_flags |= GC_TL_IN ; p_ngc->gc_flags |= GC_BL_IN ; } if ( p_gc->gc_flags & GC_T_EDGE_PARITY ) { io_state = !io_state ; } if ( io_state ) { /* change cell right corners to inside */ p_gc->gc_flags |= GC_TR_IN ; p_ngc->gc_flags |= GC_BR_IN ; } p_gc++ ; p_ngc++ ; } } p_gc = p_gs->gc ; for ( i = 0; i < p_gs->tot_cells ; i++ ) { /* reverse parity of edge clear (1==edge clear) */ gc_clear_flags = p_gc->gc_flags ^ GC_ALL_EDGE_CLEAR ; if ( gc_clear_flags & GC_L_EDGE_CLEAR ) { p_gc->gc_flags |= GC_AIM_L ; } else if ( gc_clear_flags & GC_B_EDGE_CLEAR ) { p_gc->gc_flags |= GC_AIM_B ; } else if ( gc_clear_flags & GC_R_EDGE_CLEAR ) { p_gc->gc_flags |= GC_AIM_R ; } else if ( gc_clear_flags & GC_T_EDGE_CLEAR ) { p_gc->gc_flags |= GC_AIM_T ; } else { /* all edges have something on them, do full test */ p_gc->gc_flags |= GC_AIM_C ; } p_gc++ ; }}int AddGridRecAlloc( p_gc, xa, ya, xb, yb, eps )pGridCell p_gc ;double xa,ya,xb,yb,eps ;{pGridRec p_gr ;double slope, inv_slope ; if ( Near(ya, yb, eps) ) { if ( Near(xa, xb, eps) ) { /* edge is 0 length, so get rid of it */ return( FALSE ) ; } else { /* horizontal line */ slope = HUGE ; inv_slope = 0.0 ; } } else { if ( Near(xa, xb, eps) ) { /* vertical line */ slope = 0.0 ; inv_slope = HUGE ; } else { slope = (xb-xa)/(yb-ya) ; inv_slope = (yb-ya)/(xb-xa) ; } } p_gc->tot_edges++ ; if ( p_gc->tot_edges <= 1 ) { p_gc->gr = (pGridRec)malloc( sizeof(GridRec) ) ; } else { p_gc->gr = (pGridRec)realloc( p_gc->gr, p_gc->tot_edges * sizeof(GridRec) ) ; } MALLOC_CHECK( p_gc->gr ) ; p_gr = &p_gc->gr[p_gc->tot_edges-1] ; p_gr->slope = slope ; p_gr->inv_slope = inv_slope ; p_gr->xa = xa ; p_gr->ya = ya ; if ( xa <= xb ) { p_gr->minx = xa ; p_gr->maxx = xb ; } else { p_gr->minx = xb ; p_gr->maxx = xa ; } if ( ya <= yb ) { p_gr->miny = ya ; p_gr->maxy = yb ; } else { p_gr->miny = yb ; p_gr->maxy = ya ; } /* P2 - P1 */ p_gr->ax = xb - xa ; p_gr->ay = yb - ya ; return( TRUE ) ;}/* Test point against grid and edges in the cell (if any). Algorithm: * Check bounding box; if outside then return. * Check cell point is inside; if simple inside or outside then return. * Find which edge or corner is considered to be the best for testing and * send a test ray towards it, counting the crossings. Add in the * state of the edge or corner the ray went to and so determine the * state of the point (inside or outside). */int GridTest( p_gs, point )register pGridSet p_gs ;double point[2] ;{int j, count, init_flag ;pGridCell p_gc ;pGridRec p_gr ;double tx, ty, xcell, ycell, bx,by,cx,cy, cornerx, cornery ;double alpha, beta, denom ;unsigned short gc_flags ;int inside_flag ; /* first, is point inside bounding rectangle? */ if ( ( ty = point[Y] ) < p_gs->miny || ty >= p_gs->maxy || ( tx = point[X] ) < p_gs->minx || tx >= p_gs->maxx ) { /* outside of box */ inside_flag = FALSE ; } else { /* what cell are we in? */ ycell = ( ty - p_gs->miny ) * p_gs->inv_ydelta ; xcell = ( tx - p_gs->minx ) * p_gs->inv_xdelta ; p_gc = &p_gs->gc[((int)ycell)*p_gs->xres + (int)xcell] ; /* is cell simple? */ count = p_gc->tot_edges ; if ( count ) { /* no, so find an edge which is free. */ gc_flags = p_gc->gc_flags ; p_gr = p_gc->gr ; switch( gc_flags & GC_AIM ) { case GC_AIM_L: /* left edge is clear, shoot X- ray */ /* note - this next statement requires that GC_BL_IN is 1 */ inside_flag = gc_flags & GC_BL_IN ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -