magic/plow/PlowRules1.c

1029 lines
31 KiB
C

/*
* PlowRules1.c --
*
* Plowing rules.
* These are applied by plowProcessEdge() for each edge that is to be moved.
*
* *********************************************************************
* * 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/PlowRules1.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 "drc/drc.h"
/* Forward declarations */
int plowPenumbraTopProc();
int plowPenumbraBotProc();
int plowSliverTopExtent(), plowSliverTopMove();
int plowSliverBotExtent(), plowSliverBotMove();
int plowApplyRule();
int plowPenumbraRule();
bool plowSliverApplyRules();
/*
* ----------------------------------------------------------------------------
*
* prClearUmbra --
*
* SEARCH RULE.
* Sweep all edges out of the umbra.
* This rule preserves horizontal edge orderings within a plane.
* Two segments that overlap in Y cannot cross each other in X:
*
*
* 1 2
* 1 ------------> 2 1 will force 2 to move
* 1 2 regardless of design rules
*
*
* Below, this rule doesn't apply, so 1 can slide past 2.
*
* 1
* 1 ------------------>
* 1
* 2
* 2
* 2
*
* Results:
* None.
*
* Side effects:
* May add edges to the queue of edges to be processed.
*
* ----------------------------------------------------------------------------
*/
void
prClearUmbra(edge)
Edge *edge; /* Edge being moved */
{
TileTypeBitMask rhsTypes;
struct applyRule ar;
TTMaskSetOnlyType(&rhsTypes, edge->e_rtype);
ar.ar_moving = edge;
ar.ar_rule = (PlowRule *) NULL;
(void) plowSrShadow(edge->e_pNum, &edge->e_rect,
&rhsTypes, plowApplyRule, (ClientData) &ar);
}
/*
* ----------------------------------------------------------------------------
*
* prUmbra --
*
* SEARCH RULE.
* Apply width or spacing rules in the umbra.
* The umbra of an edge is the area through which it moves from its
* initial position (I) to its final position (F):
*
* I ======================F-------D
* I F D
* I umbra F halo D
* I F D
* I ======================F-------D
*
* --------------------> + --d-->
*
* For each rule in the list 'rules', we shadow-search the umbra plus an
* additional area of width 'd' to the right (ending at (D) above), where
* 'd' is the distance associated with the rule. We queue to be moved the
* edges of any material found that is not in the rule's pr_oktypes set.
*
* Results:
* None.
*
* Side effects:
* May add edges to the queue of edges to be processed.
*
* ----------------------------------------------------------------------------
*/
void
prUmbra(edge, rules)
Edge *edge; /* Edge being moved */
PlowRule *rules; /* List of rules */
{
PlowRule *pr;
struct applyRule ar;
Rect searchArea;
ar.ar_moving = edge;
searchArea = edge->e_rect;
for (pr = rules; pr; pr = pr->pr_next)
{
ar.ar_rule = pr;
searchArea.r_xtop = edge->e_newx + pr->pr_dist;
(void) plowSrShadow(pr->pr_pNum, &searchArea,
&pr->pr_oktypes, plowApplyRule, (ClientData) &ar);
}
}
/*
* ----------------------------------------------------------------------------
*
* prPenumbraTop --
* prPenumbraBot --
*
* Search the penumbra of the supplied edge for each rule in the list
* 'list', moving all edges necessary to insure that width or spacing
* rules are satisfied. The upper part of the penumbra is searched by
* plowPenumbraTop, and the lower part by plowPenumbraBot.
*
* The penumbra of an edge (E) is that region above and below it, in the
* direction the edge is moving. The left-hand border of the penumbra,
* and the distance it extends to the right of the final position of
* the edge (F), are equal to the distance associated with the design
* rule (d).
*
* ^ T===============|
* | T |
* d T |
* | TTTTTTTTT upper |
* v T=======================|
* E F
* E F
* E ------------> F <--d-->
* E F
* E F
* ^ BBBBBBBBB===============|
* | B lower |
* d BBBBBBBBB |
* | B |
* v B=======|
*
* The left-hand border of the penumbra (T, B) for a rule is not a
* simple extension of the original edge E, but is instead the result
* of following the outline of the material in the pr_ltypes set
* associated with that rule.
*
* If the outline (O) turns left before reaching the top (bottom) of the
* clip area, the remainder of the left-hand border (X) of the penumbra
* is considered to extend from where the outline turned left, out to
* the top (bottom) of the clip area:
*
* ^ X=======================+
* extension| X
* v X
* OOOOOOOOO
* O
* O
* OOOOOOOOO=======================
* E
* E
* E
*
* Results:
* None.
*
* Side effects:
* May add edges to the queue of edges to be processed.
*
* Algorithm notes:
* We use plowSrOutline() to trace the penumbra outlines. Since
* it moves counterclockwise around the outline of a collection of
* types, this is ideal for following the top penumbra's boundary.
* Ideally, we wish to follow the bottom penumbra's boundary in a
* clockwise direction instead. To achieve this, we follow the
* outline of the complementary set of types in a counterclockwise
* direction.
*
* ----------------------------------------------------------------------------
*/
void
prPenumbraTop(edge, rules)
Edge *edge; /* Edge being moved */
PlowRule *rules; /* Rules to apply (must be non-NULL) */
{
PlowRule *pr;
struct applyRule ar;
Point startPoint;
ar.ar_moving = edge;
startPoint.p_x = edge->e_x;
startPoint.p_y = edge->e_ytop;
for (pr = rules; pr; pr = pr->pr_next)
{
ar.ar_rule = pr;
ar.ar_clip.p_x = edge->e_newx + pr->pr_dist;
ar.ar_clip.p_y = edge->e_ytop + pr->pr_dist;
plowSrOutline(edge->e_pNum, &startPoint, &pr->pr_ltypes, GEO_NORTH,
GMASK_WEST|GMASK_NORTH|GMASK_SOUTH,
plowPenumbraTopProc, (ClientData) &ar);
}
}
int
prPenumbraBot(edge, rules)
Edge *edge; /* Edge being moved */
PlowRule *rules; /* Rules to apply (must be non-NULL) */
{
TileTypeBitMask insideTypes;
PlowRule *pr;
struct applyRule ar;
Point startPoint;
ar.ar_moving = edge;
startPoint.p_x = edge->e_x;
startPoint.p_y = edge->e_ybot;
for (pr = rules; pr; pr = pr->pr_next)
{
ar.ar_rule = pr;
ar.ar_clip.p_x = edge->e_newx + pr->pr_dist;
ar.ar_clip.p_y = edge->e_ybot - pr->pr_dist;
TTMaskCom2(&insideTypes, &pr->pr_ltypes);
plowSrOutline(edge->e_pNum, &startPoint, &insideTypes, GEO_SOUTH,
GMASK_WEST|GMASK_NORTH|GMASK_SOUTH,
plowPenumbraBotProc, (ClientData) &ar);
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* plowPenumbraTopProc --
* plowPenumbraBotProc --
*
* Process a vertical (GEO_NORTH or GEO_SOUTH) segment of the left-hand
* boundary of the penumbra, for purposes of applying a width or spacing
* rule. Width and spacing rules for the upper part of the penumbra are
* processed by plowPenumbraTopProc(); rules for the lower part are
* processed by plowPenumbraBotProc().
*
* We expect the following fields of the applyRule struct 'ar' to be filled in:
*
* ar_clip -- boundary of the penumbra. If a segment of the
* penumbra's outline extends to the right of this
* boundary point, we stop. If a segment extends
* above this point (if plowPenumbraTopProc), or
* below this point (if plowPenumbraBotProc), we stop.
* ar_moving -- edge being moved that caused this search.
* ar_rule -- design rule (width or spacing) being applied.
*
* When processing the upper penumbra, the "inside" material is to the left,
* and the "outside" to the right. When processing the lower penumbra, the
* situation is reversed because we are following the complementary material:
* "inside" is to the right and "outside" to the left. See the algorithm
* notes for prPenumbraTop() and prPenumbraBot() above.
*
* Results:
* 0 if plowSrOutline should keep going, 1 if not.
*
* Side effects:
* May add edges to the queue of edges to be processed.
*
* ----------------------------------------------------------------------------
*/
int
plowPenumbraTopProc(outline, ar)
Outline *outline; /* Segment along penumbra border */
struct applyRule *ar; /* Info needed for shadow search */
{
Edge *movingEdge = ar->ar_moving;
PlowRule *pr = ar->ar_rule;
Rect searchArea;
int ret = 0;
/*
* Test for immediate termination conditions:
* - a southward-bound edge
* - a northward-bound edge to the right of the clip point
*/
if (outline->o_currentDir == GEO_SOUTH
|| outline->o_rect.r_xbot >= ar->ar_clip.p_x)
return (1);
/*
* Test for termination after processing this edge:
* - a northward-bound edge touching or crossing the top
* of the clip area.
* If this is true, clip the search area to the top of the clip area.
*/
searchArea = outline->o_rect;
if (searchArea.r_ytop >= ar->ar_clip.p_y)
ret = 1, searchArea.r_ytop = ar->ar_clip.p_y;
searchArea.r_xtop = movingEdge->e_newx + pr->pr_dist;
/*
* If we just turned left and haven't reached the outside of
* the clip area, extend for one last shadow search.
*/
if (outline->o_currentDir == GEO_WEST)
{
if (outline->o_rect.r_ytop < ar->ar_clip.p_y)
{
searchArea.r_xbot = outline->o_rect.r_xtop - 1;
searchArea.r_ybot = outline->o_rect.r_ytop;
searchArea.r_ytop = ar->ar_clip.p_y;
(void) plowSrShadow(pr->pr_pNum,
&searchArea, &pr->pr_oktypes, plowPenumbraRule,
(ClientData) ar);
}
return (1);
}
/* Shadow search to right of this segment of the penumbra boundary */
(void) plowSrShadow(pr->pr_pNum, &searchArea,
&pr->pr_oktypes, plowApplyRule, (ClientData) ar);
return (ret);
}
int
plowPenumbraBotProc(outline, ar)
Outline *outline; /* Segment along penumbra border */
struct applyRule *ar; /* Info needed for shadow search */
{
Edge *movingEdge = ar->ar_moving;
PlowRule *pr = ar->ar_rule;
Rect searchArea;
int ret = 0;
/*
* Test for immediate termination conditions:
* - a northward-bound edge
* - a southward-bound edge to the right of the clip point
*/
if (outline->o_currentDir == GEO_NORTH
|| outline->o_rect.r_xbot >= ar->ar_clip.p_x)
return (1);
/*
* Test for termination after processing this edge:
* - a southward-bound edge touching or crossing the bottom
* of the clip area.
* If this is true, clip the search area to the bottom of the clip area.
*/
searchArea = outline->o_rect;
if (searchArea.r_ybot <= ar->ar_clip.p_y)
ret = 1, searchArea.r_ybot = ar->ar_clip.p_y;
searchArea.r_xtop = movingEdge->e_newx + pr->pr_dist;
/*
* If we just turned left and haven't reached the outside of
* the clip area, extend for one last shadow search.
*/
if (outline->o_currentDir == GEO_WEST)
{
if (outline->o_rect.r_ybot > ar->ar_clip.p_y)
{
searchArea.r_xbot = outline->o_rect.r_xtop - 1;
searchArea.r_ybot = ar->ar_clip.p_y;
searchArea.r_ytop = outline->o_rect.r_ybot;
(void) plowSrShadow(pr->pr_pNum,
&searchArea, &pr->pr_oktypes, plowPenumbraRule,
(ClientData) ar);
}
return (1);
}
/* Shadow search to right of this segment of the penumbra boundary */
(void) plowSrShadow(pr->pr_pNum, &searchArea,
&pr->pr_oktypes, plowApplyRule, (ClientData) ar);
return (ret);
}
/*
* plowPenumbraRule --
*
* Like plowApplyRule, except we don't queue an edge whose LHS
* is not in ar->ar_rule->pr_oktypes. This should prevent the
* penumbra extension search from finding bad edges.
*
* Results:
* Returns 0 always.
*
* Side effects:
* May queue the edge.
*/
int
plowPenumbraRule(impactedEdge, ar)
Edge *impactedEdge; /* Edge found by shadow search */
struct applyRule *ar; /* Edge causing the shadow search, and
* the design rule to apply.
*/
{
PlowRule *pr;
Edge *movingEdge = ar->ar_moving;
int newsep, oldsep, newx;
oldsep = impactedEdge->e_x - movingEdge->e_x;
if ((pr = ar->ar_rule))
{
if (!TTMaskHasType(&pr->pr_oktypes, impactedEdge->e_ltype))
return (0);
newsep = pr->pr_dist;
}
else newsep = 0;
if (oldsep < newsep)
newsep = oldsep;
/* Queue the edge if it hasn't already moved far enough */
newx = movingEdge->e_newx + newsep;
if (newx > impactedEdge->e_newx)
{
impactedEdge->e_newx = newx;
(void) (*plowPropagateProcPtr)(impactedEdge);
}
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* prSliverTop --
* prSliverBot --
*
* Avoid introducing slivers due to width or spacing rule violations.
* For each design rule in 'rules', we perform two passes. The first
* pass determines which slivers must move, and how far they must move.
* The second pass actually queues the edges to be moved.
*
* Results:
* Return 0 always
*
* Side effects:
* May add an edge to the queue of edges to be processed.
*
* ----------------------------------------------------------------------------
*/
int
prSliverTop(edge, rules)
Edge *edge;
PlowRule *rules;
{
PlowRule *pr;
struct applyRule ar;
Point startPoint;
/*
* The rules in the list 'rules' only determine the outline that will be
* traced, not the rules that will be applied to detect slivers. Hence
* the halo will depend on edge->e_ltype and the "sliver type" that will
* be stored in ar.ar_type. Since we don't know ar.ar_type at the start,
* we play conservative and just use the maximum halo size.
*/
if (plowMaxDist[edge->e_ltype] == 0)
return 0;
ar.ar_clip.p_x = edge->e_newx;
ar.ar_clip.p_y = edge->e_ytop + plowMaxDist[edge->e_ltype];
startPoint.p_x = edge->e_x;
startPoint.p_y = edge->e_ytop;
ar.ar_moving = edge;
/* We don't use ar.ar_rule */
for (pr = rules; pr; pr = pr->pr_next)
{
/*
* Pass 1.
* Find out how far slivers will have to move to the right.
* After the call to plowSrOutline, ar.ar_mustmove is set to
* the distance rightward we will have to move each sliver.
*/
ar.ar_slivtype = (TileType) -1;
ar.ar_lastx = ar.ar_mustmove = edge->e_x;
plowSrOutline(edge->e_pNum, &startPoint, &pr->pr_ltypes, GEO_NORTH,
GMASK_NORTH|GMASK_EAST|GMASK_SOUTH,
plowSliverTopExtent, (ClientData) &ar);
/*
* Pass 2.
* No work is required if there aren't any potential slivers.
* If we must do any work, move each sliver as far rightward
* as ar.ar_mustmove. This second pass doesn't use ar_slivtype,
* ar_lastx, or ar_clip.
*/
if (ar.ar_mustmove > edge->e_x)
plowSrOutline(edge->e_pNum, &startPoint, &pr->pr_ltypes, GEO_NORTH,
GMASK_SOUTH|GMASK_NORTH,
plowSliverTopMove, (ClientData) &ar);
}
return 0;
}
int
prSliverBot(edge, rules)
Edge *edge;
PlowRule *rules;
{
TileTypeBitMask insideTypes;
PlowRule *pr;
struct applyRule ar;
Point startPoint;
/*
* The rules in the list 'rules' only determine the outline that will be
* traced, not the rules that will be applied to detect slivers. Hence
* the halo will depend on edge->e_ltype and the "sliver type" that will
* be stored in ar.ar_type. Since we don't know ar.ar_type at the start,
* we play conservative and just use the maximum halo size.
*/
if (plowMaxDist[edge->e_ltype] == 0)
return 0;
ar.ar_clip.p_x = edge->e_newx;
ar.ar_clip.p_y = edge->e_ybot - plowMaxDist[edge->e_ltype];
startPoint.p_x = edge->e_x;
startPoint.p_y = edge->e_ybot;
ar.ar_moving = edge;
/* We don't use ar.ar_rule */
for (pr = rules; pr; pr = pr->pr_next)
{
/*
* Pass 1.
* Find out how far slivers will have to move to the right.
* After the call to plowSrOutline, ar.ar_mustmove is set to
* the distance rightward we will have to move each sliver.
*/
ar.ar_slivtype = (TileType) -1;
ar.ar_lastx = ar.ar_mustmove = edge->e_x;
TTMaskCom2(&insideTypes, &pr->pr_ltypes);
plowSrOutline(edge->e_pNum, &startPoint, &insideTypes, GEO_SOUTH,
GMASK_NORTH|GMASK_EAST|GMASK_SOUTH,
plowSliverBotExtent, (ClientData) &ar);
/*
* Pass 2.
* No work is required if there aren't any potential slivers.
* If we must do any work, move each sliver as far rightward
* as ar.ar_mustmove. This second pass doesn't use ar_slivtype,
* ar_lastx, or ar_clip.
*/
if (ar.ar_mustmove > edge->e_x)
plowSrOutline(edge->e_pNum, &startPoint, &insideTypes, GEO_SOUTH,
GMASK_SOUTH|GMASK_NORTH,
plowSliverBotMove, (ClientData) &ar);
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* plowSliverTopMove --
* plowSliverBotMove --
*
* After a first pass through plowSrOutline using plowSliverTopExtent()
* or plowSliverBotExtent() has filled in ar->ar_mustmove, we get called
* by a second pass of plowSrOutline to eliminate slivers by moving
* vertical edges in the outline.
*
* All slivers must be moved as far as ar->ar_mustmove. If we see a vertical
* edge as far as or farther right than ar->ar_mustmove, we are done.
* (We know from plowSliverTopExtent() or plowSliverBotExtent() that the final
* X coordinates of all vertical edges passed to this procedure are
* monotonically nondecreasing up to ar->ar_mustmove).
*
* If the vertical edge is going GEO_SOUTH instead of GEO_NORTH (in the
* case of plowSliverTopMove), or going GEO_NORTH instead of GEO_SOUTH
* (in the case of plowSliverBotMove), this is also a stopping condition.
*
* Results:
* Returns 1 to stop plowSrOutline from following the outline
* any more, or 0 to continue.
*
* Side effects:
* May add an edge to the queue of edges to be processed.
*
* ----------------------------------------------------------------------------
*/
int
plowSliverTopMove(outline, ar)
Outline *outline; /* Segment of outline being followed */
struct applyRule *ar;
{
int howfar = ar->ar_moving->e_newx - ar->ar_moving->e_x;
Edge edge;
/* Done if we turned south or if this edge is far enough to the right */
if (outline->o_currentDir == GEO_SOUTH
|| TRAILING(outline->o_outside) >= ar->ar_mustmove)
return (1);
/* Queue the edge to be moved */
edge.e_rect = outline->o_rect;
edge.e_newx = ar->ar_mustmove;
edge.e_ltype = TiGetTypeExact(outline->o_inside);
edge.e_rtype = TiGetTypeExact(outline->o_outside);
if (TTMaskHasType(&PlowFixedTypes, edge.e_rtype)
&& edge.e_newx > edge.e_x + howfar)
edge.e_newx = edge.e_x + howfar;
edge.e_pNum = outline->o_pNum;
edge.e_use = (CellUse *) NULL;
edge.e_flags = 0;
(void) (*plowPropagateProcPtr)(&edge);
/* Keep going */
return (0);
}
int
plowSliverBotMove(outline, ar)
Outline *outline; /* Segment of outline being followed.
* The sense of "inside" and "outside"
* is reversed from that when handling
* the top half of the penumbra.
*/
struct applyRule *ar;
{
int howfar = ar->ar_moving->e_newx - ar->ar_moving->e_x;
Edge edge;
/* Done if we turned north or if this edge is far enough to the right */
if (outline->o_currentDir == GEO_NORTH
|| TRAILING(outline->o_inside) >= ar->ar_mustmove)
return (1);
/* Queue the edge to be moved */
edge.e_rect = outline->o_rect;
edge.e_newx = ar->ar_mustmove;
edge.e_ltype = TiGetTypeExact(outline->o_outside);
edge.e_rtype = TiGetTypeExact(outline->o_inside);
if (TTMaskHasType(&PlowFixedTypes, edge.e_rtype)
&& edge.e_newx > edge.e_x + howfar)
edge.e_newx = edge.e_x + howfar;
edge.e_pNum = outline->o_pNum;
edge.e_use = (CellUse *) NULL;
edge.e_flags = 0;
(void) (*plowPropagateProcPtr)(&edge);
/* Keep going */
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowSliverTopExtent --
* plowSliverBotExtent --
*
* Called by plowSrOutline() to follow the upper/lower penumbra's
* outline(s), and see how much of that outline must be moved to
* avoid introducing slivers.
*
* Results:
* Returns 1 to stop plowSrOutline from following the outline
* any more, or 0 to continue.
*
* Side effects:
* Modifies the following fields of the applyRule struct pointed
* to by 'ar':
*
* ar_lastx -- continuously updated to the rightmost
* final X coordinate of any vertical
* edge. When a vertical edge is seen
* with a final X coordinate less than
* ar_lastx, we know we're done.
* ar_mustmove -- new X to which each sliver must be moved.
* ar_slivtype -- material forming the sliver itself.
*
* ----------------------------------------------------------------------------
*/
int
plowSliverTopExtent(outline, ar)
Outline *outline; /* Segment of outline being followed */
struct applyRule *ar;
{
Edge *movingEdge = ar->ar_moving;
int newx, xmove, ret = 0;
/*
* The direction in which we are following an outline
* segment generally determines how it is processed.
*
* If heading south, we are definitely done.
* If heading north, we check (by comparing with ar->ar_lastx)
* to make sure the outline's X coordinate is monotonically
* nondecreating.
* If heading east, we check to see if this segment of the outline
* causes a design-rule violation (i.e, if there is a sliver
* between movingEdge->e_ltype and outline->o_inside).
*
* A few exceptions arise because we are considering final instead
* of initial coordinates. These are both when heading north. See
* the code for details.
*/
switch (outline->o_currentDir)
{
case GEO_SOUTH:
/* Done if we turn back down */
return (1);
case GEO_NORTH:
/* Done if we turned left in final coordinates */
if (TRAILING(outline->o_outside) < ar->ar_lastx)
return (1);
/* Almost done if we exit the right or top of the clip area */
newx = TRAILING(outline->o_outside);
if (newx >= ar->ar_clip.p_x
|| outline->o_rect.r_ytop >= ar->ar_clip.p_y)
ret = 1;
/* Set the type to be used in applying the design rules */
if (outline->o_rect.r_ybot == movingEdge->e_ytop)
ar->ar_slivtype = TiGetTypeExact(outline->o_outside);
/*
* If this is not a special case where we have to check for
* a sliver, we're done.
*
* Normally, slivers are only checked for when going east.
* There are two exceptions, both because we are looking at
* final coordinates.
*
* 1. If we were going west and then turned north, check
* for a sliver with the bottom of the inside material.
*
* initial | ------------> | final
* +-------+
* |
*
* 2. If we were going north and kept going, and this segment
* is further right than the last one (in final coordinates),
* check for a sliver with the bottom of the inside material.
*
* initial | ------------> | final
* |
*/
if (outline->o_prevDir == GEO_WEST
|| (outline->o_prevDir == GEO_NORTH && newx > ar->ar_lastx))
{
ar->ar_lastx = newx;
xmove = MIN(newx, ar->ar_clip.p_x);
break;
}
/* We know newx >= ar->ar_lastx, so update the latter */
ar->ar_lastx = newx;
return (ret);
case GEO_EAST:
/* Almost done if we leave the clipping area */
if (outline->o_rect.r_xtop >= ar->ar_clip.p_x)
ret = 1;
/*
* Return if we haven't yet processed a vertical segment of
* the outline (i.e, no potential slivers have been seen yet).
*/
if (ar->ar_slivtype == (TileType) -1)
return (ret);
/* How far the penumbra LHS will have to move to avoid a sliver */
xmove = ar->ar_clip.p_x;
if (outline->o_nextDir == GEO_NORTH
&& TRAILING(outline->o_nextOut) < xmove)
xmove = TRAILING(outline->o_nextOut);
break;
}
/* Apply the plowing rules to see if this is a sliver */
if (plowSliverApplyRules(ar, TiGetTypeExact(outline->o_inside),
outline->o_rect.r_ybot - movingEdge->e_ytop))
ar->ar_mustmove = xmove;
return (ret);
}
int
plowSliverBotExtent(outline, ar)
Outline *outline; /* Segment of outline being followed */
struct applyRule *ar;
{
Edge *movingEdge = ar->ar_moving;
int newx, xmove, ret = 0;
/*
* The direction in which we are following an outline
* segment generally determines how it is processed.
*
* If heading north, we are definitely done.
* If heading south, we check (by comparing with ar->ar_lastx)
* to make sure the outline's X coordinate is monotonically
* nondecreating.
* If heading east, we check to see if this segment of the outline
* causes a design-rule violation (i.e, if there is a sliver
* between movingEdge->e_ltype and outline->o_outside).
*
* A few exceptions arise because we are considering final instead
* of initial coordinates. These are both when heading south. See
* the code for details.
*/
switch (outline->o_currentDir)
{
case GEO_NORTH:
/* Done if we turn back up */
return (1);
case GEO_SOUTH:
/* Done if we turned left in final coordinates */
if (TRAILING(outline->o_inside) < ar->ar_lastx)
return (1);
/* Almost done if we exit the right or bottom of the clip area */
newx = TRAILING(outline->o_inside);
if (newx >= ar->ar_clip.p_x
|| outline->o_rect.r_ybot <= ar->ar_clip.p_y)
ret = 1;
/* Set the type to be used in applying the design rules */
if (outline->o_rect.r_ytop == movingEdge->e_ybot)
ar->ar_slivtype = TiGetTypeExact(outline->o_inside);
/*
* If this is not a special case where we have to check for
* a sliver, we're done.
*
* Normally, slivers are only checked for when going east.
* There are two exceptions, both because we are looking at
* final coordinates.
*
* 1. If we were going west and then turned south, check
* for a sliver with the top of the outside material.
*
* |
* +-------+
* initial | ------------> | final
*
* 2. If we were going south and kept going, and this segment
* is further right than the last one (in final coordinates),
* check for a sliver with the top of the outside material.
*
* |
* initial | ------------> | final
*/
if (outline->o_prevDir == GEO_WEST
|| (outline->o_prevDir == GEO_SOUTH && newx > ar->ar_lastx))
{
ar->ar_lastx = newx;
xmove = MIN(newx, ar->ar_clip.p_x);
break;
}
/* We know newx >= ar->ar_lastx, so update the latter */
ar->ar_lastx = newx;
return (ret);
case GEO_EAST:
/* Almost done if we leave the clipping area */
if (outline->o_rect.r_xtop >= ar->ar_clip.p_x)
ret = 1;
/*
* Return if we haven't yet processed a vertical segment of
* the outline (i.e, no potential slivers have been seen yet).
*/
if (ar->ar_slivtype == (TileType) -1)
return (ret);
/* How far the penumbra LHS will have to move to avoid a sliver */
xmove = ar->ar_clip.p_x;
if (outline->o_nextDir == GEO_SOUTH
&& TRAILING(outline->o_nextIn) < xmove)
xmove = TRAILING(outline->o_nextIn);
break;
}
/* Apply the plowing rules to see if this is a sliver */
if (plowSliverApplyRules(ar, TiGetTypeExact(outline->o_outside),
movingEdge->e_ybot - outline->o_rect.r_ytop))
ar->ar_mustmove = xmove;
return (ret);
}
/*
* ----------------------------------------------------------------------------
*
* plowSliverApplyRules --
*
* Determine whether a sliver exists that must be moved. The configuration
* we are evaluating is:
*
*
* far
* --------
* ^
* |
* | farDist
* |
* s v
* --------
* near
*
* where s = ar->ar_slivtype and near = ar->ar_moving->e_ltype.
* We apply all the plowing rules triggered by the near|s edge
* (both width and spacing rules).
*
* Results:
* TRUE if the sliver must move, FALSE if not.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
bool
plowSliverApplyRules(ar, far, farDist)
struct applyRule *ar;
TileType far;
int farDist;
{
TileType near = ar->ar_moving->e_ltype;
PlowRule *pr;
for (pr = plowWidthRulesTbl[near][ar->ar_slivtype]; pr; pr = pr->pr_next)
if (pr->pr_dist > farDist && !TTMaskHasType(&pr->pr_oktypes, far))
return (TRUE);
for (pr = plowSpacingRulesTbl[near][ar->ar_slivtype]; pr; pr = pr->pr_next)
if (pr->pr_dist > farDist && !TTMaskHasType(&pr->pr_oktypes, far))
return (TRUE);
return (FALSE);
}
/*
* ----------------------------------------------------------------------------
*
* plowApplyRule --
*
* Add an edge ('impactedEdge') found by shadow search to the edge queue.
* Normally, this edge will be added at a new position equal to the new
* position of the original moving edge (ar->ar_moving), plus the distance
* associated with the design rule (ar->ar_rule->pr_dist). However, if
* the impacted edge were already closer than ar->ar_rule->pr_dist to the
* moving edge (i.e, there was a design-rule violation in the original
* layout), we use the original separation instead of the minimum separation.
*
* If the impacted edge has already moved far enough (impactedEdge->e_newx
* is far enough to the right), we don't add it.
*
* If ar->ar_rule is NULL, we assume a distance of zero.
*
* Results:
* Always returns 0.
*
* Side effects:
* May add an edge to the queue of edges to be processed.
*
* ----------------------------------------------------------------------------
*/
int
plowApplyRule(impactedEdge, ar)
Edge *impactedEdge; /* Edge found by shadow search */
struct applyRule *ar; /* Edge causing the shadow search, and
* the design rule to apply.
*/
{
Edge *movingEdge = ar->ar_moving;
int newsep, oldsep, newx;
oldsep = impactedEdge->e_x - movingEdge->e_x;
newsep = ar->ar_rule ? ar->ar_rule->pr_dist : 0;
if (oldsep < newsep)
newsep = oldsep;
/* Queue the edge if it hasn't already moved far enough */
newx = movingEdge->e_newx + newsep;
if (newx > impactedEdge->e_newx)
{
impactedEdge->e_newx = newx;
(void) (*plowPropagateProcPtr)(impactedEdge);
}
return (0);
}