555 lines
17 KiB
C
555 lines
17 KiB
C
/*
|
|
* PlowWidth.c --
|
|
*
|
|
* Plowing.
|
|
* Determine the true minimum width of a piece of geometry.
|
|
*
|
|
* *********************************************************************
|
|
* * Copyright (C) 1985, 1990 Regents of the University of California. *
|
|
* * Permission to use, copy, modify, and distribute this *
|
|
* * software and its documentation for any purpose and without *
|
|
* * fee is hereby granted, provided that the above copyright *
|
|
* * notice appear in all copies. The University of California *
|
|
* * makes no representations about the suitability of this *
|
|
* * software for any purpose. It is provided "as is" without *
|
|
* * express or implied warranty. Export of this software outside *
|
|
* * of the United States of America may require an export license. *
|
|
* *********************************************************************
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/plow/PlowWidth.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "utils/magic.h"
|
|
#include "utils/geometry.h"
|
|
#include "tiles/tile.h"
|
|
#include "utils/hash.h"
|
|
#include "database/database.h"
|
|
#include "plow/plowInt.h"
|
|
|
|
struct wclip
|
|
{
|
|
Edge *wc_edge; /* Initial edge */
|
|
Rect wc_area; /* Area being clipped repeatedly */
|
|
};
|
|
|
|
int plowInitWidthFunc(), plowWidthFunc();
|
|
int plowInitWidthBackFunc(), plowWidthBackFunc();
|
|
|
|
#ifdef COUNTWIDTHCALLS
|
|
int plowWidthNumCalls = 0;
|
|
int plowWidthNumChoices = 0;
|
|
#endif /* COUNTWIDTHCALLS */
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plowFindWidth --
|
|
*
|
|
* Find the minimum width of material consisting of those types in the
|
|
* TileTypeBitMask 'types', starting the search from the Edge 'edge'.
|
|
*
|
|
* Results:
|
|
* Returns the minimum width.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
plowFindWidth(
|
|
Edge *edge, /* Edge along the LHS of this material */
|
|
const TileTypeBitMask *typesp,/* Types whose width is being computed.
|
|
*/
|
|
Rect *bbox, /* Bounding box of the cell */
|
|
Rect *prect) /* (Debugging only): if this is non-NULL,
|
|
* the rectangle it points to is filled
|
|
* in with the rectangle found by this
|
|
* algorithm to be the largest containing
|
|
* the edge 'edge' with only types in the
|
|
* set 'types'.
|
|
*/
|
|
{
|
|
TileTypeBitMask types = *typesp; /* TTMaskCopy(&types, typesp); */
|
|
Plane *plane = plowYankDef->cd_planes[edge->e_pNum];
|
|
TileTypeBitMask ctypes;
|
|
struct wclip wc;
|
|
int x, y;
|
|
|
|
TTMaskCom2(&ctypes, &types);
|
|
#ifdef COUNTWIDTHCALLS
|
|
plowWidthNumCalls++;
|
|
#endif /* COUNTWIDTHCALLS */
|
|
|
|
/*
|
|
* Start with wc.wc_area extending from the edge rightward to the
|
|
* cell boundary. Search right to find some tile (not necessarily
|
|
* the leftmost) that is not one of 'types'. This provides an
|
|
* upper bound on the minimum width, which we use to set wa.wa_area
|
|
* for the next loop.
|
|
*/
|
|
wc.wc_edge = edge;
|
|
wc.wc_area.r_xtop = bbox->r_xtop + 1;
|
|
wc.wc_area.r_xbot = edge->e_x;
|
|
wc.wc_area.r_ybot = edge->e_ybot;
|
|
wc.wc_area.r_ytop = edge->e_ytop;
|
|
#ifdef DEBUGWIDTH
|
|
TxPrintf("Initial area: X: %d .. %d Y: %d .. %d\n",
|
|
wc.wc_area.r_xbot, wc.wc_area.r_xtop,
|
|
wc.wc_area.r_ybot, wc.wc_area.r_ytop);
|
|
#endif /* DEBUGWIDTH */
|
|
(void) DBSrPaintArea((Tile *) NULL,
|
|
plane, &wc.wc_area, &ctypes,
|
|
plowInitWidthFunc, (ClientData) &wc);
|
|
#ifdef DEBUGWIDTH
|
|
TxPrintf("After first search, area: X: %d .. %d Y: %d .. %d\n",
|
|
wc.wc_area.r_xbot, wc.wc_area.r_xtop,
|
|
wc.wc_area.r_ybot, wc.wc_area.r_ytop);
|
|
#endif /* DEBUGWIDTH */
|
|
|
|
/*
|
|
* Repeatedly search wc.wc_area for tiles whose
|
|
* types are not in the set 'types'. Each time we find such a
|
|
* tile, we clip wc.wc_area so it still contains the original
|
|
* edge in its entirety, but excludes the offending tile.
|
|
* Continue until either the edge is degenerate (shouldn't
|
|
* happen!) or everything in wc_area is of types in 'types'.
|
|
*/
|
|
while (DBSrPaintArea((Tile *) NULL, plane, &wc.wc_area, &ctypes,
|
|
plowWidthFunc, (ClientData) &wc))
|
|
{
|
|
#ifdef DEBUGWIDTH
|
|
TxPrintf("Next area: X: %d .. %d Y: %d .. %d\n",
|
|
wc.wc_area.r_xbot, wc.wc_area.r_xtop,
|
|
wc.wc_area.r_ybot, wc.wc_area.r_ytop);
|
|
#endif /* DEBUGWIDTH */
|
|
if (wc.wc_area.r_xbot == wc.wc_area.r_xtop)
|
|
break;
|
|
}
|
|
|
|
if (prect) *prect = wc.wc_area;
|
|
x = wc.wc_area.r_xtop - wc.wc_area.r_xbot;
|
|
y = wc.wc_area.r_ytop - wc.wc_area.r_ybot;
|
|
#ifdef DEBUGWIDTH
|
|
TxPrintf("Width = %d\n", MIN(x, y));
|
|
#endif /* DEBUGWIDTH */
|
|
return (MIN(x, y));
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plowInitWidthFunc --
|
|
*
|
|
* Called by area search to obtain an initial estimate of the minimum
|
|
* width of a wire. The argument 'tile' will be of a type not included
|
|
* in the set 'types' passed to plowFindWidth() above.
|
|
*
|
|
* We set our upper bound on minimum width to be the distance between
|
|
* wc->wc_edge and the LHS of tile. This means that the search rectangle
|
|
* wc->wc_area has to be modified as follows:
|
|
*
|
|
* wc_area.r_xtop will be (upper bound) to the right of wc_edge
|
|
* wc_area.r_ybot will be (upper bound) below the top of wc_edge
|
|
* wc_area.r_ytop will be (upper bound) above the bottom of wc_edge
|
|
*
|
|
* Results:
|
|
* Returns 1 always.
|
|
*
|
|
* Side effects:
|
|
* Modifies wc->wc_area as described above.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
plowInitWidthFunc(tile, wc)
|
|
Tile *tile; /* Tile whose type is not among the types
|
|
* passed to plowFindWidth(), whose LHS will
|
|
* provide the right-hand boundary.
|
|
*/
|
|
struct wclip *wc; /* Contains original edge and area to clip */
|
|
{
|
|
Edge *edge = wc->wc_edge;
|
|
int upperBound = LEFT(tile) - edge->e_x;
|
|
|
|
#ifdef DEBUGWIDTH
|
|
TxPrintf("Tile type = %s, edgeHeight = %d, upperBound = %d\n",
|
|
DBTypeLongName(TiGetTypeExact(tile)), edge->e_ytop-edge->e_ybot, upperBound);
|
|
#endif /* DEBUGWIDTH */
|
|
wc->wc_area.r_ytop = MAX(edge->e_ybot + upperBound, edge->e_ytop);
|
|
wc->wc_area.r_ybot = MIN(edge->e_ytop - upperBound, edge->e_ybot);
|
|
wc->wc_area.r_xtop = LEFT(tile);
|
|
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plowWidthFunc --
|
|
*
|
|
* Called by area search to update an estimate of the minimum width of
|
|
* a wire. The argument 'tile' will be of a type not included in the
|
|
* set 'types' passed to plowFindWidth() above.
|
|
*
|
|
* The basic idea is to modify wc->wc_area so it does not contain the
|
|
* area of 'tile', but still contains the entire edge wc->wc_edge.
|
|
* When 'tile' overlaps wc->wc_edge in Y, this can only be achieved
|
|
* by throwing away everything to the right of LEFT(tile). When 'tile'
|
|
* doesn't overlap wc->wc_edge in Y, there are generally two choices:
|
|
* clip away the right-hand side of wc_area, or clip away the top (or
|
|
* bottom). To avoid a combinatoric explosion, we act greedily and
|
|
* clip in such a way as to keep the largest dimension largest.
|
|
*
|
|
* Results:
|
|
* Returns 1 always.
|
|
*
|
|
* Side effects:
|
|
* Modifies wc->wc_area as described above.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
plowWidthFunc(tile, wc)
|
|
Tile *tile; /* Tile whose type is not among the types
|
|
* passed to plowFindWidth(), which will be
|
|
* clipped out of the area wc->wc_area in
|
|
* such a way that the original edge will
|
|
* remain a part of wc->wc_area.
|
|
*/
|
|
struct wclip *wc; /* Contains original edge and area to clip */
|
|
{
|
|
Edge *edge = wc->wc_edge;
|
|
int xw, yw;
|
|
int yt, yb;
|
|
|
|
/*
|
|
* If the tile overlaps the edge in Y, we have to clip the
|
|
* RHS of the area. If clipping the RHS makes X the new
|
|
* smallest dimension, clip Y appropriately.
|
|
*/
|
|
xw = LEFT(tile) - wc->wc_area.r_xbot;
|
|
if (BOTTOM(tile) < edge->e_ytop && TOP(tile) > edge->e_ybot)
|
|
{
|
|
wc->wc_area.r_xtop = LEFT(tile);
|
|
goto clipvert;
|
|
}
|
|
|
|
/*
|
|
* If the tile is above the edge in Y, consider both
|
|
* alternatives (clipping the top, and clipping the
|
|
* right), choosing whichever one leaves us with the
|
|
* larger minimum width. This may not always work,
|
|
* but is simple.
|
|
*/
|
|
#ifdef COUNTWIDTHCALLS
|
|
plowWidthNumChoices++;
|
|
#endif /* COUNTWIDTHCALLS */
|
|
if (BOTTOM(tile) >= edge->e_ytop)
|
|
{
|
|
yw = BOTTOM(tile) - wc->wc_area.r_ybot;
|
|
if (xw >= yw)
|
|
{
|
|
wc->wc_area.r_xtop = LEFT(tile);
|
|
goto clipvert;
|
|
}
|
|
else
|
|
{
|
|
wc->wc_area.r_ytop = BOTTOM(tile);
|
|
goto clipright;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The tile is below the edge in Y, so consider both
|
|
* alternatives (clipping the bottom, and clipping the
|
|
* right), choosing whichever one leaves us with the
|
|
* larger minimum width. This may not always work,
|
|
* but is simple.
|
|
*/
|
|
yw = wc->wc_area.r_ytop - TOP(tile);
|
|
if (xw >= yw)
|
|
{
|
|
wc->wc_area.r_xtop = LEFT(tile);
|
|
goto clipvert;
|
|
}
|
|
else
|
|
{
|
|
wc->wc_area.r_ybot = TOP(tile);
|
|
goto clipright;
|
|
}
|
|
|
|
clipvert:
|
|
yt = MIN(edge->e_ybot + xw, wc->wc_area.r_ytop);
|
|
yb = MAX(edge->e_ytop - xw, wc->wc_area.r_ybot);
|
|
if (yt > wc->wc_edge->e_ytop) wc->wc_area.r_ytop = yt;
|
|
if (yb < wc->wc_edge->e_ybot) wc->wc_area.r_ybot = yb;
|
|
return (1);
|
|
|
|
clipright:
|
|
yw = wc->wc_area.r_ytop - wc->wc_area.r_ybot;
|
|
xw = wc->wc_area.r_xtop - wc->wc_area.r_xbot;
|
|
if (yw < xw)
|
|
wc->wc_area.r_xtop = wc->wc_area.r_xbot + yw;
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plowFindWidthBack --
|
|
*
|
|
* Find the minimum width of material consisting of those types in the
|
|
* TileTypeBitMask 'types', starting the search from the Edge 'edge',
|
|
* but searching to the left instead of to the right.
|
|
*
|
|
* Results:
|
|
* Returns the minimum width.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
plowFindWidthBack(
|
|
Edge *edge, /* Edge along the RHS of this material */
|
|
const TileTypeBitMask *typesp,/* Types whose width is being computed.
|
|
*/
|
|
Rect *bbox, /* Bounding box of the cell */
|
|
Rect *prect) /* (Debugging only): if this is non-NULL,
|
|
* the rectangle it points to is filled
|
|
* in with the rectangle found by this
|
|
* algorithm to be the largest containing
|
|
* the edge 'edge' with only types in the
|
|
* set 'types'.
|
|
*/
|
|
{
|
|
TileTypeBitMask types = *typesp; /* TTMaskCopy(&types, typesp); */
|
|
Plane *plane = plowYankDef->cd_planes[edge->e_pNum];
|
|
TileTypeBitMask ctypes;
|
|
struct wclip wc;
|
|
int x, y;
|
|
|
|
TTMaskCom2(&ctypes, &types);
|
|
|
|
/*
|
|
* Start with wc.wc_area extending from the edge rightward to the
|
|
* cell boundary. Search left to find some tile (not necessarily
|
|
* the rightmost) that is not one of 'types'. This provides an
|
|
* upper bound on the minimum width, which we use to set wa.wa_area
|
|
* for the next loop.
|
|
*/
|
|
wc.wc_edge = edge;
|
|
wc.wc_area.r_xtop = edge->e_x;
|
|
wc.wc_area.r_xbot = bbox->r_xbot - 1;
|
|
wc.wc_area.r_ybot = edge->e_ybot;
|
|
wc.wc_area.r_ytop = edge->e_ytop;
|
|
#ifdef DEBUGWIDTH
|
|
TxPrintf("Initial area: X: %d .. %d Y: %d .. %d\n",
|
|
wc.wc_area.r_xbot, wc.wc_area.r_xtop,
|
|
wc.wc_area.r_ybot, wc.wc_area.r_ytop);
|
|
#endif /* DEBUGWIDTH */
|
|
(void) DBSrPaintArea((Tile *) NULL,
|
|
plane, &wc.wc_area, &ctypes,
|
|
plowInitWidthBackFunc, (ClientData) &wc);
|
|
#ifdef DEBUGWIDTH
|
|
TxPrintf("After first search, area: X: %d .. %d Y: %d .. %d\n",
|
|
wc.wc_area.r_xbot, wc.wc_area.r_xtop,
|
|
wc.wc_area.r_ybot, wc.wc_area.r_ytop);
|
|
#endif /* DEBUGWIDTH */
|
|
|
|
/*
|
|
* Repeatedly search wc.wc_area for tiles whose
|
|
* types are not in the set 'types'. Each time we find such a
|
|
* tile, we clip wc.wc_area so it still contains the original
|
|
* edge in its entirety, but excludes the offending tile.
|
|
* Continue until either the edge is degenerate (shouldn't
|
|
* happen!) or everything in wc_area is of types in 'types'.
|
|
*/
|
|
while (DBSrPaintArea((Tile *) NULL, plane, &wc.wc_area, &ctypes,
|
|
plowWidthBackFunc, (ClientData) &wc))
|
|
{
|
|
#ifdef DEBUGWIDTH
|
|
TxPrintf("Next area: X: %d .. %d Y: %d .. %d\n",
|
|
wc.wc_area.r_xbot, wc.wc_area.r_xtop,
|
|
wc.wc_area.r_ybot, wc.wc_area.r_ytop);
|
|
#endif /* DEBUGWIDTH */
|
|
if (wc.wc_area.r_xbot == wc.wc_area.r_xtop)
|
|
break;
|
|
}
|
|
|
|
if (prect) *prect = wc.wc_area;
|
|
x = wc.wc_area.r_xtop - wc.wc_area.r_xbot;
|
|
y = wc.wc_area.r_ytop - wc.wc_area.r_ybot;
|
|
#ifdef DEBUGWIDTH
|
|
TxPrintf("Width = %d\n", MIN(x, y));
|
|
#endif /* DEBUGWIDTH */
|
|
return (MIN(x, y));
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plowInitWidthBackFunc --
|
|
*
|
|
* Called by area search to obtain an initial estimate of the minimum
|
|
* width of a wire. The argument 'tile' will be of a type not included
|
|
* in the set 'types' passed to plowFindWidth() above.
|
|
*
|
|
* We set our upper bound on minimum width to be the distance between
|
|
* wc->wc_edge and the RHS of tile. This means that the search rectangle
|
|
* wc->wc_area has to be modified as follows:
|
|
*
|
|
* wc_area.r_xbot will be (upper bound) to the left of wc_edge
|
|
* wc_area.r_ybot will be (upper bound) below the top of wc_edge
|
|
* wc_area.r_ytop will be (upper bound) above the bottom of wc_edge
|
|
*
|
|
* Results:
|
|
* Returns 1 always.
|
|
*
|
|
* Side effects:
|
|
* Modifies wc->wc_area as described above.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
plowInitWidthBackFunc(tile, wc)
|
|
Tile *tile; /* Tile whose type is not among the types
|
|
* passed to plowFindWidthBack(), whose RHS will
|
|
* provide the left-hand boundary.
|
|
*/
|
|
struct wclip *wc; /* Contains original edge and area to clip */
|
|
{
|
|
Edge *edge = wc->wc_edge;
|
|
int upperBound = edge->e_x - RIGHT(tile);
|
|
|
|
#ifdef DEBUGWIDTH
|
|
TxPrintf("Tile type = %s, edgeHeight = %d, upperBound = %d\n",
|
|
DBTypeLongName(TiGetTypeExact(tile)), edge->e_ytop-edge->e_ybot, upperBound);
|
|
#endif /* DEBUGWIDTH */
|
|
wc->wc_area.r_ytop = MAX(edge->e_ybot + upperBound, edge->e_ytop);
|
|
wc->wc_area.r_ybot = MIN(edge->e_ytop - upperBound, edge->e_ybot);
|
|
wc->wc_area.r_xbot = RIGHT(tile);
|
|
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* plowWidthBackFunc --
|
|
*
|
|
* Called by area search to update an estimate of the minimum width of
|
|
* a wire. The argument 'tile' will be of a type not included in the
|
|
* set 'types' passed to plowFindWidth() above.
|
|
*
|
|
* The basic idea is to modify wc->wc_area so it does not contain the
|
|
* area of 'tile', but still contains the entire edge wc->wc_edge.
|
|
* When 'tile' overlaps wc->wc_edge in Y, this can only be achieved
|
|
* by throwing away everything to the left of RIGHT(tile). When 'tile'
|
|
* doesn't overlap wc->wc_edge in Y, there are generally two choices:
|
|
* clip away the left-hand side of wc_area, or clip away the top (or
|
|
* bottom). To avoid a combinatoric explosion, we act greedily and
|
|
* clip in such a way as to keep the largest dimension largest.
|
|
*
|
|
* Results:
|
|
* Returns 1 always.
|
|
*
|
|
* Side effects:
|
|
* Modifies wc->wc_area as described above.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
plowWidthBackFunc(tile, wc)
|
|
Tile *tile; /* Tile whose type is not among the types
|
|
* passed to plowFindWidth(), which will be
|
|
* clipped out of the area wc->wc_area in
|
|
* such a way that the original edge will
|
|
* remain a part of wc->wc_area.
|
|
*/
|
|
struct wclip *wc; /* Contains original edge and area to clip */
|
|
{
|
|
Edge *edge = wc->wc_edge;
|
|
int xw, yw;
|
|
int yt, yb;
|
|
|
|
/*
|
|
* If the tile overlaps the edge in Y, we have to clip the
|
|
* LHS of the area. If clipping the LHS makes X the new
|
|
* smallest dimension, clip Y appropriately.
|
|
*/
|
|
xw = wc->wc_area.r_xtop - RIGHT(tile);
|
|
if (BOTTOM(tile) < edge->e_ytop && TOP(tile) > edge->e_ybot)
|
|
{
|
|
wc->wc_area.r_xbot = RIGHT(tile);
|
|
goto clipvert;
|
|
}
|
|
|
|
/*
|
|
* If the tile is above the edge in Y, consider both
|
|
* alternatives (clipping the top, and clipping the
|
|
* right), choosing whichever one leaves us with the
|
|
* larger minimum width. This may not always work,
|
|
* but is simple.
|
|
*/
|
|
if (BOTTOM(tile) >= edge->e_ytop)
|
|
{
|
|
yw = BOTTOM(tile) - wc->wc_area.r_ybot;
|
|
if (xw >= yw)
|
|
{
|
|
wc->wc_area.r_xbot = RIGHT(tile);
|
|
goto clipvert;
|
|
}
|
|
else
|
|
{
|
|
wc->wc_area.r_ytop = BOTTOM(tile);
|
|
goto clipright;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The tile is below the edge in Y, so consider both
|
|
* alternatives (clipping the bottom, and clipping the
|
|
* right), choosing whichever one leaves us with the
|
|
* larger minimum width. This may not always work,
|
|
* but is simple.
|
|
*/
|
|
yw = wc->wc_area.r_ytop - TOP(tile);
|
|
if (xw >= yw)
|
|
{
|
|
wc->wc_area.r_xbot = RIGHT(tile);
|
|
goto clipvert;
|
|
}
|
|
else
|
|
{
|
|
wc->wc_area.r_ybot = TOP(tile);
|
|
goto clipright;
|
|
}
|
|
|
|
clipvert:
|
|
yt = MIN(edge->e_ybot + xw, wc->wc_area.r_ytop);
|
|
yb = MAX(edge->e_ytop - xw, wc->wc_area.r_ybot);
|
|
if (yt > wc->wc_edge->e_ytop) wc->wc_area.r_ytop = yt;
|
|
if (yb < wc->wc_edge->e_ybot) wc->wc_area.r_ybot = yb;
|
|
return (1);
|
|
|
|
clipright:
|
|
yw = wc->wc_area.r_ytop - wc->wc_area.r_ybot;
|
|
xw = wc->wc_area.r_xtop - wc->wc_area.r_xbot;
|
|
if (yw < xw)
|
|
wc->wc_area.r_xbot = wc->wc_area.r_xtop - yw;
|
|
return (1);
|
|
}
|