mirror of
https://github.com/RTimothyEdwards/magic.git
synced 2026-09-05 08:51:53 +02:00
and forcing it to be passed as an argument to all the callback functions for the search routines that require it. Magic now compiles and runs with the new code, but there are a number of known issues that need to be fixed up. Committing now so that I can rebase on the last update to the master branch.
2275 lines
65 KiB
C
2275 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;
|
|
free_magic1_t mm1 = freeMagic1_init();
|
|
for ( ; pb; pb = pb->pb_next)
|
|
{
|
|
DBWHLRedraw(pb->pb_rootDef, &pb->pb_rootArea, TRUE);
|
|
freeMagic1(&mm1, (char *) pb);
|
|
}
|
|
freeMagic1_end(&mm1);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* 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(Tile *tile, TileType dinfo, ClientData clientdata)
|
|
{
|
|
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(<ypes, 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
|