magic/plow/PlowJogs.c

619 lines
17 KiB
C

/*
* PlowJogs.c --
*
* Jog cleanup.
*
* *********************************************************************
* * 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/PlowJogs.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif /* not lint */
#include <stdio.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 "plow/plow.h"
#include "plow/plowInt.h"
#include "utils/malloc.h"
#include "debug/debug.h"
/* Passed to filter functions */
Edge *jogEdge; /* Points to RHS edge of jog */
Rect *jogArea; /* Area in which jogs are eliminated, in
* plowYankDef coordinates.
*/
int jogTopDir, jogBotDir; /* Direction of top and bottom of the outline
* at the top and bottom of the jog candidate.
*/
Point jogTopPoint, jogBotPoint; /* Location of top and bot of above outline */
/* Used when applying plowing rules */
Rect *plowJogLHS; /* If non-NULL, it's also OK for any edge
* along this rectangle's LHS to move as
* a result of applying the plowing rules.
*/
bool plowJogMoved; /* TRUE if any other edges moved */
LinkedRect *plowJogEraseList; /* List of areas to erase */
Rect plowJogChangedArea; /* Area changed */
/*
* Codes for jogTopDir, jogBotDir above.
* For jogBotDir, we use the same codes, except "up" really means "down".
*/
#define J_N 0 /* Left via top of clip area */
#define J_NE 1 /* Left via RHS of clip area */
#define J_NW 2 /* Went up then turned left */
#define J_NES 3 /* Went up, turned right, then turned down */
#define J_NEN 4 /* Went up, turned right, then turned up */
/* Imports from PlowMain.c */
extern int plowInitialPaint();
/* Forward declarations */
int plowJogPropagateLeft();
int plowProcessJogFunc();
int plowJogMoveFunc();
int plowJogDragLHS();
int plowJogTopProc();
int plowJogBotProc();
extern void plowProcessJog();
/*
* ----------------------------------------------------------------------------
*
* plowCleanupJogs --
*
* Clean up all jogs in the interior of the Rect 'area', in the CellDef
* plowYankDef, by scanning from right to left and eliminating jogs if
* we can do so without moving any new edges.
*
* Results:
* None.
*
* Side effects:
* Modifies the geometry of plowYankDef in the area 'area'.
* Updates changedArea via GeoIncludes to reflect the area
* modified by jog reduction.
*
* ----------------------------------------------------------------------------
*/
void
plowCleanupJogs(area, changedArea)
Rect *area;
Rect *changedArea;
{
Edge edge;
/*
* We use our own procedure for propagating the effects of
* moving an edge. Instead of adding more edges to a queue,
* our procedure simply keeps track of whether any edges moved
* at all, since we won't want to eliminate a jog if it causes
* other stuff to move.
*/
plowPropagateProcPtr = plowJogMoveFunc;
/* Initialize the queue of edges to move */
plowQueueInit(area, area->r_xtop - area->r_xbot);
/* We store the area changed in a global for easy access */
plowJogChangedArea = *changedArea;
/*
* Process as though the RHS of the area were an edge.
* This means searching leftward for edges with material
* on their LHS and space on their RHS.
*/
edge.e_x = edge.e_newx = area->r_xtop;
edge.e_ybot = area->r_ybot;
edge.e_ytop = area->r_ytop;
edge.e_use = (CellUse *) NULL;
edge.e_flags = 0;
for (edge.e_pNum = PL_TECHDEPBASE; edge.e_pNum < DBNumPlanes; edge.e_pNum++)
plowProcessJog(&edge, area);
/* While edges remain, process them, and propagate leftward */
while (plowQueueRightmost(&edge))
plowProcessJog(&edge, area);
/* Clean up */
plowQueueDone();
*changedArea = plowJogChangedArea;
}
/*
* ----------------------------------------------------------------------------
*
* plowProcessJog --
*
* Process an edge between space and material for jog elimination.
* The idea is to search to the left of this edge for jogs that
* can be straightened without forcing other material to move.
* We repeat this, searching for a jog and eliminating it, until
* we can move no more jogs.
*
* When finally done, we search leftward for any edges between
* space and material, and add these to the queue to be processed.
*
* The argument 'area' bounds the area in which jog elimination
* will take place.
*
* Results:
* None.
*
* Side effects:
* Updates the geometry of plowYankDef.
*
* ----------------------------------------------------------------------------
*/
void
plowProcessJog(edge, area)
Edge *edge;
Rect *area;
{
Rect r;
if (DebugIsSet(plowDebugID, plowDebJogs))
plowDebugEdge(edge, (RuleTableEntry *) NULL, "plowProcessJog");
/* Scan left from this edge to the LHS of the jog reduction area */
r.r_xbot = area->r_xbot;
r.r_xtop = edge->e_x;
r.r_ybot = edge->e_ybot;
r.r_ytop = edge->e_ytop;
/*
* Scan leftward for edges between material and space. Such edges
* are potentially jogs that can be removed. If plowProcessJogFunc
* does in fact remove a jog containing one of the edges it finds,
* it returns 1 and aborts. We must therefore iterate until no more
* jogs are eliminated.
*/
while (plowSrShadowBack(edge->e_pNum, &r, DBSpaceBits,
plowProcessJogFunc, (ClientData) area))
/* Nothing */;
/* Scan to next edge between space and material */
(void) plowSrShadowBack(edge->e_pNum, &r, DBAllButSpaceBits,
plowJogPropagateLeft, (ClientData) NULL);
}
/*
* ----------------------------------------------------------------------------
*
* plowJogPropagateLeft --
*
* Called by plowSrShadowBack(), we add edges between space and material
* to the queue for further processing.
*
* Results:
* Always returns 0.
*
* Side effects:
* Adds the edge to the queue of edges to move via plowQueueAdd()
* if e_ltype == TT_SPACE and e_rtype != TT_SPACE.
* Leaves edge->e_newx == edge->e_x.
*
* ----------------------------------------------------------------------------
*/
int
plowJogPropagateLeft(edge)
Edge *edge;
{
if (DebugIsSet(plowDebugID, plowDebJogs))
plowDebugEdge(edge, (RuleTableEntry *) NULL, "plowJogPropagateLeft");
edge->e_newx = edge->e_x;
if (edge->e_ltype == TT_SPACE && edge->e_rtype != TT_SPACE)
(void) plowQueueAdd(edge);
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowProcessJogFunc --
*
* Do the real work of eliminating a jog.
* This procedure is called via plowSrShadowBack() for each non-space
* edge in the shadow of a space edge. Each edge we find will have
* e_ltype != TT_SPACE and e_rtype == TT_SPACE.
*
* We start following the outline of this edge; if it turns out that we
* can eliminate a jog, we do so and return 1 to stop the search; otherwise,
* we return 0 to force the search to continue.
*
* Results:
* Returns 1 if we eliminated a jog, 0 otherwise.
*
* Side effects:
* May update the geometry of plowYankDef.
*
* ----------------------------------------------------------------------------
*/
int
plowProcessJogFunc(edge, area)
Edge *edge; /* Edge found by shadow search */
Rect *area; /* Area in which jogs are being eliminated */
{
LinkedRect *lr;
Rect r, lhs;
TileTypeBitMask mask;
Point startPoint;
int width, ret;
Edge newedge;
Plane *plane;
if (DebugIsSet(plowDebugID, plowDebJogs))
plowDebugEdge(edge, (RuleTableEntry *) NULL, "plowProcessJogFunc");
TTMaskSetOnlyType(&mask, edge->e_ltype);
startPoint.p_x = edge->e_x;
jogEdge = edge;
jogArea = area;
/* Walk up the outline of which this edge is a part */
startPoint.p_y = edge->e_ytop;
jogTopPoint = startPoint;
jogTopDir = J_N;
plowSrOutline(edge->e_pNum, &startPoint, mask, GEO_NORTH,
GMASK_NORTH|GMASK_WEST|GMASK_EAST,
plowJogTopProc, (ClientData) NULL);
/* Walk down the outline of which this edge is a part */
TTMaskCom(&mask);
startPoint.p_y = edge->e_ybot;
jogBotPoint = startPoint;
jogBotDir = J_N;
plowSrOutline(edge->e_pNum, &startPoint, mask, GEO_SOUTH,
GMASK_SOUTH|GMASK_WEST|GMASK_EAST,
plowJogBotProc, (ClientData) NULL);
/* Try to reject this jog based solely on the geometry */
if (jogTopDir == J_N || jogBotDir == J_N) return (0);
if (jogTopDir != J_NEN && jogBotDir != J_NEN)
return (0);
/*
* More geometry-based rejection.
* Reject if we found either of the following configurations:
*
* +-----+ |
* | | |
* | |
* +-------+ +-------+
* | |
* | | |
* | +-----+
*/
if (jogTopDir == J_NES && jogTopPoint.p_x <= jogBotPoint.p_x)
return (0);
if (jogBotDir == J_NES && jogBotPoint.p_x <= jogTopPoint.p_x)
return (0);
/*
* Extend the edge to the full height of the jog.
* The jog will move as far as the leftmost of the two endpoints
* (jogTopPoint.p_x, jogBotPoint.p_x) if it is a C-shaped jog, or the
* farther of the two if it is a Z-shaped jog.
*/
newedge = *edge;
newedge.e_ybot = jogBotPoint.p_y;
newedge.e_ytop = jogTopPoint.p_y;
newedge.e_newx = (jogTopDir == J_NW || jogBotDir == J_NW)
? MAX(jogTopPoint.p_x, jogBotPoint.p_x)
: MIN(jogTopPoint.p_x, jogBotPoint.p_x);
jogEdge = &newedge;
if (DebugIsSet(plowDebugID, plowDebJogs))
plowDebugEdge(&newedge, (RuleTableEntry *) NULL, "jog extended edge");
/* Reject if this jog extends outside of the area */
if (!GEO_SURROUND(area, &newedge.e_rect))
return (0);
/*
* Apply the plowing rules.
* Don't do anything if moving the RHS edge will cause
* other edges to move.
*/
plowJogMoved = FALSE;
plowJogLHS = (Rect *) NULL;
plowApplySearchRules(&newedge);
if (plowJogMoved)
return (0);
/*
* Now handle the LHS.
* Find the width of this wire (looking leftward) and
* search for edges 'width' to the left of the RHS.
* Extend the search area by 'width' to the top or bottom
* depending on the shape of this jog, to be sure to catch
* all of the LHS of the jog.
*/
TTMaskSetOnlyType(&mask, edge->e_ltype);
width = plowFindWidthBack(&newedge, mask, area, (Rect *) NULL);
r.r_xtop = newedge.e_x;
r.r_xbot = newedge.e_x - width - 1;
r.r_ytop = newedge.e_ytop;
r.r_ybot = newedge.e_ybot;
if (jogTopDir != J_NW) r.r_ytop += width; /* Extend */
if (jogBotDir != J_NW) r.r_ybot -= width; /* Extend */
/* Reject if the LHS extends outside of area */
if (!GEO_SURROUND(area, &r))
return (0);
/* Also OK to move parts of the LHS (e.g, sliver elimination) */
lhs = r;
lhs.r_xbot++;
plowJogLHS = &lhs;
ret = 0;
plowJogEraseList = (LinkedRect *) NULL;
if (plowSrShadowBack(newedge.e_pNum, &r, mask,
plowJogDragLHS, (ClientData) newedge.e_newx - width) == 0)
{
/* Success: first paint to extend the RHS of the jog */
plane = plowYankDef->cd_planes[newedge.e_pNum];
DBPaintPlane(plane, &newedge.e_rect,
DBWriteResultTbl[newedge.e_ltype], (PaintUndoInfo *) NULL);
(void) GeoInclude(&newedge.e_rect, &plowJogChangedArea);
/* Now erase to extend the pieces of the LHS of the jog */
for (lr = plowJogEraseList; lr; lr = lr->r_next)
{
DBPaintPlane(plane, &lr->r_r,
DBWriteResultTbl[TT_SPACE], (PaintUndoInfo *) NULL);
(void) GeoInclude(&lr->r_r, &plowJogChangedArea);
}
ret = 1;
}
/* Free the erase list we built in plowJogDragLHS */
for (lr = plowJogEraseList; lr; lr = lr->r_next)
freeMagic((char *) lr);
plowJogEraseList = (LinkedRect *) NULL;
return (ret);
}
/*
* ----------------------------------------------------------------------------
*
* plowJogTopProc --
* plowJogBotProc --
*
* Procedures called by plowSrOutline() to trace the outline of
* a potential jog.
*
* Results:
* Both procedures return 1 to stop the search, 0 to continue it.
*
* Side effects:
* plowJogTopProc fills in jogTopDir, jogTopPoint.
* plowJogBotProc fills in jogBotDir, jogBotPoint.
*
* ----------------------------------------------------------------------------
*/
int
plowJogTopProc(outline)
Outline *outline;
{
/* Stop if we're no longer adjacent to space */
if (TiGetTypeExact(outline->o_outside) != TT_SPACE)
return (1);
switch (outline->o_currentDir)
{
case GEO_WEST:
jogTopDir = J_NW;
return (1);
case GEO_NORTH:
jogTopPoint = outline->o_rect.r_ur;
jogTopDir = J_N;
if (outline->o_rect.r_ytop > jogArea->r_ytop)
{
jogTopPoint.p_y = jogArea->r_ytop;
jogTopDir = J_N;
return (1);
}
break;
case GEO_EAST:
jogTopPoint = outline->o_rect.r_ur;
jogTopDir = J_NE;
if (outline->o_rect.r_xtop >= jogArea->r_xtop)
{
jogTopPoint.p_x = jogArea->r_xtop;
jogTopDir = J_NE;
return (1);
}
switch (outline->o_nextDir)
{
case GEO_NORTH:
jogTopDir = J_NEN;
return (1);
case GEO_SOUTH:
jogTopDir = J_NES;
return (1);
}
break;
}
return (0);
}
int
plowJogBotProc(outline)
Outline *outline;
{
/* Stop if we're no longer adjacent to space */
if (TiGetTypeExact(outline->o_inside) != TT_SPACE)
return (1);
switch (outline->o_currentDir)
{
case GEO_WEST:
jogBotDir = J_NW;
return (1);
case GEO_SOUTH:
jogBotPoint = outline->o_rect.r_ll;
jogBotDir = J_N;
if (outline->o_rect.r_ybot < jogArea->r_ybot)
{
jogBotPoint.p_y = jogArea->r_ybot;
jogBotDir = J_N;
return (1);
}
break;
case GEO_EAST:
jogBotPoint = outline->o_rect.r_ur;
jogBotDir = J_NE;
if (outline->o_rect.r_xtop >= jogArea->r_xtop)
{
jogBotPoint.p_x = jogArea->r_xtop;
jogBotDir = J_NE;
return (1);
}
/* Note that these directions are reversed from plowJogTopProc */
switch (outline->o_nextDir)
{
case GEO_NORTH:
jogBotDir = J_NES;
return (1);
case GEO_SOUTH:
jogBotDir = J_NEN;
return (1);
}
break;
}
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowJogDragLHS --
*
* Procedure called via plowSrShadowBack() from the RHS of a potential
* jog, to see if dragging each of the pieces of the LHS will cause
* any other edges to move. We ignore 'edge' if it doesn't have space
* to its left.
*
* Results:
* Returns 1 if we detect that something else must move, 0 otherwise.
*
* Side effects:
* Prepends a LinkedRect to plowJogEraseList if we return 0.
* This LinkedRect has a rectangle equal to the Edge 'edge'
* with e_newx == newx.
*
* ----------------------------------------------------------------------------
*/
int
plowJogDragLHS(edge, newx)
Edge *edge; /* Edge potentially on the LHS of the jog */
int newx; /* Move the edge to this position */
{
LinkedRect *lr;
/* Ignore edges that are not to space */
if (edge->e_ltype != TT_SPACE) return (0);
/* See what will happen if we move this edge right to newx */
edge->e_newx = newx;
plowJogMoved = FALSE;
plowApplySearchRules(edge);
if (plowJogMoved)
return (1);
/*
* Success: nothing else (except perhaps the original RHS edge) moved.
* Append this edge to the list of linked rectangles.
*/
lr = (LinkedRect *) mallocMagic((unsigned) (sizeof (LinkedRect)));
lr->r_r = edge->e_rect;
lr->r_next = plowJogEraseList;
plowJogEraseList = lr;
/* Keep going */
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowJogMoveFunc --
*
* Procedure called via (*plowPropagateProcPtr)() if we find that
* eliminating a jog might cause other edges to move. If our argument
* edge is not a sub-piece of the RHS of the jog (jogEdge),
* then we set plowJogMoved to TRUE.
*
* Results:
* Returns 0 always.
*
* Side effects:
* Sets plowJogMoved as described above.
*
* ----------------------------------------------------------------------------
*/
int
plowJogMoveFunc(edge)
Edge *edge;
{
Edge *origEdge = jogEdge;
if (DebugIsSet(plowDebugID, plowDebJogs))
plowDebugEdge(edge, (RuleTableEntry *) NULL, "plowJogMoveFunc");
if (origEdge->e_pNum == edge->e_pNum)
{
/* It's OK to move the original edge */
if (origEdge->e_x == edge->e_x
&& origEdge->e_ytop >= edge->e_ytop
&& origEdge->e_ybot <= edge->e_ybot)
{
return (0);
}
/* It's OK to move other parts of the LHS, e.g, slivers */
if (plowJogLHS
&& edge->e_x == plowJogLHS->r_xbot
&& edge->e_ybot >= plowJogLHS->r_ybot
&& edge->e_ytop <= plowJogLHS->r_ytop
&& edge->e_ltype == TT_SPACE
&& edge->e_rtype == origEdge->e_ltype)
{
return (0);
}
}
plowJogMoved = TRUE;
return (0);
}