magic/plow/PlowSearch.c

1318 lines
35 KiB
C
Raw Normal View History

/*
* PlowSearch.c --
*
* Plowing.
* Shadow and other searches.
*
* *********************************************************************
* * 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/PlowSearch.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 "plow/plowInt.h"
#include "utils/stack.h"
#include "textio/textio.h"
/* Argument used in shadow search */
struct shadow
{
Rect s_area; /* Area being searched */
TileTypeBitMask s_okTypes; /* Complement of this set is the RHS
* boundary of the shadow.
*/
Edge s_edge; /* Edge being built up */
int (*s_proc)(); /* Filter procedure */
ClientData s_cdata; /* Additional argument to (*s_proc)() */
};
/* Stack used by plowSrOutline */
Stack *plowOutlineStack = NULL;
#define FLUSHSTACK(s) while (STACKLOOK(s)) (void) STACKPOP(s)
/* Outline tracing: is tile inside the outline */
#define IsInside(tp, out) TTMaskHasType(&(out)->o_insideTypes, TiGetTypeExact(tp))
/* Forward declarations */
extern void plowSrOutlineInit();
extern void plowSrOutlineNext();
/*
* ----------------------------------------------------------------------------
*
* EXTENDOUTLINE --
*
* EXTENDOUTLINE(outline)
* Outline *outline;
* {
* }
*
* Used to extend outline->o_nextRect in the direction being followed,
* based on the tiles outline->o_nextIn and outline->o_nextOut.
*
* Assumes that outline->o_nextRect is initially a degenerate box equal
* to the starting point of the segment. We update exactly one coordinate
* of outline->o_nextRect, depending on the direction outline->o_nextDir:
*
* GEO_NORTH r_ytop
* GEO_SOUTH r_ybot
* GEO_EAST r_xtop
* GEO_WEST r_xbot
*
* Results:
* None.
*
* Side effects:
* Updates outline->o_nextRect as described above.
*
* ----------------------------------------------------------------------------
*/
#define EXTENDOUTLINE(o) { \
Tile *in = (o)->o_nextIn; \
Tile *out = (o)->o_nextOut; \
switch ((o)->o_nextDir) \
{ \
case GEO_NORTH: (o)->o_nextRect.r_ytop=MIN(TOP(out),TOP(in)); break; \
case GEO_SOUTH: (o)->o_nextRect.r_ybot=MAX(BOTTOM(out),BOTTOM(in)); break; \
case GEO_EAST: (o)->o_nextRect.r_xtop=MIN(RIGHT(in),RIGHT(out)); break; \
case GEO_WEST: (o)->o_nextRect.r_xbot=MAX(LEFT(in),LEFT(out)); break; \
} \
}
/*
* ----------------------------------------------------------------------------
*
* STACKOUTLINE --
*
* STACKOUTLINE(outline)
* Outline *outline;
*
* Called whenever we advance to a new inside/outside tile to
* load the tile stack with the tiles on the opposite side of
* the tile we just advanced to.
*
* The tile stack contains all the tiles along the outside of
* outline->o_nextIn if we are going up or down, or all the
* tiles along the inside of outline->o_nextOut if we are going
* going left or right.
*
* Flushes the tile stack before we push any tiles of our own.
*
* Results:
* None.
*
* Side effects:
* May push tiles on plowOutlineStack.
* Sets outline->o_nextIn if going east or west.
* Sets outline->o_nextOut if going north or south.
*
* ----------------------------------------------------------------------------
*/
#define STACKOUTLINE(o) { \
Tile *tp; \
\
FLUSHSTACK(plowOutlineStack); \
switch ((o)->o_nextDir) \
{ \
case GEO_NORTH: \
for (tp = TR((o)->o_nextIn); \
BOTTOM(tp) > (o)->o_nextRect.r_ybot; tp = LB(tp)) \
STACKPUSH((ClientData) tp, plowOutlineStack); \
(o)->o_nextOut = tp; \
break; \
case GEO_SOUTH: \
for (tp = BL((o)->o_nextIn); \
TOP(tp) < (o)->o_nextRect.r_ytop; tp = RT(tp)) \
STACKPUSH((ClientData) tp, plowOutlineStack); \
(o)->o_nextOut = tp; \
break; \
case GEO_EAST: \
for (tp = RT((o)->o_nextOut); \
LEFT(tp) > (o)->o_nextRect.r_xbot; tp = BL(tp)) \
STACKPUSH((ClientData) tp, plowOutlineStack); \
(o)->o_nextIn = tp; \
break; \
case GEO_WEST: \
for (tp = LB((o)->o_nextOut); \
RIGHT(tp) < (o)->o_nextRect.r_xtop; tp = TR(tp)) \
STACKPUSH((ClientData) tp, plowOutlineStack); \
(o)->o_nextIn = tp; \
break; \
} \
}
/*
* ----------------------------------------------------------------------------
*
* plowSrShadow --
*
* Shadow search.
* Searches area from left to right for edges whose far side is not in
* okTypes. The edges we find will have exactly one tile on each side.
*
* This is similar to an area search, except we call the procedure with
* edges instead of tiles:
*
* int (*proc)(edge, cdata)
* Edge *edge;
* ClientData cdata;
* {
* }
*
* The Edge specifies the initial X location of the edge (e_x), the final
* X location (e_newx, taken from the TRAILING coordinate of the tile on the
* edge's right-hand side), and the edge's top and bottom (e_ytop, e_ybot).
* It also contains the plane on which this edge was found (our argument
* 'plane'), and the types of the tiles on each side of the edge (e_ltype
* and e_rtype).
*
* The client is allowed to modify e_newx, but none of the other fields
* of the Edge.
*
* If the procedure returns 1, we abort the shadow search.
*
* Results:
* Returns 1 if aborted, 0 if the search completed normally.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
int
plowSrShadow(
int pNum, /* Plane from plowYankDef to search */
Rect *area, /* Area to search. Edges coincident with the
* right-hand side of this area are not seen;
* they must lie to the left of area->r_xtop.
*/
const TileTypeBitMask *okTypesp,
int (*proc)(), /* Function to apply at each edge */
ClientData cdata) /* Additional argument to pass to (*proc)() */
{
TileTypeBitMask okTypes = *okTypesp; /* TTMaskCopy(&okTypes, okTypesp) */
Plane *plane = plowYankDef->cd_planes[pNum];
struct shadow s;
Tile *tp;
int bottom, ret;
Point p;
ret = 0;
/* Copy our arguments into 's' */
s.s_area = *area;
s.s_okTypes = okTypes;
s.s_proc = proc;
s.s_cdata = cdata;
s.s_edge.e_use = (CellUse *) NULL;
s.s_edge.e_flags = 0;
s.s_edge.e_pNum = pNum;
s.s_edge.e_ytop = s.s_area.r_ytop;
/* Walk along the LHS of the sweep area from top to bottom */
p.p_x = s.s_area.r_xbot;
p.p_y = s.s_area.r_ytop - 1;
tp = PlaneGetHint(plane);
while (p.p_y >= s.s_area.r_ybot)
{
/* Find the next tile along the LHS of the sweep area */
GOTOPOINT(tp, &p);
p.p_y = BOTTOM(tp) - 1;
bottom = MAX(BOTTOM(tp), s.s_area.r_ybot);
if (RIGHT(tp) >= s.s_area.r_xtop)
{
s.s_edge.e_ytop = bottom;
}
else if (plowShadowRHS(tp, &s, bottom))
{
ret = 1;
break;
}
}
PlaneSetHint(plane, tp);
return (ret);
}
/*
* ----------------------------------------------------------------------------
*
* plowShadowRHS --
*
* Walk along the right-hand side of tile 'tp', enumerating all edges formed
* with tiles whose RHS is not a member of s->s_okTypes, and whose tops are
* larger than 'bottomLeft'.
*
* Results:
* Returns 0 normally, but returns 1 if a client decided to
* interrupt the search by returning 1.
*
* Side effects:
* May call the filter function.
* Modifies s->s_edge.
*
* ----------------------------------------------------------------------------
*/
int
plowShadowRHS(tp, s, bottomLeft)
Tile *tp; /* Tile whose RHS is to be followed */
struct shadow *s; /* Shadow search argument */
int bottomLeft; /* Bottom of 'tp', clipped to area */
{
Tile *tpR;
int bottom, left;
/* Walk along the RHS of 'tp' from top to bottom */
tpR = TR(tp), left = LEFT(tpR);
do
{
/*
* Only process tiles between s->s_edge.e_ytop (on the top)
* and bottomLeft (on the bottom).
*/
bottom = MAX(bottomLeft, BOTTOM(tpR));
if (bottom < s->s_edge.e_ytop)
{
/* If tpR is not in okTypes, pass the edge to the client function */
if (!TTMaskHasType(&s->s_okTypes, TiGetTypeExact(tpR)))
{
/* Left, right hand types */
s->s_edge.e_ltype = TiGetTypeExact(tp);
s->s_edge.e_rtype = TiGetTypeExact(tpR);
/* Coordinates */
s->s_edge.e_x = left;
s->s_edge.e_newx = TRAILING(tpR);
s->s_edge.e_ybot = bottom;
if ((*s->s_proc)(&s->s_edge, s->s_cdata))
return (1);
/* Skip to bottom of the edge we just processed */
s->s_edge.e_ytop = s->s_edge.e_ybot;
}
else if (RIGHT(tpR) >= s->s_area.r_xtop)
{
/* Skip this edge segment */
s->s_edge.e_ytop = bottom;
}
else
{
if (plowShadowRHS(tpR, s, bottom))
return (1);
/* Here s->s_edge.e_ytop == bottom */
}
}
tpR = LB(tpR);
}
while (TOP(tpR) > bottomLeft);
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowSrShadowInitial --
*
* Shadow search.
* Searches area from left to right for edges whose far side is not in
* okTypes, or whose near side is not in okTypes, and for which both
* sides are different types.
*
* See plowSrShadow() above for more details.
*
* Results:
* Returns 1 if aborted, 0 if the search completed normally.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
int
plowSrShadowInitial(
int pNum, /* Plane from plowYankDef to search */
Rect *area, /* Area to search. Edges coincident with the
* right-hand side of this area are not seen;
* they must lie to the left of area->r_xtop.
*/
const TileTypeBitMask *okTypesp,
int (*proc)(), /* Function to apply at each edge */
ClientData cdata) /* Additional argument to pass to (*proc)() */
{
TileTypeBitMask okTypes = *okTypesp; /* TTMaskCopy(&okTypes, okTypesp) */
Plane *plane = plowYankDef->cd_planes[pNum];
struct shadow s;
Tile *tp;
int bottom, ret;
Point p;
ret = 0;
/* Copy our arguments into 's' */
s.s_area = *area;
s.s_okTypes = okTypes;
s.s_proc = proc;
s.s_cdata = cdata;
s.s_edge.e_use = (CellUse *) NULL;
s.s_edge.e_flags = 0;
s.s_edge.e_pNum = pNum;
s.s_edge.e_ytop = s.s_area.r_ytop;
/* Walk along the LHS of the sweep area from top to bottom */
p.p_x = s.s_area.r_xbot;
p.p_y = s.s_area.r_ytop - 1;
tp = PlaneGetHint(plane);
while (p.p_y >= s.s_area.r_ybot)
{
/* Find the next tile along the LHS of the sweep area */
GOTOPOINT(tp, &p);
p.p_y = BOTTOM(tp) - 1;
bottom = MAX(BOTTOM(tp), s.s_area.r_ybot);
if (RIGHT(tp) >= s.s_area.r_xtop)
{
s.s_edge.e_ytop = bottom;
}
else if (plowShadowInitialRHS(tp, &s, bottom))
{
ret = 1;
break;
}
}
PlaneSetHint(plane, tp);
return (ret);
}
/*
* ----------------------------------------------------------------------------
*
* plowShadowInitialRHS --
*
* Walk along the right-hand side of tile 'tp', enumerating all edges formed
* with tiles whose RHS is not a member of s->s_okTypes, or whose LHS is not
* a member of s->s_okTypes, and whose LHS and RHS are different, and whose
* tops are larger than 'bottomLeft'.
*
* Results:
* Returns 0 normally, but returns 1 if a client decided to
* interrupt the search by returning 1.
*
* Side effects:
* May call the filter function.
* Modifies s->s_edge.
*
* ----------------------------------------------------------------------------
*/
int
plowShadowInitialRHS(tp, s, bottomLeft)
Tile *tp; /* Tile whose RHS is to be followed */
struct shadow *s; /* Shadow search argument */
int bottomLeft; /* Bottom of 'tp', clipped to area */
{
Tile *tpR;
int bottom, left;
/* Walk along the RHS of 'tp' from top to bottom */
tpR = TR(tp), left = LEFT(tpR);
do
{
/*
* Only process tiles between s->s_edge.e_ytop (on the top)
* and bottomLeft (on the bottom).
*/
bottom = MAX(bottomLeft, BOTTOM(tpR));
if (bottom < s->s_edge.e_ytop)
{
/* If tpR is not in okTypes, pass the edge to the client function */
if (TiGetTypeExact(tp) != TiGetTypeExact(tpR)
&& (!TTMaskHasType(&s->s_okTypes, TiGetTypeExact(tpR))
|| !TTMaskHasType(&s->s_okTypes, TiGetTypeExact(tp))))
{
/* Left, right hand types */
s->s_edge.e_ltype = TiGetTypeExact(tp);
s->s_edge.e_rtype = TiGetTypeExact(tpR);
/* Coordinates */
s->s_edge.e_x = left;
s->s_edge.e_newx = TRAILING(tpR);
s->s_edge.e_ybot = bottom;
if ((*s->s_proc)(&s->s_edge, s->s_cdata))
return (1);
/* Skip to bottom of the edge we just processed */
s->s_edge.e_ytop = s->s_edge.e_ybot;
}
else if (RIGHT(tpR) >= s->s_area.r_xtop)
{
/* Skip this edge segment */
s->s_edge.e_ytop = bottom;
}
else
{
if (plowShadowRHS(tpR, s, bottom))
return (1);
/* Here s->s_edge.e_ytop == bottom */
}
}
tpR = LB(tpR);
}
while (TOP(tpR) > bottomLeft);
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowSrShadowBack --
*
* Shadow search, backwards.
* Searches area from right to left for edges whose far side is not in
* okTypes. The edges we find will have exactly one tile on each side.
*
* See the comments in plowSrShadow() for details on the client procedure.
*
* Results:
* Returns 1 if aborted, 0 if the search completed normally.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
int
plowSrShadowBack(
int pNum, /* Plane from plowYankDef to search */
Rect *area, /* Area to search. Edges coincident with the
* left-hand side of this area are not seen;
* they must lie to the right of area->r_xbot.
*/
const TileTypeBitMask *okTypesp,
int (*proc)(), /* Function to apply at each edge */
ClientData cdata) /* Additional argument to pass to (*proc)() */
{
TileTypeBitMask okTypes = *okTypesp; /* TTMaskCopy(&okTypes, okTypesp) */
Plane *plane = plowYankDef->cd_planes[pNum];
struct shadow s;
Tile *tp;
int top, ret;
Point p;
ret = 0;
/* Copy our arguments into 's' */
s.s_area = *area;
s.s_okTypes = okTypes;
s.s_proc = proc;
s.s_cdata = cdata;
s.s_edge.e_use = (CellUse *) NULL;
s.s_edge.e_flags = 0;
s.s_edge.e_pNum = pNum;
s.s_edge.e_ybot = s.s_area.r_ybot;
/* Walk along the RHS of the sweep area from bottom to top */
p.p_x = s.s_area.r_xtop - 1;
p.p_y = s.s_area.r_ybot;
tp = PlaneGetHint(plane);
while (p.p_y < s.s_area.r_ytop)
{
/* Find the next tile along the RHS of the sweep area */
GOTOPOINT(tp, &p);
p.p_y = TOP(tp);
top = MIN(TOP(tp), s.s_area.r_ytop);
if (LEFT(tp) <= s.s_area.r_xbot)
{
s.s_edge.e_ybot = top;
}
else if (plowShadowLHS(tp, &s, top))
{
ret = 1;
break;
}
}
/* if ret = 1 then tp may point to an invalid tile --- Tim 6/15/01 */
if (!ret) PlaneSetHint(plane, tp);
return (ret);
}
/*
* ----------------------------------------------------------------------------
*
* plowShadowLHS --
*
* Walk along the left-hand side of tile 'tp', enumerating all edges formed
* with tiles whose LHS is not a member of s->s_okTypes, and whose bottoms are
* less than 'topRight'.
*
* Results:
* Returns 0 normally, but returns 1 if a client decided to
* interrupt the search by returning 1.
*
* Side effects:
* May call the filter function.
* Modifies s->s_edge.
*
* ----------------------------------------------------------------------------
*/
int
plowShadowLHS(tp, s, topRight)
Tile *tp; /* Tile whose LHS is to be followed */
struct shadow *s; /* Shadow search argument */
int topRight; /* Top of 'tp', clipped to area */
{
Tile *tpL;
int top, right;
/* Walk along the LHS of 'tp' from bottom to top */
tpL = BL(tp), right = RIGHT(tpL);
do
{
/*
* Only process tiles between s->s_edge.e_ybot (on the bottom)
* and topRight (on the top).
*/
top = MIN(topRight, TOP(tpL));
if (top > s->s_edge.e_ybot)
{
/* If tpL is not in okTypes, pass the edge to the client function */
if (!TTMaskHasType(&s->s_okTypes, TiGetTypeExact(tpL)))
{
/* Left, right hand types */
s->s_edge.e_ltype = TiGetTypeExact(tpL);
s->s_edge.e_rtype = TiGetTypeExact(tp);
/* Coordinates */
s->s_edge.e_x = right;
s->s_edge.e_newx = TRAILING(tp);
s->s_edge.e_ytop = top;
if ((*s->s_proc)(&s->s_edge, s->s_cdata))
return (1);
/* Skip to top of the edge we just processed */
s->s_edge.e_ybot = s->s_edge.e_ytop;
}
else if (LEFT(tpL) <= s->s_area.r_xbot)
{
/* Skip this edge segment */
s->s_edge.e_ybot = top;
}
else
{
if (plowShadowLHS(tpL, s, top))
return (1);
/* Here s->s_edge.e_ybot == top */
}
}
tpL = RT(tpL);
}
while (BOTTOM(tpL) < topRight);
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowAtomize --
*
* Given a geometrical edge (the LHS of the rectangle 'rect'), call
* (*proc)() for each real Edge (between two tiles) found along
* the geometrical edge. Each Edge added will have a new x value of
* rect->r_xtop.
*
* The procedure (*proc)() should be the same kind (in arguments and
* return value) as that used in plowSrShadow() above.
*
* Don't call the procedure if the final coordinate of the tiles along
* a given Edge is already farther right than rect->r_xtop.
*
* Results:
* Returns 1 if (*proc)() returned 1 to abort the atomizing;
* otherwise, returns 0.
*
* Side effects:
* Whatever the client procedure has.
*
* ----------------------------------------------------------------------------
*/
int
plowAtomize(pNum, rect, proc, cdata)
int pNum; /* Plane from plowYankDef to search */
Rect *rect; /* LHS is the geometrical edge we search; each
* Edge found will have an e_newx coordinate
* equal to the RHS of this rect.
*/
int (*proc)(); /* Procedure to apply to each Edge */
ClientData cdata; /* Additional argument to (*proc)() */
{
Tile *tpL, *tpR;
Plane *plane = plowYankDef->cd_planes[pNum];
Point startPoint;
Edge edge;
int ytop;
edge.e_x = rect->r_xbot;
edge.e_newx = rect->r_xtop;
edge.e_use = (CellUse *) NULL;
edge.e_flags = 0;
edge.e_pNum = pNum;
ytop = rect->r_ytop;
startPoint.p_x = rect->r_xbot;
startPoint.p_y = rect->r_ytop - 1;
/* Walk down the RHS of the edge */
tpR = PlaneGetHint(plane);
GOTOPOINT(tpR, &startPoint);
PlaneSetHint(plane, tpR);
for ( ; TOP(tpR) > rect->r_ybot; tpR = LB(tpR))
{
/* Only process edges that haven't moved far enough */
if (TRAILING(tpR) < rect->r_xtop)
{
edge.e_rtype = TiGetTypeExact(tpR);
edge.e_ybot = MAX(BOTTOM(tpR), rect->r_ybot);
/* Walk up the LHS of the edge */
for (tpL = BL(tpR); BOTTOM(tpL) < ytop; tpL = RT(tpL))
{
/* Only process edges above the bottom of the clip area */
if (TOP(tpL) > edge.e_ybot)
{
edge.e_ytop = MIN(ytop, TOP(tpL));
edge.e_ltype = TiGetTypeExact(tpL);
if ((*proc)(&edge, cdata))
return (1);
edge.e_ybot = edge.e_ytop;
}
}
}
/* Advance the top of the clipping are downward */
ytop = BOTTOM(tpR);
}
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowSrOutline --
*
* Outline enumeration.
* This procedure follows the outline of a collection of materials in a
* counter-clockwise direction. For each edge found while moving in a
* direction specified in dirMask, call a supplied client procedure:
*
* (*proc)(outline, cdata)
* Outline *outline;
* ClientData cdata;
* {
* }
*
* The bits in dirMask are GMASK_SOUTH, GMASK_NORTH, GMASK_EAST,
* or GMASK_WEST. They may be or'd together in any combination.
*
* The Outline gives a degenerate rectangle for this segment of the
* outline. Either this rectangle has zero height, or zero width.
* It also contains the plane on which this segment was found (our
* argument 'plane'), and pointers to the tiles on each side of the
* outline (o_inside, o_outside). Finally, it contains the direction
* followed before this segment (o_prevDir), the direction being
* followed along this segment (o_currentDir), and the direction to
* be followed after this segment (o_nextDir).
*
* If the procedure returns 1, we abort the outline enumeration.
* Otherwise, we keep going indefinitely.
*
* Results:
* None.
*
* Side effects:
* Whatever occur as a result of applying the supplied client
* procedure.
*
* Assumptions:
* The interior of the region whose perimeter is being traced
* cannot extend to infinity in any direction. Hence it is unwise
* to try tracing the perimeter of a region consisting of empty
* space, unless the client can be counted on to abort the search
* by returning 1.
*
* ----------------------------------------------------------------------------
*/
void
plowSrOutline(
int pNum, /* Plane # in plowYankDef to search */
Point *startPoint, /* Point on boundary; material of types
* in insideTypes should be to the
* inside (as determined by initialDir
* below).
*/
const TileTypeBitMask *insideTypesp,/* Mask of types inside the region
* whose outline is being traced.
*/
int initialDir, /* Initial direction to go from the
* starting point. One of GEO_NORTH,
* or GEO_SOUTH.
*/
int dirMask, /* Mask of those directions for which
* we will call the client procedure.
*/
int (*proc)(), /* Client procedure */
ClientData cdata) /* Argument to client procedure */
{
TileTypeBitMask insideTypes = *insideTypesp; /* TTMaskCopy(&insideTypes, insideTypesp) */
Outline outline;
/*
* Initialize the stack used for processing tiles
* along the outside of the perimeter.
*/
if (plowOutlineStack == (Stack *) NULL)
plowOutlineStack = StackNew(50);
/*
* Push a bottom marker on the stack.
* This allows plowSrOutline to be re-entrant.
*/
STACKPUSH((ClientData) NULL, plowOutlineStack);
/*
* Start out going in the direction specified by initialDir.
* This direction is really just a hint; we may go either 90 degrees
* to the left or to the right if appropriate.
*/
outline.o_pNum = pNum;
outline.o_insideTypes = insideTypes;
outline.o_currentDir = initialDir;
outline.o_rect.r_ll = outline.o_rect.r_ur = *startPoint;
plowSrOutlineInit(&outline);
EXTENDOUTLINE(&outline);
/*
* Now we have found the initial segment and are ready for the main loop.
* On entry to each iteration of this loop, o_currentDir and o_nextDir
* are set from the previous iteration; we advance by one (current becomes
* prev, next becomes current) and find the next direction, etc. before
* calling the client procedure.
*/
for (;;)
{
/* Advance along the boundary */
outline.o_prevDir = outline.o_currentDir;
outline.o_currentDir = outline.o_nextDir;
outline.o_inside = outline.o_nextIn;
outline.o_outside = outline.o_nextOut;
outline.o_rect = outline.o_nextRect;
/* Find outline.o_nextDir and next two tiles along boundary */
plowSrOutlineNext(&outline);
EXTENDOUTLINE(&outline);
/* Now we know the next direction: apply the filter procedure */
if (GMaskHasDir(dirMask, outline.o_currentDir))
if ((*proc)(&outline, cdata))
break;
}
/* Flush anything remaining on the stack */
while (STACKPOP(plowOutlineStack) != (ClientData) NULL)
/* Nothing */;
}
/*
* ----------------------------------------------------------------------------
*
* plowSrOutlineInit --
*
* Initialization for outline enumeration.
* Find the first edge in outline enumeration.
* On entry, o_rect should be degenerate (ll == ur) around the starting
* point for the search. This starting point should be on the outline.
* The direction o_currentDir should be one of GEO_NORTH or GEO_SOUTH; we
* pretend as though we arrived at the starting point from this direction.
*
* Results:
* None.
*
* Side effects:
* Fills in fields of 'outline':
* o_nextIn, o_nextOut -- tiles on inside and outside of
* current edge.
* o_nextDir -- direction of current edge; this will
* differ by at most 90 degrees from
* o_currentDir.
* o_nextRect -- current edge itself.
*
* ----------------------------------------------------------------------------
*/
void
plowSrOutlineInit(outline)
Outline *outline;
{
Plane *plane = plowYankDef->cd_planes[outline->o_pNum];
Tile *in, *out;
Point p;
outline->o_nextDir = outline->o_currentDir;
outline->o_nextRect = outline->o_rect;
switch (outline->o_nextDir)
{
case GEO_NORTH:
/*
* Find the tiles on either side of the starting point (+):
*
* |
* in | out
* --------+--------
* |
* |
*
* It is possible for 'in' and 'out' to be the same tile.
*/
out = PlaneGetHint(plane);
p = outline->o_rect.r_ll;
GOTOPOINT(out, &p);
p.p_x--;
in = out;
GOTOPOINT(in, &p);
if (!IsInside(in, outline))
{
/*
* Tile 'in' is really outside the outline, so go left.
* We know that BOTTOM(in) == outline->o_rect.r_xbot because
* the starting point is on the outline. We want:
* |
* out |
* --------+--------
* in |
* |
*/
outline->o_nextDir = GEO_WEST;
outline->o_nextOut = in;
}
else if (IsInside(out, outline))
{
/*
* Tile 'out' is really inside the outline, so go right.
* We know that TOP(out) == outline->o_rect.r_xbot because
* the starting point is on the outline. We want:
* |
* | in
* --------+--------
* | out
* |
*/
outline->o_nextDir = GEO_EAST;
for (out = LB(out);
RIGHT(out) <= outline->o_rect.r_xbot; out = TR(out))
/* Nothing */;
outline->o_nextOut = out;
}
else
{
/*
* Tile 'in' is really inside the outline, and tile 'out'
* is really outside, so we already are where we want to be.
*/
outline->o_nextIn = in;
}
break;
case GEO_SOUTH:
/*
* Find the tiles on either side of the starting point (+):
*
* |
* |
* --------+--------
* out | in
* |
*
* It is possible for 'in' and 'out' to be the same tile.
*/
p.p_x = outline->o_rect.r_xbot - 1;
p.p_y = outline->o_rect.r_ybot - 1;
out = PlaneGetHint(plane);
GOTOPOINT(out, &p);
p.p_x++;
in = out;
GOTOPOINT(in, &p);
if (!IsInside(in, outline))
{
/*
* Tile 'in' is really outside the outline, so go right.
* We know that TOP(in) == outline->o_rect.r_xbot because
* the starting point is on the outline. We want:
* |
* | in
* --------+--------
* | out
* |
*/
outline->o_nextDir = GEO_EAST;
outline->o_nextOut = in;
}
else if (IsInside(out, outline))
{
/*
* Tile 'out' is really inside the outline, so go left.
* We know that TOP(out) == outline->o_rect.r_xbot because
* the starting point is on the outline. We want:
* |
* out |
* --------+--------
* in |
* |
*/
outline->o_nextDir = GEO_WEST;
for (out = RT(out);
LEFT(out) >= outline->o_rect.r_xbot; out = BL(out))
/* Nothing */;
outline->o_nextOut = out;
}
else
{
/*
* Tile 'in' is really inside the outline, and tile 'out'
* is really outside, so we already are where we want to be.
*/
outline->o_nextIn = in;
}
break;
default:
TxError("Illegal initialDir (%d) for plowSrOutline\n",
outline->o_nextDir);
niceabort();
return;
}
STACKOUTLINE(outline);
}
/*
* ----------------------------------------------------------------------------
*
* plowSrOutlineNext --
*
* Find the next edge in outline enumeration.
*
* Results:
* None.
*
* Side effects:
* Fills in fields of 'outline':
* o_nextIn, o_nextOut -- tiles on inside and outside of
* next edge.
* o_nextDir -- direction of next edge.
* o_nextRect -- next edge's rectangle
*
* ----------------------------------------------------------------------------
*/
void
plowSrOutlineNext(outline)
Outline *outline;
{
Tile *tpL, *tpR;
/*
* Initialize the "next" segment to be the degenerate
* endpoint of the current segment.
*/
outline->o_nextDir = outline->o_currentDir;
switch (outline->o_currentDir)
{
case GEO_NORTH:
case GEO_EAST:
outline->o_nextRect.r_ur = outline->o_rect.r_ur;
outline->o_nextRect.r_ll = outline->o_rect.r_ur;
break;
case GEO_SOUTH:
case GEO_WEST:
outline->o_nextRect.r_ur = outline->o_rect.r_ll;
outline->o_nextRect.r_ll = outline->o_rect.r_ll;
break;
}
/*
* If the tile stack was not yet empty, we do things the
* easy way. Advance to the next tile along the side of
* the one inside (if going north/south) or the one outside
* (if going east/west).
*/
if (STACKLOOK(plowOutlineStack))
{
switch (outline->o_currentDir)
{
/*
* Heading north.
*
* | nextOut
* nextIn +--------
* | outside
*
* Turn east if nextOut is in o_insideTypes.
* Otherwise, keep going north.
*/
case GEO_NORTH:
outline->o_nextOut = (Tile *) STACKPOP(plowOutlineStack);
if (IsInside(outline->o_nextOut, outline))
{
outline->o_nextOut = LB(outline->o_nextOut);
outline->o_nextDir = GEO_EAST;
}
break;
/*
* Heading south.
*
* outside |
* --------+ nextIn
* nextOut |
*
* Turn west if nextOut is in o_insideTypes.
* Otherwise, keep going south.
*/
case GEO_SOUTH:
outline->o_nextOut = (Tile *) STACKPOP(plowOutlineStack);
if (IsInside(outline->o_nextOut, outline))
{
outline->o_nextOut = RT(outline->o_nextOut);
outline->o_nextDir = GEO_WEST;
}
break;
/*
* Heading east.
*
* inside | nextIn
* --------+--------
* nextOut
*
* Turn north if nextIn is not in o_insideTypes.
* Otherwise, keep going east.
*/
case GEO_EAST:
outline->o_nextIn = (Tile *) STACKPOP(plowOutlineStack);
if (!IsInside(outline->o_nextIn, outline))
{
outline->o_nextIn = BL(outline->o_nextIn);
outline->o_nextDir = GEO_NORTH;
}
break;
/*
* Heading west.
*
* nextOut
* --------+--------
* nextIn | inside
*
* Turn south if nextIn is not in o_insideTypes.
* Otherwise, keep going west.
*/
case GEO_WEST:
outline->o_nextIn = (Tile *) STACKPOP(plowOutlineStack);
if (!IsInside(outline->o_nextIn, outline))
{
outline->o_nextIn = TR(outline->o_nextIn);
outline->o_nextDir = GEO_SOUTH;
}
break;
}
/*
* Stack a new set of tiles along the opposite side if
* we changed direction, and set the appropriate one of
* o_nextIn (if going east/west) or o_nextOut (if going
* north/south).
*/
if (outline->o_nextDir != outline->o_currentDir)
STACKOUTLINE(outline);
return;
}
/*
* The tile stack was empty, so we must advance the primary
* tile (inside for north/south, outside for east/west).
* This code is more complex than that above, because we
* have three choices: continue going straight ahead, turn
* left, or turn right.
*/
switch (outline->o_currentDir)
{
/*
* Heading north.
*
* tpL tpR
* --------+
* nextIn |
*
* If tpL is outside, turn west.
* If tpR is inside, turn east.
* Otherwise, continue north.
*/
case GEO_NORTH:
tpL = RT(outline->o_nextIn);
if (!IsInside(tpL, outline))
{
outline->o_nextOut = tpL;
outline->o_nextDir = GEO_WEST;
break;
}
/* Find tpR */
if (RIGHT(tpL) > outline->o_nextRect.r_xbot) tpR = tpL;
else for (tpR = TR(tpL);
BOTTOM(tpR) > outline->o_nextRect.r_ybot; tpR = LB(tpR))
/* Nothing */;
if (IsInside(tpR, outline))
{
outline->o_nextOut = TR(outline->o_nextIn);
outline->o_nextDir = GEO_EAST;
}
else outline->o_nextIn = tpL;
break;
/*
* Heading south.
*
* | nextIn
* --------+--------
* tpL tpR
*
* If tpR is outside, turn east.
* If tpL is inside, turn west.
* Otherwise, continue south.
*/
case GEO_SOUTH:
tpR = LB(outline->o_nextIn);
if (!IsInside(tpR, outline))
{
outline->o_nextOut = tpR;
outline->o_nextDir = GEO_EAST;
break;
}
/* Find tpL */
if (LEFT(tpR) < outline->o_nextRect.r_xbot) tpL = tpR;
else for (tpL = BL(tpR);
TOP(tpL) < outline->o_nextRect.r_ytop; tpL = RT(tpL))
/* Nothing */;
if (IsInside(tpL, outline))
{
outline->o_nextOut = BL(outline->o_nextIn);
outline->o_nextDir = GEO_WEST;
}
else outline->o_nextIn = tpR;
break;
/*
* Heading east.
*
* tpL
* --------+
* nextOut | tpR
*
* If tpR is inside, turn south.
* If tpL is outside, turn north.
* Otherwise, continue east.
*/
case GEO_EAST:
tpR = TR(outline->o_nextOut);
if (TOP(tpR) > outline->o_nextRect.r_ybot) tpL = tpR;
else for (tpL = RT(tpR);
LEFT(tpL) > outline->o_nextRect.r_xbot; tpL = BL(tpL))
/* Nothing */;
if (!IsInside(tpL, outline))
{
outline->o_nextIn = RT(outline->o_nextOut);
outline->o_nextDir = GEO_NORTH;
}
else if (IsInside(tpR, outline))
{
outline->o_nextIn = tpR;
outline->o_nextDir = GEO_SOUTH;
}
else outline->o_nextOut = tpR;
break;
/*
* Heading west.
*
* tpL nextOut
* +--------
* tpR
*
* If tpL is inside, turn north.
* If tpR is outside, turn south.
* Otherwise, continue west.
*/
case GEO_WEST:
tpL = BL(outline->o_nextOut);
if (BOTTOM(tpL) < outline->o_nextRect.r_ybot) tpR = tpL;
else for (tpR = LB(tpL);
RIGHT(tpR) < outline->o_nextRect.r_xbot; tpR = TR(tpR))
/* Nothing */;
if (!IsInside(tpR, outline))
{
outline->o_nextIn = LB(outline->o_nextOut);
outline->o_nextDir = GEO_SOUTH;
}
else if (IsInside(tpL, outline))
{
outline->o_nextIn = tpL;
outline->o_nextDir = GEO_NORTH;
}
else outline->o_nextOut = tpL;
break;
}
/*
* Stack a new set of tiles along the opposite side always,
* since we either advanced to the next tile or changed
* direction. Set the appropriate one of o_nextIn (if
* going east/west) or o_nextOut (if going north/south).
*/
STACKOUTLINE(outline);
}