magic/plow/PlowMain.c

2273 lines
65 KiB
C

/*
* PlowMain.c --
*
* Plowing.
* Main loop: set everything up, and then repeatedly remove an
* edge from the queue of pending edges and process it.
*
* *********************************************************************
* * 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/PlowMain.c,v 1.2 2008/12/11 04:20:12 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <string.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <sys/resource.h>
#include "utils/magic.h"
#include "utils/geometry.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"
#include "utils/styles.h"
#include "utils/malloc.h"
#include "utils/signals.h"
#include "utils/main.h"
#include "select/select.h"
#include "graphics/graphics.h"
#if defined(SYSV) || defined(__APPLE__) || defined(EMSCRIPTEN)
# define NO_RUSAGE
#endif
/* Plowing jog horizon: see PlowExtendJogHorizon() for an explanation */
global int PlowJogHorizon = 0;
/* TRUE if we should straighten jogs automatically after each plow */
global bool PlowDoStraighten = FALSE;
/*
* Search rule table. These rules are used to search from a moving
* edge to find the other edges it causes to move. The procedure
* implementing each rule should be of the form:
*
* (*proc)(edge, rules)
* Edge *edge;
* PlowRule *rules;
* {
* }
*
* It may not modify the Edge pointed to by 'edge'.
* The edge is in the cell plowYankDef.
*/
RuleTableEntry plowSearchRulesTbl[MAXRULES];
RuleTableEntry *plowSearchRulesPtr = plowSearchRulesTbl;
/*
* Cell rules.
* Same as above.
*/
RuleTableEntry plowCellRulesTbl[MAXRULES];
RuleTableEntry *plowCellRulesPtr = plowCellRulesTbl;
/* Imported rules */
extern int prClearUmbra();
extern int prUmbra();
extern int prPenumbraTop(), prPenumbraBot();
extern int prFixedPenumbraTop(), prFixedPenumbraBot();
extern int prSliverTop(), prSliverBot();
extern int prInSliver();
extern int prIllegalTop(), prIllegalBot();
extern int prCoverTop(), prCoverBot();
extern int prFixedLHS(), prFixedRHS(), prFixedDragStubs();
extern int prContactLHS(), prContactRHS();
extern int prFindCells();
extern int prCell();
/* Defined elsewhere in this module */
extern CellDef *plowYankDef;
extern CellUse *plowYankUse;
extern CellDef *plowSpareDef;
extern CellUse *plowSpareUse;
extern Rect plowYankedArea;
extern int plowYankHalo;
/* Plow boundary information */
typedef struct pb
{
CellDef *pb_editDef; /* Cell to which this applies */
Rect pb_editArea; /* Area of boundary in edit cell coords */
/* The following exist for redisplay only */
CellDef *pb_rootDef; /* Display in all windows with this root */
Rect pb_rootArea; /* Area of boundary in root cell coords */
struct pb *pb_next; /* Next record in chain */
} PlowBoundary;
/* If the following is TRUE, enable checking of boundaries */
bool plowCheckBoundary = FALSE;
/* List of PlowBoundary records above */
PlowBoundary *plowBoundaryList = NULL;
/* TRUE if labels or cells were changed by plowing */
bool plowLabelsChanged;
/* Transform information to yank cell coordinates */
Transform plowYankTrans; /* Transform from original cell to yank cell */
Transform plowInverseTrans; /* Transform from yank cell to original cell */
Rect plowCellBbox; /* Transformed initial cell bounding box */
int plowDirection; /* Direction of plowing (GEO_*) */
/* Dummy whose cu_def pointer is reset to point to the def being plowed */
CellUse *plowDummyUse = (CellUse *) NULL;
/* Debugging */
RuleTableEntry *plowCurrentRule;/* Rule currently being applied */
RuleTableEntry plowRuleInitial; /* Dummy rule for debugging */
/* Procedure called for each edge affected by the one being moved */
int (*plowPropagateProcPtr)() = (int (*)()) NULL;
/* Statistics */
int plowQueuedEdges; /* Number of edges passed to plowQueueAdd */
int plowProcessedEdges; /* Number of times plowProcessEdge called */
int plowMovedEdges; /* Number of edges actually moved */
/* Forward declarations */
int plowInitialPaint(), plowInitialCell();
int plowUpdatePaintTile(), plowUpdateCell();
bool plowPastBoundary();
bool plowPropagateSel();
bool plowPropagateRect(CellDef *def, Rect *userRect, const TileTypeBitMask *lcp, Rect *changedArea);
PlowRule *plowBuildWidthRules();
void plowMergeBottom(Tile **delay1, Tile *tp, Plane *plane);
void plowInitRule(RuleTableEntry *rtePtr, RuleTableEntry *rteEnd, int whichRules, int (*proc)(), const char *name,
const TileTypeBitMask *ltypesp, const TileTypeBitMask *rtypesp);
extern void PlowRedrawBound();
extern void PlowClearBound();
extern void plowUpdate();
extern void plowSetTrans();
extern void plowProcessEdge();
extern void plowMoveEdge();
extern void plowMergeTop(Tile **delay1, Tile *tp, Plane *plane);
extern void plowYankCreate();
/*
* ----------------------------------------------------------------------------
*
* PlowSetBound --
*
* Set the bounding box for plowing.
*
* Results:
* None.
*
* Side effects:
* See above.
*
* ----------------------------------------------------------------------------
*/
void
PlowSetBound(def, area, rootDef, rootArea)
CellDef *def; /* Def in which bounding area applies */
Rect *area; /* Area in 'def' coordinates */
CellDef *rootDef; /* Display bounding area in windows with this root */
Rect *rootArea; /* Area in 'rootDef' coordinates */
{
static bool firstTime = TRUE;
PlowBoundary *pb;
/* May eventually support a list, but for now, there's just one */
PlowClearBound();
pb = (PlowBoundary *) mallocMagic(sizeof (PlowBoundary));
pb->pb_rootDef = rootDef;
pb->pb_rootArea = *rootArea;
pb->pb_editDef = def;
pb->pb_editArea = *area;
pb->pb_next = (PlowBoundary *) NULL;
plowBoundaryList = pb;
plowCheckBoundary = TRUE;
/* Add ourselves as a client of the highlight handler */
if (firstTime)
DBWHLAddClient(PlowRedrawBound), firstTime = FALSE;
/* Redisplay the highlight we just added */
DBWHLRedraw(rootDef, rootArea, FALSE);
}
/*
* ----------------------------------------------------------------------------
*
* PlowClearBound --
*
* Eliminate boundary checking for plowing.
*
* Results:
* None.
*
* Side effects:
* Eliminates the highlight of the plowing bounding box.
*
* ----------------------------------------------------------------------------
*/
void
PlowClearBound()
{
PlowBoundary *pb;
pb = plowBoundaryList;
plowCheckBoundary = FALSE;
plowBoundaryList = (PlowBoundary *) NULL;
for ( ; pb; pb = pb->pb_next)
{
DBWHLRedraw(pb->pb_rootDef, &pb->pb_rootArea, TRUE);
freeMagic((char *) pb);
}
}
/*
* ----------------------------------------------------------------------------
*
* PlowRedrawBound --
*
* This procedure is called by the highlight manager to redisplay
* plowing highlights. The window is locked before entry.
*
* Results:
* None.
*
* Side effects:
* Plowing highlight information is redrawn, if there is any
* that needs redisplaying.
*
* ----------------------------------------------------------------------------
*/
void
PlowRedrawBound(window, plane)
MagWindow *window; /* Window in which to redraw. */
Plane *plane; /* Non-space tiles on this plane indicate
* areas where highlights need to be
* redisplayed.
*/
{
Rect worldArea, screenArea;
CellDef *windowRoot;
PlowBoundary *pb;
extern int plowBoundAlways1(); /* Forward reference. */
/* Nothing to do if no boundaries */
if (!plowCheckBoundary)
return;
windowRoot = ((CellUse *) (window->w_surfaceID))->cu_def;
GrSetStuff(STYLE_DOTTEDHIGHLIGHTS);
WindSurfaceToScreen(window, &window->w_surfaceArea, &worldArea);
for (pb = plowBoundaryList; pb; pb = pb->pb_next)
{
/* Nothing to do if not in the right window */
if (windowRoot != pb->pb_rootDef)
continue;
/* See if the current area needs to be redisplayed */
if (!DBSrPaintArea((Tile *) NULL, plane, &pb->pb_rootArea,
&DBAllButSpaceBits, plowBoundAlways1, (ClientData) NULL))
continue;
WindSurfaceToScreen(window, &pb->pb_rootArea, &screenArea);
GeoClip(&screenArea, &worldArea);
GrFastBox(&screenArea);
}
}
int
plowBoundAlways1()
{
return 1;
}
/*
* ----------------------------------------------------------------------------
*
* PlowStraighten --
*
* Straighten all jogs in the interior of the Rect 'area' in the CellDef 'def'
* by pulling them all in 'direction' (one of GEO_NORTH, GEO_SOUTH, GEO_EAST,
* or GEO_WEST) in such a way as to avoid moving any edges other than the jogs.
*
* Results:
* None.
*
* Side effects:
* Modifies the geometry of def in the area 'area'.
*
* ----------------------------------------------------------------------------
*/
void
PlowStraighten(def, area, direction)
CellDef *def; /* Def whose jogs we should straighten */
Rect *area; /* Area in which jogs are to be straightened */
int direction; /* Pull all jogs in this direction to straighten them */
{
Rect changedArea, changedUserArea, yankArea;
bool saveCheckBoundary;
int saveJogHorizon;
SearchContext scx;
PaintUndoInfo ui;
/* Make sure the yank buffers exist */
plowYankCreate();
/* Set the yank transforms plowYankTrans and plowInverseTrans */
plowSetTrans(direction);
/* Set the bounding box of this cell in yanked coordinates */
GeoTransRect(&plowYankTrans, &def->cd_bbox, &plowCellBbox);
/* Transform the straightening area into yank def coordinates */
GeoTransRect(&plowYankTrans, area, &yankArea);
/*
* Yank into yank buffer.
* Yank at least a tech halo around the area to make sure
* we detect all potential design-rule violations.
*/
plowDummyUse->cu_def = def;
UndoDisable();
DBCellClearDef(plowYankDef);
plowYankedArea.r_xbot = yankArea.r_xbot - DRCTechHalo;
plowYankedArea.r_ybot = yankArea.r_ybot - DRCTechHalo;
plowYankedArea.r_xtop = yankArea.r_xtop + DRCTechHalo;
plowYankedArea.r_ytop = yankArea.r_ytop + DRCTechHalo;
scx.scx_use = plowDummyUse;
scx.scx_trans = plowYankTrans;
GeoTransRect(&plowInverseTrans, &plowYankedArea, &scx.scx_area);
(void) DBCellCopyPaint(&scx, &DBAllButSpaceAndDRCBits, 0, plowYankUse);
(void) DBCellCopyCells(&scx, plowYankUse, (Rect *) NULL);
DBReComputeBbox(plowYankDef);
UndoEnable();
/* Temporarily disable boundary checking and jog horizon for the plow */
saveCheckBoundary = plowCheckBoundary;
saveJogHorizon = PlowJogHorizon;
plowCheckBoundary = FALSE;
PlowJogHorizon = 0;
/* Reduce jogs */
UndoDisable();
changedArea.r_xbot = changedArea.r_xtop = 0;
changedArea.r_ybot = changedArea.r_ytop = 0;
plowCleanupJogs(&yankArea, &changedArea);
UndoEnable();
/* Debugging */
DBWAreaChanged(plowYankDef,&TiPlaneRect,DBW_ALLWINDOWS,&DBAllButSpaceBits);
DBReComputeBbox(plowYankDef);
/* Restore previous state of boundary checking and jog horizon */
plowCheckBoundary = saveCheckBoundary;
PlowJogHorizon = saveJogHorizon;
/* Done if nothing was changed */
if (GEO_RECTNULL(&changedArea))
return;
/* Erase area in original def */
ui.pu_def = def;
GeoTransRect(&plowInverseTrans, &changedArea, &changedUserArea);
GeoClip(&changedUserArea, &TiPlaneRect);
for (ui.pu_pNum = PL_TECHDEPBASE; ui.pu_pNum < DBNumPlanes; ui.pu_pNum++)
DBPaintPlane(def->cd_planes[ui.pu_pNum], &changedUserArea,
DBWriteResultTbl[TT_SPACE], &ui);
/* Stuff from yank buffer back into original def */
scx.scx_area = changedArea;
scx.scx_use = plowYankUse;
scx.scx_trans = plowInverseTrans;
(void) DBCellCopyPaint(&scx, &DBAllButSpaceAndDRCBits, 0, plowDummyUse);
DBReComputeBbox(def);
DBWAreaChanged(def, &changedUserArea, DBW_ALLWINDOWS, &DBAllButSpaceBits);
DRCCheckThis(def, TT_CHECKPAINT, &changedUserArea);
}
/*
* ----------------------------------------------------------------------------
*
* PlowSelection --
*
* Plow the entire selection by the distance indicated.
*
* Results:
* Returns FALSE if *pdistance had to be modified in order to keep the
* effects of plowing entirely within the limits specified by
* plowBoundaryList, or TRUE otherwise.
*
* Side effects:
* Plows.
* If we return FALSE, then *pdistance will be modified as described above.
*
* ----------------------------------------------------------------------------
*/
bool
PlowSelection(def, pdistance, direction)
CellDef *def; /* Cell being plowed */
int *pdistance; /* Distance to plow */
int direction; /* One of GEO_NORTH, GEO_SOUTH, GEO_WEST, or GEO_EAST */
{
Rect changedArea;
bool firstTime;
/* Create the dummy yank buffers if they don't already exist */
plowYankCreate();
/* Set plowYankTrans and plowInverseTrans */
plowSetTrans(direction);
/* Set the bounding box of this cell in yanked coordinates */
GeoTransRect(&plowYankTrans, &def->cd_bbox, &plowCellBbox);
/*
* If boundary checking is enabled, the following loop may be
* executed several times because the original plow affected too
* much of the circuit. In this case, userRect gets updated to
* the amount the plow finally did move.
*/
firstTime = TRUE;
while (plowPropagateSel(def, pdistance, &changedArea))
firstTime = FALSE;
if (!GEO_RECTNULL(&changedArea))
plowUpdate(def, direction, &changedArea);
return (firstTime);
}
/*
* ----------------------------------------------------------------------------
*
* Plow --
*
* Plow a given collection of layers.
*
* Results:
* Returns FALSE if userRect had to be modified in order to keep the
* effects of plowing entirely within the limits specified by
* plowBoundaryList, or TRUE otherwise.
*
* Side effects:
* Plows.
* If we return FALSE, then userRect will be modified as described above.
*
* ----------------------------------------------------------------------------
*/
bool
Plow(
CellDef *def, /* Cell being plowed */
Rect *userRect, /* The plow. Interpreted as per direction
* below.
*/
const TileTypeBitMask *layersp,/* The initial plow only sees these layers */
int direction) /* One of GEO_NORTH, GEO_SOUTH, GEO_WEST,
* or GEO_EAST.
*/
{
#ifdef COUNTWIDTHCALLS
extern int plowWidthNumCalls;
extern int plowWidthNumChoices;
#endif /* COUNTWIDTHCALLS */
TileTypeBitMask layers = *layersp; /* TTMaskCopy(&layers, layersp) */
TileTypeBitMask lc;
Rect changedArea;
bool firstTime;
/* Create the dummy yank buffers if they don't already exist */
plowYankCreate();
/* Set plowYankTrans and plowInverseTrans */
plowSetTrans(direction);
/* Set the bounding box of this cell in yanked coordinates */
GeoTransRect(&plowYankTrans, &def->cd_bbox, &plowCellBbox);
/*
* If boundary checking is enabled, the following loop may be
* executed several times because the original plow affected too
* much of the circuit. In this case, userRect gets updated to
* the amount the plow finally did move.
*/
firstTime = TRUE;
TTMaskCom2(&lc, &layers);
while (plowPropagateRect(def, userRect, &lc, &changedArea))
firstTime = FALSE;
if (!GEO_RECTNULL(&changedArea))
plowUpdate(def, direction, &changedArea);
#ifdef COUNTWIDTHCALLS
TxPrintf("Choices = %d Calls = %d Choices/Call = %.1f\n",
plowWidthNumChoices, plowWidthNumCalls,
((double) plowWidthNumChoices) / ((double) plowWidthNumCalls));
#endif /* COUNTWIDTHCALLS */
return (firstTime);
}
/*
* ----------------------------------------------------------------------------
*
* plowUpdate --
*
* Update the original def after plowing.
*
* Results:
* None.
*
* Side effects:
* Updates the layout from plowYankDef.
*
* ----------------------------------------------------------------------------
*/
void
plowUpdate(def, direction, pChangedArea)
CellDef *def;
int direction;
Rect *pChangedArea;
{
Rect changedUserArea;
TileTypeBitMask *m;
PaintUndoInfo ui;
if (SigInterruptPending)
goto done;
/* Mark cell as modified */
def->cd_flags |= CDMODIFIED|CDGETNEWSTAMP;
/* Bloat the changed area to catch edges on the LHS */
pChangedArea->r_xbot--, pChangedArea->r_ybot--;
pChangedArea->r_xtop++, pChangedArea->r_ytop++;
GeoTransRect(&plowInverseTrans, pChangedArea, &changedUserArea);
GeoClip(&changedUserArea, &TiPlaneRect); /* SANITY */
plowLabelsChanged = FALSE;
/*
* Update the cells. Find whether each cell in the plowed
* planes has moved, and if so, move the corresponding cell
* in the original def.
*/
(void) DBCellEnum(plowYankDef, plowUpdateCell, (ClientData) def);
/*
* Update the labels. This consists of changing the positions
* of each label that was dragged along with its paint. The
* labels come from the original def, since they didn't have
* to be yanked.
*/
plowUpdateLabels(plowYankDef, def, &changedUserArea);
/*
* Update the paint. Erase the changed area from the original
* layout, and then paint back from the new layout. Use the
* transform plowInverseTrans to transform back from the yanked
* planes into coordinates of the original def.
*/
ui.pu_def = def;
for (ui.pu_pNum = PL_TECHDEPBASE; ui.pu_pNum < DBNumPlanes; ui.pu_pNum++)
{
/* Erase area in original def */
DBPaintPlane(def->cd_planes[ui.pu_pNum], &changedUserArea,
DBWriteResultTbl[TT_SPACE], &ui);
/* Update from yanked def */
(void) DBSrPaintArea((Tile *) NULL, plowYankDef->cd_planes[ui.pu_pNum],
pChangedArea, &DBAllButSpaceBits,
plowUpdatePaintTile, (ClientData) &ui);
}
/* Ashes to ashes */
done:
DBAdjustLabels(def, &changedUserArea);
DBReComputeBbox(plowYankDef);
DBReComputeBbox(def);
m = &DBAllButSpaceBits;
if (plowLabelsChanged) m = (TileTypeBitMask *) NULL;
DBWAreaChanged(def, &changedUserArea, DBW_ALLWINDOWS, m);
DRCCheckThis(def, TT_CHECKSUBCELL, &changedUserArea);
/*
* Final postpass: straighten any jogs in the area
* affected by this plow operation.
*/
if (PlowDoStraighten && !SigInterruptPending)
PlowStraighten(def, &changedUserArea, direction);
}
/*
* ----------------------------------------------------------------------------
*
* plowPropagateRect --
*
* Do the actual work of plowing a single plow, propagating the
* effects of the initial plow to all geometry eventually affected by it.
*
* If the global bool plowCheckBoundary is TRUE, then we check against
* the boundaries in the list plowBoundaryList for edges that violate
* the limits set by those boundaries. If the effects of plowing extend
* outside of this area, we adjust userRect to the largest plowing rectangle
* that will not cause propagation into illegal areas. If userRect had to
* be adjusted, we return TRUE.
*
* Results:
* Returns TRUE if we had to adjust userRect, FALSE if not.
* If plowCheckBoundary is FALSE, we never return TRUE.
*
* Side effects:
* See above.
* Sets the rectangle pointed to by 'changedArea' to be a
* bounding box around the area modified by plowing.
*
* ----------------------------------------------------------------------------
*/
bool
plowPropagateRect(
CellDef *def, /* Def being plowed */
Rect *userRect, /* User-specified plow (we transform this) */
const TileTypeBitMask *lcp, /* Complement of set of layers to plow */
Rect *changedArea) /* Set to bounding box around area modified */
{
TileTypeBitMask lc = *lcp; /* TTMaskCopy(&lc, lcp) */
Rect cellPlowRect, plowRect, r;
#ifndef NO_RUSAGE
struct rusage t1, t2;
#endif
int tooFar, pNum;
SearchContext scx;
Edge edge;
changedArea->r_xbot = changedArea->r_xtop = 0;
changedArea->r_ybot = changedArea->r_ytop = 0;
/*
* Back off by one lambda to catch edges underneath the plow.
* If no work to do, then we return FALSE.
*/
GeoTransRect(&plowYankTrans, userRect, &plowRect);
if (plowRect.r_xbot == plowRect.r_xtop)
return (FALSE);
cellPlowRect = plowRect;
plowRect.r_xbot--;
/* Clear the yank buffer for this iteration */
DBCellClearDef(plowYankDef);
/*
* Part 0.
* Yank the area of the plow, plus plowYankHalo, into a separate
* set of tile planes.
*/
plowDummyUse->cu_def = def;
UndoDisable();
scx.scx_use = plowDummyUse;
scx.scx_trans = plowYankTrans;
if (DebugIsSet(plowDebugID, plowDebYankAll))
{
scx.scx_area.r_xbot = def->cd_bbox.r_xbot - 1;
scx.scx_area.r_ybot = def->cd_bbox.r_ybot - 1;
scx.scx_area.r_xtop = def->cd_bbox.r_xtop + 1;
scx.scx_area.r_ytop = def->cd_bbox.r_ytop + 1;
GeoTransRect(&plowYankTrans, &scx.scx_area, &plowYankedArea);
}
else
{
plowYankedArea.r_xbot = plowRect.r_xbot - plowYankHalo;
plowYankedArea.r_xtop = plowRect.r_xtop + plowYankHalo;
plowYankedArea.r_ybot = plowRect.r_ybot - plowYankHalo;
plowYankedArea.r_ytop = plowRect.r_ytop + plowYankHalo;
GeoTransRect(&plowInverseTrans, &plowYankedArea, &scx.scx_area);
}
(void) DBCellCopyPaint(&scx, &DBAllButSpaceAndDRCBits, 0, plowYankUse);
(void) DBCellCopyCells(&scx, plowYankUse, (Rect *) NULL);
UndoEnable();
/*
* Part 1.
* Searching. This finds all the edges in the layout that have
* to move, and marks them with the distance they must move.
* Everything here works with the transformed cell. We initialize
* plowCurrentRule to null here so the debugging output for plowQueueAdd
* will reflect the fact that the edges found below are "initial" edges
* (those found by the plow itself, as opposed to by other edges).
*/
#ifndef NO_RUSAGE
if (DebugIsSet(plowDebugID, plowDebTime))
getrusage(RUSAGE_SELF, &t1);
#endif
plowMovedEdges = plowProcessedEdges = plowQueuedEdges = 0;
plowQueueInit(&plowCellBbox, plowRect.r_xtop - plowRect.r_xbot);
/* Queue each edge found by the plowing rules */
plowPropagateProcPtr = plowQueueAdd;
/* Debugging */
plowCurrentRule = &plowRuleInitial;
/* Add the initial edges */
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
(void) plowSrShadowInitial(pNum, &plowRect,
&lc, plowInitialPaint, INT2CD(plowRect.r_xtop));
/* Find any subcells crossed by the plow */
(void) DBSrCellPlaneArea(plowYankDef->cd_cellPlane,
&cellPlowRect, plowInitialCell, PTR2CD(&cellPlowRect));
/* While edges remain, process them */
tooFar = 0;
while (plowQueueLeftmost(&edge))
{
/* Ignore edges that don't move (sanity check) */
if (edge.e_x == edge.e_newx)
continue;
/*
* If we are doing boundary checking, don't add edges to the right of
* the boundary; just record how far they move. Edges whose original
* position is to the left of the boundary but cross it must still be
* queued for processing, since they can affect edges on the right of
* the boundary.
*/
if (plowCheckBoundary && plowPastBoundary(def, &edge, &tooFar))
continue;
if (!SigInterruptPending)
plowProcessEdge(&edge, changedArea);
}
/* Clean up */
plowQueueDone();
#ifndef NO_RUSAGE
if (DebugIsSet(plowDebugID, plowDebTime))
{
getrusage(RUSAGE_SELF, &t2);
plowShowTime(&t1, &t2, plowQueuedEdges,
plowProcessedEdges, plowMovedEdges);
}
#endif
/*
* If geometry to the right of the boundary moved, adjust
* the user's plow.
*/
if (tooFar)
{
GeoTransRect(&plowYankTrans, userRect, &r);
r.r_xtop -= tooFar;
GeoTransRect(&plowInverseTrans, &r, userRect);
return (TRUE);
}
/* Successful plow */
return (FALSE);
}
/*
* ----------------------------------------------------------------------------
*
* plowPropagateSel --
*
* Do the actual work of plowing the selection, propagating the
* effects of each initial plow to all geometry eventually affected
* by them.
*
* If the global bool plowCheckBoundary is TRUE, then we check against
* the boundaries in the list plowBoundaryList for edges that violate
* the limits set by those boundaries. If the effects of plowing extend
* outside of this area, we adjust *pdistance to the largest plowing distance
* that will not cause propagation into illegal areas. If *pdistance had to
* be adjusted, we return TRUE.
*
* Results:
* Returns TRUE if we had to adjust *pdistance, FALSE if not.
* If plowCheckBoundary is FALSE, we never return TRUE.
*
* Side effects:
* See above.
* Sets the rectangle pointed to by 'changedArea' to be a
* bounding box around the area modified by plowing.
*
* ----------------------------------------------------------------------------
*/
bool
plowPropagateSel(def, pdistance, changedArea)
CellDef *def; /* Def being plowed */
int *pdistance; /* Distance to plow */
Rect *changedArea; /* Set to bounding box around area modified */
{
#ifndef NO_RUSAGE
struct rusage t1, t2;
#endif
int plowSelPaintBox(), plowSelCellBox();
int plowSelPaintPlow(), plowSelCellPlow();
Rect selBox;
int tooFar;
SearchContext scx;
bool dummy;
Edge edge;
changedArea->r_xbot = changedArea->r_xtop = 0;
changedArea->r_ybot = changedArea->r_ytop = 0;
if (*pdistance <= 0)
return (FALSE);
/*
* Find the bounding box for all material in the selection
* that lies in the edit cell.
*/
selBox.r_xbot = selBox.r_ybot = INFINITY;
selBox.r_xtop = selBox.r_ytop = MINFINITY;
SelEnumPaint(&DBAllButSpaceBits, TRUE, &dummy,
plowSelPaintBox, (ClientData) &selBox);
SelEnumCells(TRUE, &dummy, (SearchContext *) NULL,
plowSelCellBox, (ClientData) &selBox);
if (GEO_RECTNULL(&selBox))
return (FALSE);
/* Clear the yank buffer for this iteration */
DBCellClearDef(plowYankDef);
/*
* Yank the area of the selection, plus plowYankHalo,
* into a separate set of tile planes.
*/
plowDummyUse->cu_def = def;
UndoDisable();
scx.scx_use = plowDummyUse;
scx.scx_trans = plowYankTrans;
if (DebugIsSet(plowDebugID, plowDebYankAll))
{
scx.scx_area.r_xbot = def->cd_bbox.r_xbot - 1;
scx.scx_area.r_ybot = def->cd_bbox.r_ybot - 1;
scx.scx_area.r_xtop = def->cd_bbox.r_xtop + 1;
scx.scx_area.r_ytop = def->cd_bbox.r_ytop + 1;
GeoTransRect(&plowYankTrans, &scx.scx_area, &plowYankedArea);
}
else
{
/* Note selBox is in parent def coordinates */
GeoTransRect(&plowYankTrans, &selBox, &plowYankedArea);
plowYankedArea.r_xtop += *pdistance + plowYankHalo;
plowYankedArea.r_xbot -= plowYankHalo;
plowYankedArea.r_ybot -= plowYankHalo;
plowYankedArea.r_ytop += plowYankHalo;
GeoTransRect(&plowInverseTrans, &plowYankedArea, &scx.scx_area);
}
(void) DBCellCopyPaint(&scx, &DBAllButSpaceAndDRCBits, 0, plowYankUse);
(void) DBCellCopyCells(&scx, plowYankUse, (Rect *) NULL);
UndoEnable();
/*
* Searching. This finds all the edges in the layout that have
* to move, and marks them with the distance they must move.
* Everything here works with the transformed cell. We initialize
* plowCurrentRule to null here so the debugging output for plowQueueAdd
* will reflect the fact that the edges found below are "initial" edges
* (those found by the plow itself, as opposed to by other edges).
*/
#ifndef NO_RUSAGE
if (DebugIsSet(plowDebugID, plowDebTime))
getrusage(RUSAGE_SELF, &t1);
#endif
plowMovedEdges = plowProcessedEdges = plowQueuedEdges = 0;
plowQueueInit(&plowCellBbox, *pdistance);
/* Queue each edge found by the plowing rules */
plowPropagateProcPtr = plowQueueAdd;
/* Debugging */
plowCurrentRule = &plowRuleInitial;
/* Add everything in the selection */
SelEnumPaint(&DBAllButSpaceBits, TRUE, &dummy,
plowSelPaintPlow, INT2CD(*pdistance));
SelEnumCells(TRUE, &dummy, (SearchContext *) NULL,
plowSelCellPlow, INT2CD(*pdistance));
/* While edges remain, process them */
tooFar = 0;
while (plowQueueLeftmost(&edge))
{
/* Ignore edges that don't move (sanity check) */
if (edge.e_x == edge.e_newx)
continue;
/*
* If we are doing boundary checking, don't add edges to the right of
* the boundary; just record how far they move. Edges whose original
* position is to the left of the boundary but cross it must still be
* queued for processing, since they can affect edges on the right of
* the boundary.
*/
if (plowCheckBoundary && plowPastBoundary(def, &edge, &tooFar))
continue;
if (!SigInterruptPending)
plowProcessEdge(&edge, changedArea);
}
/* Clean up */
plowQueueDone();
#ifndef NO_RUSAGE
if (DebugIsSet(plowDebugID, plowDebTime))
{
getrusage(RUSAGE_SELF, &t2);
plowShowTime(&t1, &t2, plowQueuedEdges,
plowProcessedEdges, plowMovedEdges);
}
#endif
/*
* If geometry to the right of the boundary moved,
* adjust the plow distance.
*/
if (tooFar)
{
*pdistance -= tooFar;
return (TRUE);
}
/* Successful plow */
return (FALSE);
}
/*
* ----------------------------------------------------------------------------
*
* plowSelPaintBox --
* plowSelCellBox --
*
* Called on behalf of plowPropagateSel() above to find the bounding
* box for the material in the selection. Each is called for an element
* in the selection: a paint tile for plowSelPaintBox, or a subcell for
* plowSelCellBox. The bounding box *pSelBox is updated to include the
* area of the element.
*
* Results:
* Both return 0 always.
*
* Side effects:
* Both adjust *pSelBox;
*
* ----------------------------------------------------------------------------
*/
int
plowSelPaintBox(rect, type, pSelBox)
Rect *rect;
TileType type;
Rect *pSelBox;
{
Rect editRect;
GeoTransRect(&RootToEditTransform, rect, &editRect);
GeoInclude(&editRect, pSelBox);
return (0);
}
int
plowSelCellBox(selUse, realUse, transform, pSelBox)
CellUse *selUse;
CellUse *realUse;
Transform *transform;
Rect *pSelBox;
{
GeoInclude(&realUse->cu_bbox, pSelBox);
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowSelPaintPlow --
*
* Called on behalf of plowPropagateSel() above to queue the initial
* edges belonging to material in the selection. For paint, we create
* a plow at both the right and left edges of the tile.
*
* Results:
* Returns 0 always.
*
* Side effects:
* Queues edges marked as initial (e_flags has E_ISINITIAL set).
*
* ----------------------------------------------------------------------------
*/
int
plowSelPaintPlow(rect, type, distance)
Rect *rect;
TileType type;
int distance;
{
int plowSelPaintAdd();
Rect editRect, plowRect, plowLHS, plowRHS;
TileTypeBitMask mask;
GeoTransRect(&RootToEditTransform, rect, &editRect);
GeoTransRect(&plowYankTrans, &editRect, &plowRect);
plowLHS = plowRHS = plowRect;
/* Queue the LHS */
plowLHS.r_xtop = plowLHS.r_xbot + distance;
#ifdef notdef
plowAtomize(DBPlane(type), &plowLHS, plowSelPaintAdd, (ClientData) NULL);
#endif /* notdef */
plowLHS.r_xbot--;
plowSrShadow(DBPlane(type), &plowLHS, &DBZeroTypeBits,
plowInitialPaint, INT2CD(plowLHS.r_xtop));
/* Queue the RHS */
plowRHS.r_xbot = plowRHS.r_xtop;
plowRHS.r_xtop += distance;
#ifdef notdef
plowAtomize(DBPlane(type), &plowRHS, plowSelPaintAdd, (ClientData) NULL);
#endif /* notdef */
plowRHS.r_xbot--;
TTMaskSetOnlyType(&mask, type);
plowSrShadow(DBPlane(type), &plowRHS, &mask,
plowInitialPaint, INT2CD(plowRHS.r_xtop));
return (0);
}
int
plowSelPaintAdd(edge)
Edge *edge;
{
int saveFlags = edge->e_flags;
edge->e_flags |= E_ISINITIAL;
plowQueueAdd(edge);
edge->e_flags = saveFlags;
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowSelCellPlow --
*
* Called on behalf of plowPropagateSel() above to queue the initial
* edges belonging to material in the selection. For subcells, we
* have to look for the subcell with the same use-id in our yank
* buffer, and then move it by its leading edge.
*
* Results:
* Returns 0 always.
*
* Side effects:
* Queues edges.
*
* ----------------------------------------------------------------------------
*/
int
plowSelCellPlow(selUse, realUse, transform, distance)
CellUse *selUse; /* Cell in selection */
CellUse *realUse; /* Corresponding cell in def being plowed */
Transform *transform; /* UNUSED */
int distance; /* Plow distance */
{
int plowFindSelCell();
ClientData save;
/* Find the cell in the yanked def that has the same use-id as this one */
save = realUse->cu_client;
realUse->cu_client = INT2CD(distance);
(void) DBCellEnum(plowYankDef, plowFindSelCell, PTR2CD(realUse));
realUse->cu_client = save;
return (0);
}
int
plowFindSelCell(yankUse, editUse)
CellUse *yankUse; /* Cell in the plow yank buffer */
CellUse *editUse; /* Cell from the original cell def */
{
Edge edge;
if (strcmp(yankUse->cu_id, editUse->cu_id) != 0)
return (0);
edge.e_flags = 0;
edge.e_pNum = PL_ROUTER;
edge.e_use = yankUse;
edge.e_ytop = yankUse->cu_bbox.r_ytop;
edge.e_ybot = yankUse->cu_bbox.r_ybot;
edge.e_x = yankUse->cu_bbox.r_xtop;
edge.e_newx = yankUse->cu_bbox.r_xtop + (int)CD2INT(editUse->cu_client);
edge.e_ltype = PLOWTYPE_CELL;
edge.e_rtype = PLOWTYPE_CELL;
(void) plowQueueAdd(&edge);
return (1);
}
/*
* ----------------------------------------------------------------------------
*
* PlowExtendJogHorizon --
*
* Search above and below 'edge' for the closest "natural" jogs within
* plowJogHorizon of the top or bottom of the edge, and extend 'edge'
* to these jogs if they are found. If no jog is found in a particular
* direction, we leave that end of 'edge' (top/bottom) alone.
*
* A "natural" jog is nothing more than a change in the direction of
* an edge being followed. The following diagram gives an example, with
* the vertical arrows indicating the jog horizons. Note that the edge
* is extended up, because there is a natural jog there, but not down
* because the jog on the bottom is outside the horizon.
*
* +-------- ^ E--------
* | | E
* | | E
* | v E
* E E
* E E
* E E
* | ^ |
* | | |
* | | |
* | v |
* | |
* --------+ --------+
*
* Results:
* None.
*
* Side effects:
* May modify its argument 'edge' by extending it vertically.
* May also add additional edges via plowAtomizeEdge().
*
* ----------------------------------------------------------------------------
*/
void
PlowExtendJogHorizon(edge)
Edge *edge; /* Edge being moved */
{
int horizonTop, horizonBot, eTop, eBot;
Tile *tpR, *tpL;
Point startPoint;
bool rhsChanged;
Rect r, newEdgeR;
if (PlowJogHorizon == 0)
return;
horizonTop = edge->e_ytop + PlowJogHorizon;
horizonBot = edge->e_ybot - PlowJogHorizon;
r.r_xbot = edge->e_x - 1;
r.r_xtop = edge->e_x + 1;
newEdgeR = edge->e_rect;
/* Extend to the top */
restarttop:
startPoint.p_x = edge->e_x - 1;
startPoint.p_y = edge->e_ytop;
tpL = TiSrPointNoHint(plowYankDef->cd_planes[edge->e_pNum], &startPoint);
r.r_ybot = r.r_ytop = edge->e_ytop;
/*
* Walk upwards.
* The loop terminates with r.r_ytop equal to the Y coordinate
* of the closest jog to our top, and eTop equal to the smaller
* of r.r_ytop and the Y coordinate of the closest point at which
* the RHS of the edge changed type.
*/
rhsChanged = FALSE;
while (RIGHT(tpL) == edge->e_x
&& TiGetTypeExact(tpL) == edge->e_ltype
&& BOTTOM(tpL) < horizonTop)
{
r.r_ytop = TOP(tpL);
if (plowYankMore(&r, 1, 1))
goto restarttop;
/*
* Walk along RHS to see if its type changed.
* If so, make eTop record the lowest point
* at which this happened.
*/
if (!rhsChanged)
for (tpR = TR(tpL); TOP(tpR) > r.r_ybot; tpR = LB(tpR))
if (TiGetTypeExact(tpR) != edge->e_rtype)
rhsChanged = TRUE, eTop = BOTTOM(tpR);
tpL = RT(tpL);
r.r_ybot = r.r_ytop;
}
/* Update if within the jog horizon */
if (r.r_ytop <= horizonTop && r.r_ytop > edge->e_ytop)
{
newEdgeR.r_ytop = r.r_ytop;
edge->e_ytop = (rhsChanged) ? eTop : r.r_ytop;
}
/* Extend to the bottom */
restartbot:
startPoint.p_x = edge->e_x;
startPoint.p_y = edge->e_ybot - 1;
tpR = TiSrPointNoHint(plowYankDef->cd_planes[edge->e_pNum], &startPoint);
r.r_ybot = r.r_ytop = edge->e_ybot;
/*
* Walk down.
* The loop terminates with r.r_ybot equal to the Y coordinate
* of the closest jog to our bottom, and eBot equal to the larger
* of r.r_ytop and the Y coordinate of the closest point at which
* the RHS of the edge changed type.
*/
rhsChanged = FALSE;
while (LEFT(tpR) == edge->e_x && TOP(tpR) > horizonBot)
{
r.r_ybot = BOTTOM(tpR);
if (plowYankMore(&r, 1, 1))
goto restartbot;
/* Record where the RHS type changed if it did */
if (!rhsChanged && TiGetTypeExact(tpR) != edge->e_rtype)
rhsChanged = TRUE, eBot = TOP(tpR);
/* Walk up the LHS */
for (tpL = BL(tpR); BOTTOM(tpL) < r.r_ytop; tpL = RT(tpL))
if (TiGetTypeExact(tpL) != edge->e_ltype)
r.r_ybot = TOP(tpL);
if (r.r_ybot > BOTTOM(tpR))
break;
tpR = LB(tpR);
r.r_ytop = r.r_ybot;
}
/* Update if within the jog horizon */
if (r.r_ybot >= horizonBot && r.r_ybot < edge->e_ybot)
{
newEdgeR.r_ybot = r.r_ybot;
edge->e_ybot = (rhsChanged) ? eBot : r.r_ybot;
}
if (newEdgeR.r_ytop > edge->e_ytop)
{
r = newEdgeR;
r.r_ybot = edge->e_ytop;
(void) plowAtomize(edge->e_pNum, &r, plowQueueAdd, (ClientData) NULL);
}
if (newEdgeR.r_ybot < edge->e_ybot)
{
r = newEdgeR;
r.r_ytop = edge->e_ybot;
(void) plowAtomize(edge->e_pNum, &r, plowQueueAdd, (ClientData) NULL);
}
}
/*
* ----------------------------------------------------------------------------
*
* plowSetTrans --
*
* Set up the transforms based on the direction we will be plowing.
* Just use simple rotations, since we always have the inverse
* transform around for copying stuff back.
*
* Results:
* None.
*
* Side effects:
* Sets plowYankTrans and plowInverseTrans.
*
* ----------------------------------------------------------------------------
*/
void
plowSetTrans(direction)
int direction;
{
plowDirection = direction;
switch (direction)
{
case GEO_NORTH:
plowYankTrans = Geo90Transform;
break;
case GEO_SOUTH:
plowYankTrans = Geo270Transform;
break;
case GEO_WEST:
plowYankTrans = Geo180Transform;
break;
case GEO_EAST:
plowYankTrans = GeoIdentityTransform;
break;
}
GeoInvertTrans(&plowYankTrans, &plowInverseTrans);
}
/*
* ----------------------------------------------------------------------------
*
* plowPastBoundary --
*
* Check to see if the edge is in an illegal region according to the
* boundaries on the list plowBoundaryList.
*
* Results:
* TRUE if we should not even bother to process this edge (because
* its initial position was in an invalid area), FALSE otherwise.
*
* Side effects:
* Updates *pmove to the farthest distance by which anything in an
* illegal area moves.
*
* ----------------------------------------------------------------------------
*/
bool
plowPastBoundary(def, edge, pmove)
CellDef *def; /* Def being plowed */
Edge *edge; /* Edge being moved */
int *pmove; /* Updated to be the maximum distance by
* which something moves in an illegal area.
*/
{
PlowBoundary *pb;
int delta;
bool ret;
Rect r;
ret = FALSE;
delta = 0;
for (pb = plowBoundaryList; pb; pb = pb->pb_next)
{
if (pb->pb_editDef != def)
continue;
GeoTransRect(&plowYankTrans, &pb->pb_editArea, &r);
if (edge->e_x < r.r_xbot)
{
/* To the left of the boundary */
delta = MAX(edge->e_newx, r.r_xbot) - edge->e_x;
}
else if (edge->e_newx > r.r_xtop)
{
/* To the right of the boundary */
delta = edge->e_newx - MAX(edge->e_x, r.r_xtop);
if (edge->e_x > r.r_xtop) ret = TRUE;
}
else if (edge->e_ytop > r.r_ytop || edge->e_ybot < r.r_ybot)
{
/* Above or below the boundary */
delta = edge->e_newx - edge->e_x;
}
if (delta > *pmove) *pmove = delta;
}
return (ret);
}
/*
* ----------------------------------------------------------------------------
*
* plowInitialPaint --
*
* Add one of the edges found initially by the plow to the queue
* of edges to move. The edge will move as far as 'xnew'.
*
* Results:
* Always returns 0.
*
* Side effects:
* Adds the edge to the queue of edges to move via plowQueueAdd().
*
* ----------------------------------------------------------------------------
*/
int
plowInitialPaint(edge, xnew)
Edge *edge;
int xnew;
{
edge->e_newx = xnew;
edge->e_flags = E_ISINITIAL;
(void) plowQueueAdd(edge);
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowInitialCell --
*
* Add a cell to the queue of edges to move. The cell will move as far
* as 'plowRect->r_xtop'.
*
* Results:
* Always returns 0.
*
* Side effects:
* Adds an edge to the queue of edges to move via plowQueueAdd().
*
* ----------------------------------------------------------------------------
*/
int
plowInitialCell(use, plowRect)
CellUse *use;
Rect *plowRect;
{
int xmove;
Edge edge;
if (use->cu_bbox.r_xbot < plowRect->r_xbot)
{
if (use->cu_bbox.r_xtop >= plowRect->r_xtop)
return 0;
/* Dragging this cell by its front edge */
xmove = plowRect->r_xtop - use->cu_bbox.r_xtop;
}
else
{
/* Pushing this cell by its back edge */
xmove = plowRect->r_xtop - use->cu_bbox.r_xbot;
}
edge.e_pNum = PL_ROUTER;
edge.e_use = use;
edge.e_flags = E_ISINITIAL;
edge.e_ytop = use->cu_bbox.r_ytop;
edge.e_ybot = use->cu_bbox.r_ybot;
edge.e_x = use->cu_bbox.r_xtop;
edge.e_newx = use->cu_bbox.r_xtop + xmove;
edge.e_ltype = PLOWTYPE_CELL;
edge.e_rtype = PLOWTYPE_CELL;
(void) plowQueueAdd(&edge);
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* plowProcessEdge --
*
* Process a single edge from the queue.
* Plowing is rule-based, so processing an edge consists of applying
* a sequence of rules. The overall algorithm is:
*
* - Yank more of the original cell if necessary.
* - Check to see if the edge has already moved; if so, we don't do
* anything further.
* - Apply extension rules. These are allowed to extend the edge
* but shouldn't search for new edges to be added to the edge queue.
* - Compute the real width rules for this material.
* - Apply search rules to each remaining segment of the clipped edge.
* These look for other edges to be added to the edge queue.
* - Update the edge's position. This consists of modifying the
* LEADING coordinates in the tiles along the edge, possibly
* splitting the tiles at the top and bottom of the edge.
*
* Results:
* None.
*
* Side effects:
* May split and merge tiles.
* May cause additional area to be yanked from the original cell.
* Updates changedArea to include any additional area modified.
*
* ----------------------------------------------------------------------------
*/
void
plowProcessEdge(edge, changedArea)
Edge *edge; /* Edge to be processed (in plowYankDef) */
Rect *changedArea; /* Include any additional area changed in this area */
{
int amountToMove = edge->e_newx - edge->e_x;
RuleTableEntry *rte;
Tile *tp;
Point p;
Rect r;
/* Debugging */
if ((plowWhenTop && edge->e_x == plowWhenTopPoint.p_x
&& edge->e_ytop == plowWhenTopPoint.p_y)
|| (plowWhenBot && edge->e_x == plowWhenBotPoint.p_x
&& edge->e_ybot == plowWhenBotPoint.p_y))
{
plowDebugEdge(edge, (RuleTableEntry *) NULL, "matched edge");
}
/*
* Process cells specially.
* These don't get clipped, but are either processed
* completely or not at all.
*/
plowProcessedEdges++;
if (edge->e_use)
{
if (amountToMove > (int)CD2INT(edge->e_use->cu_client))
{
/* Update area modified by plowing */
(void) GeoInclude(&edge->e_rect, changedArea);
/*
* See if the cell's bbox plus the distance to move
* forces us to yank more from the original cell.
*/
r = edge->e_use->cu_bbox;
r.r_xtop = edge->e_newx;
(void) plowYankMore(&r, plowYankHalo, 1);
/*
* Update the cell's position.
* We do this here so we don't see the cell a
* second time. The whole cell moves, so we have
* to update the area changed by the area of the
* cell PLUS the area swept out by its RHS.
*/
edge->e_use->cu_client = INT2CD(amountToMove);
r = edge->e_use->cu_bbox;
r.r_xbot += amountToMove;
r.r_xtop += amountToMove;
(void) GeoInclude(&r, changedArea);
/* Apply cell rules */
for (rte = plowCellRulesTbl; rte < plowCellRulesPtr; rte++)
{
if (TTMaskHasType(&rte->rte_ltypes, edge->e_ltype)
&& TTMaskHasType(&rte->rte_rtypes, edge->e_rtype))
{
plowCurrentRule = rte;
(*rte->rte_proc)(edge, (PlowRule *) NULL);
}
}
plowMovedEdges++;
}
return;
}
/*
* Check to see if any of this edge needs to move. If it has
* already moved far enough, we can just return; otherwise,
* we process the entire edge as a whole. This check is necessary
* to avoid infinite loops.
*/
p.p_x = edge->e_x, p.p_y = edge->e_ytop - 1;
tp = TiSrPointNoHint(plowYankDef->cd_planes[edge->e_pNum], &p);
for ( ; TOP(tp) > edge->e_ybot; tp = LB(tp))
if (TRAILING(tp) < edge->e_newx)
goto worktodo;
return;
/*
* Some or all of this edge must be moved.
* Extend it if necessary.
* Yank more if necessary.
* Compute its width.
* Apply the search rules.
* Update the coordinates of all tiles along this edge.
*/
worktodo:
/*
* Extend this edge if we're reducing jogs.
* May cause other edges to be added.
*/
plowMovedEdges++;
if (PlowJogHorizon > 0)
PlowExtendJogHorizon(edge);
/*
* Update area modified by plowing.
* This must happen after applying the extension rules above.
*/
(void) GeoInclude(&edge->e_rect, changedArea);
/* Apply search rules */
plowApplySearchRules(edge);
/* Update edge position */
plowMoveEdge(edge);
}
/*
* ----------------------------------------------------------------------------
*
* plowApplySearchRules --
*
* Apply the search rules to an edge.
*
* Results:
* None.
*
* Side effects:
* May call (*plowPropagateProcPtr)() if the search rules
* determine that edges must move. May yank more.
* May cause additional area to be yanked from the original cell.
*
* ----------------------------------------------------------------------------
*/
int
plowApplySearchRules(edge)
Edge *edge;
{
PlowRule *widthRules, *rules;
RuleTableEntry *rte;
int halo;
/*
* Build list of width rules using computed (instead of min) width.
* This can also yank more.
*/
halo = plowYankHalo;
widthRules = plowBuildWidthRules(edge, &plowCellBbox, &halo);
/* Yank more if necessary */
(void) plowYankMore(&edge->e_rect, halo, 1);
/*
* Search rules.
* These generally won't need to yank more unless they deal
* with entire tiles.
*/
for (rte = plowSearchRulesTbl; rte < plowSearchRulesPtr; rte++)
{
if (TTMaskHasType(&rte->rte_ltypes, edge->e_ltype)
&& TTMaskHasType(&rte->rte_rtypes, edge->e_rtype))
{
plowCurrentRule = rte;
switch (rte->rte_whichRules)
{
/* Apply no rules */
case RTE_NULL:
rules = (PlowRule *) NULL;
break;
/* Apply width rules, but use actual widths */
case RTE_REALWIDTH:
rules = widthRules;
break;
/* Apply minimum-width rules */
case RTE_MINWIDTH:
rules = plowWidthRulesTbl[edge->e_ltype][edge->e_rtype];
break;
/* Apply spacing rules */
case RTE_SPACING:
rules = plowSpacingRulesTbl[edge->e_ltype][edge->e_rtype];
break;
/* Only apply rule if no spacing rules apply */
case RTE_NOSPACING:
rules = plowSpacingRulesTbl[edge->e_ltype][edge->e_rtype];
if (rules)
continue;
break;
}
(*rte->rte_proc)(edge, rules);
}
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* plowBuildWidthRules --
*
* Find the real width of the material being plowed, and construct
* a list of width rules that is the same as the normal list for
* this type of edge, but with the minimum distance replaced by the
* actual width if the actual width is greater than the minimum
* distance. (If the actual width is less than or equal to the
* minimum distance, we use the minimum distance instead).
*
* If the minimum-width rectangle about the starting edge touches the
* boundary of the yanked area, yank more and retry.
*
* Results:
* Returns a pointer to the statically constructed rules list,
* or NULL if no width rules apply.
*
* Side effects:
* Since the width rules list is statically allocated, subsequent
* calls to plowBuildWidthRules() will trash the results of previous
* calls.
*
* May cause more of the original cell to be yanked.
*
* ----------------------------------------------------------------------------
*/
PlowRule *
plowBuildWidthRules(edge, bbox, phalo)
Edge *edge; /* Edge being moved */
Rect *bbox; /* Bounding box of def being plowed */
int *phalo; /* Update *phalo to be the max of its initial value
* and each of the widths we compute for the rules
* we return.
*/
{
extern char *maskToPrint();
static PlowRule widthRuleList[MAXRULES];
PlowRule *prMin, *prReal;
Rect maxBox;
int dist;
retry:
prMin = plowWidthRulesTbl[edge->e_ltype][edge->e_rtype];
if (prMin == NULL)
return ((PlowRule *) NULL);
/* At this point, know there will be at least one rule in the list */
for (prReal = widthRuleList;
prMin && prReal < &widthRuleList[MAXRULES];
prMin = prMin->pr_next, prReal++)
{
*prReal = *prMin;
prReal->pr_next = prReal + 1;
dist = plowFindWidth(edge, &prMin->pr_oktypes, bbox, &maxBox);
/* Conservative test of whether we need to yank more */
if (plowYankMore(&maxBox, 1, 1))
{
if (DebugIsSet(plowDebugID, plowDebWidth))
TxPrintf("width: yank more and retry\n");
goto retry;
}
prReal->pr_dist = MAX(dist, prReal->pr_dist);
*phalo = MAX(*phalo, dist);
if (DebugIsSet(plowDebugID, plowDebWidth))
TxPrintf("width: %d types: %s\n",
prReal->pr_dist, maskToPrint(&prReal->pr_oktypes));
}
(--prReal)->pr_next = (PlowRule *) NULL;
if (DebugIsSet(plowDebugID, plowDebWidth))
plowDebugEdge(edge, (RuleTableEntry *) NULL, "find width");
return (widthRuleList);
}
/*
* ----------------------------------------------------------------------------
*
* plowMoveEdge --
*
* Do the actual work of updating the coordinates of an edge. In general,
* the edge may have more than one tile on either side, as below:
*
* |
* |
* --------+
* +---------
* |
* |
* --------+
* |---------
* |
* --------+
* |
*
* Updating the coordinates consists of first clipping the tiles on
* either side so they do not extend vertically past the edge, then
* updating the TRAILING coordinates of all tiles along the RHS, and
* then merging vertically where possible.
*
* We only update TRAILING coordinates if they are not already far
* enough to the right.
*
* Results:
* None.
*
* Side effects:
* May split and merge tiles.
*
* ----------------------------------------------------------------------------
*/
void
plowMoveEdge(edge)
Edge *edge; /* Edge to be moved */
{
Plane *plane = plowYankDef->cd_planes[edge->e_pNum];
Tile *delayed = NULL; /* delayed free to extend lifetime */
Tile *tp, *tpL;
Point p;
/*
* Find topmost tile along LHS of edge.
* The goal is to clip the topmost LHS and topmost RHS tiles
* so their tops are equal to the top of the edge.
*/
p.p_x = edge->e_x - 1;
p.p_y = edge->e_ytop - 1;
tp = TiSrPointNoHint(plane, &p);
ASSERT(RIGHT(tp) == edge->e_x, "plowMoveEdge");
/* Only clip top tiles if we must update their coordinates */
if (LEADING(tp) < edge->e_newx)
{
if (TOP(tp) > edge->e_ytop)
(void) plowSplitY(tp, edge->e_ytop); /* Tp is bottom tile */
tp = TR(tp); /* Top tile on RHS */
if (TOP(tp) > edge->e_ytop)
(void) plowSplitY(tp, edge->e_ytop); /* Tp is bottom tile */
}
else for (tp = TR(tp); BOTTOM(tp) >= edge->e_ytop; tp = LB(tp))
/* Nothing */;
/*
* Now 'tp' is the top-right tile. Walk down the RHS, updating TRAILING
* coordinates if necessary, and merging each tile with its upper neighbor.
* Stop one short of the last tile.
*/
for (; BOTTOM(tp) > edge->e_ybot; tp = LB(tp))
{
if (TRAILING(tp) < edge->e_newx)
plowSetTrailing(tp, edge->e_newx);
plowMergeTop(&delayed, tp, plane);
}
/*
* Now 'tp' is the bottom-right tile.
* Clip it if necessary, and update its TRAILING coordinate. Merge it
* with both its upper and lower neighbors. If tp is the only tile on
* the RHS, its TRAILING coordinate doesn't get updated until here.
*/
if (TRAILING(tp) < edge->e_newx)
{
if (BOTTOM(tp) < edge->e_ybot)
{
/* Clip (no merging will be necessary) */
tp = plowSplitY(tp, edge->e_ybot);
plowSetTrailing(tp, edge->e_newx);
tpL = BL(tp);
}
else /* BOTTOM(tp) == edge->e_ybot */
{
/* Merge (no clipping was necessary) */
tpL = BL(tp);
plowSetTrailing(tp, edge->e_newx);
plowMergeBottom(&delayed, tp, plane);
}
/* Split the bottom-left tile if necessary; otherwise, merge down */
if (BOTTOM(tpL) < edge->e_ybot)
tpL = plowSplitY(tpL, edge->e_ybot); /* TpL is upper tile */
else
plowMergeBottom(&delayed, tpL, plane);
}
else for (tpL = BL(tp); TOP(tpL) <= edge->e_ybot; tpL = RT(tpL))
/* Nothing */;
plowMergeTop(&delayed, tp, plane);
/*
* Now 'tpL' is the bottom-left tile, which has already been merged
* with its lower neighbor. Walk up the rest of the LHS, merging
* each tile with its lower neighbor.
*/
for (tp = RT(tpL); BOTTOM(tp) < edge->e_ytop; tp = RT(tp))
plowMergeBottom(&delayed, tp, plane);
/*
* If tp now extends above edge->e_ytop, then it must not have been split
* way up in the first step in this procedure, which means that its LEADING
* coordinate was already far enough to the right, which means that it was
* not changed. Hence, we needn't try to merge to its bottom.
*/
if (BOTTOM(tp) == edge->e_ytop)
plowMergeBottom(&delayed, tp, plane);
if (DebugIsSet(plowDebugID, plowDebMove))
plowDebugEdge(edge, (RuleTableEntry *) NULL, "move");
TiFreeIf(delayed);
}
/*
* ----------------------------------------------------------------------------
*
* plowSplitY --
*
* Split a tile vertically in two, returning a pointer to
* the newly created upper tile.
*
* The new tile has its ti_client edge positions set to that
* of the original tile.
*
* Results:
* Returns a pointer to the newly created tile.
*
* Side effects:
* Splits the tile 'tp'.
*
* ----------------------------------------------------------------------------
*/
Tile *
plowSplitY(tp, y)
Tile *tp;
int y;
{
Tile *newTile;
newTile = TiSplitY(tp, y);
TiSetClient(newTile, TiGetClient(tp));
TiSetBody(newTile, TiGetBody(tp));
return (newTile);
}
/*
* ----------------------------------------------------------------------------
*
* plowMergeTop --
* plowMergeBottom --
*
* Merge the given tile with its upper/lower neighbor if appropriate:
* plowMergeTop merges with the upper neighbor, plowMergeBottom with
* the lower. This may happen only if the types are the same, the
* LEADING/TRAILING coordinate the same, and the LEFT and RIGHT
* coordinates the same.
*
* Guarantees that the tile pointer passed it as an argument will
* remain valid after the call.
*
* Results:
* None.
*
* Side effects:
* May cause two tiles to be joined.
*
* ----------------------------------------------------------------------------
*/
void
plowMergeTop(Tile **delay1, Tile *tp, Plane *plane)
{
Tile *tpRT = RT(tp);
if (TiGetTypeExact(tp) == TiGetTypeExact(tpRT)
&& LEFT(tp) == LEFT(tpRT) && RIGHT(tp) == RIGHT(tpRT)
&& LEADING(tp) == LEADING(tpRT) && TRAILING(tp) == TRAILING(tpRT))
{
TiJoinY1(delay1, tp, tpRT, plane);
}
}
void
plowMergeBottom(Tile **delay1, Tile *tp, Plane *plane)
{
Tile *tpLB = LB(tp);
if (TiGetTypeExact(tp) == TiGetTypeExact(tpLB)
&& LEFT(tp) == LEFT(tpLB) && RIGHT(tp) == RIGHT(tpLB)
&& LEADING(tp) == LEADING(tpLB) && TRAILING(tp) == TRAILING(tpLB))
{
TiJoinY1(delay1, tp, tpLB, plane);
}
}
/*
* ----------------------------------------------------------------------------
*
* PlowAfterTech --
*
* Initialize the rule tables for plowing. This gets called after
* the technology file has been read, since we need to know about
* fixed-width objects.
*
* Also initialize the debugging information.
*
* Sets plowYankHalo to be the size of the halo around each edge that
* we use when checking to see if plowProcessEdge must yank more area
* from the original cell. If this halo extends outside plowYankedArea
* or touches it, we yank more. The size of the halo should be such
* that most of the plowing rules are guaranteed not to visit any area
* outside of it. Currently, DRCTechHalo is sufficient; only the rules
* moving whole tiles need anything bigger than this, and they are
* responsible for doing their own yanking if they need it.
*
* Results:
* None.
*
* Side effects:
* See above.
*
* ----------------------------------------------------------------------------
*/
void
PlowAfterTech()
{
RuleTableEntry *rp, *re;
TileTypeBitMask allButSpace, allBits;
TileTypeBitMask cellTypes;
TileTypeBitMask widthL, widthR;
TileTypeBitMask spaceL, spaceR;
TileTypeBitMask mask;
TileType i, j;
/* Reset rules tables */
plowSearchRulesPtr = plowSearchRulesTbl;
plowCellRulesPtr = plowCellRulesTbl;
/* Set the masks we will use for all the rules below */
allButSpace = DBAllButSpaceAndDRCBits;
allBits = DBAllTypeBits;
TTMaskSetOnlyType(&cellTypes, PLOWTYPE_CELL);
TTMaskZero(&widthL);
TTMaskZero(&widthR);
TTMaskZero(&spaceL);
TTMaskZero(&spaceR);
for (i = 0; i < DBNumTypes; i++)
{
for (j = 0; j < DBNumTypes; j++)
{
if (plowWidthRulesTbl[i][j])
{
TTMaskSetType(&widthL, i);
TTMaskSetType(&widthR, j);
}
if (plowSpacingRulesTbl[i][j])
{
TTMaskSetType(&spaceL, i);
TTMaskSetType(&spaceR, j);
}
}
}
/* Dummy rule for debugging */
plowInitRule(&plowRuleInitial, (&plowRuleInitial) + 1, RTE_NULL,
(int (*)()) NULL,
"initial edge",
&DBZeroTypeBits, &DBZeroTypeBits);
/* Cell rules */
rp = plowCellRulesPtr;
re = &plowCellRulesTbl[MAXRULES];
/* Drag geometry with cells */
plowInitRule(rp++, re, RTE_NULL, prCell,
"drag paint with cells",
&allBits, &cellTypes);
if (rp >= re) rp = re;
plowCellRulesPtr = rp;
/* Search rules */
rp = plowSearchRulesPtr;
re = &plowSearchRulesTbl[MAXRULES];
/* Clear the umbra */
plowInitRule(rp++, re, RTE_NULL, prClearUmbra, "clear umbra",
&allBits, &allButSpace);
plowInitRule(rp++, re, RTE_REALWIDTH, prUmbra, "umbra width",
&widthL, &widthR);
plowInitRule(rp++, re, RTE_SPACING, prUmbra, "umbra spacing",
&spaceL, &spaceR);
/* Clear the penumbra */
plowInitRule(rp++, re, RTE_REALWIDTH, prPenumbraTop,
"top penumbra width",
&widthL, &widthR);
plowInitRule(rp++, re, RTE_SPACING, prPenumbraTop,
"top penumbra spacing",
&spaceL, &spaceR);
plowInitRule(rp++, re, RTE_REALWIDTH, prPenumbraBot,
"bottom penumbra width",
&widthL, &widthR);
plowInitRule(rp++, re, RTE_SPACING, prPenumbraBot,
"bottom penumbra spacing",
&spaceL, &spaceR);
/* Special penumbra searching when RHS is fixed */
plowInitRule(rp++, re, RTE_NOSPACING, prFixedPenumbraTop,
"top penumbra spacing (RHS fixed-width)",
&allBits, &PlowFixedTypes);
plowInitRule(rp++, re, RTE_NOSPACING, prFixedPenumbraBot,
"bottom penumbra spacing (RHS fixed-width)",
&allBits, &PlowFixedTypes);
/* Avoid introducing slivers */
plowInitRule(rp++, re, RTE_MINWIDTH, prSliverTop,
"top width slivers",
&widthL, &widthR);
plowInitRule(rp++, re, RTE_SPACING, prSliverTop,
"top spacing slivers",
&spaceL, &spaceR);
plowInitRule(rp++, re, RTE_MINWIDTH, prSliverBot,
"bottom width slivers",
&widthL, &widthR);
plowInitRule(rp++, re, RTE_SPACING, prSliverBot,
"bottom spacing slivers",
&spaceL, &spaceR);
/* Inside slivers (plow too small) */
TTMaskCom2(&mask, &PlowFixedTypes);
plowInitRule(rp++, re, RTE_NULL, prInSliver,
"inside slivers",
&mask, &mask);
/* Avoid introducing illegal edges */
plowInitRule(rp++, re, RTE_NULL, prIllegalTop,
"top illegal edges",
&allBits, &allBits);
plowInitRule(rp++, re, RTE_NULL, prIllegalBot,
"bottom illegal edges",
&allBits, &allBits);
/* Avoid uncovering "covered" materials (e.g, fets) */
plowInitRule(rp++, re, RTE_NULL, prCoverTop,
"top covering",
&PlowCoveredTypes, &allBits);
plowInitRule(rp++, re, RTE_NULL, prCoverBot,
"bottom covering",
&PlowCoveredTypes, &allBits);
/* Preserve fixed-width objects */
plowInitRule(rp++, re, RTE_NULL, prFixedLHS,
"LHS is fixed",
&PlowFixedTypes, &allBits);
plowInitRule(rp++, re, RTE_NULL, prFixedRHS,
"RHS is fixed",
&allBits, &PlowFixedTypes);
/* Fixed-width objects drag trailing stubs */
TTMaskCom2(&mask, &PlowDragTypes);
TTMaskClearType(&mask, TT_SPACE);
plowInitRule(rp++, re, RTE_NULL, prFixedDragStubs,
"RHS fixed dragging stubs",
&mask, &PlowDragTypes);
/* Couple contacts */
plowInitRule(rp++, re, RTE_NULL, prContactLHS,
"LHS is contact",
&PlowContactTypes, &allBits);
plowInitRule(rp++, re, RTE_NULL, prContactRHS,
"RHS is contact",
&allBits, &PlowContactTypes);
/* Move cells out of the way */
plowInitRule(rp++, re, RTE_NULL, prFindCells,
"find cells",
&allBits, &allBits);
if (rp >= re) rp = re;
plowSearchRulesPtr = rp;
/* Initialize debugging flags */
plowDebugInit();
/* Initialize the yank halo */
plowYankHalo = DRCTechHalo;
}
void
plowInitRule(
RuleTableEntry *rtePtr, /* Pointer to entry to be added */
RuleTableEntry *rteEnd, /* Pointer to one past last entry in table */
int whichRules, /* Which rules to use (RTE_* from earlier) */
int (*proc)(), /* Procedure implementing the rule */
const char *name, /* Name of this rule */
const TileTypeBitMask *ltypesp,
const TileTypeBitMask *rtypesp)
{
TileTypeBitMask ltypes = *ltypesp; /* TTMaskCopy(&ltypes, ltypesp) */
TileTypeBitMask rtypes = *rtypesp; /* TTMaskCopy(&rtypes, rtypesp) */
if (rtePtr >= rteEnd)
{
TxError("Too many rules in PlowMain.c (maximum %d)\n", MAXRULES);
return;
}
rtePtr->rte_whichRules = whichRules;
rtePtr->rte_proc = proc;
rtePtr->rte_name = name;
rtePtr->rte_ltypes = ltypes;
rtePtr->rte_rtypes = rtypes;
}
/*
* ----------------------------------------------------------------------------
*
* plowYankCreate --
*
* Create the yank buffers used for plowing.
*
* Results:
* None.
*
* Side effects:
* Creates the cells __PLOWYANK__ and __PLOWINCR__ if they
* don't already exist, and initializes plowYankDef, plowYankUse,
* and plowDummyUse.
*
* ----------------------------------------------------------------------------
*/
void
plowYankCreate()
{
if (plowYankDef == NULL)
{
DBNewYank("__PLOWYANK__", &plowYankUse, &plowYankDef);
DBNewYank("__PLOWYANK__", &plowDummyUse, &plowYankDef);
DBNewYank("__PLOWINCR__", &plowSpareUse, &plowSpareDef);
}
}
#ifndef NO_RUSAGE
void
plowShowTime(t1, t2, nqueued, nprocessed, nmoved)
struct rusage *t1, *t2;
int nqueued, nprocessed, nmoved;
{
double secs, usecs;
secs = t2->ru_utime.tv_sec - t1->ru_utime.tv_sec;
usecs = (secs * 1000000.) + (t2->ru_utime.tv_usec - t1->ru_utime.tv_usec);
printf("%.2f sec, %d queued, %d processed, %d moved\n",
usecs/1000000., nqueued, nprocessed, nmoved);
}
#endif