717 lines
15 KiB
C
717 lines
15 KiB
C
// ************************************************************************
|
|
//
|
|
// 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 <stdio.h>
|
|
#include <math.h>
|
|
#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(xOver<yOver)
|
|
{
|
|
/* reduce x-dim to minimize oversized */
|
|
dx = maxDX;
|
|
dy = h+1;
|
|
}
|
|
else
|
|
{
|
|
/* are we making progress? */
|
|
if(yOver == count) return FALSE;
|
|
|
|
/* reduce y-dim to minimize oversized */
|
|
dx = w+1;
|
|
dy = maxDY;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* tentative number of bins */
|
|
xDim = roundUp(w,dx)/dx;
|
|
yDim = roundUp(h,dy)/dy;
|
|
numBins = xDim*((double)yDim);
|
|
|
|
|
|
/* if too many bins, need to increase at least one dimension */
|
|
/* (note this step will NOT reduce dimensions) */
|
|
maxBins = MAX(count / bpMinAvgBinPop,1);
|
|
/*
|
|
fprintf(stderr,"DEBUG numBins = %g count= %d bpMinAvgBinPop=%f maxBins= %d\n",
|
|
numBins,count,bpMinAvgBinPop,maxBins);
|
|
*/
|
|
if(numBins>maxBins)
|
|
{
|
|
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(d<dx)
|
|
{
|
|
/* target d too small in x-dim
|
|
* leave xdim fixed and just increase y-dim
|
|
*/
|
|
int yDimTarget = maxBins/xDim;
|
|
|
|
dy = (h+1) / MAX(yDimTarget,1);
|
|
dy = MIN(dy,maxDY);
|
|
}
|
|
else if (d<dy)
|
|
{
|
|
/* target d too small in y-dim
|
|
* leave xdim fixed and just increase y-dim
|
|
*/
|
|
int xDimTarget = maxBins/yDim;
|
|
|
|
dx = (w+1) / MAX(xDimTarget,1);
|
|
dx = MIN(dx,maxDX);
|
|
}
|
|
else if(d>maxDX)
|
|
{
|
|
/* 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;i<numBins;i++)
|
|
{
|
|
BinArray *sub;
|
|
|
|
sub = bpBinArrayBuild(bpBinArea(ba,i),
|
|
bpBinList(ba, i),
|
|
TRUE);
|
|
|
|
if(sub)
|
|
{
|
|
ba->ba_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); */
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|