/* selCreate.c - * * This file provides routines to make selections by copying * things into a special cell named "__SELECT__". * * ********************************************************************* * * 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/select/selCreate.c,v 1.10 2010/06/24 12:37:56 tim Exp $"; #endif /* not lint */ #include #include #include "utils/magic.h" #include "utils/malloc.h" #include "utils/geometry.h" #include "tiles/tile.h" #include "utils/hash.h" #include "database/database.h" #include "windows/windows.h" #include "dbwind/dbwind.h" #include "utils/undo.h" #include "commands/commands.h" #include "select/selInt.h" #include "select/select.h" #include "drc/drc.h" #include "utils/main.h" #include "utils/signals.h" /* C99 compat */ #include "graphics/graphics.h" /* Two cells worth of information are kept around by the selection * module. SelectDef and SelectUse are for the cells whose contents * are the current selection. Select2Def and Select2Use provide a * temporary working space for procedures that manipulate the selection. * for example, Select2Def is used to hold nets or regions while they * are being extracted by SelectRegion or SelectNet. Once completely * extracted, information is copied to SelectDef. Changes to * SelectDef are undo-able and redo-able (so that the undo package * can deal with selection changes), but changes to Select2Def are * not undo-able (undoing is always disabled when the cell is modified). */ global CellDef *SelectDef, *Select2Def; global CellUse *SelectUse, *Select2Use; /* The CellDef below points to the definition FROM which the selection * is extracted. This is the root definition of a window. Everything * in the selection must have come from the same place, so we clear the * selection whenever the user tries to select from a new hierarchy. */ CellDef *SelectRootDef = NULL; /* The CellUse below is the last use selected by SelectUse. It is * kept around to support the "replace" feature of SelectUse. * * Procedures which deselect a cell must reset this to null if they * happen to deselect this usage. (Danger Will Robinson) */ global CellUse *selectLastUse = NULL; /* * ---------------------------------------------------------------------------- * * SelectInit -- * * This procedure initializes the selection code by creating * the selection cells. * * Results: * None. * * Side effects: * The select cells are created if they don't already exist. * Selection undo-ing is also initialized. * * ---------------------------------------------------------------------------- */ void SelectInit() { static bool initialized = FALSE; if (initialized) return; else initialized = TRUE; /* Create the working cells used internally to this module to * hold selected information. Don't allow any of this to be * undone, or else it could invalidate all the pointers we * keep around to the cells. */ UndoDisable(); SelectDef = DBCellLookDef("__SELECT__"); if (SelectDef == (CellDef *) NULL) { SelectDef = DBCellNewDef("__SELECT__"); ASSERT(SelectDef != (CellDef *) NULL, "SelectInit"); DBCellSetAvail(SelectDef); SelectDef->cd_flags |= CDINTERNAL; TTMaskZero(&SelectDef->cd_types); } SelectUse = DBCellNewUse(SelectDef, (char *) NULL); DBSetTrans(SelectUse, &GeoIdentityTransform); SelectUse->cu_expandMask = CU_DESCEND_SPECIAL; /* This is always expanded. */ SelectUse->cu_flags = 0; /* never locked down */ Select2Def = DBCellLookDef("__SELECT2__"); if (Select2Def == (CellDef *) NULL) { Select2Def = DBCellNewDef("__SELECT2__"); ASSERT(Select2Def != (CellDef *) NULL, "SelectInit"); DBCellSetAvail(Select2Def); Select2Def->cd_flags |= CDINTERNAL; } Select2Use = DBCellNewUse(Select2Def, (char *) NULL); DBSetTrans(Select2Use, &GeoIdentityTransform); Select2Use->cu_expandMask = CU_DESCEND_SPECIAL; /* This is always expanded. */ Select2Use->cu_flags = 0; /* never locked down */ UndoEnable(); SelUndoInit(); } /* * ---------------------------------------------------------------------------- * * SelectClear -- * * This procedure clears the current selection. * * Results: * None. * * Side effects: * All information is removed from the select cell, and selection * information is also taken off the screen. * * ---------------------------------------------------------------------------- */ /* The variables below are used to record information about subcells that * must be cleared from the select cell. */ #define MAXUSES 30 static CellUse *(selDeleteUses[MAXUSES]); static int selNDelete; void SelectClear() { SearchContext scx; Rect r, expand; extern int selClearFunc(); /* Forward declaration. */ if (SelectRootDef == NULL) return; scx.scx_area = SelectDef->cd_bbox; expand = scx.scx_area; /* Special handling when the cell is flagged as a net selection */ if (SelectUse->cu_flags & CU_SELECT_NET) { SelNetRememberForUndo((CellDef *)NULL, (Point *)NULL, TT_SPACE, FALSE, FALSE); SelectUse->cu_flags = 0; DBCellClearDef(SelectDef); } else { SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL); SelectUse->cu_flags = 0; /* Erase all the paint from the select cell. */ DBEraseMask(SelectDef, &TiPlaneRect, &DBAllButSpaceBits); /* For the line below to work, paint tables need to be set up correctly! */ /* DBErase(SelectDef, &TiPlaneRect, TT_SPACE); */ /* Erase all of the labels from the select cell. */ (void) DBEraseLabel(SelectDef, &TiPlaneRect, &DBAllTypeBits, &expand); /* Erase all of the subcells from the select cell. This is a bit tricky, * because we can't erase the subcells while searching for them (it will * cause problems for the database). The code below first grabs up a * few subcells, then deletes them, then grabs up a few more, then deletes * them, and so on until done. */ scx.scx_use = SelectUse; scx.scx_trans = GeoIdentityTransform; while (TRUE) { int i; selNDelete = 0; (void) DBCellSrArea(&scx, selClearFunc, (ClientData) NULL); for (i = 0; i < selNDelete; i += 1) { DBUnLinkCell(selDeleteUses[i], SelectDef); DBDeleteCell(selDeleteUses[i]); (void) DBCellDeleteUse(selDeleteUses[i]); } if (selNDelete < MAXUSES) break; } selectLastUse = NULL; SelRememberForUndo(FALSE, SelectRootDef, &scx.scx_area); } TTMaskZero(&SelectDef->cd_types); /* Reset the transform, if we have been moving the selection around */ GeoTransRect(&SelectUse->cu_transform, &expand, &r); SelectUse->cu_transform = GeoIdentityTransform; /* Erase the selection from the screen. */ DBWHLRedraw(SelectRootDef, &r, TRUE); DBReComputeBbox(SelectDef); DBWAreaChanged(SelectDef, &expand, DBW_ALLWINDOWS, (TileTypeBitMask *) NULL); } /* Search function to help clear subcells from the selection. It just * records information about several subcells (up to MAXUSES). */ int selClearFunc(scx) SearchContext *scx; /* Describes a cell that was found. */ { selDeleteUses[selNDelete] = scx->scx_use; selNDelete += 1; if (selNDelete == MAXUSES) return 1; else return 2; } /* * ---------------------------------------------------------------------------- * * selIntersectPaintFunc2 --- * * ---------------------------------------------------------------------------- */ int selIntersectPaintFunc2(tile, rect) Tile *tile; /* The tile to copy paint from. */ Rect *rect; /* Area to clip to */ { Rect r; TiToRect(tile, &r); GEOCLIP(&r, rect); /* Clip out the intersection area */ DBPaint(SelectDef, &r, TiGetTypeExact(tile)); /* Paint back into SelectDef */ return 0; /* Keep the search going. */ } /* * ---------------------------------------------------------------------------- * * selIntersectPaintFunc -- * * Erase paint of types in rMask from the area of the tile. * * ---------------------------------------------------------------------------- */ int selIntersectPaintFunc(tile) Tile *tile; /* The tile to copy paint from. */ { TileTypeBitMask tMask; Rect r; int pNum; TiToRect(tile, &r); for (pNum = 0; pNum < DBNumPlanes; pNum++) { DBSrPaintArea((Tile *)NULL, Select2Def->cd_planes[pNum], &r, &DBAllButSpaceAndDRCBits, selIntersectPaintFunc2, (ClientData)&r); } return 0; /* Keep the search going. */ } /* * ---------------------------------------------------------------------------- * * SelectIntersect -- * * This procedure selects all information that falls in a given area * and contains the intersection of all the supplied types. * * Results: * None. * * Side effects: * The indicated information is added to the select cell, and * outlined on the screen. Only information of particular * types, and in expanded cells (according to xMask) is * selected. * * ---------------------------------------------------------------------------- */ void SelectIntersect(scx, type, xMask, negate) SearchContext *scx; /* Describes the area in which material * is to be selected. The resulting * coordinates should map to the coordinates * of EditRootDef. The cell use should be * the root of a window. */ TileType type; /* Indicates which layer to intersect with * the current selection. */ int xMask; /* Indicates window (or windows) where cells * must be expanded for their contents to be * considered. 0 means treat everything as * expanded. */ bool negate; /* If true, search on NOT(type) */ { TileTypeBitMask tMask, rMask; TileType s, t; int plane; SearchContext scx2; /* The source definition may not change */ if (SelectRootDef != scx->scx_use->cu_def) return; SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL); /* Copy SelectDef contents (paint only) into Select2Def */ DBCellClearDef(Select2Def); scx2.scx_use = SelectUse; scx2.scx_area = SelectUse->cu_bbox; GeoTransTrans(&GeoIdentityTransform, &SelectUse->cu_transform, &scx2.scx_trans); DBCellCopyAllPaint(&scx2, &DBAllButSpaceAndDRCBits, CU_DESCEND_NO_LOCK, Select2Use); /* Clear the original selection */ DBCellClearDef(SelectDef); /* Select all paint of type "type" and copy into SelectDef */ TTMaskSetOnlyType(&tMask, type); plane = DBPlane(type); (void) DBCellCopyAllPaint(scx, &tMask, xMask, SelectUse); /* Scan Select2Def for all geometry inside the area of "type", and */ /* copy back to SelectDef as "type" */ if (negate) { TTMaskCom(&tMask); TTMaskAndMask(&tMask, &DBPlaneTypes[plane]); } DBSrPaintArea((Tile *)NULL, SelectDef->cd_planes[plane], &scx->scx_area, &tMask, selIntersectPaintFunc, (ClientData)NULL); if (negate) TTMaskSetOnlyType(&tMask, type); /* Restore original mask */ DBEraseMask(SelectDef, &TiPlaneRect, &tMask); /* Display the new selection. */ SelRememberForUndo(FALSE, SelectRootDef, &scx->scx_area); DBReComputeBbox(SelectDef); DBWHLRedraw(SelectRootDef, &scx->scx_area, TRUE); DBWAreaChanged(SelectDef, &SelectDef->cd_extended, DBW_ALLWINDOWS, &DBAllButSpaceBits); } /* * ---------------------------------------------------------------------------- * * SelectArea -- * * This procedure selects all information of given types that * falls in a given area. * * Results: * None. * * Side effects: * The indicated information is added to the select cell, and * outlined on the screen. Only information of particular * types, and in expanded cells (according to xMask) is * selected. * * ---------------------------------------------------------------------------- */ void SelectArea(scx, types, xMask, globmatch) SearchContext *scx; /* Describes the area in which material * is to be selected. The resulting * coordinates should map to the coordinates * of EditRootDef. The cell use should be * the root of a window. */ TileTypeBitMask *types; /* Indicates which layers to select. Can * include L_CELL and L_LABELS to select * labels and unexpanded subcells. If L_LABELS * is specified then all labels touching the * area are selected. If L_LABELS isn't * specified, then only labels attached to * selected material are selected. */ int xMask; /* Indicates window (or windows) where cells * must be expanded for their contents to be * considered. 0 means treat everything as * expanded. */ char *globmatch; /* If non-NULL, and if L_LABELS is among the * selection types, then do glob-style matching * of any labels against this string. */ { Rect labelArea, cellArea; /* If the source definition is changing, clear the old selection. */ if (SelectRootDef != scx->scx_use->cu_def) { if (SelectRootDef != NULL) SelectClear(); SelectRootDef = scx->scx_use->cu_def; SelSetDisplay(SelectUse, SelectRootDef); } SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL); /* Select paint. */ (void) DBCellCopyAllPaint(scx, types, xMask, SelectUse); SelectDef->cd_types = *types; /* Remember what types were requested */ /* Select labels. */ if (TTMaskHasType(types, L_LABEL)) { if (globmatch != NULL) DBCellCopyGlobLabels(scx, &DBAllTypeBits, xMask, SelectUse, &labelArea, globmatch); else DBCellCopyAllLabels(scx, &DBAllTypeBits, xMask, SelectUse, &labelArea); } else (void) DBCellCopyAllLabels(scx, types, xMask, SelectUse, &labelArea); /* Select cell uses. */ if (TTMaskHasType(types, L_CELL)) (void) DBCellCopyAllCells(scx, xMask, SelectUse, &cellArea); else { cellArea.r_xbot = 0; cellArea.r_xtop = -1; } /* Display the new selection. */ (void) GeoIncludeAll(&scx->scx_area, &labelArea); (void) GeoIncludeAll(&cellArea, &labelArea); SelRememberForUndo(FALSE, SelectRootDef, &labelArea); DBReComputeBbox(SelectDef); DBWHLRedraw(SelectRootDef, &labelArea, TRUE); DBWAreaChanged(SelectDef, &SelectDef->cd_extended, DBW_ALLWINDOWS, &DBAllButSpaceBits); } /* * ---------------------------------------------------------------------------- * * selFindChunk -- * * This is a recursive procedure to find the largest chunk of * material in a particular area. It locates a rectangular * area of given materials whose minimum dimension is as * large as possible, and whose maximum dimension is also as * large as possible (but minimum dimension is more important). * Furthermore, the chunk must lie within a particular area and * must contain a given area. * * Results: * None. * * Side effects: * None. * * ---------------------------------------------------------------------------- */ #define MAX_RECURSION_LEVEL 22 void selFindChunk(plane, wrongTypes, searchArea, containedArea, bestMin, bestMax, bestChunk, level) Plane *plane; /* Plane on which to hunt for chunk. */ TileTypeBitMask *wrongTypes; /* Types that are not allowed to be * part of the chunk. */ Rect *searchArea; /* Largest allowable size for the * chunk. Note: don't overestimate * this or the procedure will take a * long time! (it processes every * tile in this area). */ Rect *containedArea; /* The chunk returned must contain * this entire area. */ int *bestMin; /* Largest minimum dimension seen so * far: skip any chunks that can't * match this. Updated by this * procedure. */ int *bestMax; /* Largest maximum dimension seen * so far. */ Rect *bestChunk; /* Filled in with largest chunk seen * so far, if we find one better than * bestMin and bestMax. */ int level; /* Recursion level (to avoid worst- * case scenarios) */ { Rect smaller; int min, max; extern int selChunkFunc(); Rect wrong; if (level == MAX_RECURSION_LEVEL) return; /* If the search area is already smaller than the chunk to beat, * there's no point in even examining this chunk. */ min = searchArea->r_xtop - searchArea->r_xbot; max = searchArea->r_ytop - searchArea->r_ybot; if (min > max) { int tmp; tmp = min; min = max; max = tmp; } if (min < *bestMin) return; if ((min == *bestMin) && (max <= *bestMax)) return; /* At each stage, search the area that's left for material of the * wrong type. */ if (DBSrPaintArea((Tile *) NULL, plane, searchArea, wrongTypes, selChunkFunc, (ClientData) &wrong) == 0) { /* The area contains nothing but material of the right type, * so it is now the "chunk to beat". */ *bestMin = min; *bestMax = max; *bestChunk = *searchArea; return; } if (SigInterruptPending) return; /* At this point the current search area contains some material of * the wrong type. We have to reduce the search area to exclude this * material. There are two ways that this can be done while still * producing areas that contain containedArea. Try both of those, * and repeat the whole thing recursively on the smaller areas. */ /* First, try reducing the x-range. */ smaller = *searchArea; if (wrong.r_xbot >= containedArea->r_xtop) smaller.r_xtop = wrong.r_xbot; else if (wrong.r_xtop <= containedArea->r_xbot) smaller.r_xbot = wrong.r_xtop; else goto tryY; /* Bad material overlaps containedArea in x. */ selFindChunk(plane, wrongTypes, &smaller, containedArea, bestMin, bestMax, bestChunk, level + 1); /* Also try reducing the y-range to see if that works better. */ tryY: smaller = *searchArea; if (wrong.r_ybot >= containedArea->r_ytop) smaller.r_ytop = wrong.r_ybot; else if (wrong.r_ytop <= containedArea->r_ybot) smaller.r_ybot = wrong.r_ytop; else return; /* Bad material overlaps containedArea in y. */ selFindChunk(plane, wrongTypes, &smaller, containedArea, bestMin, bestMax, bestChunk, level + 1); } /* Search function to find split tiles in the area of interest. */ int selSplitFunc(tile, cxp) Tile *tile; TreeContext *cxp; { SearchContext *scx = cxp->tc_scx; Rect *area = (Rect *) cxp->tc_filter->tf_arg; Rect locarea; if (IsSplit(tile)) { TiToRect(tile, &locarea); GeoTransRect(&scx->scx_trans, &locarea, area); return 1; } return 0; } /* This procedure is called for each tile of the wrong type in an * area that is supposed to contain only tiles of other types. It * just returns the area of the wrong material and aborts the search. */ int selChunkFunc(tile, wrong) Tile *tile; /* The offending tile. */ Rect *wrong; /* Place to store the tile's area. */ { TiToRect(tile, wrong); return 1; /* Abort the search. */ } /* * ---------------------------------------------------------------------------- * * SelectChunk -- * * This procedure selects a single rectangular chunk of * homogeneous material. * * Results: * None. * * Side effects: * More material is added to the select cell and displayed * on the screen. This procedure finds the largest rectangular * chunk of material "type" that contains the area given in * in scx. The material need not all be in one cell, but it * must all be in cells that are expanded according to "xMask". * If pArea is given, the rectangle it points to is filled in * with the area of the chunk that was selected. * * ---------------------------------------------------------------------------- */ void SelectChunk(scx, type, xMask, pArea, less) SearchContext *scx; /* Area to tree-search for material. The * transform must map to root coordinates * of the edit cell. */ TileType type; /* The type of material to be considered. */ int xMask; /* Indicates window (or windows) where cells * must be expanded for their contents to be * considered. 0 means treat everything as * expanded. */ Rect *pArea; /* If non-NULL, gets filled in with the area * of the selection. */ bool less; { #define INITIALSIZE 10 SearchContext newscx; TileTypeBitMask wrongTypes, typeMask; TileType ttype; Rect bestChunk; int bestMin, bestMax, width, height; extern int selSplitFunc(); /* Forward reference. */ /* If the source definition is changing, clear the old selection. */ if (SelectRootDef != scx->scx_use->cu_def) { if (SelectRootDef != NULL) SelectClear(); SelectRootDef = scx->scx_use->cu_def; SelSetDisplay(SelectUse, SelectRootDef); } /* Ignore if type is TT_SPACE */ if (type == TT_SPACE) return; /* The chunk is computed iteratively. First extract a small * region (defined by INITIALSIZE) into Select2Def. Then find * the largest chunk in the region. If the chunk touches a * side of the region, then extract a larger region and try * again. Keep making the region larger and larger until we * eventually find a region that completely contains the chunk * with space left over around the edges. */ UndoDisable(); TTMaskSetOnlyType(&typeMask, type); /* Stacked types containing "type" need to be added to the mask */ if (DBIsContact(type)) DBMaskAddStacking(&typeMask); TTMaskCom2(&wrongTypes, &typeMask); newscx = *scx; /* If the tile under the area of interest is a split tile, */ /* then we use that tile as the chunk. This routine should */ /* work like the while() loop underneath, looking for the */ /* largest split tile, but this is a minor enhancement. */ /* *However*, if it is done, the functions */ /* DBCellCopyManhattanPaint()/dbCopyManhattanPaint() should */ /* be reconsidered. */ if (DBTreeSrTiles(&newscx, &typeMask, 0, selSplitFunc, (ClientData)&bestChunk) != 0) { goto chunkdone; } bestMin = bestMax = 0; bestChunk = GeoNullRect; GEO_EXPAND(&newscx.scx_area, INITIALSIZE, &newscx.scx_area); while (TRUE) { /* Extract a bunch of junk. */ DBCellClearDef(Select2Def); DBCellCopyManhattanPaint(&newscx, &typeMask, xMask, Select2Use); /* Now find the best chunk in the area. */ selFindChunk(Select2Def->cd_planes[DBPlane(type)], &wrongTypes, &newscx.scx_area, &scx->scx_area, &bestMin, &bestMax, &bestChunk, 0); if (GEO_RECTNULL(&bestChunk)) { /* No chunk was found, so return. */ UndoEnable(); if (pArea != NULL) *pArea = bestChunk; return; } /* If the chunk is completely inside the area we yanked, then we're * done. */ if (GEO_SURROUND_STRONG(&newscx.scx_area, &bestChunk)) break; /* The chunk extends to the edge of the area. Any place that the * chunk touches an edge, move that edge out by a factor of two. * Any place it doesn't touch, move the edge in to be just one * unit out from the chunk. */ width = newscx.scx_area.r_xtop - newscx.scx_area.r_xbot; height = newscx.scx_area.r_ytop - newscx.scx_area.r_ybot; if (bestChunk.r_xbot == newscx.scx_area.r_xbot) newscx.scx_area.r_xbot -= width; else newscx.scx_area.r_xbot = bestChunk.r_xbot - 1; if (bestChunk.r_ybot == newscx.scx_area.r_ybot) newscx.scx_area.r_ybot -= height; else newscx.scx_area.r_ybot= bestChunk.r_ybot - 1; if (bestChunk.r_xtop == newscx.scx_area.r_xtop) newscx.scx_area.r_xtop += width; else newscx.scx_area.r_xtop = bestChunk.r_xtop + 1; if (bestChunk.r_ytop == newscx.scx_area.r_ytop) newscx.scx_area.r_ytop += height; else newscx.scx_area.r_ytop = bestChunk.r_ytop + 1; } chunkdone: /* Flag the cell as a chunk selection so we can treat */ /* the contents differently when doing a move op. */ SelectUse->cu_flags |= CU_SELECT_CHUNK; UndoEnable(); if (less) { SelRemoveArea(&bestChunk, &typeMask, NULL); } else { newscx.scx_area = bestChunk; /* Remove any stacked contact types for SelectArea */ if (DBIsContact(type)) TTMaskSetOnlyType(&typeMask, type); SelectArea(&newscx, &typeMask, xMask, NULL); } if (pArea != NULL) *pArea = bestChunk; } /* * ---------------------------------------------------------------------------- * * SelectRegion -- * * Select an entire region of material, no matter what its * shape. * * Results: * None. * * Side effects: * This procedure traces out the region consisting entirely * of type "type", and selects all that material. The search * starts from "type" material under scx and continues outward * to get all material in all cells connected to the area under * scx by material of type "type". If pArea is specified, then * the rectangle that it points to is filled in with the bounding * box of the region that was selected. * * ---------------------------------------------------------------------------- */ void SelectRegion(scx, type, xMask, pArea, less) SearchContext *scx; /* Area to tree-search for material. The * transform must map to EditRoot coordinates. */ TileType type; /* The type of material to be considered. */ int xMask; /* Indicates window (or windows) where cells * must be expanded for their contents to be * considered. 0 means treat everything as * expanded. */ Rect *pArea; /* If non-NULL, points to rectangle to be * filled in with region's bounding box. */ bool less; { TileTypeBitMask connections[TT_MAXTYPES]; int i; SearchContext scx2; /* If the source definition is changing, clear the old selection. */ if (SelectRootDef != scx->scx_use->cu_def) { if (SelectRootDef != NULL) SelectClear(); SelectRootDef = scx->scx_use->cu_def; SelSetDisplay(SelectUse, SelectRootDef); } /* Set up a connection table that allows only a particular type * of material to be considered in the region. */ for (i=0; icd_bbox; scx2.scx_trans = GeoIdentityTransform; DBCellCopyAllPaint(&scx2, &DBAllButSpaceAndDRCBits, 0, SelectUse); DBCellCopyAllLabels(&scx2, &DBAllTypeBits, CU_DESCEND_SPECIAL, SelectUse, (Rect *) NULL); } /* Display the new selection. */ SelRememberForUndo(FALSE, SelectRootDef, &Select2Def->cd_bbox); DBReComputeBbox(SelectDef); DBComputeUseBbox(SelectUse); DBWHLRedraw(SelectRootDef, &Select2Def->cd_extended, TRUE); DBWAreaChanged(SelectDef, &Select2Def->cd_extended, DBW_ALLWINDOWS, &DBAllButSpaceBits); if (pArea != NULL) *pArea = Select2Def->cd_extended; } /* * ---------------------------------------------------------------------------- * * SelectNet -- * * This procedure selects an entire electrically-connected net. * * Results: * None. * * Side effects: * Starting from material of type "type" under scx, this procedure * finds and highlights all material in all expanded cells that * is electrically-connected to the starting material through a * chain of expanded cells. If pArea is specified, then the * rectangle that it points to is filled in with the bounding box * of the net that was selected. * * ---------------------------------------------------------------------------- */ void SelectNet(scx, type, xMask, pArea, less) SearchContext *scx; /* Area to tree-search for material. The * transform must map to EditRoot coordinates. */ TileType type; /* The type of material to be considered. */ int xMask; /* Indicates window (or windows) where cells * must be expanded for their contents to be * considered. 0 means treat everything as * expanded. */ Rect *pArea; /* If non-NULL, points to rectangle to be * filled in with net's bounding box. */ bool less; /* Whether to remove material from the * selection using SelDeleteSel2 */ { TileTypeBitMask mask; SearchContext scx2; Point savePoint = scx->scx_area.r_ll; /* If the source definition is changing, clear the old selection. */ if (SelectRootDef != scx->scx_use->cu_def) { if (SelectRootDef != NULL) SelectClear(); SelectRootDef = scx->scx_use->cu_def; SelSetDisplay(SelectUse, SelectRootDef); } TTMaskZero(&mask); // Make sure that SelectNet() matches connection-compatible // types with the type passed to the routine. // TTMaskSetType(&mask, type); TTMaskSetMask(&mask, &DBConnectTbl[type]); UndoDisable(); DBCellClearDef(Select2Def); DBTreeCopyConnect(scx, &mask, xMask, DBConnectTbl, &TiPlaneRect, SelectDoLabels, Select2Use); UndoEnable(); /* Network undo method added by Nishit and Tim, July 8-10, 2004 */ SelNetRememberForUndo(SelectRootDef, &savePoint, type, less, TRUE); /* Now transfer what we found into the main selection cell. Pick * up all the labels that correspond to the selected material. */ UndoDisable(); if (less) { (void) SelRemoveSel2(); } else { scx2.scx_use = Select2Use; scx2.scx_area = Select2Def->cd_bbox; scx2.scx_trans = GeoIdentityTransform; DBCellCopyAllPaint(&scx2, &DBAllButSpaceAndDRCBits, 0, SelectUse); DBCellCopyAllLabels(&scx2, &DBAllTypeBits, CU_DESCEND_SPECIAL, SelectUse, (Rect *) NULL); } /* Set the cell use flags to mark this as a net selection, */ /* so we can treat it differently when it comes time to */ /* unselect it. */ SelectUse->cu_flags |= CU_SELECT_NET; UndoEnable(); DBReComputeBbox(SelectDef); DBComputeUseBbox(SelectUse); DBWHLRedraw(SelectRootDef, &Select2Def->cd_extended, TRUE); DBWAreaChanged(SelectDef, &Select2Def->cd_extended, DBW_ALLWINDOWS, &DBAllButSpaceBits); if (pArea != NULL) *pArea = Select2Def->cd_extended; } /* * ---------------------------------------------------------------------------- * * SelectCell -- * * Select a subcell by making a copy of it in the __SELECT__ cell. * * Results: * None. * * Side effects: * The given use is copied into the selection. If replace is TRUE, * then the last subcell to be selected via this procedure is * deselected. * * ---------------------------------------------------------------------------- */ void SelectCell(use, rootDef, trans, replace) CellUse *use; /* Cell use to be selected. */ CellDef *rootDef; /* Root definition of window in which selection * is being made. */ Transform *trans; /* Transform from the coordinates of use's * definition to the coordinates of rootDef. */ bool replace; /* TRUE means deselect the last cell selected * by this procedure, if it's still selected. */ { CellUse *newUse; /* If the source definition is changing, clear the old selection. */ if (SelectRootDef != rootDef) { if (SelectRootDef != NULL) SelectClear(); SelectRootDef = rootDef; SelSetDisplay(SelectUse, SelectRootDef); } /* Deselect the last cell selected, if requested. */ if (replace && (selectLastUse != NULL)) { Rect area; SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL); area = selectLastUse->cu_bbox; DBUnLinkCell(selectLastUse, SelectDef); DBDeleteCell(selectLastUse); (void) DBCellDeleteUse(selectLastUse); SelRememberForUndo(FALSE, SelectRootDef, &area); DBWHLRedraw(SelectRootDef, &area, TRUE); selectLastUse = (CellUse *)NULL; } /* When creating a new use, try to re-use the id from the old * one. Only create a new one if the old id can't be used. */ newUse = DBCellNewUse(use->cu_def, (char *) use->cu_id); if (!DBLinkCell(newUse, SelectDef)) { freeMagic((char *) newUse->cu_id); newUse->cu_id = NULL; (void) DBLinkCell(newUse, SelectDef); } DBSetArray(use, newUse); DBSetTrans(newUse, trans); newUse->cu_expandMask = use->cu_expandMask; newUse->cu_flags = use->cu_flags; /* If this cell is already selected, there's nothing more to do. * Since we didn't change the selection here, be sure NOT to remember * it for future deselection! */ if (DBCellFindDup(newUse, SelectDef) != NULL) { DBUnLinkCell(newUse, SelectDef); (void) DBCellDeleteUse(newUse); selectLastUse = (CellUse *) NULL; return; } SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL); DBPlaceCell(newUse, SelectDef); selectLastUse = newUse; SelRememberForUndo(FALSE, SelectRootDef, &newUse->cu_bbox); DBReComputeBbox(SelectDef); DBWHLRedraw(SelectRootDef, &newUse->cu_bbox, TRUE); DBWAreaChanged(SelectDef, &newUse->cu_bbox, DBW_ALLWINDOWS, &DBAllButSpaceBits); } /* * ---------------------------------------------------------------------------- * * SelectAndCopy1 -- * * This procedure takes the contents of SelectDef, and makes a * copy of them in the edit cell. Unlike SelectAndCopy2, the * original selection is unchanged, and SelectDef is not cleared. * This allows the implementation of "drag and drop" selections. * * Results: * None. * * Side effects: * The selection is augmented with what's in Select2Def. The caller * should normally have cleared the selection before calling us. * The edit cell is modified to include everything that was in * Select2Def. * * ---------------------------------------------------------------------------- */ void SelectAndCopy1() { SearchContext scx; Rect editArea; TileTypeBitMask mask; /* Just copy the information in Select2Def twice, once into the * edit cell and once into the main selection cell. */ scx.scx_use = SelectUse; scx.scx_area = SelectUse->cu_bbox; GeoTransTrans(&SelectUse->cu_transform, &RootToEditTransform, &scx.scx_trans); TTMaskAndMask3(&mask, &DBAllButSpaceAndDRCBits, &DBActiveLayerBits); (void) DBCellCopyAllPaint(&scx, &mask, CU_DESCEND_SPECIAL, EditCellUse); (void) DBCellCopyAllLabels(&scx, &DBActiveLayerBits, CU_DESCEND_SPECIAL, EditCellUse, (Rect *) NULL); (void) DBCellCopyAllCells(&scx, CU_DESCEND_SPECIAL, EditCellUse, (Rect *) NULL); GeoTransRect(&scx.scx_trans, &scx.scx_area, &editArea); DBAdjustLabels(EditCellUse->cu_def, &editArea); DBWAreaChanged(EditCellUse->cu_def, &editArea, DBW_ALLWINDOWS, (TileTypeBitMask *) NULL); DRCCheckThis(EditCellUse->cu_def, TT_CHECKPAINT, &editArea); DBReComputeBbox(EditCellUse->cu_def); } /* * ---------------------------------------------------------------------------- * * SelectAndCopy2 -- * * This procedure is intended for use only within the selection * module. It takes what's in Select2Def, makes a copy of it in the * edit cell, and makes the copy the selection. It's used, for * example, by the transformation and copying routines. * * Results: * None. * * Side effects: * The selection is augmented with what's in Select2Def. The caller * should normally have cleared the selection before calling us. * The edit cell is modified to include everything that was in * Select2Def. * * ---------------------------------------------------------------------------- */ void SelectAndCopy2(newSourceDef) CellDef *newSourceDef; /* The new selection is to be * associated with this cell in the * user's layout. */ { SearchContext scx; Rect editArea, labelArea, expanded; int plane; int (*savedPaintPlane)(); extern int selACPaintFunc(); /* Forward reference. */ extern int selACCellFunc(); /* Just copy the information in Select2Def twice, once into the * edit cell and once into the main selection cell. */ scx.scx_use = Select2Use; scx.scx_area = Select2Use->cu_bbox; scx.scx_trans = RootToEditTransform; savedPaintPlane = DBNewPaintPlane(DBPaintPlaneActive); (void) DBCellCopyAllPaint(&scx, &DBAllButSpaceAndDRCBits, CU_DESCEND_SPECIAL, EditCellUse); DBNewPaintPlane(savedPaintPlane); (void) DBCellCopyAllLabels(&scx, &DBAllTypeBits, CU_DESCEND_SPECIAL, EditCellUse, (Rect *) NULL); (void) DBCellCopyAllCells(&scx, CU_DESCEND_SPECIAL, EditCellUse, (Rect *) NULL); GeoTransRect(&scx.scx_trans, &scx.scx_area, &editArea); if (EditCellUse != NULL) { DBAdjustLabels(EditCellUse->cu_def, &editArea); DBWAreaChanged(EditCellUse->cu_def, &editArea, DBW_ALLWINDOWS, (TileTypeBitMask *) NULL); DRCCheckThis(EditCellUse->cu_def, TT_CHECKPAINT, &editArea); DBReComputeBbox(EditCellUse->cu_def); } SelectRootDef = newSourceDef; SelSetDisplay(SelectUse, SelectRootDef); SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL); scx.scx_trans = GeoIdentityTransform; /* In copying stuff into SelectUse, we have to be careful. The problem * is that the stuff now in the edit cell may have switched layers. * (for example, Select2Def might have diff, which got painted * over poly in the edit cell to form transistor). As a result, we * use Select2Def to figure out what areas of what planes to put into * SelectUse, but use the actual tile types from the edit cell. */ for (plane = PL_SELECTBASE; plane < DBNumPlanes; plane++) { (void) DBSrPaintArea((Tile *) NULL, Select2Def->cd_planes[plane], &TiPlaneRect, &DBAllButSpaceAndDRCBits, selACPaintFunc, INT2CD(plane)); DBMergeNMTiles(Select2Def->cd_planes[plane], &TiPlaneRect, (PaintUndoInfo *)NULL); } (void) DBCellCopyAllLabels(&scx, &DBAllTypeBits, CU_DESCEND_SPECIAL, SelectUse, NULL); /* We also have to be careful about copying subcells into the * main selection cell. It might not have been possible to copy * a subcell into the edit cell (above), because the copying * would have formed a circularity. In that case, we need to * drop that subcell from the new selection. The code below just * copies those that are still in the edit cell. */ (void) SelEnumCells(TRUE, (bool *) NULL, &scx, selACCellFunc, (ClientData) NULL); DBReComputeBbox(SelectDef); DBComputeUseBbox(SelectUse); /* A little hack here: don't do explicit redisplay of the selection, * or record a very large redisplay area for undo-ing. It's not * necessary since the layout redisplay also redisplays the highlights. * If we do it too, then we're just double-displaying and wasting * time. (note: must record something for undo-ing in order to get * SelectRootDef set right... just don't pass a redisplay area). */ SelRememberForUndo(FALSE, SelectRootDef, (Rect *) NULL); DBWAreaChanged(SelectDef, &SelectDef->cd_extended, DBW_ALLWINDOWS, (TileTypeBitMask *) NULL); } /* Structure to hold information about an edit area, which may be * a triangular region. */ typedef struct { Rect *editClip; TileType ttype; } acparg; /* Utility function: for each tile, copy information over its area from * the given edit cell plane to SelectDef. Always return 0 to keep the * search alive. */ int selACPaintFunc(tile, plane) Tile *tile; /* Tile in Select2Def. */ int plane; /* Index of plane this tile came from. */ { Rect area, editArea; acparg selACarg; int selACPaintFunc2(); /* Forward reference. */ TiToRect(tile, &area); /* we want editClip in root coordinates. . . */ selACarg.editClip = &area; GeoTransRect(&RootToEditTransform, &area, &editArea); selACarg.ttype = TiGetTypeExact(tile); if (IsSplit(tile)) { DBSrPaintNMArea((Tile *) NULL, EditCellUse->cu_def->cd_planes[plane], selACarg.ttype, &editArea, &DBAllButSpaceAndDRCBits, selACPaintFunc2, (ClientData) &selACarg); } else (void) DBSrPaintArea((Tile *) NULL, EditCellUse->cu_def->cd_planes[plane], &editArea, &DBAllButSpaceAndDRCBits, selACPaintFunc2, (ClientData) &selACarg); return 0; } /* Second-level paint function: just paint the overlap between * tile and editClip into SelectDef. * * This function is like dbCopyAllPaint() but differs just enough * that a separate function is required. However, much of the code * could be shared between the two functions if it were properly * broken out into subroutines. */ int selACPaintFunc2(tile, selACarg) Tile *tile; /* Tile in edit cell. */ acparg *selACarg; /* Contains edit-cell area to clip to * before painting into selection. */ { Rect *editClip = selACarg->editClip; Rect area, selArea; TileType type = TiGetTypeExact(tile); TileTypeBitMask tmask, *rmask; TileType ttype, rtype; TileType dinfo = selACarg->ttype & (TT_DIAGONAL | TT_DIRECTION | TT_SIDE); TiToRect(tile, &area); GeoTransRect(&EditToRootTransform, &area, &selArea); if ((dinfo & TT_DIAGONAL) || (type & TT_DIAGONAL)) { /* If the select area is triangular, then we need to */ /* clip in a more complicated manner. */ /* Likewise if the edit cell tile is triangular and */ /* the select area not. */ Point points[5]; Rect rrect, orect; int np, i, j; ttype = (selACarg->ttype & TT_SIDE) ? ((selACarg->ttype & TT_RIGHTMASK) >> 14) : selACarg->ttype & TT_LEFTMASK; if (type & TT_DIAGONAL) rtype = (type & TT_SIDE) ? SplitRightType(tile) : SplitLeftType(tile); else rtype = type; if (rtype >= DBNumUserLayers) { rmask = DBResidueMask(rtype); if (TTMaskHasType(rmask, ttype)) rtype = ttype; } TTMaskSetOnlyType(&tmask, rtype); type = (dinfo & TT_SIDE) ? (rtype << 14) : rtype; type |= dinfo; if (dinfo & TT_DIAGONAL) GrClipTriangle(editClip, &selArea, TRUE, dinfo, points, &np); else GrClipTriangle(&selArea, editClip, TRUE, type, points, &np); if (np == 0) return 0; if (np >= 3) { for (i = 0; i < np; i++) { j = (i + 1) % np; if (points[i].p_x != points[j].p_x && points[i].p_y != points[j].p_y) { /* Break out the triangle */ rrect.r_xbot = points[i].p_x; rrect.r_xtop = points[j].p_x; rrect.r_ybot = points[i].p_y; rrect.r_ytop = points[j].p_y; GeoCanonicalRect(&rrect, &selArea); break; } } if (i == np) /* Exactly one Manhattan rectangle */ { rrect.r_xbot = points[0].p_x; rrect.r_xtop = points[2].p_x; rrect.r_ybot = points[0].p_y; rrect.r_ytop = points[2].p_y; GeoCanonicalRect(&rrect, &selArea); type = rtype; } else if (np >= 4) /* Process extra rectangles in the area */ { /* "orect" is the bounding box of the polygon returned */ /* by ClipTriangle. */ orect.r_xtop = orect.r_xbot = points[0].p_x; orect.r_ytop = orect.r_ybot = points[0].p_y; for (i = 0; i < np; i++) GeoIncludePoint(&points[i], &orect); /* Rectangle to left or right */ rrect.r_ybot = orect.r_ybot; rrect.r_ytop = orect.r_ytop; if (selArea.r_xbot > orect.r_xbot) { rrect.r_xbot = orect.r_xbot; rrect.r_xtop = selArea.r_xbot; } else if (selArea.r_xtop < orect.r_xtop) { rrect.r_xtop = orect.r_xtop; rrect.r_xbot = selArea.r_xtop; } else goto topbottom; DBPaintValid(SelectDef, &rrect, &tmask, 0); topbottom: /* Rectangle to top or bottom */ rrect.r_xbot = selArea.r_xbot; rrect.r_xtop = selArea.r_xtop; if (selArea.r_ybot > orect.r_ybot) { rrect.r_ybot = orect.r_ybot; rrect.r_ytop = selArea.r_ybot; } else if (selArea.r_ytop < orect.r_ytop) { rrect.r_ytop = orect.r_ytop; rrect.r_ybot = selArea.r_ytop; } else goto splitdone; DBPaintValid(SelectDef, &rrect, &tmask, 0); } } } else { ttype = selACarg->ttype; if (type & TT_DIAGONAL) rtype = (type & TT_SIDE) ? SplitRightType(tile) : SplitLeftType(tile); else rtype = type; if (rtype >= DBNumUserLayers) { rmask = DBResidueMask(rtype); if (TTMaskHasType(rmask, ttype)) rtype = ttype; } TTMaskSetOnlyType(&tmask, rtype); } GeoClip(&selArea, editClip); /* editClip already in root coords */ splitdone: DBPaintValid(SelectDef, &selArea, &tmask, type); return 0; } /* Cell search function: invoked for each subcell in Select2Def that's * also in the edit cell. Make a copy of the cell in SelectDef. */ int selACCellFunc(selUse, realUse) CellUse *selUse; /* Use to be copied into SelectDef. This * is the instance inside Select2Def. */ CellUse *realUse; /* The cellUse (in the edit cell) corresponding * to selUse. We need this in order to use its * instance id and expand mask in the selection. */ { CellUse *newUse; newUse = DBCellNewUse(selUse->cu_def, realUse->cu_id); if (!DBLinkCell(newUse, SelectDef)) { freeMagic((char *) newUse->cu_id); newUse->cu_id = NULL; (void) DBLinkCell(newUse, SelectDef); } newUse->cu_expandMask = realUse->cu_expandMask; newUse->cu_flags = realUse->cu_flags; DBSetArray(selUse, newUse); DBSetTrans(newUse, &selUse->cu_transform); DBPlaceCell(newUse, SelectDef); return 0; }