/* * PlowYank.c -- * * Plowing. * Incremental yanking, and painting the results back into the * original cell when done. * * ********************************************************************* * * 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/PlowYank.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $"; #endif /* not lint */ #include #include #include "utils/magic.h" #include "utils/geometry.h" #include "utils/geofast.h" #include "tiles/tile.h" #include "utils/hash.h" #include "database/database.h" #include "utils/undo.h" #include "debug/debug.h" #include "plow/plow.h" #include "plow/plowInt.h" #include "textio/textio.h" #include "windows/windows.h" #include "dbwind/dbwind.h" #include "drc/drc.h" /* Yank buffer used to hold the actual geometry being plowed */ CellDef *plowYankDef; CellUse *plowYankUse; /* Incremental yanking information */ CellDef *plowSpareDef; /* Def into which we do each incremental yank */ CellUse *plowSpareUse; /* Use of above */ Rect plowYankedArea; /* Area yanked so far. Cells may extend outside * this area, so when we do incremental yanking, * we need to avoid copying any cells that were * already yanked on a previous pass. */ int plowYankHalo; /* Halo around each edge. If this halo extends * outside the area already yanked, or touches * its border, we yank more. */ /* Imports from the rest of this module */ extern Rect plowCellBbox; extern Transform plowYankTrans; extern Transform plowInverseTrans; extern int plowDirection; extern bool plowLabelsChanged; /* Dummy whose cu_def pointer is reset to point to the def being plowed */ extern CellUse *plowDummyUse; /* Forward declarations */ int plowYankUpdateCell(); int plowYankUpdatePaint(); int plowCheckLabel(); /* * ---------------------------------------------------------------------------- * * plowYankMore -- * * Yank more area from the original cell. This is a little tricky in * the case of subcells, because we don't always update plowYankedArea * to enclose all subcell bounding boxes. This means that a subcell * may appear in more than a single yank area. To combat this, we do * not actually copy a cell if it already has been copied (which we * determine based on the use-id). * * The rectangle causing us to yank more area is 'area'. To try to * make the cost of yanking stay roughly linear in the area plowed, * we try to make the area grow by the same factor each time we yank * more. * * We don't do anything if we don't have to yank any more area; this * is the case if 'area' plus its halo lies entirely inside plowYankedArea. * * Results: * TRUE if we yanked more, FALSE if not. * * Side effects: * May yank more area from the original cell. * The spare cell (plowSpareDef) is always left cleared. * * ---------------------------------------------------------------------------- */ bool plowYankMore(area, halo, back) Rect *area; int halo; /* Distance to right, top, bottom of area that * must have been yanked. */ int back; /* Distance to left that must have been * yanked. */ { Rect grownR, newArea, oldArea; SearchContext scx; int xsize, ysize; CellDef tmpDef; int pNum; grownR.r_xbot = area->r_xbot - back; grownR.r_xtop = area->r_xtop + halo; grownR.r_ybot = area->r_ybot - halo; grownR.r_ytop = area->r_ytop + halo; GEOCLIP(&grownR, &plowCellBbox); /* Don't have to yank if entirely within the yanked area */ if (GEO_SURROUND_STRONG(&plowYankedArea, &grownR)) return (FALSE); /* Figure out the additional area to yank */ xsize = (plowYankedArea.r_xtop - plowYankedArea.r_xbot) >> 1; ysize = (plowYankedArea.r_ytop - plowYankedArea.r_ybot) >> 1; newArea = plowYankedArea; if (grownR.r_xbot <= plowYankedArea.r_xbot) newArea.r_xbot -= xsize >> 1; if (grownR.r_xtop >= plowYankedArea.r_xtop) newArea.r_xtop += xsize; if (grownR.r_ybot <= plowYankedArea.r_ybot) newArea.r_ybot -= ysize; if (grownR.r_ytop >= plowYankedArea.r_ytop) newArea.r_ytop += ysize; (void) GeoInclude(&grownR, &newArea); /* Clip to the cell bbox; if inside the existing yanked area, we're done */ GEOCLIP(&newArea, &plowCellBbox); if (GEO_SURROUND(&plowYankedArea, &newArea)) return (FALSE); oldArea = plowYankedArea; plowYankedArea = newArea; /* * Work to do. * Right now, we use a very simple approach: * * Yank the complete area into a separate set of tile planes. * Use the original set to update cu_clients and trailing tile * coordinates. * * Run without undo since we're mucking with yank cells. */ /* Yank the larger area into the spare cell */ UndoDisable(); scx.scx_use = plowDummyUse; scx.scx_trans = plowYankTrans; GeoTransRect(&plowInverseTrans, &plowYankedArea, &scx.scx_area); (void) DBCellCopyPaint(&scx, &DBAllButSpaceAndDRCBits, 0, plowSpareUse); (void) DBCellCopyCells(&scx, plowSpareUse, (Rect *) NULL); /* * Update cell positions in the new cell. * The following loop executes as many times as there * are cells in the original yank def; each cell gets * deleted and replaces its counterpart in the new yank * buffer. */ while (DBCellEnum(plowYankDef, plowYankUpdateCell, (ClientData) NULL)) /* Nothing */; /* * Update paint edges in the new cell. * We extend the search one lambda to the right to catch the * trailing edges of space tiles in the original yank buffer. */ oldArea.r_xtop++; for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++) { (void) DBSrPaintArea((Tile *) NULL, plowYankDef->cd_planes[pNum], &oldArea, &DBAllTypeBits, plowYankUpdatePaint, (ClientData) pNum); } /* Switch the yank cell and the spare cell */ DBCellClearDef(plowYankDef); DBCellSetAvail(plowYankDef); DBCellCopyDefBody(plowYankDef, &tmpDef); DBCellCopyDefBody(plowSpareDef, plowYankDef); DBCellCopyDefBody(&tmpDef, plowSpareDef); UndoEnable(); return (TRUE); } /* * ---------------------------------------------------------------------------- * * plowYankUpdateCell -- * * Called for each cell in the old yank buffer. We search the * new yank buffer for the corresponding cell (same child def, * same use-id) delete the cell from the new yank buffer, and * replace it with the cell from the old one. * * We have to do this, rather than simply updating the deltas * in the new yank buffer, because edges in the queue may have * pointers to the uses from the old yank buffer. * * Results: * Always returns 1. * * Side effects: * Moves its argument cell over to plowSpareUse, replacing * the cell by the same name in plowSpareUse. * * ---------------------------------------------------------------------------- */ int plowYankUpdateCell(yankChildUse) CellUse *yankChildUse; /* Use in the yank cell */ { CellUse *spareChildUse; ClientData savedelta; savedelta = yankChildUse->cu_client; for (spareChildUse = yankChildUse->cu_def->cd_parents; spareChildUse; spareChildUse = spareChildUse->cu_nextuse) { if (spareChildUse->cu_parent == plowSpareDef && strcmp(spareChildUse->cu_id, yankChildUse->cu_id) == 0) { /* Delete the use from the new yank def */ DBDeleteCell(spareChildUse); /* Move the use from the old yank def to the new yank def */ DBDeleteCell(yankChildUse); DBPlaceCell(yankChildUse, plowSpareDef); /* Restore the delta since DBPlaceCell re-initializes it to 0 */ yankChildUse->cu_client = savedelta; /* Return 1 so DBSrCellPlaneArea doesn't bomb */ return (1); } } TxError("Couldn't find use %s in spare yank buffer\n", yankChildUse->cu_id); return (0); } /* * ---------------------------------------------------------------------------- * * plowYankUpdatePaint -- * * Called for each paint tile in the old yank buffer. We search the new * yank buffer for the corresponding tile. The new yank buffer's tile * may need to be clipped at its top and bottom if it is bigger than the * old yank buffer's tile. We set the TRAILING coordinate of the clipped * tile to that of the old yank buffer's tile. * * Results: * Always returns 0. * * Side effects: * May update the TRAILING coordinate of the corresponding tile * in plowSpareUse. * * ---------------------------------------------------------------------------- */ int plowYankUpdatePaint(yankTp, pNum) Tile *yankTp; int pNum; { Tile *spareTp; Point startPoint; Plane *plane; /* * Walk down the inside of the LHS of yankTp, finding all tiles * spareTp along that LHS and updating their client values. * There may be more than one spareTp along this edge, owing * to additional material added outside the original yank buffer * that caused re-merging into maximal horizontal strips. */ startPoint.p_x = LEFT(yankTp); startPoint.p_y = TOP(yankTp) - 1; plane = plowSpareDef->cd_planes[pNum]; spareTp = (Tile *) NULL; do { spareTp = TiSrPoint(spareTp, plane, &startPoint); /* * Only update if both tiles are of the same type. * This should always be true except when yankTp is a space tile * from the RHS of the original yank buffer that lies over a * non-space tile in the new yank buffer (spare). */ if (TiGetTypeExact(spareTp) == TiGetTypeExact(yankTp)) { if (TOP(spareTp) > TOP(yankTp)) (void) plowSplitY(spareTp, TOP(yankTp)); if (BOTTOM(spareTp) < BOTTOM(yankTp)) spareTp = plowSplitY(spareTp, BOTTOM(yankTp)); spareTp->ti_client = yankTp->ti_client; } startPoint.p_y = BOTTOM(spareTp) - 1; } while (startPoint.p_y >= BOTTOM(yankTp)); return (0); } /* * ---------------------------------------------------------------------------- * * plowUpdateLabels -- * * For each label in the original cell that is attached to geometry that * has moved in the plow yank cell, determine how far the label must move * and move it. * * Results: * None. * * Side effects: * Moves labels. * Sets plowLabelsChanged if we actually move a label. * * ---------------------------------------------------------------------------- */ struct labelUpdate { Rect lu_rect; /* Label itself */ int lu_adjust; /* How much to adjust it rightwards */ }; void plowUpdateLabels(yankDef, origDef, origArea) CellDef *yankDef; /* Def containing plowed paint */ CellDef *origDef; /* Original def whose labels are to be adjusted */ Rect *origArea; /* Area in original def that was modified */ { extern void DBUndoPutLabel(), DBUndoEraseLabel(); Rect yankSearch; TileTypeBitMask typeBits; struct labelUpdate lu; Label *origLab; int pNum; for (origLab = origDef->cd_labels; origLab; origLab = origLab->lab_next) { /* Labels attached to space don't ever move */ if (origLab->lab_type == TT_SPACE) continue; /* Labels outside the area changed don't move either */ if (!GEO_TOUCH(&origLab->lab_rect, origArea)) continue; /* * If any of the tiles to which this label "belongs" * moved far enough to touch or pass this label, drag * it along with them. */ pNum = DBPlane(origLab->lab_type); GeoTransRect(&plowYankTrans, &origLab->lab_rect, &lu.lu_rect); lu.lu_adjust = 0; yankSearch = lu.lu_rect; yankSearch.r_xbot--, yankSearch.r_xtop++; yankSearch.r_ybot--, yankSearch.r_ytop++; TTMaskSetOnlyType(&typeBits, origLab->lab_type); (void) DBSrPaintArea((Tile *) NULL, yankDef->cd_planes[pNum], &yankSearch, &typeBits, plowCheckLabel, (ClientData) &lu); if (lu.lu_adjust) { lu.lu_rect.r_xbot += lu.lu_adjust; lu.lu_rect.r_xtop += lu.lu_adjust; DBUndoEraseLabel(origDef, origLab); GeoTransRect(&plowInverseTrans, &lu.lu_rect, &origLab->lab_rect); DBUndoPutLabel(origDef, origLab); plowLabelsChanged = TRUE; } } } int plowCheckLabel(tile, lu) Tile *tile; struct labelUpdate *lu; { int adjust; /* * If the RHS of the label touches the RHS of this tile, move * the label with the leading edge; otherwise, move it with the * trailing edge. */ if (lu->lu_rect.r_xtop == RIGHT(tile)) adjust = LEADING(tile) - lu->lu_rect.r_xtop; else adjust = TRAILING(tile) - lu->lu_rect.r_xbot; if (adjust > lu->lu_adjust) lu->lu_adjust = adjust; return (0); } /* * ---------------------------------------------------------------------------- * * plowUpdateCell -- * * Called by DBCellEnum(). * Update the positions of each cell moved by plowing. The cu_client * fields of various cells in the cell plane of yankDef have been updated * as a result of plowing. For each cell with a non-zero cu_client, we * find the corresponding one in the parent (the use with the same * instance-id), rip it up, and replace it. * * Results: * Returns 0 always. * * Side effects: * Sets plowLabelsChanged if we have to move a cell. * May cause the cell corresponding to 'use' to be re-placed * in the original def, 'origDef'. * * ---------------------------------------------------------------------------- */ int plowUpdateCell(use, origDef) CellUse *use; /* Use from yank def; corresponding use in 'def' will * be moved if use->cu_client is non-zero. */ CellDef *origDef; /* Original def whose cells are to be adjusted based * on the corresponding cells in yankDef. */ { CellUse *origUse; Transform newTrans; int x, y; if (use->cu_client == (ClientData)CLIENTDEFAULT || use->cu_client == (ClientData)0) return (0); /* * Find the corresponding use in the original parent. * This will be a use of the same def with the same * instance identifier; if we can't find it, something * very strange is going on. */ for (origUse = use->cu_def->cd_parents; origUse; origUse = origUse->cu_nextuse) { if (origUse->cu_parent == plowDummyUse->cu_def && strcmp(origUse->cu_id, use->cu_id) == 0) break; } if (origUse == NULL) { TxError("Oops! Can't find cell use %s in parent\n", use->cu_id); return (0); } plowLabelsChanged = TRUE; /* Figure out how much to translate by based on the plowing direction */ x = y = 0; switch (plowDirection) { case GEO_NORTH: y = (int)use->cu_client; break; case GEO_SOUTH: y = -(int)use->cu_client; break; case GEO_WEST: x = -(int)use->cu_client; break; case GEO_EAST: x = (int)use->cu_client; break; } GeoTranslateTrans(&origUse->cu_transform, x, y, &newTrans); DBDeleteCell(origUse); DBWAreaChanged(origDef, &origUse->cu_bbox, DBW_ALLWINDOWS, (TileTypeBitMask *) NULL); DBSetTrans(origUse, &newTrans); DBPlaceCell(origUse, origDef); DBWAreaChanged(origDef, &origUse->cu_bbox, DBW_ALLWINDOWS, (TileTypeBitMask *) NULL); return (0); } /* * ---------------------------------------------------------------------------- * * plowUpdatePaintTile -- * * Copy back a tile from the plowed planes into the orignal cell def. * Instead of using the normal left and right coordinates of this tile, * however, we use the leading/trailing edges. * * Results: * Always return 0 to keep the search alive. * * Side effects: * Paints a tile of the same type as 'tile' into the * original def, after first transforming by the inverse * transform of that used to rotate the original def * into the plowing yank buffer. * * ---------------------------------------------------------------------------- */ int plowUpdatePaintTile(tile, ui) Tile *tile; /* Tile in yanked, plowed def */ PaintUndoInfo *ui; /* Identifies original cell and plane being searched */ { Rect r, rtrans; TileType type = TiGetTypeExact(tile); int pNum, pMask; r.r_ybot = BOTTOM(tile); r.r_ytop = TOP(tile); r.r_xbot = TRAILING(tile); r.r_xtop = LEADING(tile); GeoTransRect(&plowInverseTrans, &r, &rtrans); pMask = DBTypePlaneMaskTbl[type]; for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++) { if (PlaneMaskHasPlane(pMask, pNum)) { ui->pu_pNum = pNum; DBPaintPlane(ui->pu_def->cd_planes[pNum], &rtrans, DBWriteResultTbl[type], ui); } } return (0); }