// ************************************************************************ // // Copyright (c) 1995-2002 Juniper Networks, Inc. All rights reserved. // // Permission is hereby granted, without written agreement and without // license or royalty fees, to use, copy, modify, and distribute this // software and its documentation for any purpose, provided that the // above copyright notice and the following three paragraphs appear in // all copies of this software. // // IN NO EVENT SHALL JUNIPER NETWORKS, INC. BE LIABLE TO ANY PARTY FOR // DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF // JUNIPER NETWORKS, INC. HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH // DAMAGE. // // JUNIPER NETWORKS, INC. SPECIFICALLY DISCLAIMS ANY WARRANTIES, // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND // NON-INFRINGEMENT. // // THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND JUNIPER // NETWORKS, INC. HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. // // ************************************************************************ /* bpBins.c * * Routines for creating and manipulating bin arrays. * */ #include #include #include "utils/utils.h" #include "utils/malloc.h" #include "database/database.h" #include "utils/geometry.h" #include "bplane/bplaneInt.h" /* debug */ #define BPD 0 /* Tcl linked Parameters */ int bpMinBAPop = 10; /* don't sub(bin) when count less than this */ double bpMinAvgBinPop = 1.0; /* try to keep average bin pop at or * below this */ /* * ---------------------------------------------------------------------------- * * roundUp -- Round up a number to a grid. * * ---------------------------------------------------------------------------- */ static __inline__ int roundUp(int i, int res) { int r = (i % res); /* Subtract negative number */ if (r > 0) r = r - res; return i - r; } /* * ---------------------------------------------------------------------------- * * bpBinArrayNew -- allocate new bin array. * * ---------------------------------------------------------------------------- */ static BinArray *bpBinArrayNew(int dx, /* x diameter of bins */ int dy, /* y diameter of bins */ Rect *bbox) /* area covered */ { BinArray *new; Rect abbox; int w, h, dimX, dimY, numBins; int size; /* compute array dimensions */ w = roundUp(GEO_WIDTH(bbox),dx); h = roundUp(GEO_HEIGHT(bbox),dy); dimX = w/dx; dimY = h/dy; numBins = dimX*dimY; /* allocate array */ size = sizeof(BinArray) + numBins*(sizeof(void *)); new = (BinArray *)callocMagic(size); /* initial */ new->ba_bbox = *bbox; new->ba_dx = dx; new->ba_dy = dy; new->ba_dimX = dimX; new->ba_numBins = numBins; /* pull bbox back one from top-edge, right-edge, to simplify index * computation in bpEnumPush */ new->ba_bbox.r_xtop --; new->ba_bbox.r_ytop --; return new; } /* * ---------------------------------------------------------------------------- * * bpBinAdd -- add element to bin array * * ---------------------------------------------------------------------------- */ void bpBinAdd(BinArray *ba, Element *e) { int i; /* bin index */ /* compute bin index */ if(GEO_WIDTH(&e->e_rect) >= ba->ba_dx || GEO_HEIGHT(&e->e_rect) >= ba->ba_dy) { /* oversized */ i = ba->ba_numBins; } else { /* x and y indices */ int xi = (e->e_rect.r_xbot - ba->ba_bbox.r_xbot) / ba->ba_dx; int yi = (e->e_rect.r_ybot - ba->ba_bbox.r_ybot) / ba->ba_dy; i = xi + yi*ba->ba_dimX ; } /* add element */ if(bpBinType(ba,i) == BT_ARRAY) { /* sub-binned */ bpBinAdd(bpSubArray(ba,i), e); } else { /* simple list */ Element *next = bpBinList(ba,i); /* link with next */ e->e_link = next; if(next) next->e_linkp = &e->e_link; /* link to head */ ba->ba_bins[i] = e; e->e_linkp = (Element **) &ba->ba_bins[i]; } } /* * ---------------------------------------------------------------------------- * * bpBinArrayUnbuild - remove elements from bin array and Free the array * * Returns: (singly linked) list of elements formerly in the array * * ---------------------------------------------------------------------------- */ static Element *bpBinArrayUnbuild(BinArray *ba) { Element *elements = NULL; int numBins = ba->ba_numBins; int i; /* empty the bins */ for(i=0;i<=numBins;i++) { Element *l; if(bpBinType(ba,i) == BT_ARRAY) { /* sub-array, unbuild recursively */ l = bpBinArrayUnbuild(bpSubArray(ba,i)); } else { /* Simple list */ l = bpBinList(ba,i); } /* collect elements */ while(l) { Element *e = l; l = e->e_link; e->e_link = elements; elements = e; } } /* free the array */ freeMagic((char *)ba); return elements; } /* * ---------------------------------------------------------------------------- * bpListExceedsQ -- * * check if element list exceeds given length * * Returns size of list. * * ---------------------------------------------------------------------------- */ static __inline__ int bpListExceedsQ(Element *e, /* list */ int n) /* length to check against */ { n++; while(e && n) { n--; e = e->e_link; } return n==0; } /* * ---------------------------------------------------------------------------- * * bpBinArraySizeIt -- choose bin sizes for new bin array. * * RESULT: * * normally returns TRUE, * returns FALSE on failure: could not come up with binning that * makes progress. * * NOTE: the various 'return' parameters are not set on failure. * * ---------------------------------------------------------------------------- */ static __inline__ bool bpBinArraySizeIt(Rect *bbox, /* bin array bbox */ Element *elements, /* initial elements */ int *dxp, /* return bin x-diameter here */ int *dyp, /* return bin y-diameter here */ int *maxDXp, int *maxDYp, int *numBinsp, /* return number of bins here */ int *countp) /* return number of elements here */ { BinArray *ba; int count; double numBins; /* need double to avoid overflow on tentative calc. */ int h = GEO_HEIGHT(bbox); int w = GEO_WIDTH(bbox); int dx,dy; /* individual bin diameter */ int maxEX, maxEY; /* max element dimensions */ int maxDX, maxDY; /* max bin diameter allowed */ int xDim, yDim; /* array dimensions */ int maxBins; /* max number of bins * (due to bpMinAvgBinPop) */ /* compute max element dimensions * (would like bins coarser than max dimensisons) */ { Element *e; maxEX = 0; maxEY = 0; count = 0; for(e=elements; e; e=e->e_link) { int ew = GEO_WIDTH(&e->e_rect); int eh = GEO_HEIGHT(&e->e_rect); maxEX = MAX(maxEX,ew); maxEY = MAX(maxEY,eh); count++; } } /* if too few elements, don't bother with binning */ if(count < bpMinBAPop) return FALSE; /* if too tiny don't subbin, * avoid nasty corner-cases in code below */ if(h<2 || w<2) return FALSE; /* tentatively choose bin size to fit all elements */ dx = maxEX+1; dy = maxEY+1; /* ensure we get at least two bins, so that * subbining of sparse designs will work. */ maxDX = (w+1)/2; maxDY = (h+1)/2; /* fprintf(stderr,"bpBinArraySizeIt, initial " "maxEX=%d maxEY=%d maxDX=%d maxDY=%d dx=%d dy=%d\n", maxEX,maxEY,maxDX,maxDY,dx,dy); */ if(dx <= maxDX) { /* x is cool */ if(dy <= maxDY) { /* totally cool */ ; } else { /* y-dim too big for two bins, but x-dim cool, * just reduce in x this time. */ dy = h+1; } } else { /* x-dim too big for two bins */ if(dy <= maxDY) { /* x-dim too big for two bins but y=dim cool, * just reduce in y this time */ dx = w+1; } else { /* BOTH x-dim and y-dim too big for two bins. * We are screwed: will have some oversized. * * Choose betwen splitting in two horizontally or * vertically, by which ever method results in the minimum * number of oversized elements. */ int xOver=0; /* number of oversized if we reduce x-dim. */ int yOver=0; /* number of oversized if we reduce y-dim. */ Element *e; /* count potential oversized */ for(e=elements; e; e=e->e_link) { int ew = GEO_WIDTH(&e->e_rect); int eh = GEO_HEIGHT(&e->e_rect); if(ew >= maxDX) xOver++; if(eh >= maxDY) yOver++; } if(xOvermaxBins) { if(dx == w+1) { /* can't increase x-dim, so try increasing y-dim */ int yDimTarget = maxBins/xDim; dy = (h+1) / MAX(yDimTarget,1); dy = MIN(dy,maxDY); } else if (dy == h+1) { /* can't increase y-dim, so try increasing x-dim */ int xDimTarget = maxBins/yDim; dx = (w+1) / MAX(xDimTarget,1); dx = MIN(dx,maxDX); } else { /* try for square bins */ double area = h * (w + 0.0); int d = MAX(sqrt(area/maxBins),1); if(dmaxDX) { /* d too big for x-dim * (this can happen for tall skinny bins) * * make x-dim maximal, and adjust y accordingly */ dx = w+1; dy = MAX((h+1)/maxBins,dy); dy = MIN(dy,maxDY); } else if(d>maxDY) { /* d too big for y-dim * (this can happen for long squat bins) * * make y-dim maximal, and adjust x-dim accordingly */ dy = h+1; dx = MAX((w+1)/maxBins,dx); dx = MIN(dx,maxDX); } else { /* we're cool, create square bins */ dx = d; dy = d; } } /* update numBins */ xDim = roundUp(w,dx)/dx; yDim = roundUp(h,dy)/dy; numBins = xDim*yDim; } /* DEBUG */ if(BPD) { fprintf(stderr,"\nDEBUG bpBinArraySizeIt DONE, count=%d h=%d w=%d\n" "\tmaxDX=%d maxDY=%d maxBins=%d\n" "\tnumBins=%g dx=%d dy=%d\n", count,h,w, maxDX,maxDY,maxBins, numBins,dx,dy); } /* set results */ if(dxp) *dxp = dx; if(dyp) *dyp = dy; if(maxDXp) *maxDXp = maxDX; if(maxDYp) *maxDYp = maxDY; if(numBinsp) *numBinsp = numBins; if(countp) *countp = count; /* success */ return TRUE; } /* * ---------------------------------------------------------------------------- * * bpBinArrayBuild1 -- build and populate bin array of given area and * bin size. * * Returns: pointer to new bin array. * * ---------------------------------------------------------------------------- */ static BinArray *bpBinArrayBuild1(Rect *bbox, Element *elements, /* initial elements */ int dx, /* bin diameter */ int dy) { BinArray *ba; /* build bin array */ ba = bpBinArrayNew(dx, dy, bbox); /* transfer elements to bin array */ while(elements) { Element *e; /* pop list */ e = elements; elements = e->e_link; bpBinAdd(ba, e); } return ba; } /* * ---------------------------------------------------------------------------- * * bpBinArrayBuild -- build and populate bin array of given area, * * NOTE: optimal bin size determined by trial and error. * oversized subbinned, as indicated. * * Returns: pointer to new bin array, NULL on failure. * * ---------------------------------------------------------------------------- */ BinArray *bpBinArrayBuild(Rect bbox, Element *elements, /* initial elements */ bool subbin) /* subbin as needed */ { BinArray *ba; int dx,dy; /* individual bin diameter */ int maxDX, maxDY; int numBins; int count; if(BPD) DumpRect("#### bpBinArrayBuild, TOP bbox= ", &bbox); /* figure out good bin dimensions */ if(!bpBinArraySizeIt(&bbox, elements, &dx, &dy, &maxDX, &maxDY, &numBins, &count)) return NULL; /* build the bin array */ ba = bpBinArrayBuild1(&bbox, elements, dx, dy); if(!subbin) return ba; /* sub-bin normal bins */ { int dimX = ba->ba_dimX; int i; for(i=0;iba_bins[i] = (void *) ((pointertype) sub | BT_ARRAY); } } } /* sub-bin oversized */ { BinArray *sub; sub = bpBinArrayBuild(bbox, bpBinList(ba, numBins), TRUE); if(sub) { ba->ba_bins[numBins] = (void *) ((pointertype) sub | BT_ARRAY); } } if(BPD) { DumpRect("\n#### bpBinArrayBuild, DONE bbox= ", &bbox); fprintf(stderr,"\n"); } return ba; } /* * ---------------------------------------------------------------------------- * * bpBinsUpdate -- update bplane bins * * Called prior to enumerations. * * ---------------------------------------------------------------------------- */ int bpBinLife = 0; void bpBinsUpdate(BPlane *bp) { Rect bbox; bool oldBins; /* rebuild whenever inbox gets big */ if(!bpListExceedsQ(bp->bp_inBox, bpMinBAPop-1)) return; /* fprintf(stderr,"DEBUG bpBinsUpdate - rebuilding bins.\n"); */ /* do bins already exist ? */ oldBins = (bp->bp_rootNode != 0); /* if bins exist, dissolve them */ if(oldBins) { Element *elist = bpBinArrayUnbuild(bp->bp_rootNode); /* add inbox to list */ while(bp->bp_inBox) { /* pop from inbox */ Element *e = bp->bp_inBox; bp->bp_inBox = e->e_link; /* add to elist */ e->e_link = elist; elist = e; } bp->bp_inBox = elist; } /* compute accurate bbox */ { Element *e = bp->bp_inBox; bbox = e->e_rect; for(e=bp->bp_inBox; e; e=e->e_link) { GeoIncludeRectInBBox(&e->e_rect, &bbox); } } /* if rebuild, double bounding box, to avoid too many rebuilds */ if(oldBins) { int dx = GEO_WIDTH(&bbox)/2; int dy = GEO_HEIGHT(&bbox)/2; bbox.r_xbot -= dx; bbox.r_ybot -= dy; bbox.r_xtop += dx; bbox.r_ytop += dy; } /* build and populate bin array */ bp->bp_rootNode = bpBinArrayBuild(bbox, bp->bp_inBox, TRUE); if(bp->bp_rootNode) bp->bp_inBox = NULL; bp->bp_binArea = bbox; bp->bp_binLife = bpBinLife; bp->bp_inAdds = 0; /* if(BPD) bpDump(bp, 0); */ }