diff --git a/VERSION b/VERSION index f3d26a30..4fe55683 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.67 +8.3.77 diff --git a/cif/CIFgen.c b/cif/CIFgen.c index 1095024a..cde2142d 100644 --- a/cif/CIFgen.c +++ b/cif/CIFgen.c @@ -1024,6 +1024,27 @@ endbloat: STACKPUSH((ClientData) (tp), stack); \ } +/* + *------------------------------------------------------- + * + * cifProcessResetFunc -- + * + * Unmark tiles + * + * Results: Return 0 to keep the search going. + * + *------------------------------------------------------- + */ + +int +cifProcessResetFunc(tile, clientData) + Tile *tile; + ClientData clientData; /* unused */ +{ + tile->ti_client = (ClientData) CIF_UNPROCESSED; + return 0; +} + /* *------------------------------------------------------- * @@ -1260,6 +1281,21 @@ cifBloatAllFunc(tile, bls) STACKPUSH(tp, BloatStack); } } + + /* Reset marked tiles */ + + if (bloats->bl_plane < 0) /* Bloat types are CIF types */ + { + temps = bls->temps; + for (ttype = 0; ttype < TT_MAXTYPES; ttype++, temps++) + if (bloats->bl_distance[ttype] > 0) + (void) DBSrPaintArea((Tile *)NULL, *temps, &TiPlaneRect, + &CIFSolidBits, cifProcessResetFunc, (ClientData)NULL); + } + else + DBSrPaintArea((Tile *)NULL, def->cd_planes[bloats->bl_plane], &TiPlaneRect, + &connect, cifProcessResetFunc, (ClientData)NULL); + return 0; /* Keep the search alive. . . */ } @@ -2188,28 +2224,6 @@ cifSquaresStripFunc(tile, stripsData) return 0; } - -/* - *------------------------------------------------------- - * - * cifSquaresResetFunc -- - * - * Unmark tiles - * - * Results: Return 0 to keep the search going. - * - *------------------------------------------------------- - */ - -int -cifSquaresResetFunc(tile, clientData) - Tile *tile; - ClientData clientData; /* unused */ -{ - tile->ti_client = (ClientData) CIF_UNPROCESSED; - return 0; -} - /* *------------------------------------------------------- * @@ -2387,10 +2401,6 @@ cifRectBoundingBox(op, cellDef, plane) } } } - - /* Clear all the tiles that were processed */ - DBSrPaintArea((Tile *)NULL, plane, &TiPlaneRect, &CIFSolidBits, - cifSquaresResetFunc, (ClientData)NULL); } /* @@ -2725,7 +2735,7 @@ cifSquaresFillArea(op, cellDef, plane) /* Clear all the tiles that were processed */ DBSrPaintArea((Tile *)NULL, plane, &TiPlaneRect, &CIFSolidBits, - cifSquaresResetFunc, (ClientData)NULL); + cifProcessResetFunc, (ClientData)NULL); } /* @@ -3088,7 +3098,7 @@ cifSlotsFillArea(op, cellDef, plane) /* Clear all the tiles that were processed */ DBSrPaintArea((Tile *)NULL, plane, &TiPlaneRect, &CIFSolidBits, - cifSquaresResetFunc, (ClientData)NULL); + cifProcessResetFunc, (ClientData)NULL); } /* @@ -3775,6 +3785,831 @@ cifSrTiles(cifOp, area, cellDef, temps, func, cdArg) &CIFSolidBits, func, (ClientData) cdArg); } +/* Data structure to pass plane and minimum width to the callback function */ +typedef struct _bridgeLimStruct { + Plane *plane; + BridgeData *bridge; + CellDef *def; + Plane **temps; + TileTypeBitMask co_paintMask;/* Zero or more paint layers to consider. */ + TileTypeBitMask co_cifMask; /* Zero or more other CIF layers. */ +} BridgeLimStruct; + +static int xOverlap, yOverlap; + +/* Bridge-lim Check data structure */ +typedef struct _bridgeLimCheckStruct { + Tile *tile; /* Tile that triggered search (ignore this tile) */ + int direction; /* What outside corner to look for */ + Tile *violator; /* Return the violator tile in this space */ + TileType checktype; /* Type to check for, either TT_SPACE or CIF_SOLIDTYPE */ + long sqdistance; /* Square of the minimum distance */ +} BridgeLimCheckStruct; +/* + *----------------------------------------------------------------------- + * Callback function for bridgeLimSrTiles used when a limiting layer tile + * was found in the specified area. If calcOverlap is TRUE then the xOverlap + * and yOverlap are updated and return 0 to continue searching. Otherwise + * return 1 to stop the search. + *----------------------------------------------------------------------- + */ +int +bridgeLimFound(tile, calcOverlap) + Tile *tile; + bool calcOverlap; +{ + if (calcOverlap) + { + if (RIGHT(tile) > xOverlap) + xOverlap = RIGHT(tile); + if (TOP(tile) > yOverlap) + yOverlap = TOP(tile); + return 0; // continue searching + } else + return 1; // tile found, stop the search +} + +/* + *------------------------------------------------------------------------ + * Callback function used in bridge-lim operations to find if the specified + * area contains tiles of the limiting layers. + *------------------------------------------------------------------------ + */ +int +bridgeLimSrTiles(brlims, area, calcOverlap) + BridgeLimStruct *brlims; /* Bridge-Lim structure. */ + Rect *area; /* Area of Magic paint to consider. */ + bool calcOverlap; /* TRUE to calculate the overlap of the limiting tiles in the specified area. */ +{ + TileTypeBitMask maskBits; + TileType t; + int i; + Plane **temps = brlims->temps; + xOverlap = area->r_xbot; + yOverlap = area->r_ybot; + + for (i = PL_DRC_CHECK; i < DBNumPlanes; i++) + { + maskBits = DBPlaneTypes[i]; + TTMaskAndMask(&maskBits, &brlims->co_paintMask); + if (!TTMaskEqual(&maskBits, &DBZeroTypeBits)) + if (DBSrPaintArea((Tile *) NULL, brlims->def->cd_planes[i], area, &brlims->co_paintMask, bridgeLimFound, calcOverlap)) + return 0; + } + + for (t = 0; t < TT_MAXTYPES; t++, temps++) + if (TTMaskHasType(&brlims->co_cifMask, t)) + if (DBSrPaintArea((Tile *) NULL, *temps, area, &CIFSolidBits, bridgeLimFound, calcOverlap)) + return 0; + + if (( xOverlap != area->r_xbot) || (yOverlap != area->r_ybot)) + return 0; //Constraint tile found + else + return 1; //Nothing found +} + +/* + * ---------------------------------------------------------------------------- + * + * cifBridgeLimFunc0 -- + * + * Called for each relevant tile during bridge-lim operations. The + * bridge-lim operation is similar to the bridge operation with the + * difference that the material created to meet the minimum spacing/width + * rules do not overlap the limiting layers. + * + * Results: + * Always returns 0 to keep the search alive. + * + * Side effects: + * May paint into cifNewPlane + * + * Algorithm (based on maximum horizontal stripes rule): + * This function grows dimentions of tiles to meet a minimimum width rule (only + * applies to vertical or horizontal directions). This is done in two steps: + * 1. Adjust tile width to accomplish the minimimum width rule, limited by + * the constraint layers. + * 2. Search top and bottom tiles of same type, for each segment verify if + * height meets the minimimum value, if not, adjust the segment height + * but do not paint over limiting layer tiles. + * ---------------------------------------------------------------------------- + */ +int +cifBridgeLimFunc0(tile, brlims) + Tile *tile; + BridgeLimStruct *brlims; +{ + Plane *plane = brlims->plane; + Rect area, parea; + int minDistance = brlims->bridge->br_width; + int width, height, ybot0, tp2lim; + Tile *tp, *tp2; + + TiToRect(tile, &area); + + /* Check whole tile for minimum width */ + width = area.r_xtop - area.r_xbot; + if (width < minDistance) + { + area.r_xbot = area.r_xtop - minDistance; + if (!bridgeLimSrTiles(brlims, &area, TRUE)) + { + area.r_xbot = MIN(LEFT(tile), xOverlap); + area.r_xtop = area.r_xbot + minDistance; + } + } + + /* If there is another tile on top or bottom, and the height is */ + /* less than minimum, then extend height in the direction of */ + /* the bordering tile. Otherwise, if the height is less than */ + /* minimum, then grow considering the limiting layers. */ + + height = area.r_ytop - area.r_ybot; + if (height < minDistance) + { + for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp)) + { + tp2lim = MAX(LEFT(tp), area.r_xbot); + for (tp2 = RT(tile); RIGHT(tp2) > tp2lim; tp2 = BL(tp2)) + if (LEFT(tp2) < RIGHT(tp)) + { + parea.r_xbot = MAX(LEFT(tp2), tp2lim); + parea.r_xtop = MIN(MIN(RIGHT(tp2), RIGHT(tp)), area.r_xtop); + if (TiGetBottomType(tp2) == TiGetTopType(tile)) + parea.r_ytop = TOP(tp2); + else + parea.r_ytop = area.r_ytop; + if (TiGetTopType(tp) == TiGetBottomType(tile)) + ybot0 = BOTTOM(tp); + else + ybot0 = area.r_ybot; + height = parea.r_ytop - ybot0; + if (height < minDistance) + { + parea.r_ybot = parea.r_ytop - minDistance; + if (!bridgeLimSrTiles(brlims, &parea, TRUE)) + { + parea.r_ybot = MIN(ybot0 , yOverlap); + parea.r_ytop = parea.r_ybot + minDistance; + } + DBPaintPlane(cifPlane, &parea, CIFPaintTable, (PaintUndoInfo *) NULL); + } + } + } + } + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + return 0; +} + +/* + *----------------------------------------------------------------------- + * Callback function for cifBridgeLimFunc1 and cifBridgeLimFunc2 to find if + * there are violator cells in the search area. If a violator cell is + * found, then put the tile pointer in the BridgeCheckStruct and return + * value 1 to stop the search. Otherwise return 0 to keep going. + *----------------------------------------------------------------------- + */ +int +bridgeLimCheckFunc(tile, brlimcs) + Tile *tile; + BridgeLimCheckStruct *brlimcs; +{ + int dir = brlimcs->direction; + Tile *self = brlimcs->tile; + Tile *tp1, *tp2; + TileType checktype = brlimcs->checktype; + long sqdistance = brlimcs->sqdistance; + int dx, dy; + long sqcheck; + + if (self == tile) return 0; /* Ignore the triggering tile */ + + switch (dir) { + case BRIDGE_NW: + /* Ignore tile if split, and SE corner is clipped */ + if (TiGetRightType(tile) == checktype || TiGetBottomType(tile) == checktype) + break; + for (tp1 = RT(tile); LEFT(tp1) > LEFT(tile); tp1 = BL(tp1)); + for (tp2 = BL(tile); TOP(tp2) < TOP(tile); tp2 = RT(tp2)); + if ((TiGetBottomType(tp1) == checktype) && + (TiGetRightType(tp2) == checktype)) + { + dx = LEFT(tile) - RIGHT(self); + dy = BOTTOM(self) - TOP(tile); + if ((dx > 0) && (dy > 0)) + { + sqcheck = (long)dx*dx + (long)dy*dy; + if (sqcheck >= sqdistance) + return 0; + } + brlimcs->violator = tile; + return 1; /* Violator found */ + } + break; + case BRIDGE_NE: + /* Ignore tile if split, and SW corner is clipped */ + if (TiGetLeftType(tile) == checktype || TiGetBottomType(tile) == checktype) + break; + tp1 = RT(tile); + tp2 = TR(tile); + if ((TiGetBottomType(tp1) == checktype) && + (TiGetLeftType(tp2) == checktype)) + { + dx = LEFT(self) - RIGHT(tile); + dy = BOTTOM(self) - TOP(tile); + if ((dx > 0) && (dy > 0)) + { + sqcheck = (long)dx*dx + (long)dy*dy; + if (sqcheck >= sqdistance) + return 0; + } + brlimcs->violator = tile; + return 1; /* Violator found */ + } + break; + case BRIDGE_SW: + /* Ignore tile if split, and NE corner is clipped */ + if (TiGetRightType(tile) == checktype || TiGetTopType(tile) == checktype) + break; + tp1 = LB(tile); + tp2 = BL(tile); + if ((TiGetTopType(tp1) == checktype) && + (TiGetRightType(tp2) == checktype)) + { + dx = LEFT(tile) - RIGHT(self); + dy = BOTTOM(tile) - TOP(self); + if ((dx > 0) && (dy > 0)) + { + sqcheck = (long)dx*dx + (long)dy*dy; + if (sqcheck >= sqdistance) + return 0; + } + brlimcs->violator = tile; + return 1; /* Violator found */ + } + break; + case BRIDGE_SE: + /* Ignore tile if split, and NW corner is clipped */ + if (TiGetLeftType(tile) == checktype || TiGetTopType(tile) == checktype) + break; + for (tp1 = LB(tile); RIGHT(tp1) < RIGHT(tile); tp1 = TR(tp1)); + for (tp2 = TR(tile); BOTTOM(tp2) > BOTTOM(tile); tp2 = LB(tp2)); + if ((TiGetTopType(tp1) == checktype) && + (TiGetLeftType(tp2) == checktype)) + { + dx = LEFT(self) - RIGHT(tile); + dy = BOTTOM(tile) - TOP(self); + if ((dx > 0) && (dy > 0)) + { + sqcheck = (long)dx*dx + (long)dy*dy; + if (sqcheck >= sqdistance) + return 0; + } + brlimcs->violator = tile; + return 1; /* Violator found */ + } + break; + } + return 0; /* Nothing found here, so keep going */ +} + +/* + *----------------------------------------------------------------------- + * Callback function used in bridge-lim operations to do not paint areas + * where new bridge overlaps the limiting layer tiles. + *----------------------------------------------------------------------- + */ +int +bridgeErase(brlims, area) + BridgeLimStruct *brlims; /* Bridge-lim structure. */ + Rect *area; /* Area of Magic paint to consider. */ +{ + TileTypeBitMask maskBits; + TileType t; + int i; + Plane **temps = brlims->temps; + + for (i = PL_DRC_CHECK; i < DBNumPlanes; i++) + { + maskBits = DBPlaneTypes[i]; + TTMaskAndMask(&maskBits, &brlims->co_paintMask); + if (!TTMaskEqual(&maskBits, &DBZeroTypeBits)) + if (DBSrPaintArea((Tile *) NULL, brlims->def->cd_planes[i], area, &brlims->co_paintMask, cifPaintFunc, CIFEraseTable)) + return 0; + } + + for (t = 0; t < TT_MAXTYPES; t++, temps++) + if (TTMaskHasType(&brlims->co_cifMask, t)) + if (DBSrPaintArea((Tile *) NULL, *temps, area, &CIFSolidBits, cifPaintFunc, CIFEraseTable)) + return 0; + + return 1; //Nothing found +} + +/* + * ---------------------------------------------------------------------------- + * + * cifBridgeLimFunc1 -- + * + * Called for each relevant tile during bridge-lim operations. The + * bridge-lim operation is similar to the bridge operation with the + * difference that the material created to meet the minimum spacing/width + * rules do not overlap the limiting layers. + * + * Results: + * Always returns 0 to keep the search alive. + * + * Side effects: + * May paint into cifNewPlane + * ---------------------------------------------------------------------------- + */ +int +cifBridgeLimFunc1(tile, brlims) + Tile *tile; + BridgeLimStruct *brlims; +{ + Plane *plane = brlims->plane; + Rect area; + Tile *tp1, *tp2, *tpx; + int width = brlims->bridge->br_width; + int spacing = growDistance; + BridgeLimCheckStruct brlimcs; + brlimcs.sqdistance = (long) spacing * spacing; + + /* If tile is marked, then it has been handled, so ignore it */ + if (tile->ti_client != (ClientData)CIF_UNPROCESSED) return 0; + + /* Find each tile outside corner (up to four) */ + + /* Check for NE outside corner */ + tp1 = TR(tile); /* Tile on right side at the top of this tile */ + tp2 = RT(tile); /* Tile on top side at the right of this tile */ + if ((TiGetLeftType(tp1) == TT_SPACE) && + (TiGetBottomType(tp2) == TT_SPACE)) + { + /* Set search box */ + area.r_xbot = RIGHT(tile); + area.r_xtop = RIGHT(tile) + spacing; + area.r_ybot = TOP(tile); + area.r_ytop = TOP(tile) + spacing; + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_SW; + brlimcs.checktype = TT_SPACE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &CIFSolidBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = MAX(RIGHT(tile), LEFT(tpx) + width); + area.r_ytop = MAX(TOP(tile), BOTTOM(tpx)); + area.r_xbot = MIN(LEFT(tpx), RIGHT(tile)); + area.r_ybot = MIN(BOTTOM(tpx), TOP(tile) - width); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* First option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_ytop = TOP(tile); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xbot = LEFT(tpx); + area.r_ytop = MAX(TOP(tile), BOTTOM(tpx)); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + area.r_xtop = MAX(RIGHT(tile), LEFT(tpx)); + area.r_ytop = MAX(TOP(tile), BOTTOM(tpx) + width); + area.r_xbot = MIN(LEFT(tpx), RIGHT(tile) - width); + area.r_ybot = MIN(BOTTOM(tpx), TOP(tile)); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* Second option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_ybot = BOTTOM(tpx); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xtop = RIGHT(tile); + area.r_ybot = MIN(BOTTOM(tpx), TOP(tile)); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + /* Last option (Limiting tiles are present on both sides, those areas must be excluded from the bridge)*/ + area.r_xtop = MAX(RIGHT(tile), LEFT(tpx) + width); + area.r_ytop = MAX(TOP(tile), BOTTOM(tpx) + width); + area.r_xbot = MIN(LEFT(tpx), RIGHT(tile) - width); + area.r_ybot = MIN(BOTTOM(tpx), TOP(tile) - width); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + bridgeErase(brlims, &area); + } + } + } + } + + /* Check for SW outside corner */ + tp1 = BL(tile); /* Tile on left side at the bottom of this tile */ + tp2 = LB(tile); /* Tile on bottom side at the left of this tile */ + if ((TiGetRightType(tp1) == TT_SPACE) && + (TiGetTopType(tp2) == TT_SPACE)) + { + /* Set search box */ + area.r_xbot = LEFT(tile) - spacing; + area.r_xtop = LEFT(tile); + area.r_ybot = BOTTOM(tile) - spacing; + area.r_ytop = BOTTOM(tile); + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_NE; + brlimcs.checktype = TT_SPACE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &CIFSolidBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = MAX(RIGHT(tpx), LEFT(tile) + width); + area.r_ytop = MAX(TOP(tpx), BOTTOM(tile)); + area.r_xbot = MIN(LEFT(tile), RIGHT(tpx)); + area.r_ybot = MIN(BOTTOM(tile), TOP(tpx) - width); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* First option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_ytop = TOP(tpx); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xbot = LEFT(tile); + area.r_ytop = MAX(TOP(tpx), BOTTOM(tile)); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + area.r_xtop = MAX(RIGHT(tpx), LEFT(tile)); + area.r_ytop = MAX(TOP(tpx), BOTTOM(tile) + width); + area.r_xbot = MIN(LEFT(tile), RIGHT(tpx) - width); + area.r_ybot = MIN(BOTTOM(tile), TOP(tpx)); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* Second option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_ybot = BOTTOM(tile); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xtop = RIGHT(tpx); + area.r_ybot = MIN(BOTTOM(tile), TOP(tpx)); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + /* Last option (Limiting tiles are present on both sides, those areas must be excluded from the bridge)*/ + area.r_xtop = MAX(RIGHT(tpx), LEFT(tile) + width); + area.r_ytop = MAX(TOP(tpx), BOTTOM(tile) + width); + area.r_xbot = MIN(LEFT(tile), RIGHT(tpx) - width); + area.r_ybot = MIN(BOTTOM(tile), TOP(tpx) - width); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + bridgeErase(brlims, &area); + } + } + } + } + + /* Check for SE outside corner */ + for (tp1 = TR(tile); BOTTOM(tp1) > BOTTOM(tile); tp1 = LB(tp1)); + for (tp2 = LB(tile); RIGHT(tp1) < RIGHT(tile); tp2 = TR(tp2)); + if ((TiGetLeftType(tp1) == TT_SPACE) && + (TiGetTopType(tp2) == TT_SPACE)) + { + /* Set search box */ + area.r_xbot = RIGHT(tile); + area.r_xtop = RIGHT(tile) + spacing; + area.r_ybot = BOTTOM(tile) - spacing; + area.r_ytop = BOTTOM(tile); + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_NW; + brlimcs.checktype = TT_SPACE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &CIFSolidBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = MAX(RIGHT(tile), LEFT(tpx)); + area.r_ybot = MIN(BOTTOM(tile), TOP(tpx) - width); + area.r_xbot = MIN(LEFT(tpx), RIGHT(tile) - width); + area.r_ytop = MAX(TOP(tpx), BOTTOM(tile)); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* First option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_xtop = RIGHT(tile); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xtop = MAX(RIGHT(tile), LEFT(tpx)); + area.r_ytop = TOP(tpx); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + area.r_xtop = MAX(RIGHT(tile), LEFT(tpx) + width); + area.r_ybot = MIN(BOTTOM(tile), TOP(tpx)); + area.r_xbot = MIN(LEFT(tpx), RIGHT(tile)); + area.r_ytop = MAX(TOP(tpx), BOTTOM(tile) + width); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* Second option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_xbot = LEFT(tpx); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xbot = MIN(LEFT(tpx), RIGHT(tile)); + area.r_ybot = BOTTOM(tile); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + /* Last option (Limiting tiles are present on both sides, those areas must be excluded from the bridge)*/ + area.r_xtop = MAX(RIGHT(tile), LEFT(tpx) + width); + area.r_ybot = MIN(BOTTOM(tile), TOP(tpx) - width); + area.r_xbot = MIN(LEFT(tpx), RIGHT(tile) - width); + area.r_ytop = MAX(TOP(tpx), BOTTOM(tile) + width); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + bridgeErase(brlims, &area); + } + } + } + } + + /* Check for NW outside corner */ + for (tp1 = BL(tile); TOP(tp1) < TOP(tile); tp1 = RT(tp1)); + for (tp2 = RT(tile); LEFT(tp1) > LEFT(tile); tp2 = BL(tp1)); + if ((TiGetRightType(tp1) == TT_SPACE) && + (TiGetBottomType(tp2) == TT_SPACE)) + { + /* Set search box */ + area.r_xbot = LEFT(tile) - spacing; + area.r_xtop = LEFT(tile); + area.r_ybot = TOP(tile); + area.r_ytop = TOP(tile) + spacing; + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_SE; + brlimcs.checktype = TT_SPACE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &CIFSolidBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = MAX(RIGHT(tpx), LEFT(tile)); + area.r_ybot = MIN(BOTTOM(tpx), TOP(tile) - width); + area.r_xbot = MIN(LEFT(tile), RIGHT(tpx) - width); + area.r_ytop = MAX(TOP(tile), BOTTOM(tpx)); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* First option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_xtop = RIGHT(tpx); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xtop = MAX(RIGHT(tpx), LEFT(tile)); + area.r_ytop = TOP(tile); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + area.r_xtop = MAX(RIGHT(tpx), LEFT(tile) + width); + area.r_ybot = MIN(BOTTOM(tpx), TOP(tile)); + area.r_xbot = MIN(LEFT(tile), RIGHT(tpx)); + area.r_ytop = MAX(TOP(tile), BOTTOM(tpx) + width); + if (bridgeLimSrTiles(brlims, &area, FALSE)) + { + /* Second option of bridge can be implemented (there are not limiting tiles in the area)*/ + area.r_xbot = LEFT(tile); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + area.r_xbot = MIN(LEFT(tile), RIGHT(tpx)); + area.r_ybot = BOTTOM(tpx); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + } + else + { + /* Last option (Limiting tiles are present on both sides, those areas must be excluded from the bridge)*/ + area.r_xtop = MAX(RIGHT(tpx), LEFT(tile) + width); + area.r_ybot = MIN(BOTTOM(tpx), TOP(tile) - width); + area.r_xbot = MIN(LEFT(tile), RIGHT(tpx) - width); + area.r_ytop = MAX(TOP(tile), BOTTOM(tpx) + width); + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + bridgeErase(brlims, &area); + } + } + } + } + + return 0; +} + +/* + * ---------------------------------------------------------------------------- + * cifBridgeLimFunc2 -- + * + * Called for each relevant tile during bridge-lim operations. The + * bridge-lim operation is similar to the bridge operation with the + * difference that the material created to meet the minimum spacing/width + * rules do not overlap the limiting layers. + * + * Results: + * Always returns 0 to keep the search alive. + * + * Side effects: + * May paint into cifNewPlane + * ---------------------------------------------------------------------------- + */ +int +cifBridgeLimFunc2(tile, brlims) + Tile *tile; + BridgeLimStruct *brlims; +{ + Plane *plane = brlims->plane; + Rect area; + Tile *tp1, *tp2, *tpx; + int width = brlims->bridge->br_width; + int thirdOption; + int spacing = growDistance; + BridgeLimCheckStruct brlimcs; + brlimcs.sqdistance = (long) width * width; + + /* If tile is marked, then it has been handled, so ignore it */ + if (tile->ti_client != (ClientData)CIF_UNPROCESSED) return 0; + + /* Find each tile outside corner (up to four) */ + + /* Check for NE outside corner */ + tp1 = TR(tile); /* Tile on right side at the top of this tile */ + tp2 = RT(tile); /* Tile on top side at the right of this tile */ + if ((TiGetLeftType(tp1) == CIF_SOLIDTYPE) && + (TiGetBottomType(tp2) == CIF_SOLIDTYPE)) + { + /* Set search box */ + area.r_xbot = RIGHT(tile); + area.r_xtop = RIGHT(tile) + width; + area.r_ybot = TOP(tile); + area.r_ytop = TOP(tile) + width; + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_SW; + brlimcs.checktype = CIF_SOLIDTYPE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &DBSpaceBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = RIGHT(tile); + area.r_ytop = TOP(tile); + area.r_xbot = LEFT(tpx) - width; + area.r_ybot = BOTTOM(tpx) - width; + + thirdOption = 0; + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xtop = RIGHT(tile) + width; + area.r_ytop = TOP(tile) + width; + area.r_xbot = LEFT(tpx); + area.r_ybot = BOTTOM(tpx); + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xbot = LEFT(tpx) - width; + area.r_ybot = BOTTOM(tpx) - width; + thirdOption = 1; + } + } + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + if (thirdOption) + bridgeErase(brlims, &area); + } + } + + /* Check for SW outside corner */ + tp1 = BL(tile); /* Tile on left side at the bottom of this tile */ + tp2 = LB(tile); /* Tile on bottom side at the left of this tile */ + if ((TiGetRightType(tp1) == CIF_SOLIDTYPE) && + (TiGetTopType(tp2) == CIF_SOLIDTYPE)) + { + /* Set search box */ + area.r_xbot = LEFT(tile) - width; + area.r_xtop = LEFT(tile); + area.r_ybot = BOTTOM(tile) - width; + area.r_ytop = BOTTOM(tile); + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_NE; + brlimcs.checktype = CIF_SOLIDTYPE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &DBSpaceBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = RIGHT(tpx); + area.r_ytop = TOP(tpx); + area.r_xbot = LEFT(tile) - width; + area.r_ybot = BOTTOM(tile) - width; + + thirdOption = 0; + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xtop = RIGHT(tpx) + width; + area.r_ytop = TOP(tpx) + width; + area.r_xbot = LEFT(tile); + area.r_ybot = BOTTOM(tile); + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xbot = LEFT(tile) - width; + area.r_ybot = BOTTOM(tile) - width; + thirdOption = 1; + } + } + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + if (thirdOption) + bridgeErase(brlims, &area); + } + } + + /* Check for SE outside corner */ + for (tp1 = TR(tile); BOTTOM(tp1) > BOTTOM(tile); tp1 = LB(tp1)); + for (tp2 = LB(tile); RIGHT(tp2) < RIGHT(tile); tp2 = TR(tp2)); + if ((TiGetLeftType(tp1) == CIF_SOLIDTYPE) && + (TiGetTopType(tp2) == CIF_SOLIDTYPE)) + { + /* Set search box */ + area.r_xbot = RIGHT(tile); + area.r_xtop = RIGHT(tile) + width; + area.r_ybot = BOTTOM(tile) - width; + area.r_ytop = BOTTOM(tile); + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_NW; + brlimcs.checktype = CIF_SOLIDTYPE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &DBSpaceBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = RIGHT(tile) + width; + area.r_ytop = TOP(tpx); + area.r_xbot = LEFT(tpx); + area.r_ybot = BOTTOM(tile) - width; + + thirdOption = 0; + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xtop = RIGHT(tile); + area.r_ytop = TOP(tpx) + width; + area.r_xbot = LEFT(tpx) - width; + area.r_ybot = BOTTOM(tile); + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xtop = RIGHT(tile) + width; + area.r_ybot = BOTTOM(tile) - width; + thirdOption = 1; + } + } + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + if (thirdOption) + bridgeErase(brlims, &area); + } + } + + /* Check for NW outside corner */ + for (tp1 = BL(tile); TOP(tp1) < TOP(tile); tp1 = RT(tp1)); + for (tp2 = RT(tile); LEFT(tp2) > LEFT(tile); tp2 = BL(tp2)); + if ((TiGetRightType(tp1) == CIF_SOLIDTYPE) && + (TiGetBottomType(tp2) == CIF_SOLIDTYPE)) + { + /* Set search box */ + area.r_xbot = LEFT(tile) - width; + area.r_xtop = LEFT(tile); + area.r_ybot = TOP(tile); + area.r_ytop = TOP(tile) + width; + + /* Find violator tiles */ + brlimcs.tile = tile; + brlimcs.direction = BRIDGE_SE; + brlimcs.checktype = CIF_SOLIDTYPE; + if (DBSrPaintArea((Tile *) NULL, plane, &area, + &DBSpaceBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1) + { + tpx = brlimcs.violator; + area.r_xtop = RIGHT(tpx) + width; + area.r_ytop = TOP(tile); + area.r_xbot = LEFT(tile); + area.r_ybot = BOTTOM(tpx) - width; + + thirdOption = 0; + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xtop = RIGHT(tpx); + area.r_ytop = TOP(tile) + width; + area.r_xbot = LEFT(tile) - width; + area.r_ybot = BOTTOM(tpx); + if (!bridgeLimSrTiles(brlims, &area, FALSE)) + { + area.r_xtop = RIGHT(tpx) + width; + area.r_ybot = BOTTOM(tpx) - width; + thirdOption = 1; + } + } + DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL); + if (thirdOption) + bridgeErase(brlims, &area); + } + } + + return 0; +} + /* * ---------------------------------------------------------------------------- * @@ -3828,6 +4663,7 @@ CIFGenLayer(op, area, cellDef, origDef, temps, clientdata) char *netname; BloatStruct bls; BridgeStruct brs; + BridgeLimStruct brlims; BridgeData *bridge; int (*cifGrowFuncPtr)() = (CIFCurStyle->cs_flags & CWF_GROW_EUCLIDEAN) ? cifGrowEuclideanFunc : cifGrowFunc; @@ -4022,11 +4858,11 @@ CIFGenLayer(op, area, cellDef, origDef, temps, clientdata) cifPlane = nextPlane; cifScale = 1; /* First copy the existing paint into the target plane */ - (void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect, - &CIFSolidBits, cifPaintFunc, (ClientData)CIFPaintTable); + DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect, + &CIFSolidBits, cifPaintFunc, (ClientData)CIFPaintTable); + DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect, + &DBSpaceBits, cifCloseFunc, (ClientData)&curPlane); - (void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect, - &DBSpaceBits, cifCloseFunc, (ClientData)&curPlane); temp = curPlane; curPlane = nextPlane; nextPlane = temp; @@ -4052,6 +4888,32 @@ CIFGenLayer(op, area, cellDef, origDef, temps, clientdata) nextPlane = temp; break; + case CIFOP_BRIDGELIM: + growDistance = op->co_distance; + DBClearPaintPlane(nextPlane); + cifPlane = nextPlane; + cifScale = 1; + + brlims.bridge = (BridgeData *)op->co_client; + brlims.def = cellDef; + brlims.temps = temps; + brlims.co_paintMask = op->co_paintMask; + brlims.co_cifMask = op->co_cifMask; + + (void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect, + &CIFSolidBits, cifBridgeLimFunc0, (ClientData)&brlims); + temp = curPlane; + curPlane = nextPlane; + brlims.plane = curPlane; + + (void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect, + &CIFSolidBits, cifBridgeLimFunc1, (ClientData)&brlims); + (void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect, + &DBSpaceBits, cifBridgeLimFunc2, (ClientData)&brlims); + curPlane = nextPlane; + nextPlane = temp; + break; + case CIFOP_BLOAT: cifPlane = curPlane; cifSrTiles(op, area, cellDef, temps, diff --git a/cif/CIFint.h b/cif/CIFint.h index a95cc0f3..fcde35e3 100644 --- a/cif/CIFint.h +++ b/cif/CIFint.h @@ -141,6 +141,7 @@ typedef struct cifop * current cell. This replaces the "fault" method. * CIFOP_CLOSE - Added 11/25/19---close up areas smaller than indicated * CIFOP_BRIDGE - Added 6/11/20---Bridge across catecorner gaps + * CIFOP_BRIDGELIM - Added 27/07/20---Bridge across catecorner gaps, but with limiting layers */ #define CIFOP_AND 1 @@ -164,6 +165,8 @@ typedef struct cifop #define CIFOP_COPYUP 19 #define CIFOP_CLOSE 20 #define CIFOP_BRIDGE 21 +#define CIFOP_BRIDGELIM 22 + /* Added by Tim 10/21/2004 */ /* The following structure is used to pass information on how to draw diff --git a/cif/CIFtech.c b/cif/CIFtech.c index 0c905592..07343f05 100644 --- a/cif/CIFtech.c +++ b/cif/CIFtech.c @@ -1058,6 +1058,8 @@ CIFTechLine(sectionName, argc, argv) newOp->co_opcode = CIFOP_CLOSE; else if (strcmp(argv[0], "bridge") == 0) newOp->co_opcode = CIFOP_BRIDGE; + else if (strcmp(argv[0], "bridge-lim") == 0) + newOp->co_opcode = CIFOP_BRIDGELIM; else { TechError("Unknown statement \"%s\".\n", argv[0]); @@ -1107,6 +1109,26 @@ CIFTechLine(sectionName, argc, argv) newOp->co_client = (ClientData)bridge; break; + case CIFOP_BRIDGELIM: + if (argc != 4) goto wrongNumArgs; + newOp->co_distance = atoi(argv[1]); + if (newOp->co_distance <= 0) + { + TechError("Bridge distance must be greater than zero.\n"); + goto errorReturn; + } + bridge = (BridgeData *)mallocMagic(sizeof(BridgeData)); + bridge->br_width = atoi(argv[2]); + if (bridge->br_width <= 0) + { + TechError("Bridge width must be greater than zero.\n"); + freeMagic(bridge); + goto errorReturn; + } + cifParseLayers(argv[3], CIFCurStyle, &newOp->co_paintMask, &newOp->co_cifMask,FALSE); + newOp->co_client = (ClientData)bridge; + break; + case CIFOP_BLOATALL: if (argc != 3) goto wrongNumArgs; cifParseLayers(argv[1], CIFCurStyle, &newOp->co_paintMask, @@ -1572,6 +1594,7 @@ cifComputeRadii(layer, des) break; case CIFOP_BRIDGE: break; + case CIFOP_BRIDGELIM: break; case CIFOP_SQUARES: break; case CIFOP_SQUARES_G: break; } @@ -1831,6 +1854,7 @@ CIFTechFinal() case CIFOP_MAXRECT: case CIFOP_NET: break; + case CIFOP_BRIDGELIM: case CIFOP_BRIDGE: bridge = (BridgeData *)op->co_client; c = FindGCF(style->cs_scaleFactor, @@ -1928,6 +1952,7 @@ CIFTechFinal() case CIFOP_ANDNOT: case CIFOP_SHRINK: case CIFOP_CLOSE: + case CIFOP_BRIDGELIM: case CIFOP_BRIDGE: needThisLayer = TRUE; break; @@ -2359,6 +2384,7 @@ CIFTechOutputScale(n, d) case CIFOP_MAXRECT: case CIFOP_NET: break; + case CIFOP_BRIDGELIM: case CIFOP_BRIDGE: bridge = (BridgeData *)op->co_client; bridge->br_width *= d; @@ -2463,6 +2489,7 @@ CIFTechOutputScale(n, d) if (bloats->bl_distance[j] != 0) bloats->bl_distance[j] /= lexpand; break; + case CIFOP_BRIDGELIM: case CIFOP_BRIDGE: bridge = (BridgeData *)op->co_client; bridge->br_width /= lexpand; diff --git a/commands/CmdCD.c b/commands/CmdCD.c index f235f693..fda146a9 100644 --- a/commands/CmdCD.c +++ b/commands/CmdCD.c @@ -624,7 +624,8 @@ outputCalma: * or * cellname property [name] [property_key [property_value]] * or - * instance orientation [name] [-def] + * instance orientation [name] [-def] [orient] + * instance abutment [name] * * Results: * None. @@ -648,7 +649,7 @@ CmdCellname(w, cmd) bool dodef = FALSE; int option; int locargc = cmd->tx_argc; - char *cellname = NULL; + char *cellname = NULL, *orient = NULL; void (*func)(); CellDef *newDef, *cellDef; @@ -671,6 +672,7 @@ CmdCellname(w, cmd) "lock lock the named cell (prevent changes to cell use)", "unlock unlock the named cell (allow changes to cell use)", "property list or set cell definition properties", + "abutment list instance abutment coordinates", "orientation list or set instance orientation", "rename rename the indicated cell", "writeable make the cell definition read-only or read-write", @@ -681,7 +683,7 @@ CmdCellname(w, cmd) IDX_INSTANCE, IDX_CHILDINST, IDX_CELLDEF, IDX_ALLCELLS, IDX_TOPCELLS, IDX_IN_WINDOW, IDX_CREATE, IDX_DELETE, IDX_FILEPATH, IDX_FLAGS, IDX_LOCK, IDX_UNLOCK, - IDX_PROPERTY, IDX_ORIENTATION, IDX_RENAME, + IDX_PROPERTY, IDX_ABUTMENT, IDX_ORIENTATION, IDX_RENAME, IDX_READWRITE, IDX_MODIFIED } optionType; if (strstr(cmd->tx_argv[0], "in")) @@ -720,7 +722,7 @@ CmdCellname(w, cmd) if ((locargc > 3) && (option != IDX_RENAME) && (option != IDX_DELETE) && (option != IDX_READWRITE) && (option != IDX_PROPERTY) && - (option != IDX_FILEPATH)) + (option != IDX_FILEPATH) && (option != IDX_ORIENTATION)) goto badusage; if ((locargc > 4) && (option != IDX_PROPERTY)) @@ -751,6 +753,10 @@ CmdCellname(w, cmd) TxError("Cell definitions do not have orientations." " Use \"instance\"?\n"); return; + case IDX_ABUTMENT: + TxError("Use \"property get FIXED_BBOX\" to get the cell " + "abutment box.\n"); + return; } } else @@ -1029,7 +1035,16 @@ CmdCellname(w, cmd) } break; case IDX_ORIENTATION: - DBOrientUse(cellname, dodef); + orient = (locargc == 4) ? cmd->tx_argv[3 + ((dolist) ? 1 : 0)] : NULL; + if ((cellname != NULL) && (orient != NULL)) + { + TxError("Cannot set orientation by name. Use selection.\n"); + break; + } + DBOrientUse(cellname, dodef, orient); + break; + case IDX_ABUTMENT: + DBAbutmentUse(cellname, dolist); break; case IDX_LOCK: DBLockUse(cellname, TRUE); @@ -1647,7 +1662,7 @@ CmdCif(w, cmd) * the interpreter to uniquely identify it. * * The "-origin" option has been added to allow rotating stuff - * relative to the origin, insteadd of the lower-left corner. + * relative to the origin, instead of the lower-left corner. * * ---------------------------------------------------------------------------- */ @@ -3897,9 +3912,9 @@ cmdDumpParseArgs(cmdName, w, cmd, dummy, scx) hasChild = hasRoot = hasTrans = FALSE; while (ac > 0) { - static char *kwdNames[] = { "child", "parent", "90", "180", "270", - "v", "90v", "180v", "270v", - "h", "90h", "180h", "270h", 0 }; + static char *kwdNames[] = { "child", "parent", "0", "90", "180", "270", + "v", "0v", "90v", "180v", "270v", + "h", "0h", "90h", "180h", "270h", 0 }; static char *refPointNames[] = { "ll", "lr", "ul", "ur", 0 }; Label *lab; int n,p; @@ -4043,8 +4058,8 @@ cmdDumpParseArgs(cmdName, w, cmd, dummy, scx) } hasRoot = TRUE; break; - case 2: /* 90 */ - tx_cell = &Geo90Transform; + case 2: /* 0 */ + tx_cell = &GeoIdentityTransform; transform_cell: if (ac < 2 ) { @@ -4127,34 +4142,39 @@ default_action: } hasTrans = TRUE; break; - case 3: /* 180 */ + case 3: /* 90 */ + tx_cell = &Geo90Transform; + goto transform_cell; + case 4: /* 180 */ tx_cell = &Geo180Transform; goto transform_cell; - case 4: /* 270 */ + case 5: /* 270 */ tx_cell = &Geo270Transform; goto transform_cell; - case 5: /* v */ + case 6: /* v */ + case 7: /* 0v */ tx_cell = &GeoUpsideDownTransform; goto transform_cell; - case 6: /* 90v */ + case 8: /* 90v */ tx_cell = &GeoRef45Transform; goto transform_cell; - case 7: /* 180v */ + case 9: /* 180v */ tx_cell = &GeoSidewaysTransform; goto transform_cell; - case 8: /* 270v */ + case 10: /* 270v */ tx_cell = &GeoRef135Transform; goto transform_cell; - case 9: /* h */ + case 11: /* h */ + case 12: /* 0h */ tx_cell = &GeoSidewaysTransform; goto transform_cell; - case 10: /* 90h */ + case 13: /* 90h */ tx_cell = &GeoRef135Transform; goto transform_cell; - case 11: /* 180h */ + case 14: /* 180h */ tx_cell = &GeoUpsideDownTransform; goto transform_cell; - case 12: /* 270h */ + case 15: /* 270h */ tx_cell = &GeoRef45Transform; goto transform_cell; } diff --git a/commands/CmdLQ.c b/commands/CmdLQ.c index c0bf6ce9..1c68635b 100644 --- a/commands/CmdLQ.c +++ b/commands/CmdLQ.c @@ -2276,3 +2276,158 @@ CmdNetlist(w, cmd) break; } } + +/* + * ---------------------------------------------------------------------------- + * + * CmdOrient -- + * + * Implement the "orient" command. Set the orientation of the selection and + * according to the orientation string, which can be in Magic's format + * (using number of degrees, and "h" for horizontal flip or "v" for vertical + * flip) or DEF format ("N", "S", "FN", etc.). The cursor box is rotated to + * match. + * + * Usage: + * orient [orientation] [-origin] + * + * Results: + * None. + * + * Side effects: + * Modifies the edit cell. + * + * Notes: + * The "-origin" option sets the orientation relative to the origin, + * instead of the lower-left corner. + * + * ---------------------------------------------------------------------------- + */ + +void +CmdOrient(w, cmd) + MagWindow *w; + TxCommand *cmd; +{ + Transform trans, t2; + int orientidx, locargc; + char *orientstr; + Rect rootBox, bbox; + CellDef *rootDef; + bool noAdjust = FALSE; + + static char *orientNames[] = { "0", "90", "180", "270", + "v", "0v", "90v", "180v", "270v", + "h", "0h", "90h", "180h", "270h", + "N", "E", "S", "W", + "FN", "FE", "FS", "FW", + 0 }; + + typedef enum { IDX_ORIENT_0, IDX_ORIENT_90, IDX_ORIENT_180, IDX_ORIENT_270, + IDX_ORIENT_V, IDX_ORIENT_0V, IDX_ORIENT_90V, IDX_ORIENT_180V, + IDX_ORIENT_270V, IDX_ORIENT_H, IDX_ORIENT_0H, IDX_ORIENT_90H, + IDX_ORIENT_180H, IDX_ORIENT_270H, IDX_ORIENT_N, IDX_ORIENT_E, + IDX_ORIENT_S, IDX_ORIENT_W, IDX_ORIENT_FN, IDX_ORIENT_FE, + IDX_ORIENT_FS, IDX_ORIENT_FW } orientType; + + locargc = cmd->tx_argc; + if (!strncmp(cmd->tx_argv[locargc - 1], "-orig", 5)) + { + noAdjust = TRUE; + locargc--; + } + + if (!ToolGetEditBox((Rect *)NULL)) return; + + if (locargc == 2) + orientstr = cmd->tx_argv[1]; + else + goto badusage; + + orientidx = Lookup(orientstr, orientNames); + + switch (orientidx) + { + case IDX_ORIENT_0: + case IDX_ORIENT_N: + /* ORIENT_NORTH */ + t2 = GeoIdentityTransform; + break; + case IDX_ORIENT_S: + case IDX_ORIENT_180: + /* ORIENT_SOUTH */ + t2 = Geo180Transform; + break; + case IDX_ORIENT_E: + case IDX_ORIENT_90: + /* ORIENT_EAST */ + t2 = Geo90Transform; + break; + case IDX_ORIENT_W: + case IDX_ORIENT_270: + /* ORIENT_WEST */ + t2 = Geo270Transform; + break; + case IDX_ORIENT_FN: + case IDX_ORIENT_H: + case IDX_ORIENT_0H: + /* ORIENT_FLIPPED_NORTH */ + t2 = GeoSidewaysTransform; + break; + case IDX_ORIENT_FS: + case IDX_ORIENT_180H: + case IDX_ORIENT_V: + case IDX_ORIENT_0V: + /* ORIENT_FLIPPED_SOUTH */ + t2 = GeoUpsideDownTransform; + break; + case IDX_ORIENT_FE: + case IDX_ORIENT_90H: + case IDX_ORIENT_270V: + /* ORIENT_FLIPPED_EAST */ + t2 = GeoRef135Transform; + break; + case IDX_ORIENT_FW: + case IDX_ORIENT_270H: + case IDX_ORIENT_90V: + /* ORIENT_FLIPPED_WEST */ + t2 = GeoRef45Transform; + break; + default: + goto badusage; + } + + /* To orient the selection, first orient it relative to the origin + * then move it so its lower-left corner is at the same place + * that it used to be. If the "-origin" option was selected, then + * only orient relative to the origin. + */ + + GeoTransRect(&t2, &SelectDef->cd_bbox, &bbox); + if (noAdjust) + trans = t2; + else + { + GeoTranslateTrans(&t2, SelectDef->cd_bbox.r_xbot - bbox.r_xbot, + SelectDef->cd_bbox.r_ybot - bbox.r_ybot, &trans); + } + + SelectTransform(&trans); + + /* Rotate the box, if it exists and is in the same window as the + * selection. + */ + + if (ToolGetBox(&rootDef, &rootBox) && (rootDef == SelectRootDef)) + { + Rect newBox; + + GeoTransRect(&trans, &rootBox, &newBox); + DBWSetBox(rootDef, &newBox); + } + + return; + + badusage: + TxError("Usage: %s [orientation]\n", cmd->tx_argv[0]); +} diff --git a/commands/CmdSubrs.c b/commands/CmdSubrs.c index b2f2b954..0f3b4ec1 100644 --- a/commands/CmdSubrs.c +++ b/commands/CmdSubrs.c @@ -734,8 +734,9 @@ again: } /* Remove any ".mag" file extension from the name */ - if (!strcmp(returnname + strlen(returnname) - 4, ".mag")) - *(returnname + strlen(returnname) - 4) = '\0'; + if (strlen(returnname) > 4) + if (!strcmp(returnname + strlen(returnname) - 4, ".mag")) + *(returnname + strlen(returnname) - 4) = '\0'; if (strcmp(returnname, def->cd_name) != 0) { diff --git a/database/DBcellname.c b/database/DBcellname.c index ec10d1ee..dbfc32f0 100644 --- a/database/DBcellname.c +++ b/database/DBcellname.c @@ -1204,19 +1204,20 @@ DBLockUse(UseName, bval) * * DBOrientUse -- * - * This routine sets or reports a cell instance's orientation + * This routine reports a cell instance's orientation. * UseName is the name of a specific CellUse. If NULL, then the - * operation applies to all selected cell uses. "orient" is a - * string in the form used by "getcell" (e.g., "180", "270v", - * etc.), unless "dodef" is true, in which case the output is - * given in the form used by DEF ("N", "FN", etc.). - * reported. + * operation applies to all selected cell uses. * * Results: * None. * * Side effects: - * cu_transform changed for indicated cell use. + * In the Tcl/Tk implementation, the result is set in the interpreter. + * + * Notes: + * This routine only reports orientation. Setting orientation must + * be done through the selection interface (i.e., commands "sideways", + * "upsidedown", "clockwise" ("rotate"), or "orient" (added 10/30/2020)). * * ---------------------------------------------------------------------------- */ @@ -1294,6 +1295,7 @@ dbOrientUseFunc(selUse, use, transform, data) ClientData data; { bool *dodef = (bool *)data; + int orient; if (EditCellUse && !DBIsChild(use, EditCellUse)) { @@ -1302,9 +1304,16 @@ dbOrientUseFunc(selUse, use, transform, data) return 0; } + orient = -1; + if (selUse != NULL) + orient = GeoTransOrient(&selUse->cu_transform); + else if (use != NULL) + orient = GeoTransOrient(&use->cu_transform); + + if (orient != -1) { - switch (GeoTransOrient(&selUse->cu_transform)) { + switch (orient) { #ifdef MAGIC_WRAPPER case ORIENT_NORTH: Tcl_AppendElement(magicinterp, (*dodef) ? "N" : "0"); @@ -1362,6 +1371,140 @@ dbOrientUseFunc(selUse, use, transform, data) return 0; } +/* + * ---------------------------------------------------------------------------- + * + * DBAbutmentUse -- + * + * This routine reports the cell instance's abutment box in the + * coordinate system of the parent (edit) cell. + * + * Results: + * None. + * + * Side effects: + * None. + * + * ---------------------------------------------------------------------------- + */ + +void +DBAbutmentUse(UseName, dolist) + char *UseName; + bool dolist; +{ + int found; + HashSearch hs; + HashEntry *entry; + CellDef *celldef; + CellUse *celluse; + + int dbAbutmentUseFunc(); + + /* + * + * Check to see if a cell name was specified. If not, then search + * for selected cells. + * + */ + + if (UseName == NULL) + { + SelEnumCells(TRUE, (int *)NULL, (SearchContext *)NULL, + dbAbutmentUseFunc, (ClientData)&dolist); + } + else + { + SearchContext scx; + + bzero(&scx, sizeof(SearchContext)); + found = 0; + + HashStartSearch(&hs); + while( (entry = HashNext(&dbCellDefTable, &hs)) != NULL) + { + celldef = (CellDef *) HashGetValue(entry); + if (celldef != (CellDef *) NULL) + { + celluse = celldef->cd_parents; /* only need one */ + if (celluse != (CellUse *)NULL) { + DBTreeFindUse(UseName, celluse, &scx); + if (scx.scx_use != NULL) break; + } + } + } + + if (scx.scx_use == NULL) + TxError("Cell %s is not currently loaded.\n", UseName); + else + dbAbutmentUseFunc(NULL, scx.scx_use, NULL, (ClientData)&dolist); + } +} + +/* + * dbAbutmentUseFunc() + */ + +int +dbAbutmentUseFunc(selUse, use, transform, data) + CellUse *selUse; /* Use from selection cell */ + CellUse *use; /* Use from layout corresponding to selection */ + Transform *transform; + ClientData data; +{ + Rect bbox, refbox; + Transform *trans; + char *propvalue; + bool found; + bool *dolist = (bool *)data; + +#ifdef MAGIC_WRAPPER + Tcl_Obj *pobj; +#endif + + if (EditCellUse && !DBIsChild(use, EditCellUse)) + { + TxError("Cell %s (%s) isn't a child of the edit cell.\n", + use->cu_id, use->cu_def->cd_name); + return 0; + } + + if (use == NULL) + { + TxError("No instance in selection!\n"); + return 0; + } + + trans = &use->cu_transform; + propvalue = DBPropGet(use->cu_def, "FIXED_BBOX", &found); + if (!found) + bbox = use->cu_def->cd_bbox; + else + { + if (sscanf(propvalue, "%d %d %d %d", &bbox.r_xbot, &bbox.r_ybot, + &bbox.r_xtop, &bbox.r_ytop) != 4) + bbox = use->cu_def->cd_bbox; + } + GeoTransRect(trans, &bbox, &refbox); + +#ifdef MAGIC_WRAPPER + if (*dolist) + { + pobj = Tcl_NewListObj(0, NULL); + Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_xbot)); + Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_ybot)); + Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_xtop)); + Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_ytop)); + Tcl_SetObjResult(magicinterp, pobj); + } + else +#endif + TxPrintf("Abutment box: %d %d %d %d\n", refbox.r_xbot, refbox.r_ybot, + refbox.r_xtop, refbox.r_ytop); + + return 0; +} + /* * ---------------------------------------------------------------------------- * diff --git a/database/DBio.c b/database/DBio.c index 76ab5a30..bac43ab1 100644 --- a/database/DBio.c +++ b/database/DBio.c @@ -2774,7 +2774,7 @@ DBCellWrite(cellDef, fileName) /* The cd_file should not have the .mag suffix, but make sure */ /* it doesn't before adding one. */ - if (strcmp(fileName + strlen(fileName) - 4, DBSuffix)) + if ((strlen(fileName) < 4) || (strcmp(fileName + strlen(fileName) - 4, DBSuffix))) { realname = (char *) mallocMagic(strlen(fileName) + strlen(DBSuffix) + 1); (void) sprintf(realname, "%s%s", fileName, DBSuffix); diff --git a/database/DBlabel.c b/database/DBlabel.c index 24225f7d..7786a3fa 100644 --- a/database/DBlabel.c +++ b/database/DBlabel.c @@ -426,9 +426,9 @@ DBEraseLabelsByContent(def, rect, type, text) /* * ---------------------------------------------------------------------------- * - * DBEraseLabelsByFunction -- + * DBRemoveLabel -- * - * Erase any labels found on the label list for which the function returns + * Erase a labels by reference. * TRUE. * * Results: @@ -443,19 +443,9 @@ DBEraseLabelsByContent(def, rect, type, text) */ void -DBEraseLabelsByFunction(def, func) +DBRemoveLabel(def, refLab) CellDef *def; /* Where to look for label to delete. */ - bool (*func)(); /* Function to call for each label. If it - * returns TRUE, we delete the label. - * - * Function should be of the form: - * - * bool func(lab) - * Label *lab; - * { - * return XXX; - * } - */ + Label *refLab; { Label *lab, *labPrev; @@ -464,7 +454,7 @@ DBEraseLabelsByFunction(def, func) labPrev = lab, lab = lab->lab_next) { nextCheck: - if (!(*func)(lab)) continue; + if (lab != refLab) continue; DBUndoEraseLabel(def, lab); DBWLabelChanged(def, lab, DBW_ALLWINDOWS); if (labPrev == NULL) diff --git a/database/DBtechname.c b/database/DBtechname.c index df052581..5cf97b1b 100644 --- a/database/DBtechname.c +++ b/database/DBtechname.c @@ -451,6 +451,9 @@ DBTechPrintTypes(mask, dolist) for (p = dbTypeNameLists.sn_next; p != &dbTypeNameLists; p = p->sn_next) { + /* Ignore aliases */ + if (p->sn_alias) continue; + if (((TileType)(spointertype) p->sn_value) == i) { if (dolist) diff --git a/database/DBtechtype.c b/database/DBtechtype.c index 5eeb9d1f..e7fe51ed 100644 --- a/database/DBtechtype.c +++ b/database/DBtechtype.c @@ -760,7 +760,7 @@ dbTechNameAdd(name, cdata, ptable, alias) *cp = '\0'; if (*(cp = onename)) { - if ((current = dbTechNameAddOne(cp, cdata, FALSE, ptable)) == NULL) + if ((current = dbTechNameAddOne(cp, cdata, FALSE, alias, ptable)) == NULL) return (NULL); if (first == NULL) first = current->sn_name; @@ -793,10 +793,11 @@ dbTechNameAdd(name, cdata, ptable, alias) */ NameList * -dbTechNameAddOne(name, cdata, isPrimary, ptable) +dbTechNameAddOne(name, cdata, isPrimary, isAlias, ptable) char *name; /* Name to be added */ ClientData cdata; /* Client value associated with this name */ bool isPrimary; /* TRUE if this is the primary abbreviation */ + bool isAlias; /* TRUE if this name is an alias */ NameList *ptable; /* Table of names to which we're adding this */ { int cmp; @@ -817,6 +818,7 @@ dbTechNameAddOne(name, cdata, isPrimary, ptable) new->sn_name = StrDup((char **) NULL, name); new->sn_value = cdata; new->sn_primary = isPrimary; + new->sn_alias = isAlias; /* Link this entry in to the list before 'tbl' */ new->sn_next = tbl; diff --git a/database/DBtpaint.c b/database/DBtpaint.c index 26c8d884..10090b9a 100644 --- a/database/DBtpaint.c +++ b/database/DBtpaint.c @@ -639,7 +639,14 @@ dbTechAddPaintErase(type, sectionName, argc, argv) pMask &= ~rMask; - for (tres = 0; tres < DBNumTypes; tres++) + /* 10/30/2020: Changed from DBNumTypes to DBNumUserLayers, */ + /* because DBTechNoisyNameMask() was modified to add stacking */ + /* contact types, and it is not correct for tMask to have more */ + /* than one type in the mask that share the same plane. */ + /* NOTE: Paint rules for stacked contacts probably have to be */ + /* handled too, but in a separate way. */ + + for (tres = 0; tres < DBNumUserLayers; tres++) { if (TTMaskHasType(&tMask, tres)) { diff --git a/database/database.h.in b/database/database.h.in index af818186..9069cc8d 100644 --- a/database/database.h.in +++ b/database/database.h.in @@ -729,7 +729,7 @@ extern void DBFontLabelSetBBox(); extern bool DBEraseLabel(); extern void DBEraseLabelAll(); extern void DBEraseLabelsByContent(); -extern void DBEraseLabelsByFunction(); +extern void DBRemoveLabel(); extern void DBReOrientLabel(); extern void DBAdjustLabels(); @@ -778,6 +778,7 @@ extern CellUse *DBCellFindDup(); extern void DBLockUse(); extern void DBUnlockUse(); extern void DBOrientUse(); +extern void DBAbutmentUse(); /* Cell selection */ extern CellUse *DBSelectCell(); diff --git a/database/databaseInt.h b/database/databaseInt.h index 5f631cfb..eda67bed 100644 --- a/database/databaseInt.h +++ b/database/databaseInt.h @@ -81,6 +81,7 @@ typedef struct namelist char *sn_name; /* Text of name */ ClientData sn_value; /* Value (TileType or plane number) */ bool sn_primary; /* If TRUE, this is the primary name */ + bool sn_alias; /* If TRUE, this is an alias name */ } NameList; typedef struct diff --git a/dbwind/DBWcommands.c b/dbwind/DBWcommands.c index 6a5150e4..9b1d423d 100644 --- a/dbwind/DBWcommands.c +++ b/dbwind/DBWcommands.c @@ -47,8 +47,8 @@ extern void CmdEdit(), CmdElement(), CmdErase(), CmdExpand(), CmdExtract(); extern void CmdFeedback(), CmdFill(), CmdFindBox(), CmdFindLabel(), CmdFlush(); extern void CmdGetcell(), CmdGrid(), CmdIdentify(); extern void CmdLabel(), CmdLoad(); -extern void CmdMove(), CmdNetlist(), CmdPaint(), CmdPath(), CmdPlow(); -extern void CmdPolygon(), CmdPort(), CmdProperty(); +extern void CmdMove(), CmdNetlist(), CmdOrient(), CmdPaint(), CmdPath(); +extern void CmdPlow(), CmdPolygon(), CmdPort(), CmdProperty(); extern void CmdSave(), CmdScaleGrid(), CmdSee(); extern void CmdSelect(), CmdSetLabel(), CmdSideways(); extern void CmdShell(), CmdSnap(); @@ -378,6 +378,9 @@ DBWInitCommands() for information on options", CmdNetlist, FALSE); + WindAddCommand(DBWclientID, + "orient [orientation] orient selection and box", + CmdOrient, FALSE); WindAddCommand(DBWclientID, "paint layers|cursor paint mask information", CmdPaint, FALSE); diff --git a/drc/DRCcif.c b/drc/DRCcif.c index 942501e1..c280c4bd 100644 --- a/drc/DRCcif.c +++ b/drc/DRCcif.c @@ -157,6 +157,13 @@ drcCifWarning() * Side effects: * Updates the DRC technology variables. * + * Notes: "centidistance" is by default in centimicrons, but it is really in + * whatever units are declared in the cifoutput section; so if cifoutput + * declares nanometers, then units are in nanometers. This may be in + * different units from the non-CIF part of the DRC section. The escape + * sequence substitution in the "Why" text will make sure that the correct + * physical dimensions are presented to the end user. + * * ---------------------------------------------------------------------------- */ @@ -194,7 +201,7 @@ drcCifWidth(argc, argv) } scalefactor = drcCifStyle->cs_scaleFactor; - centidistance *= drcCifStyle->cs_expander; // BSI + // centidistance *= drcCifStyle->cs_expander; // BSI dpnext = drcCifRules[thislayer][DRC_CIF_SPACE]; dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); @@ -287,7 +294,7 @@ drcCifSpacing(argc, argv) } scalefactor = drcCifStyle->cs_scaleFactor; - centidistance *= drcCifStyle->cs_expander; // BSI + // centidistance *= drcCifStyle->cs_expander; // BSI dpnext = drcCifRules[layer[0]][DRC_CIF_SOLID]; dpnew = (DRCCookie *) mallocMagic((unsigned) sizeof (DRCCookie)); drcCifAssign(dpnew, centidistance, dpnext, &DBSpaceBits, @@ -1167,7 +1174,7 @@ drcCifMaxwidth(argc, argv) } scalefactor = drcCifStyle->cs_scaleFactor; - centidistance *= drcCifStyle->cs_expander; // BSI + // centidistance *= drcCifStyle->cs_expander; // BSI dpnext = drcCifRules[thislayer][DRC_CIF_SPACE]; dpnew = (DRCCookie *) mallocMagic((unsigned) (sizeof (DRCCookie))); drcCifAssign(dpnew, centidistance, dpnext, &CIFSolidBits, &CIFSolidBits, diff --git a/drc/DRCsubcell.c b/drc/DRCsubcell.c index 6fb049a9..9ecdb353 100644 --- a/drc/DRCsubcell.c +++ b/drc/DRCsubcell.c @@ -107,10 +107,22 @@ drcFindOtherCells(use, area) return 0; } -/* For each tile found in drcCopyErrorsFunc(), translate the */ -/* tile position into the coordinate system of the parent cell */ -/* (represented by the drcTemp plane in ClientData) and */ -/* copy (paint) into it. */ +/* + * ---------------------------------------------------------------------------- + * + * drcSubCopyErrors --- + * + * For each tile found in drcCopyErrorsFunc(), translate the tile position + * into the coordinate system of the parent cell (represented by the drcTemp + * plane in ClientData) and apply the function passed in the filter, which is + * whatever function handles DRC errors inside an error tile (which is + * different for "drc why" commands than for "drc check". + * + * Returns: + * 0 to keep the search going. + * + * ---------------------------------------------------------------------------- + */ int drcSubCopyErrors(tile, cxp) @@ -121,9 +133,6 @@ drcSubCopyErrors(tile, cxp) Rect destArea; struct drcClientData *arg = (struct drcClientData *)cxp->tc_filter->tf_arg; - // DBTreeSrTiles() checks its own tiles, which we want to ignore. - if (arg->dCD_celldef == cxp->tc_scx->scx_use->cu_def) return 0; - TiToRect(tile, &area); GeoClip(&area, &cxp->tc_scx->scx_area); GeoTransRect(&cxp->tc_scx->scx_trans, &area, &destArea); @@ -135,6 +144,40 @@ drcSubCopyErrors(tile, cxp) return 0; } +/* + * ---------------------------------------------------------------------------- + * + * drcSubCopyFunc --- + * + * This routine is applied for each subcell of a parent def. It calls + * DBNoTreeSrTiles() with the above routine drcSubCopyErrors(), which + * copies all TT_ERROR_P tiles from the child cell up into the parent. + * This is used only within areas found to be non-interacting with the + * parent, such that any error found in the child cell is guaranteed to + * be a real error, and not one that might be resolved by additional + * material found in the parent or a sibling cell. + * + * Returns: + * Whatever DBNoTreeSrTiles() returns. + * + * ---------------------------------------------------------------------------- + */ + +int +drcSubCopyFunc(scx, cdarg) + SearchContext *scx; + ClientData cdarg; +{ + TileTypeBitMask drcMask; + + /* Create a mask with only TT_ERROR_P in it */ + TTMaskZero(&drcMask); + TTMaskSetType(&drcMask, TT_ERROR_P); + + /* Use DBNoTreeSrTiles() because we want to search only one level down */ + return DBNoTreeSrTiles(scx, &drcMask, 0, drcSubCopyErrors, cdarg); +} + /* * ---------------------------------------------------------------------------- * @@ -607,17 +650,12 @@ DRCInteractionCheck(def, area, erasebox, func, cdarg) void (*savedPaintPlane)(); struct drcClientData arg; SearchContext scx; - TileTypeBitMask drcMask; drcSubFunc = func; drcSubClientData = cdarg; oldTiles = DRCstatTiles; count = 0; - /* Create a mask with only TT_ERROR_P in it */ - TTMaskZero(&drcMask); - TTMaskSetType(&drcMask, TT_ERROR_P); - /* Divide the area to be checked up into squares. Process each * square separately. */ @@ -670,7 +708,7 @@ DRCInteractionCheck(def, area, erasebox, func, cdarg) /* Copy errors up from all non-interacting children */ scx.scx_area = subArea; - DBTreeSrTiles(&scx, &drcMask, 0, drcSubCopyErrors, &arg); + DBCellSrArea(&scx, drcSubCopyFunc, &arg); DRCErrorType = errorSaveType; continue; @@ -706,7 +744,7 @@ DRCInteractionCheck(def, area, erasebox, func, cdarg) DRCBasicCheck(def, &eraseHalo, &subArea, func, cdarg); /* Copy errors up from all non-interacting children */ scx.scx_area = subArea; - DBTreeSrTiles(&scx, &drcMask, 0, drcSubCopyErrors, &arg); + DBCellSrArea(&scx, drcSubCopyFunc, &arg); } /* check below */ if (intArea.r_ybot > eraseClip.r_ybot) @@ -717,7 +755,7 @@ DRCInteractionCheck(def, area, erasebox, func, cdarg) DRCBasicCheck(def, &eraseHalo, &subArea, func, cdarg); /* Copy errors up from all non-interacting children */ scx.scx_area = subArea; - DBTreeSrTiles(&scx, &drcMask, 0, drcSubCopyErrors, &arg); + DBCellSrArea(&scx, drcSubCopyFunc, &arg); } subArea.r_ytop = intArea.r_ytop; subArea.r_ybot = intArea.r_ybot; @@ -730,7 +768,7 @@ DRCInteractionCheck(def, area, erasebox, func, cdarg) DRCBasicCheck(def, &eraseHalo, &subArea, func, cdarg); /* Copy errors up from all non-interacting children */ scx.scx_area = subArea; - DBTreeSrTiles(&scx, &drcMask, 0, drcSubCopyErrors, &arg); + DBCellSrArea(&scx, drcSubCopyFunc, &arg); } /* check left */ if (intArea.r_xbot > eraseClip.r_xbot) @@ -741,7 +779,7 @@ DRCInteractionCheck(def, area, erasebox, func, cdarg) DRCBasicCheck(def, &eraseHalo, &subArea, func, cdarg); /* Copy errors up from all non-interacting children */ scx.scx_area = subArea; - DBTreeSrTiles(&scx, &drcMask, 0, drcSubCopyErrors, &arg); + DBCellSrArea(&scx, drcSubCopyFunc, &arg); } DRCErrorType = errorSaveType; } diff --git a/ext2sim/ext2sim.c b/ext2sim/ext2sim.c index 7034cc71..35b28c05 100644 --- a/ext2sim/ext2sim.c +++ b/ext2sim/ext2sim.c @@ -92,10 +92,11 @@ FILE *esLabF = NULL; static unsigned short esFormat = MIT ; struct { - short resClassSource ; /* the resistance class of the source of the dev */ - short resClassDrain ; /* the resistance class of the drain of the dev */ - short resClassSub ; /* the resistance class of the substrate of the dev */ - char *defSubs ; /* the default substrate node */ + short resClassSource ; /* The resistance class of the source of the dev */ + short resClassDrain ; /* The resistance class of the drain of the dev */ + short resClassSub ; /* The resistance class of the substrate of the dev */ + TileType devType ; /* Magic tile type of the device */ + char *defSubs ; /* The default substrate node */ } fetInfo[MAXDEVTYPES]; typedef struct { @@ -266,6 +267,7 @@ CmdExtToSim(w, cmd) short s_rclass, d_rclass, sub_rclass; char *devname; char *subname; + TileType devtype; int idx; static EFCapValue LocCapThreshold = 2; @@ -576,6 +578,7 @@ runexttosim: fetInfo[i].resClassDrain = NO_RESCLASS; fetInfo[i].resClassSub = NO_RESCLASS; fetInfo[i].defSubs = NULL; + fetInfo[i].devType = TT_SPACE; } /* Get fetInfo information from the current extraction style */ @@ -583,7 +586,8 @@ runexttosim: /* command) */ idx = 0; - while (ExtGetDevInfo(idx++, &devname, &s_rclass, &d_rclass, &sub_rclass, &subname)) + while (ExtGetDevInfo(idx++, &devname, &devtype, &s_rclass, &d_rclass, + &sub_rclass, &subname)) { if (idx == MAXDEVTYPES) { @@ -598,6 +602,7 @@ runexttosim: fetInfo[i].resClassDrain = d_rclass; fetInfo[i].resClassSub = sub_rclass; fetInfo[i].defSubs = subname; + fetInfo[i].devType = devtype; } } @@ -676,6 +681,7 @@ main(argc, argv) fetInfo[i].resClassDrain = NO_RESCLASS; fetInfo[i].resClassSub = NO_RESCLASS; fetInfo[i].defSubs = NULL; + fetInfo[i].devType = TT_SPACE; } i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, MAXDEVTYPES, "nfet"); fetInfo[i].resClassSource = fetInfo[i].resClassDrain = 0 ; @@ -895,6 +901,7 @@ simmainArgs(pargc, pargv) fetInfo[ndx].resClassDrain = rClass; fetInfo[ndx].resClassSub = rClassSub; fetInfo[ndx].defSubs = (char *) mallocMagic((unsigned) (strlen(subsNode)+1)); + fetInfo[ndx].devType = TT_SPACE; strcpy(fetInfo[ndx].defSubs,subsNode); TxError("Info: fet %s(%d) sdRclass=%d subRclass=%d dSub=%s\n", cp, ndx, fetInfo[ndx].resClassSD, fetInfo[ndx].resClassSub, @@ -1047,6 +1054,41 @@ simdevVisit(dev, hc, scale, trans) case DEV_CAPREV: fprintf(esSimF, "c"); /* sim format extension */ break; + case DEV_FET: + case DEV_MOSFET: + case DEV_ASYMMETRIC: + case DEV_MSUBCKT: + /* The sim file format only understands "n" and "p" for FETs. */ + /* The extraction method says nothing about which is which. */ + /* The EFDevTypes[] should ideally start with "n" or "p". If */ + /* it doesn't, then dev->dev_type should. If neither does, */ + /* then use EFDevTypes[] but flag an error. */ + + if (EFDevTypes[dev->dev_type][0] == 'n' || + EFDevTypes[dev->dev_type][0] == 'p') + { + fprintf(esSimF, "%c", EFDevTypes[dev->dev_type][0]); + } + else + { + TileType ttype = fetInfo[dev->dev_type].devType; + + if (DBTypeLongNameTbl[ttype][0] == 'n' || + DBTypeLongNameTbl[ttype][0] == 'p') + { + fprintf(esSimF, "%c", DBTypeLongNameTbl[ttype][0]); + } + else + { + TxError("Error: MOSFET device type \"%s\" does not start with " + "\"n\" or \"p\" as required for the .sim format\n", + EFDevTypes[dev->dev_type]); + + /* Default to "n" */ + fprintf(esSimF, "n"); + } + } + break; default: fprintf(esSimF, "%c", EFDevTypes[dev->dev_type][0]); break; diff --git a/ext2spice/ext2spice.c b/ext2spice/ext2spice.c index e38b472c..e863597b 100644 --- a/ext2spice/ext2spice.c +++ b/ext2spice/ext2spice.c @@ -242,6 +242,7 @@ CmdExtToSpice(w, cmd) short s_rclass, d_rclass, sub_rclass; char *devname; char *subname; + TileType devtype; int idx, idx2; globalList *glist = NULL; @@ -739,7 +740,8 @@ runexttospice: /* command) */ idx = 0; - while (ExtGetDevInfo(idx++, &devname, &s_rclass, &d_rclass, &sub_rclass, &subname)) + while (ExtGetDevInfo(idx++, &devname, &devtype, &s_rclass, &d_rclass, + &sub_rclass, &subname)) { if (idx == MAXDEVTYPES) { diff --git a/extract/ExtBasic.c b/extract/ExtBasic.c index 17b41ae0..0dd5cde2 100644 --- a/extract/ExtBasic.c +++ b/extract/ExtBasic.c @@ -1484,6 +1484,9 @@ extOutputParameters(def, transList, outFile) for (devptr = ExtCurStyle->exts_device[t]; devptr; devptr = devptr->exts_next) { + /* Do not output parameters for ignored devices */ + if (!strcmp(devptr->exts_deviceName, "Ignore")) continue; + plist = devptr->exts_deviceParams; if (plist != (ParamList *)NULL) { @@ -1726,9 +1729,15 @@ extOutputDevices(def, transList, outFile) tmask = &devptr->exts_deviceSDTypes[termcount]; if (TTMaskIsZero(tmask)) { if (termcount < nsd) { + ExtDevice *devcheck; /* See if there is another matching device record with */ /* a different number of terminals, and try again. */ - devptr = extDevFindMatch(devptr, t); + devcheck = extDevFindMatch(devptr, t); + if (devcheck != NULL) devptr = devcheck; + + /* Not finding another device record just means that */ + /* terminals are tied together on the same net, such as */ + /* with a MOS cap. Accept this fact and move on. */ } break; /* End of SD terminals */ } @@ -1851,6 +1860,14 @@ extOutputDevices(def, transList, outFile) #endif extTransRec.tr_devrec = devptr; + /* Model type "Ignore" in the techfile indicates a device */ + /* to be ignored (i.e., a specific combination of layers */ + /* does not form an extractable device, or overlaps another */ + /* device type that should take precedence). */ + + if (!strcmp(devptr->exts_deviceName, "Ignore")) + continue; + /* Original-style FET record backward compatibility */ if (devptr->exts_deviceClass != DEV_FET) fprintf(outFile, "device "); diff --git a/extract/ExtHard.c b/extract/ExtHard.c index a884a87e..7d733caa 100644 --- a/extract/ExtHard.c +++ b/extract/ExtHard.c @@ -427,9 +427,9 @@ extHardGenerateLabel(scx, reg, arg) len = strlen(gen) + prefixlen; newlab = (Label *) mallocMagic((unsigned) (sizeof (Label) + len - 3)); r.r_ll = reg->treg_tile->ti_ll; - GEOCLIP(&r,&scx->scx_area); r.r_ur.p_x = r.r_ll.p_x+1; r.r_ur.p_y = r.r_ll.p_y+1; + GEOCLIP(&r,&scx->scx_area); GeoTransRect(&scx->scx_trans, &r, &newlab->lab_rect); newlab->lab_type = TiGetType(reg->treg_tile); diff --git a/extract/ExtRegion.c b/extract/ExtRegion.c index 0843b7a0..658d272c 100644 --- a/extract/ExtRegion.c +++ b/extract/ExtRegion.c @@ -218,13 +218,14 @@ ExtLabelRegions(def, connTo, nodeList, clipArea) for (quad = 0; quad < 4; quad++) { /* - * Visit each of the four quadrants surrounding - * the lower-left corner of the label, searching - * for a tile whose type matches that of the label - * or connects to it. + * Visit each of the four quadrants surrounding the center + * point of the label, searching for a tile whose type matches + * that of the label or connects to it. */ - p.p_x = lab->lab_rect.r_xbot + offsets[quad].p_x; - p.p_y = lab->lab_rect.r_ybot + offsets[quad].p_y; + p.p_x = ((lab->lab_rect.r_xbot + lab->lab_rect.r_xtop) >> 1) + + offsets[quad].p_x; + p.p_y = ((lab->lab_rect.r_ybot + lab->lab_rect.r_ytop) >> 1) + + offsets[quad].p_y; tp = def->cd_planes[pNum]->pl_hint; GOTOPOINT(tp, &p); def->cd_planes[pNum]->pl_hint = tp; diff --git a/extract/ExtTech.c b/extract/ExtTech.c index c0b2da44..4f6bdb59 100644 --- a/extract/ExtTech.c +++ b/extract/ExtTech.c @@ -304,9 +304,11 @@ ExtCompareStyle(stylename) */ bool -ExtGetDevInfo(idx, devnameptr, s_rclassptr, d_rclassptr, sub_rclassptr, subnameptr) +ExtGetDevInfo(idx, devnameptr, devtypeptr, s_rclassptr, d_rclassptr, + sub_rclassptr, subnameptr) int idx; - char **devnameptr; + char **devnameptr; /* Name of extracted device model */ + TileType *devtypeptr; /* Magic tile type of device */ short *s_rclassptr; /* Source (1st terminal) type only */ short *d_rclassptr; /* Drain (2nd terminal) type only */ short *sub_rclassptr; @@ -354,6 +356,7 @@ ExtGetDevInfo(idx, devnameptr, s_rclassptr, d_rclassptr, sub_rclassptr, subnamep *devnameptr = locdname; *subnameptr = devptr->exts_deviceSubstrateName; + *devtypeptr = t; tmask = &devptr->exts_deviceSDTypes[0]; *s_rclassptr = (short)(-1); /* NO_RESCLASS */ diff --git a/extract/ExtUnique.c b/extract/ExtUnique.c index 01947754..accb53fa 100644 --- a/extract/ExtUnique.c +++ b/extract/ExtUnique.c @@ -260,7 +260,7 @@ makeUnique: lab = ll2->ll_label; saveLab = *lab; - DBEraseLabelsByContent(def, &lab->lab_rect, lab->lab_type, lab->lab_text); + DBRemoveLabel(def, lab); (void) DBPutFontLabel(def, &saveLab.lab_rect, saveLab.lab_font, saveLab.lab_size, saveLab.lab_rotate, &saveLab.lab_offset, saveLab.lab_just, name2, diff --git a/lef/lefCmd.c b/lef/lefCmd.c index e8984361..32b9e881 100644 --- a/lef/lefCmd.c +++ b/lef/lefCmd.c @@ -112,15 +112,15 @@ CmdLef(w, cmd) static char *cmdLefOption[] = { "read [filename] read a LEF file filename[.lef]\n" - " read [filename] -import read a LEF file; import cells from .mag files\n", - " read [filename] -annotate read a LEF file for cell annotation only.", + " read [filename] -import read a LEF file; import cells from .mag files\n" + " read [filename] -annotate read a LEF file for cell annotation only." "write [filename] [-tech] write LEF for current cell\n" - " write [filename] -hide hide all details other than ports\n", + " write [filename] -hide hide all details other than ports\n" " write [filename] -hide hide details in area set back distance ", "writeall write all cells including the top-level cell\n" " writeall -notop write all children of the top-level cell\n" - " writeall -all recurse on all subcells of the top-level cell\n", - " writeall -hide hide all details other than ports\n", + " writeall -all recurse on all subcells of the top-level cell\n" + " writeall -hide hide all details other than ports\n" " writeall -hide [dist] hide details in area set back distance dist", "help print this help information", NULL diff --git a/tcltk/cellmgr.tcl b/tcltk/cellmgr.tcl index 1ea0b0db..5f715857 100644 --- a/tcltk/cellmgr.tcl +++ b/tcltk/cellmgr.tcl @@ -10,7 +10,7 @@ if {$::tk_version >= 8.5} { set Opts(cellmgr) 0 -magic::tag select "magic::mgrselect %R" +magic::tag select "magic::mgrselect %r" magic::tag load "catch {magic::clearstack}; magic::cellmanager" magic::tag getcell "magic::cellmanager" diff --git a/tcltk/readspice.tcl b/tcltk/readspice.tcl index dda21d63..333534f4 100644 --- a/tcltk/readspice.tcl +++ b/tcltk/readspice.tcl @@ -220,10 +220,14 @@ proc readspice {netfile} { set pinname [lindex $infopair 0] set pindir [lindex $infopair 1] if {![catch {set pin [dict get $pindict $pinname]}]} { - case $pindir { - B {port $pin class inout} - I {port $pin class input} - O {port $pin class output} + # Only set pin class if the pin class is currently default + set pinclass [port $pin class] + if {$pinclass == "default"} { + case $pindir { + B {port $pin class inout} + I {port $pin class input} + O {port $pin class output} + } } } elseif {$pinname != ""} { puts stderr ".PININFO error: Pin $pinname not found." diff --git a/tcltk/toolkit.tcl b/tcltk/toolkit.tcl index 75fa12cd..b3d43019 100644 --- a/tcltk/toolkit.tcl +++ b/tcltk/toolkit.tcl @@ -6,6 +6,8 @@ # Revision 0 # December 15, 2016 # Revision 1 +# October 29, 2020 +# Revision 2 (names are hashed from properties) #-------------------------------------------------------------- # Sets up the environment for a toolkit. The toolkit must # supply a namespace that is the "library name". For each @@ -237,6 +239,36 @@ proc magic::gencell {gencell_name {instname {}} args} { } } +#------------------------------------------------------------- +# gencell_makecell +# +# This is a variation of magic::gencell and is used to generate +# a cell and return the cell name without creating or placing +# an instance. +#------------------------------------------------------------- + +proc magic::gencell_makecell {gencell_fullname args} { + + set argpar [dict create {*}$args] + set gencell_basename [namespace tail $gencell_fullname] + set library [namespace qualifiers $gencell_fullname] + set parameters [magic::gencell_defaults $gencell_basename $library $argpar] + set gsuffix [magic::get_gencell_hash ${parameters}] + set gname ${gencell_basename}_${gsuffix} + suspendall + cellname create $gname + pushstack $gname + if {[catch {${library}::${gencell_basename}_draw $parameters} drawerr]} { + puts stderr $drawerr + } + property library $library + property gencell $gencell_basename + property parameters $parameters + popstack + resumeall + return $gname +} + #------------------------------------------------------------- # gencell_getparams # @@ -271,6 +303,9 @@ proc magic::gencell_setparams {parameters} { if {[catch {set state [wm state .params]}]} {return} set slist [grid slaves .params.edits] foreach s $slist { + # ignore .params.edits.gencell_sel, as that does not exist in the + # parameters dictionary + if {$s == ".params.edits.gencell_sel"} {continue} if {[regexp {^.params.edits.(.*)_ent$} $s valid pname] != 0} { set value [dict get $parameters $pname] set magic::${pname}_val $value @@ -293,7 +328,16 @@ proc magic::gencell_setparams {parameters} { #------------------------------------------------------------- # gencell_change # -# Redraw a gencell with new parameters. +# Recreate a gencell with new parameters. Note that because +# each cellname is uniquely identified by the (hashed) set +# of parameters, changing parameters effectively means +# creating a new cell. If the original cell has parents +# other than the parent of the instance being changed, then +# it is retained; otherwise, it is deleted. The instance +# being edited gets replaced by an instance of the new cell. +# If the instance name was the cellname + suffix, then the +# instance name is regenerated. Otherwise, the instance +# name is retained. #------------------------------------------------------------- proc magic::gencell_change {instname gencell_type library parameters} { @@ -324,6 +368,128 @@ proc magic::gencell_change {instname gencell_type library parameters} { set parameters [dict remove $parameters gencell] } + set old_gname [instance list celldef $instname] + set gsuffix [magic::get_gencell_hash ${parameters}] + set gname ${gencell_type}_${gsuffix} + + # Guard against instance having been deleted. Also, if parameters have not + # changed as evidenced by the cell suffix not changing, then nothing further + # needs to be done. + if {$gname == "" || $gname == $old_gname} { + resumeall + return + } + + set snaptype [snap list] + snap internal + set savebox [box values] + + catch {setpoint 0 0 $Opts(focus)} + if [dict exists $parameters nocell] { + select cell $instname + set abox [instance list abutment] + delete + if {$abox != ""} {box values {*}$abox} + if {[catch {set newinst [${library}::${gencell_type}_draw $parameters]} \ + drawerr]} { + puts stderr $drawerr + } + select cell $newinst + } elseif {[cellname list exists $gname] != 0} { + # If there is already a cell of this type then it is only required to + # remove the instance and replace it with an instance of the cell + select cell $instname + # check rotate/flip before replacing and replace with same + set orient [instance list orientation] + set abox [instance list abutment] + delete + + if {$abox != ""} {box values {*}$abox} + set newinstname [getcell $gname $orient] + select cell $newinstname + expand + + # If the old instance name was not formed from the old cell name, + # then keep the old instance name. + if {[string first $old_gname $instname] != 0} { + set newinstname $instname + } + + if {[cellname list parents $old_gname] == []} { + # If the original cell has no intances left, delete it. It can + # be regenerated if and when necessary. + cellname delete $old_gname + } + + } else { + select cell $instname + set orient [instance list orientation] + set abox [instance list abutment] + delete + + # There is no cell of this name, so generate one and instantiate it. + if {$abox != ""} {box values {*}$abox} + set newinstname [magic::gencell_create $gencell_type $library $parameters $orient] + select cell $newinstname + + # If the old instance name was not formed from the old cell name, + # then keep the old instance name. + if {[string first $old_gname $instname] != 0} { + set newinstname $instname + } + } + identify $newinstname + eval "box values $savebox" + snap $snaptype + + # Update window + if {$gname != $old_gname} { + catch {.params.title.glab configure -text "$gname"} + } + if {$instname != $newinstname} { + catch {.params.title.ient delete 0 end} + catch {.params.title.ient insert 0 "$newinstname"} + } + + resumeall + redraw +} + +#------------------------------------------------------------- +# gencell_change_orig +# +# Original version: Redraw a gencell with new parameters, +# without changing the cell itself. +#------------------------------------------------------------- + +proc magic::gencell_change_orig {instname gencell_type library parameters} { + global Opts + suspendall + + set newinstname $instname + if {$parameters == {}} { + # Get device defaults + set pdefaults [${library}::${gencell_type}_defaults] + # Pull user-entered values from dialog + set parameters [dict merge $pdefaults [magic::gencell_getparams]] + set newinstname [.params.title.ient get] + if {$newinstname == "(default)"} {set newinstname $instname} + if {$newinstname == $instname} {set newinstname $instname} + if {[instance list exists $newinstname] != ""} {set newinstname $instname} + } + if {[dict exists $parameters gencell]} { + # Setting special parameter "gencell" forces the gencell to change type + set gencell_type [dict get $parameters gencell] + } + if {[catch {set parameters [${library}::${gencell_type}_check $parameters]} \ + checkerr]} { + puts stderr $checkerr + } + magic::gencell_setparams $parameters + if {[dict exists $parameters gencell]} { + set parameters [dict remove $parameters gencell] + } + set gname [instance list celldef $instname] # Guard against instance having been deleted @@ -391,6 +557,51 @@ proc magic::get_gencell_name {gencell_type} { return ${gencell_type}_$postfix } +#---------------------------------------------------------------- +# get_gencell_hash +# +# A better approach to the above. Take the parameter +# dictionary, and run all the values through a hash function +# to generate a 30-bit value, then convert to base32. This +# gives a result that is repeatable for the same set of +# parameter values with a very low probability of a collision. +# +# The hash function is similar to elfhash but reduced from 32 +# to 30 bits so that the result can form a 6-character value +# in base32 with all characters being valid for a SPICE subcell +# name (e.g., alphanumeric only and case-insensitive). +#---------------------------------------------------------------- + +proc magic::get_gencell_hash {parameters} { + set hash 0 + # Apply hash + dict for {key value} $parameters { + foreach s [split $value {}] { + set hash [expr {($hash << 4) + [scan $s %c]}] + set high [expr {$hash & 0x03c0000000}] + set hash [expr {$hash ^ ($high >> 30)}] + set hash [expr {$hash & (~$high)}] + } + } + # Divide hash up into 5 bit values and convert to base32 + # using letters A-Z less I and O, and digits 2-9. + set cvals "" + for {set i 0} {$i < 6} {incr i} { + set oval [expr {($hash >> ($i * 5)) & 0x1f}] + if {$oval < 8} { + set bval [expr {$oval + 50}] + } elseif {$oval < 16} { + set bval [expr {$oval + 57}] + } elseif {$oval < 21} { + set bval [expr {$oval + 58}] + } else { + set bval [expr {$oval + 59}] + } + append cvals [binary format c* $bval] + } + return $cvals +} + #------------------------------------------------------------- # gencell_create # @@ -402,7 +613,7 @@ proc magic::get_gencell_name {gencell_type} { # the drawing routine is going to do to the stack! #------------------------------------------------------------- -proc magic::gencell_create {gencell_type library parameters} { +proc magic::gencell_create {gencell_type library parameters {orient 0}} { global Opts suspendall @@ -451,7 +662,8 @@ proc magic::gencell_create {gencell_type library parameters} { set gname [instance list celldef $instname] eval "box values $savebox" } else { - set gname [magic::get_gencell_name ${gencell_type}] + set gsuffix [magic::get_gencell_hash ${parameters}] + set gname ${gencell_type}_${gsuffix} cellname create $gname pushstack $gname if {[catch {${library}::${gencell_type}_draw $parameters} drawerr]} { @@ -462,7 +674,7 @@ proc magic::gencell_create {gencell_type library parameters} { property parameters $parameters popstack eval "box values $savebox" - set instname [getcell $gname] + set instname [getcell $gname $orient] expand } if {$newinstname != ""} { diff --git a/tcltk/tools.tcl b/tcltk/tools.tcl index 89cd8401..6addd626 100644 --- a/tcltk/tools.tcl +++ b/tcltk/tools.tcl @@ -15,6 +15,7 @@ proc magic::suspendall {} { global Winopts if {[info commands winfo] != ""} { foreach window [magic::windownames layout] { + if {$window == 0} {continue} set framename [winfo toplevel $window] if {$framename == "."} { set framename $window @@ -30,6 +31,7 @@ proc magic::resumeall {} { global Winopts if {[info commands winfo] != ""} { foreach window [magic::windownames layout] { + if {$window == 0} {continue} set framename [winfo toplevel $window] if {$framename == "."} { set framename $window