/* * 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 #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, types, bbox, prect) Edge *edge; /* Edge along the LHS of this material */ TileTypeBitMask types; /* Types whose width is being computed. Note * that this set is passed by value. */ 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'. */ { 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, types, bbox, prect) Edge *edge; /* Edge along the RHS of this material */ TileTypeBitMask types; /* Types whose width is being computed. Note * that this set is passed by value. */ 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'. */ { 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); }