/* CIFgen.c -
 *
 *	This file provides routines that generate CIF from Magic
 *	tile information, using one of the styles read from the
 *	technology file.
 *
 *     *********************************************************************
 *     * 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 const char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/cif/CIFgen.c,v 1.23 2010/06/24 20:35:54 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>             /* for abs() */
#include <math.h>		/* for ceil() and sqrt() */
#include <ctype.h>
#include <string.h>		/* for strcmp() */

#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/signals.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "cif/CIFint.h"
#include "calma/calma.h"	/* for CalmaContactArrays */
#include "commands/commands.h"	/* for CmdFindNetProc()	*/
#include "select/selInt.h"	/* for select use and def */
#include "select/select.h"
#include "utils/stack.h"
#include "utils/malloc.h"
#include "utils/maxrect.h"
#include "drc/drc.h"

/* C99 compat */
#include "textio/textio.h"
#include "utils/undo.h"

/* TRUE to run (very slow) algorithm for optimizing non-manhattan tiles */
/* (cuts size of output;  see also the GDS "merge" option)		*/
bool CIFUnfracture = FALSE;

/* The following global arrays hold pointers to the CIF planes
 * generated by the procedures in this module.  There are two
 * kinds of planes:  real CIF, which will ostensibly be output,
 * and temporary layers used to build up more complex geometric
 * functions.
 */

global Plane *CIFPlanes[MAXCIFLAYERS];

/* The following are paint tables used by the routines that implement
 * the geometric operations on mask layers, such as AND, OR, GROW,
 * etc.  There are really only two tables.  The first will paint
 * CIF_SOLIDTYPE over anything else, and the second will paint
 * space over anything else.  These tables are used on planes with
 * only two tile types, TT_SPACE and CIF_SOLIDTYPE, so they only
 * have two entries each.
 */

const PaintResultType CIFPaintTable[] = {CIF_SOLIDTYPE, CIF_SOLIDTYPE};
const PaintResultType CIFEraseTable[] = {TT_SPACE, TT_SPACE};

/* The following local variables are used as a convenience to pass
 * information between CIFGen and the various search functions.
 */

static int growDistance;	/* Distance to grow stuff. */
static Plane *cifPlane;		/* Plane acted on by search functions. */
static int cifScale;		/* Scale factor to use on tiles. */

extern void cifClipPlane(Plane *plane, Rect *clip);
extern void cifGenClip(const Rect *area, Rect *expanded, Rect *clip);

/*
 * ----------------------------------------------------------------------------
 *
 * cifPaintFunc --
 *
 * 	This search function is called during CIF_AND and CIF_OR
 *	and CIF_ANDNOT operations for each relevant tile.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	For the area of the tile, either paints or erases in
 *	cifNewPlane, depending on the paint table passed as parameter.
 *	The tile's area is scaled by cifScale first.
 * ----------------------------------------------------------------------------
 */

int
cifPaintFunc(
    Tile *tile,
    TileType dinfo,		/* Split tile information */
    PaintResultType *table)	/* Used for painting. */
{
    Rect area;

    TiToRect(tile, &area);
    area.r_xbot *= cifScale;
    area.r_xtop *= cifScale;
    area.r_ybot *= cifScale;
    area.r_ytop *= cifScale;

    DBNMPaintPlane(cifPlane, TiGetTypeExact(tile) | dinfo, &area, table,
		(PaintUndoInfo *) NULL);
    CIFTileOps += 1;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifInteractFunc --
 *
 * 	This search function is called during CIF_INTERACT for each tile that
 *	interacts with (overlaps) any relevant material in the search plane.
 *
 * Results:
 *	Always returns 1 to stop the search
 *
 * Side effects:
 *	None.
 * ----------------------------------------------------------------------------
 */

int
cifInteractFunc(
    Tile *tile,			/* (unused) */
    TileType dinfo,		/* (unused) */
    ClientData clientdata)	/* (unused) */
{
    return 1;
}

/*
 * ----------------------------------------------------------------------------
 * SetBoxGrid ---
 *
 *	Adjust the given area by expanding each side individually to
 *	ensure that it falls on the CIF minimum grid.
 *
 *  Returns:  Nothing
 *
 *  Side Effects:  Point to Rect "area" may be modified.
 *
 * ----------------------------------------------------------------------------
 */

void
SetBoxGrid(
    Rect *area)
{
    int limit;
    int delta;

    limit = CIFCurStyle->cs_gridLimit;

    if (CIFCurStyle && (limit > 1))
    {
	delta = abs(area->r_xbot) % limit;
	if (delta > 0)
	{
	    if (area->r_xbot < 0)
	    {
		area->r_xbot += delta;
		area->r_xbot -= limit;
	    }
	    else
		area->r_xbot -= delta;
	}

	delta = abs(area->r_xtop) % limit;
	if (delta > 0)
	{
	    if (area->r_xtop < 0)
		area->r_xtop += delta;
	    else
	    {
		area->r_xtop -= delta;
		area->r_xtop += limit;
	    }
	}

	delta = abs(area->r_ybot) % limit;
	if (delta > 0)
	{
	    if (area->r_ybot < 0)
	    {
		area->r_ybot += delta;
		area->r_ybot -= limit;
	    }
	    else
		area->r_ybot -= delta;
	}

	delta = abs(area->r_ytop) % limit;
	if (delta > 0)
	{
	    if (area->r_ytop < 0)
		area->r_ytop += delta;
	    else
	    {
		area->r_ytop -= delta;
		area->r_ytop += limit;
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 * SetValueGrid ---
 *
 *	Adjust the given distance to the nearest CIF minimum grid.
 *
 *  Returns:  The adjusted value.
 *
 *  Side Effects:  None.
 *
 *  Notes:  Value is assumed to be a distance, therefore always positive.
 *
 * ----------------------------------------------------------------------------
 */

int
SetValueGrid(
    int value)
{
    int limit;
    int delta;

    limit = CIFCurStyle->cs_gridLimit;

    if (CIFCurStyle && (limit > 1))
    {
	delta = value % limit;
	if (delta > 0)
	    value += limit - delta;
    }
    return value;
}

/*
 * ----------------------------------------------------------------------------
 * SetMinBoxGrid ---
 *
 *	Adjust the given area by expanding evenly on both sides so that it
 *	has a width and heigth no less than the given width.  Then further
 *	expand the box to ensure that it falls on the CIF minimum grid.
 *
 *  Returns:  Nothing
 *
 *  Side Effects:  Point to Rect "area" may be modified.
 *
 * ----------------------------------------------------------------------------
 */

void
SetMinBoxGrid(
    Rect *area,
    int width)
{
    int wtest;
    int wtot;

    wtest = (area->r_xtop - area->r_xbot);
    wtot = area->r_xtop + area->r_xbot;
    if (wtest < width)
    {
	area->r_xbot = (wtot - width) / 2;
	area->r_xtop = (wtot + width) / 2;
    }
    wtest = (area->r_ytop - area->r_ybot);
    wtot = area->r_ytop + area->r_ybot;
    if (wtest < width)
    {
	area->r_ybot = (wtot - width) / 2;
	area->r_ytop = (wtot + width) / 2;
    }

    SetBoxGrid(area);
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifGrowMinFunc --
 *
 * 	Called for each relevant tile during grow-min operations.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	May paint into cifNewPlane
 *
 * Algorithm (based on maximum horizontal stripes rule):
 *	Scan top and bottom boundaries from left to right.  For any
 *	distance (including distance zero) sharing the same type (0 or 1)
 *	on both the tile top and bottom, find the diagonal length.  If
 *	less than co_distance, then expand this area and paint.
 *	NOTE:  This algorithm does not cover a number of geometry cases
 *	and needs to be reworked.  It should be restricted to cases of
 *	layers that have "rect_only" DRC rules.  Since the rule is usually
 *	needed for implants on FET gates to maintain the implant width for
 *	small gates, the "rect_only" requirement is not particularly
 *	constraining.
 *
 * ----------------------------------------------------------------------------
 */

int
cifGrowMinFunc(
    Tile *tile,
    TileType dinfo,
    PaintResultType *table)		/* Table to be used for painting. */
{
    Rect area, parea;
    int locDist, width, height, h;
    TileType type, tptype;
    Tile *tp, *tp2;
    bool changed;

    TiToRect(tile, &area);

    area.r_xbot *= cifScale;
    area.r_xtop *= cifScale;
    area.r_ybot *= cifScale;
    area.r_ytop *= cifScale;

    parea = area;
    changed = FALSE;

    /* Check whole tile for minimum width */
    width = area.r_xtop - area.r_xbot;
    if (width < growDistance)
    {
	locDist = (growDistance - width) / 2;
	area.r_xbot -= locDist;
	area.r_xtop += locDist;

	/* If there is another tile on top or bottom, and the height is	*/
	/* less than minimum, then extend height in the direction of	*/
	/* the bordering tile.  Otherwise, if the height is less than	*/
	/* minimum, then grow halfway in both directions.		*/

	height = area.r_ytop - area.r_ybot;
	if (height < growDistance)
	{
	    bool freeTop, freeBot;

	    freeTop = freeBot = TRUE;

	    for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
		if (TiGetTopType(tp) == TiGetBottomType(tile))
		{
		    freeBot = FALSE;
		    break;
		}

	    for (tp2 = RT(tile); RIGHT(tp2) > LEFT(tile); tp2 = BL(tp2))
		if (TiGetBottomType(tp2) == TiGetTopType(tile))
		{
		    freeTop = FALSE;
		    break;
		}

	    /* In the following, value h ensures that the euclidean */
	    /* distance between inside corners of the layer	    */
	    /* satisfies growDistance.				    */

	    if (freeTop == TRUE && freeBot == FALSE)
	    {
		locDist = (growDistance - height) / 2;
		h = (int)sqrt((double)(growDistance * growDistance) -
			    0.25 * (double)((growDistance + width) *
			    (growDistance + width)) + 0.5);
		area.r_ybot -= h;
		changed = TRUE;
	    }
	    else if (freeTop == FALSE && freeBot == TRUE)
	    {
		h = (int)sqrt((double)(growDistance * growDistance) -
			    0.25 * (double)((growDistance + width) *
			    (growDistance + width)) + 0.5);
		area.r_ytop += h;
		changed = TRUE;
	    }
	    else {
		locDist = (growDistance - height) / 2;
		area.r_ybot -= locDist;
		area.r_ytop += locDist;
		changed = TRUE;
	    }
	}
    }

    /* Ensure grid limit is not violated */
    if (changed) SetMinBoxGrid(&area, growDistance);

    DBPaintPlane(cifPlane, &area, table, (PaintUndoInfo *) NULL);

    area = parea;

    /* Scan bottom from left to right */
    for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
    {
	tptype = TiGetTopType(tp);
	/* Scan top from right to left across range of tp */
	for (tp2 = RT(tile); RIGHT(tp2) > LEFT(tile); tp2 = BL(tp2))
	    if (TiGetBottomType(tp2) == tptype)
	    {
		/* Set range to length of overlap */
		if ((LEFT(tp2) <= RIGHT(tp)) && (LEFT(tp2) >= LEFT(tp)))
		{
		    area.r_xbot = LEFT(tp2) < LEFT(tile) ? LEFT(tile) : LEFT(tp2);
		    area.r_xtop = RIGHT(tp) > RIGHT(tile) ? RIGHT(tile) : RIGHT(tp);
		}
		else if ((RIGHT(tp2) >= LEFT(tp)) && (RIGHT(tp2) <= RIGHT(tp)))
		{
		    area.r_xbot = LEFT(tp) < LEFT(tile) ? LEFT(tile) : LEFT(tp);
		    area.r_xtop = RIGHT(tp2) > RIGHT(tile) ? RIGHT(tile) : RIGHT(tp2);
		}
		else continue;

		area.r_xbot *= cifScale;
		area.r_xtop *= cifScale;

		/* Does area violate minimum width requirement? */
		width = area.r_xtop - area.r_xbot;
		height = area.r_ytop - area.r_ybot;

		/* Manhattan requirement (to-do: Euclidean) */
		if (width < growDistance)
		{
		    locDist = (growDistance - width) / 2;
		    parea.r_xbot = area.r_xbot - locDist;
		    parea.r_xtop = area.r_xtop + locDist;
		}
		else
		{
		    parea.r_xbot = area.r_xbot;
		    parea.r_xtop = area.r_xtop;
		}
		if (height < growDistance)
		{
		    locDist = (growDistance - height) / 2;
		    parea.r_ybot = area.r_ybot - locDist;
		    parea.r_ytop = area.r_ytop + locDist;
		}
		else
		{
		    parea.r_ybot = area.r_ybot;
		    parea.r_ytop = area.r_ytop;
		}
		if ((width < growDistance) || (height < growDistance))
		{
    		    /* Ensure grid limit is not violated */
		    SetMinBoxGrid(&parea, growDistance);
		    DBPaintPlane(cifPlane, &parea, table, (PaintUndoInfo *) NULL);
		}
	    }
    }

    CIFTileOps += 1;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifGrowGridFunc --
 *
 * 	Called for each relevant tile during grow-grid operations.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	Scales the tile by cifScale, then expands its area by the
 *	remainder of the distance to the nearest grid point, as
 *	defined by the grid distance (growDistance) in the current
 *	CIFOp, then paints this area into cifNewPlane using the table
 *	passed as parameter.
 * ----------------------------------------------------------------------------
 */

int
cifGrowGridFunc(
    Tile *tile,
    TileType dinfo,
    PaintResultType *table)		/* Table to be used for painting. */
{
    Rect area;
    int remainder;

    /* To be done---handle non-Manhattan geometry */
    TileType oldType = TiGetType(tile);

    TiToRect(tile, &area);

    /* In scaling the tile, watch out for infinities!!  If something
     * is already infinity, don't change it. */

    if (area.r_xbot > TiPlaneRect.r_xbot) area.r_xbot *= cifScale;
    if (area.r_ybot > TiPlaneRect.r_ybot) area.r_ybot *= cifScale;
    if (area.r_xtop < TiPlaneRect.r_xtop) area.r_xtop *= cifScale;
    if (area.r_ytop < TiPlaneRect.r_ytop) area.r_ytop *= cifScale;

    /* In scaling the tile, watch out for infinities!!  If something
     * is already infinity, don't change it. */

    if (area.r_xbot > TiPlaneRect.r_xbot)
	area.r_xbot -= (abs(area.r_xbot) % growDistance);
    if (area.r_ybot > TiPlaneRect.r_ybot)
	area.r_ybot -= (abs(area.r_ybot) % growDistance);
    if (area.r_xtop < TiPlaneRect.r_xtop)
	area.r_xtop += (abs(area.r_xtop) % growDistance);
    if (area.r_ytop < TiPlaneRect.r_ytop)
	area.r_ytop += (abs(area.r_ytop) % growDistance);

    DBPaintPlane(cifPlane, &area, table, (PaintUndoInfo *) NULL);

    CIFTileOps += 1;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifGrowEuclideanFunc --
 *
 * 	Called for each relevant tile during grow or shrink operations.
 *	For growing, these are solid tiles.  For shrinking, these are
 *	space tiles.   This routine differs from cifGrowFunc in that it
 *	grows the minimum distance on non-Manhattan edges necessary to
 *	create a euclidean distance to the edge of growDistance while
 *	keeping corner points on-grid.  This requires a substantially
 *	different algorithm from cifGrowFunc.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	Scales the tile by cifScale, then expands its area by the
 *	distance in the current CIFOp, then paints this area into
 *	cifNewPlane using the table passed as parameter.
 * ----------------------------------------------------------------------------
 */

#define GROW_NORTH 0x1
#define GROW_SOUTH 0x2
#define GROW_EAST  0x4
#define GROW_WEST  0x8

#define STOP_NW 0x1	/*	   WN     EN	*/
#define STOP_SW 0x2	/*	NW +-------+ NE	*/
#define STOP_NE 0x4	/*	   |       |	*/
#define STOP_SE 0x8	/*	   |       |	*/
#define STOP_WN 0x10	/*      SW +-------+ SE	*/
#define STOP_WS 0x20	/*         WS     ES	*/
#define STOP_EN 0x40
#define STOP_ES 0x80

int
cifGrowEuclideanFunc(
    Tile *tile,
    TileType dinfo,
    const PaintResultType *table)		/* Table to be used for painting. */
{
    Tile *tp;
    Rect area, rtmp;
    TileType oldType = TiGetTypeExact(tile) | dinfo;
    unsigned char growDirs = GROW_NORTH | GROW_SOUTH | GROW_EAST | GROW_WEST;
    unsigned char cornerDirs = 0;

    TiToRect(tile, &area);

    /* In scaling the tile, watch out for infinities!!  If something
     * is already infinity, don't change it. */

    if (area.r_xbot > TiPlaneRect.r_xbot)
	area.r_xbot *= cifScale;
    else
	growDirs &= ~GROW_WEST;
    if (area.r_ybot > TiPlaneRect.r_ybot)
	area.r_ybot *= cifScale;
    else
	growDirs &= ~GROW_SOUTH;
    if (area.r_xtop < TiPlaneRect.r_xtop)
	area.r_xtop *= cifScale;
    else
	growDirs &= ~GROW_EAST;
    if (area.r_ytop < TiPlaneRect.r_ytop)
	area.r_ytop *= cifScale;
    else
	growDirs &= ~GROW_NORTH;

    /* Grow on diagonal tiles:  grow rectangular tiles around the	*/
    /* straight edges of the right-triangle, then copy the diagonal	*/
    /* tile (at its original size) in the direction of the diagonal.	*/
    /* Note:  A diagonal tile, by definition, can't have infinities.	*/

    if (oldType & TT_DIAGONAL)
    {
	int growDistanceX, growDistanceY;
	int height, width;
	double frac, ratio;
	int limit;

	if (oldType & TT_SIDE)
	    growDirs &= ~GROW_WEST;
	else
	    growDirs &= ~GROW_EAST;

	if (((oldType & TT_SIDE) >> 1) == (oldType & TT_DIRECTION))
	    growDirs &= ~GROW_SOUTH;
	else
	    growDirs &= ~GROW_NORTH;

	/* Grow non-Manhattan edges to (the closest integer value	*/
	/* to) growDistance along the normal to the edge.  This		*/
	/* will overestimate the distance only to the minimum		*/
	/* amount necessary to ensure on-grid endpoints.		*/

	width = area.r_xtop - area.r_xbot;
	height = area.r_ytop - area.r_ybot;
        ratio = (double)height / (double)width;
	frac = ratio / (1 + sqrt(1 + ratio * ratio));
	growDistanceX = ceil((double)growDistance * frac);

        ratio = (double)width / (double)height;
	frac = ratio / (1 + sqrt(1 + ratio * ratio));
	growDistanceY = ceil((double)growDistance * frac);

	/* Adjust for grid limit */
	growDistanceX = SetValueGrid(growDistanceX);
	growDistanceY = SetValueGrid(growDistanceY);

	/* Draw vertical tile to distance X */

	rtmp = area;
	if (!(growDirs & GROW_EAST))  rtmp.r_xtop = rtmp.r_xbot + growDistanceX;
	if (!(growDirs & GROW_WEST))  rtmp.r_xbot = rtmp.r_xtop - growDistanceX;
	if (!(growDirs & GROW_SOUTH)) rtmp.r_ybot -= growDistance;
	if (!(growDirs & GROW_NORTH)) rtmp.r_ytop += growDistance;
	DBPaintPlane(cifPlane, &rtmp, table, (PaintUndoInfo *) NULL);

	/* Draw horizontal tile to distance Y */

	rtmp = area;
	if (!(growDirs & GROW_EAST))  rtmp.r_xtop += growDistance;
	if (!(growDirs & GROW_WEST))  rtmp.r_xbot -= growDistance;
	if (!(growDirs & GROW_SOUTH)) rtmp.r_ybot = rtmp.r_ytop - growDistanceY;
	if (!(growDirs & GROW_NORTH)) rtmp.r_ytop = rtmp.r_ybot + growDistanceY;
	DBPaintPlane(cifPlane, &rtmp, table, (PaintUndoInfo *) NULL);

	/* Finally, translate, resize, and paint the diagonal tile */

	rtmp = area;

	if (!(growDirs & GROW_NORTH))
	    rtmp.r_ytop += growDistance;
	else
	    rtmp.r_ytop -= growDistanceY;
	if (!(growDirs & GROW_SOUTH))
	    rtmp.r_ybot -= growDistance;
	else
	    rtmp.r_ybot += growDistanceY;
	if (!(growDirs & GROW_EAST))
	    rtmp.r_xtop += growDistance;
	else
	    rtmp.r_xtop -= growDistanceX;
	if (!(growDirs & GROW_WEST))
	    rtmp.r_xbot -= growDistance;
	else
	    rtmp.r_xbot += growDistanceX;

	DBNMPaintPlane(cifPlane, oldType, &rtmp, table, (PaintUndoInfo *) NULL);

	oldType = (growDirs & GROW_EAST) ? TiGetRightType(tile) : TiGetLeftType(tile);
    }
    else
	DBPaintPlane(cifPlane, &area, table, (PaintUndoInfo *) NULL);

    /* Check tile corner areas for paint */

    tp = TR(tile);
    if (TiGetLeftType(tp) == oldType) cornerDirs |= STOP_NE;
    for (; TOP(LB(tp)) > BOTTOM(tile); tp = LB(tp));
    if (TiGetLeftType(tp) == oldType) cornerDirs |= STOP_SE;

    tp = RT(tile);
    if (TiGetBottomType(tp) == oldType) cornerDirs |= STOP_EN;
    for (; RIGHT(BL(tp)) > LEFT(tile); tp = BL(tp));
    if (TiGetBottomType(tp) == oldType) cornerDirs |= STOP_WN;

    tp = BL(tile);
    if (TiGetRightType(tp) == oldType) cornerDirs |= STOP_SW;
    for (; BOTTOM(RT(tp)) < TOP(tile); tp = RT(tp));
    if (TiGetRightType(tp) == oldType) cornerDirs |= STOP_NW;

    tp = LB(tile);
    if (TiGetTopType(tp) == oldType) cornerDirs |= STOP_WS;
    for (; LEFT(TR(tp)) < RIGHT(tile); tp = TR(tp));
    if (TiGetTopType(tp) == oldType) cornerDirs |= STOP_ES;

    if (growDirs & GROW_NORTH)
    {
	rtmp = area;
	rtmp.r_ybot = area.r_ytop;
	rtmp.r_ytop = area.r_ytop + growDistance;
	if ((cornerDirs & (STOP_EN | STOP_NE)) == 0)
	    if (growDirs & GROW_EAST) rtmp.r_xtop += growDistance;
	if ((cornerDirs & (STOP_WN | STOP_NW)) == 0)
	    if (growDirs & GROW_WEST) rtmp.r_xbot -= growDistance;
	DBPaintPlane(cifPlane, &rtmp, table, (PaintUndoInfo *) NULL);
    }

    if (growDirs & GROW_EAST)
    {
	rtmp = area;
	rtmp.r_xbot = area.r_xtop;
	rtmp.r_xtop = area.r_xtop + growDistance;
	if ((cornerDirs & (STOP_EN | STOP_NE)) == 0)
	    if (growDirs & GROW_NORTH) rtmp.r_ytop += growDistance;
	if ((cornerDirs & (STOP_SE | STOP_ES)) == 0)
	    if (growDirs & GROW_SOUTH) rtmp.r_ybot -= growDistance;
	DBPaintPlane(cifPlane, &rtmp, table, (PaintUndoInfo *) NULL);
    }

    if (growDirs & GROW_SOUTH)
    {
	rtmp = area;
	rtmp.r_ytop = area.r_ybot;
	rtmp.r_ybot = area.r_ybot - growDistance;
	if ((cornerDirs & (STOP_SE | STOP_ES)) == 0)
	    if (growDirs & GROW_EAST) rtmp.r_xtop += growDistance;
	if ((cornerDirs & (STOP_SW | STOP_WS)) == 0)
	    if (growDirs & GROW_WEST) rtmp.r_xbot -= growDistance;
	DBPaintPlane(cifPlane, &rtmp, table, (PaintUndoInfo *) NULL);
    }

    if (growDirs & GROW_WEST)
    {
	rtmp = area;
	rtmp.r_xtop = area.r_xbot;
	rtmp.r_xbot = area.r_xbot - growDistance;
	if ((cornerDirs & (STOP_NW | STOP_WN)) == 0)
	    if (growDirs & GROW_NORTH) rtmp.r_ytop += growDistance;
	if ((cornerDirs & (STOP_SW | STOP_WS)) == 0)
	    if (growDirs & GROW_SOUTH) rtmp.r_ybot -= growDistance;
	DBPaintPlane(cifPlane, &rtmp, table, (PaintUndoInfo *) NULL);
    }

    CIFTileOps++;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifGrowFunc --
 *
 * 	Called for each relevant tile during grow or shrink operations.
 *	For growing, these are solid tiles.  For shrinking, these are
 *	space tiles.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	Scales the tile by cifScale, then expands its area by the
 *	distance in the current CIFOp, then paints this area into
 *	cifNewPlane using the table passed as parameter.
 * ----------------------------------------------------------------------------
 */

int
cifGrowFunc(
    Tile *tile,
    TileType dinfo,
    const PaintResultType *table)		/* Table to be used for painting. */
{
    Rect area;
    TileType oldType = TiGetTypeExact(tile) | dinfo;

    TiToRect(tile, &area);

    /* In scaling the tile, watch out for infinities!!  If something
     * is already infinity, don't change it. */

    if (area.r_xbot > TiPlaneRect.r_xbot) area.r_xbot *= cifScale;
    if (area.r_ybot > TiPlaneRect.r_ybot) area.r_ybot *= cifScale;
    if (area.r_xtop < TiPlaneRect.r_xtop) area.r_xtop *= cifScale;
    if (area.r_ytop < TiPlaneRect.r_ytop) area.r_ytop *= cifScale;

    /* Grow on diagonal tiles:  grow rectangular tiles around the	*/
    /* straight edges of the right-triangle, then copy the diagonal	*/
    /* tile (at its original size) in the direction of the diagonal.	*/
    /* Note:  A diagonal tile, by definition, can't have infinities.	*/

    if (oldType & TT_DIAGONAL)
    {
	Rect rtmp;

	/* Grow top and bottom */

	rtmp.r_ybot = area.r_ybot - growDistance;
	rtmp.r_ytop = area.r_ytop + growDistance;

	/* Grow around left or right edge */

	if (oldType & TT_SIDE)
	{
	    rtmp.r_xbot = area.r_xtop - growDistance;
	    rtmp.r_xtop = area.r_xtop + growDistance;
	}
	else
	{
	    rtmp.r_xbot = area.r_xbot - growDistance;
	    rtmp.r_xtop = area.r_xbot + growDistance;
	}
	DBPaintPlane(cifPlane, &rtmp, table, (PaintUndoInfo *) NULL);

	/* Now do the same for the other edge. */

	rtmp.r_xbot = area.r_xbot - growDistance;
	rtmp.r_xtop = area.r_xtop + growDistance;

	if (((oldType & TT_SIDE) >> 1) == (oldType & TT_DIRECTION)) /* top */
	{
	    rtmp.r_ybot = area.r_ytop - growDistance;
	    rtmp.r_ytop = area.r_ytop + growDistance;
	}
	else /* bottom */
	{
	    rtmp.r_ybot = area.r_ybot - growDistance;
	    rtmp.r_ytop = area.r_ybot + growDistance;
	}
	DBPaintPlane(cifPlane, &rtmp, table, (PaintUndoInfo *) NULL);

	/* Finally, Move and replace the diagonal tile */

	if (oldType & TT_SIDE)
	{
	    rtmp.r_xtop = area.r_xtop - growDistance;
	    rtmp.r_xbot = area.r_xbot - growDistance;
	}
	else
	{
	    rtmp.r_xtop = area.r_xtop + growDistance;
	    rtmp.r_xbot = area.r_xbot + growDistance;
	}

	if (((oldType & TT_SIDE) >> 1) == (oldType & TT_DIRECTION)) /* top */
	{
	    rtmp.r_ytop = area.r_ytop - growDistance;
	    rtmp.r_ybot = area.r_ybot - growDistance;
	}
	else	/* bottom */
	{
	    rtmp.r_ytop = area.r_ytop + growDistance;
	    rtmp.r_ybot = area.r_ybot + growDistance;
	}
	DBNMPaintPlane(cifPlane, oldType, &rtmp, table, (PaintUndoInfo *) NULL);
    }
    else
    {

	/* In scaling the tile, watch out for infinities!!  If something
	 * is already infinity, don't change it. */

	if (area.r_xbot > TiPlaneRect.r_xbot) area.r_xbot -= growDistance;
	if (area.r_ybot > TiPlaneRect.r_ybot) area.r_ybot -= growDistance;
	if (area.r_xtop < TiPlaneRect.r_xtop) area.r_xtop += growDistance;
	if (area.r_ytop < TiPlaneRect.r_ytop) area.r_ytop += growDistance;

	DBPaintPlane(cifPlane, &area, table, (PaintUndoInfo *) NULL);
    }

    CIFTileOps += 1;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifBloatFunc --
 *
 * 	Called once for each tile to be selectively bloated.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	Uses the bloat table in the current CIFOp to expand the
 *	tile depending on which tiles it abuts, then paints the
 *	expanded area into the new CIF plane.  The bloat table
 *	contains one entry for each tile type.  When that tile
 *	type is seen next to the current tile, the area of the current
 *	tile is bloated by the table value in that location.  The
 *	only exception to this rule is that internal edges (between
 *	two tiles of the same type) cause no bloating.
 *	Note:  the original tile is scaled into CIF coordinates.
 * ----------------------------------------------------------------------------
 */

int
cifBloatFunc(
    Tile *tile,
    TileType dinfo,
    ClientData clientData)
{
    Rect tileArea, cifArea, bloat;
    TileType oldType, type, topLeftType, bottomRightType;
    Tile *t;
    int tilestart, tilestop, cifstart, cifstop;
    BloatData *bloats = (BloatData *)clientData;
    int *bloatTable = (int *)bloats->bl_distance;

    oldType = TiGetTypeExact(tile) | dinfo;
    TiToRect(tile, &tileArea);

    /* Output the original area of the tile. */

    cifArea = tileArea;
    cifArea.r_xbot *= cifScale;
    cifArea.r_xtop *= cifScale;
    cifArea.r_ybot *= cifScale;
    cifArea.r_ytop *= cifScale;

    /* This is a modified version of the nonmanhattan grow function.	*/
    /* We grow only in the direction of the diagonal.			*/
    /* This will not work in all situations!  Corner extensions are not	*/
    /* considered (but should be, for completeness).			*/

    if (oldType & TT_DIAGONAL)
    {
	TileType otherType = (oldType & TT_SIDE) ?
		TiGetLeftType(tile) : TiGetRightType(tile);
	int dist = bloatTable[otherType];

	/* The Euclidean grow function is identical to Euclidean bloat-or */
	if (CIFCurStyle->cs_flags & CWF_GROW_EUCLIDEAN)
	{
	    growDistance = dist;
	    cifGrowEuclideanFunc(tile, dinfo, CIFPaintTable);
	}
	else
	{

	    /* Grow top and bottom */

	    if (((oldType & TT_SIDE) >> 1) == (oldType & TT_DIRECTION)) /* top */
	    {
		bloat.r_ybot = cifArea.r_ytop - dist;
		bloat.r_ytop = cifArea.r_ytop;
	    }
	    else /* bottom */
	    {
		bloat.r_ybot = cifArea.r_ybot;
		bloat.r_ytop = cifArea.r_ybot + dist;
	    }

	    /* Grow around left or right edge */

	    if (oldType & TT_SIDE)
	    {
		bloat.r_xbot = cifArea.r_xbot - dist;
		bloat.r_xtop = cifArea.r_xtop;
	    }
	    else
	    {
		bloat.r_xbot = cifArea.r_xbot;
		bloat.r_xtop = cifArea.r_xtop + dist;
	    }
	    DBPaintPlane(cifPlane, &bloat, CIFPaintTable, (PaintUndoInfo *) NULL);

	    /* Now do the same for the left or right edge. */

	    if (((oldType & TT_SIDE) >> 1) == (oldType & TT_DIRECTION)) /* top */
	    {
		bloat.r_ybot = cifArea.r_ybot - dist;
		bloat.r_ytop = cifArea.r_ytop;
	    }
	    else /* bottom */
	    {
		bloat.r_ybot = cifArea.r_ybot;
		bloat.r_ytop = cifArea.r_ytop + dist;
	    }

	    if (oldType & TT_SIDE)
	    {
		bloat.r_xbot = cifArea.r_xtop - dist;
		bloat.r_xtop = cifArea.r_xtop;
	    }
	    else
	    {
		bloat.r_xbot = cifArea.r_xbot;
		bloat.r_xtop = cifArea.r_xbot + dist;
	    }
	    DBPaintPlane(cifPlane, &bloat, CIFPaintTable, (PaintUndoInfo *) NULL);

	    /* Finally, Move and replace the diagonal tile */

	    if (oldType & TT_SIDE)
	    {
		bloat.r_xtop = cifArea.r_xtop - dist;
		bloat.r_xbot = cifArea.r_xbot - dist;
	    }
	    else
	    {
		bloat.r_xtop = cifArea.r_xtop + dist;
		bloat.r_xbot = cifArea.r_xbot + dist;
	    }

	    if (((oldType & TT_SIDE) >> 1) == (oldType & TT_DIRECTION)) /* top */
	    {
		bloat.r_ytop = cifArea.r_ytop - dist;
		bloat.r_ybot = cifArea.r_ybot - dist;
	    }
	    else	/* bottom */
	    {
		bloat.r_ytop = cifArea.r_ytop + dist;
		bloat.r_ybot = cifArea.r_ybot + dist;
	    }
	    DBNMPaintPlane(cifPlane, oldType, &bloat, CIFPaintTable,
			(PaintUndoInfo *) NULL);
	}
    }
    else
	DBNMPaintPlane(cifPlane, oldType, &cifArea, CIFPaintTable,
			(PaintUndoInfo *) NULL);

    /* Go around the tile, scanning the neighbors along each side.
     * Start with the left side, and output the bloats along that
     * side, if any.
     */

    tilestop = tileArea.r_ytop;
    cifstop = cifArea.r_ytop;
    type = oldType;

    /* If the tile type doesn't exist on the left side, skip */
    if (oldType & TT_DIAGONAL)
    {
	type = TiGetLeftType(tile);
	if (oldType & TT_SIDE)
	{
	    if (oldType & TT_DIRECTION)
	    {
		topLeftType = type;
		goto dotop;
	    }
	    else
	    {
		tilestop = tileArea.r_ybot;
		cifstop = cifArea.r_ybot;
		type = TiGetBottomType(tile);
	    }
	}
    }

    bloat.r_ybot = cifArea.r_ybot - bloatTable[TiGetRightType(LB(tile))];
    bloat.r_xtop = cifArea.r_xbot;
    for (t = BL(tile); BOTTOM(t) < TOP(tile); t = RT(t))
    {
	if (BOTTOM(t) >= tilestop) continue;
	topLeftType = TiGetRightType(t);
	bloat.r_xbot = bloat.r_xtop - bloatTable[topLeftType];
	if (TOP(t) > tilestop)
	    bloat.r_ytop = cifstop;
	else bloat.r_ytop = cifScale * TOP(t);
	if ((bloatTable[topLeftType] != 0) && (topLeftType != type))
	    DBPaintPlane(cifPlane, &bloat, CIFPaintTable,
		(PaintUndoInfo *) NULL);
	bloat.r_ybot = bloat.r_ytop;
    }

    /* Now do the top side.  Use the type of the top-left tile to
     * side-extend the left end of the top bloat in order to match
     * things up at the corner.
     */

dotop:
    cifstart = cifArea.r_xtop;
    tilestart = tileArea.r_xtop;

    /* If the tile type doesn't exist on the top side, skip */
    if (oldType & TT_DIAGONAL)
    {
	type = TiGetTopType(tile);
	if (((oldType & TT_SIDE) >> 1) != (oldType & TT_DIRECTION))
	{
	    if (oldType & TT_SIDE)
		goto doright;
	    else
	    {
		cifstart = cifArea.r_xbot;
		tilestart = tileArea.r_xbot;
		type = TiGetLeftType(tile);
	    }
	}
    }

    bloat.r_ybot = cifArea.r_ytop;
    bloat.r_xtop = cifstart;
    for (t = RT(tile); RIGHT(t) > LEFT(tile); t = BL(t))
    {
	TileType otherType;
	if (LEFT(t) >= tilestart) continue;
	otherType = TiGetBottomType(t);
	bloat.r_ytop = bloat.r_ybot + bloatTable[otherType];
	if (LEFT(t) <= tileArea.r_xbot)
	    bloat.r_xbot = cifArea.r_xbot - bloatTable[topLeftType];
	else bloat.r_xbot = cifScale * LEFT(t);
	if ((bloatTable[otherType] != 0) && (otherType != type))
	    DBPaintPlane(cifPlane, &bloat, CIFPaintTable,
		(PaintUndoInfo *) NULL);
	bloat.r_xtop = bloat.r_xbot;
    }

    /* Now do the right side. */

doright:
    tilestop = tileArea.r_ybot;
    cifstop = cifArea.r_ybot;

    /* If the tile type doesn't exist on the right side, skip */
    if (oldType & TT_DIAGONAL)
    {
	type = TiGetRightType(tile);
	if (!(oldType & TT_SIDE))
	{
	    if (oldType & TT_DIRECTION)
	    {
		bottomRightType = type;
		goto dobottom;
	    }
	    else
	    {
		tilestop = tileArea.r_ytop;
		cifstop = cifArea.r_ytop;
		type = TiGetTopType(tile);
	    }
	}
    }

    bloat.r_ytop = cifArea.r_ytop + bloatTable[TiGetLeftType(RT(tile))];
    bloat.r_xbot = cifArea.r_xtop;
    for (t = TR(tile); TOP(t) > BOTTOM(tile); t = LB(t))
    {
	if (TOP(t) <= tilestop) continue;
	bottomRightType = TiGetLeftType(t);
	bloat.r_xtop = bloat.r_xbot + bloatTable[bottomRightType];
	if (BOTTOM(t) < tilestop)
	    bloat.r_ybot = cifstop;
	else bloat.r_ybot = cifScale * BOTTOM(t);
	if ((bloatTable[bottomRightType] != 0) && (bottomRightType != type))
	    DBPaintPlane(cifPlane, &bloat, CIFPaintTable,
		(PaintUndoInfo *) NULL);
	bloat.r_ytop = bloat.r_ybot;
    }

    /* Now do the bottom side.  Use the type of the bottom-right tile
     * to side-extend the right end of the bottom bloat in order to match
     * things up at the corner.
     */

dobottom:
    cifstart = cifArea.r_xbot;
    tilestart = tileArea.r_xbot;

    /* If the tile type doesn't exist on the bottom side, skip */
    if (oldType & TT_DIAGONAL)
    {
	type = TiGetBottomType(tile);
	if (((oldType & TT_SIDE) >> 1) == (oldType & TT_DIRECTION))
	{
	    if (!(oldType & TT_DIRECTION))
		goto endbloat;
	    else
	    {
		cifstart = cifArea.r_xtop;
		tilestart = tileArea.r_xtop;
		type = TiGetRightType(tile);
	    }
	}
    }

    bloat.r_ytop = cifArea.r_ybot;
    bloat.r_xbot = cifstart;
    for (t = LB(tile); LEFT(t) < RIGHT(tile); t = TR(t))
    {
	TileType otherType;
	if (RIGHT(t) <= tilestart) continue;
	otherType = TiGetTopType(t);
	bloat.r_ybot = bloat.r_ytop - bloatTable[otherType];
	if (RIGHT(t) >= tileArea.r_xtop)
	    bloat.r_xtop = cifArea.r_xtop + bloatTable[bottomRightType];
	else bloat.r_xtop = cifScale * RIGHT(t);
	if ((bloatTable[otherType] != 0) && (otherType != type))
	    DBPaintPlane(cifPlane, &bloat, CIFPaintTable,
		(PaintUndoInfo *) NULL);
	bloat.r_xbot = bloat.r_xtop;
    }

endbloat:
    CIFTileOps += 1;
    return 0;
}

#define CIF_PENDING     0
#define CIF_UNPROCESSED CLIENTDEFAULT
#define CIF_PROCESSED   1
#define CIF_IGNORE   	2

/* Conditional push (if tile is unprocessed) */
#define PUSHTILE(tp, dinfo, stack) \
    if (TiGetClient(tp) == CIF_UNPROCESSED) { \
	TiSetClientINT(tp, CIF_PENDING); \
	STACKPUSH(PTR2CD(tp), stack); \
	STACKPUSH(INT2CD(dinfo), stack); \
    }

/* Unconditional push */
#define PUSHTILEALWAYS(tp, dinfo, stack) { \
	STACKPUSH(PTR2CD(tp), stack); \
	STACKPUSH(INT2CD(dinfo), stack); \
    }

#define POPTILE(tp, dinfo, stack) { \
	dinfo = (TileType)CD2INT(STACKPOP(stack)); \
	tp = (Tile *)STACKPOP(stack); \
    }

/* For routines that don't require split tile information */

#define PUSHTILEONLY(tp, stack) \
    if (TiGetClient(tp) == CIF_UNPROCESSED) { \
	TiSetClientINT(tp, CIF_PENDING); \
	STACKPUSH(PTR2CD(tp), stack); \
    }

#define POPTILEONLY(tp, stack) { \
	tp = (Tile *)STACKPOP(stack); \
    }

/*
 *-------------------------------------------------------
 *
 * cifProcessResetFunc --
 *
 *	Unmark tiles
 *
 * Results:	Return 0 to keep the search going.
 *
 *-------------------------------------------------------
 */

int
cifProcessResetFunc(
    Tile *tile,
    TileType dinfo,		/* (unused) */
    ClientData clientData)	/* (unused) */
{
    TiSetClient(tile, CIF_UNPROCESSED);
    return 0;
}

/*
 *-------------------------------------------------------
 *
 * cifProcessSelectiveResetFunc --
 *
 *	Unmark tiles which are partially or wholly outside
 *	the clip area (passed as client data).
 *
 * Results:	Return 0 to keep the search going.
 *
 *-------------------------------------------------------
 */

int
cifProcessSelectiveResetFunc(tile, dinfo, clipArea)
    Tile *tile;
    TileType dinfo;		/* (unused) */
    Rect *clipArea;
{
    Rect area;

    TiToRect(tile, &area);
    if (!GEO_SURROUND(&area, clipArea))
	TiSetClient(tile, CIF_UNPROCESSED);
    return 0;
}

/*
 *-------------------------------------------------------
 *
 * cifFoundFunc --
 *
 *	Find the first tile in the given area.
 *
 * Results:
 *	Return 1 to stop the search and process if an
 *	unprocessed tile was found.  Otherwise, return
 *	0 to keep the search going.
 *
 * Side effects:
 *	Push tile pointer to the stack that is passed
 *	as client data.
 *
 *-------------------------------------------------------
 */

int
cifFoundFunc(
    Tile *tile,
    TileType dinfo,
    Stack **BloatStackPtr)
{
    if (TiGetClient(tile) == CIF_UNPROCESSED)
    {
	PUSHTILE(tile, dinfo, *BloatStackPtr);
	return 1;
    }
    else
	return 0;	
}

/* Data structure for bloat-all function */
typedef struct _bloatStruct {
    CIFOp	*op;
    CellDef	*def;
    TileTypeBitMask connect;
    Plane       **temps;
} BloatStruct;

/*
 * ----------------------------------------------------------------------------
 *
 * cifBloatAllFunc --
 *
 * 	Called once for each tile to be selectively bloated
 *	using the CIFOP_BLOATALL operation.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	Uses the bloat table in the current CIFOp to expand the tile,
 *	depending on which tiles it abuts.  All bordering material that
 *	has bl_distance = 1 is painted into the result plane.
 *
 * ----------------------------------------------------------------------------
 */

int
cifBloatAllFunc(
    Tile *tile,			/* The tile to be processed. */
    TileType dinfo,		/* Split tile information */
    BloatStruct *bls)
{
    Rect area, clipArea;
    TileTypeBitMask *connect;
    Tile *t, *tp, *firstTile = NULL;
    TileType type, ttype;
    BloatData *bloats;
    int i, locScale;
    PlaneMask pmask;
    CIFOp *op;
    CellDef *def;
    Plane **temps;
    static Stack *BloatStack = (Stack *)NULL;
    static Stack *ResetStack = (Stack *)NULL;

    op = bls->op;
    def = bls->def;
    temps = bls->temps;
    connect = &bls->connect;
    bloats = (BloatData *)op->co_client;

    /* This search function is based on drcCheckArea */

    if (BloatStack == (Stack *)NULL)
	BloatStack = StackNew(64);
    if (ResetStack == (Stack *)NULL)
	ResetStack = StackNew(64);

    /* If the type of the tile to be processed is not in the same plane	*/
    /* as the bloat type(s), then find any tile under the tile to be	*/
    /* processed that belongs to the connect mask, and use that as the	*/
    /* starting tile.							*/

    type = (dinfo & TT_SIDE) ? TiGetRightType(tile) : TiGetLeftType(tile);

    if (type == CIF_SOLIDTYPE)
    {
	pmask = 0;
	if (bloats->bl_plane < 0)   /* Bloat types are CIF types */
	    locScale = 1;
	else
	    locScale = (CIFCurStyle) ? CIFCurStyle->cs_scaleFactor : 1;

	/* Get the tile into magic database coordinates if it's in CIF coords */
	TiToRect(tile, &area);
	area.r_xbot /= locScale;
	area.r_xtop /= locScale;
	area.r_ybot /= locScale;
	area.r_ytop /= locScale;

	if (op->co_distance > 0) clipArea = area;
    }
    else
    {
	if (DBIsContact(type))
	{
	    pmask = (bloats->bl_plane < 0) ? 0 :
			CoincidentPlanes(connect, DBLayerPlanes(type));
	}
	else
	{
	    int pNum = DBPlane(type);
	    pmask = (bloats->bl_plane < 0) ? 0 :
			CoincidentPlanes(connect, PlaneNumToMaskBit(pNum));
	}
	if (pmask == 0) TiToRect(tile, &area);

	if (bloats->bl_plane < 0)
	{
	    /* Get the tile into CIF database coordinates if it's in magic coords */
	    area.r_xbot *= cifScale;
	    area.r_xtop *= cifScale;
	    area.r_ybot *= cifScale;
	    area.r_ytop *= cifScale;
	    locScale = 1;
	    if (op->co_distance > 0) clipArea = area;
	}
	else
	{
	    locScale = cifScale;
	    if (op->co_distance > 0) TiToRect(tile, &clipArea);
	}
    }
    if (pmask == 0)
    {
	Rect foundArea;

	if (bloats->bl_plane < 0)   /* Bloat types are CIF types */
	{
	    /* This expands the area to the OR of all temp layers specified */
	    /* which may or may not be useful;  normally one would expand   */
	    /* into a single layer if a temp layer is specified.	    */

	    for (ttype = 0; ttype < TT_MAXTYPES; ttype++, temps++)
		if (bloats->bl_distance[ttype] > 0)
		    (void) DBSrPaintArea((Tile *)NULL, *temps, &area,
				&CIFSolidBits, cifFoundFunc, (ClientData)(&BloatStack));
	}
	else
	{
	    DBSrPaintArea((Tile *)NULL, def->cd_planes[bloats->bl_plane], &area,
		    connect, cifFoundFunc, (ClientData)(&BloatStack));
	}
    }
    else
    {
	PUSHTILE(tile, dinfo, BloatStack);
	firstTile = tile;
    }

    /* Note:  if op->co_distance is 0 then bloat distance is arbitrarily large */
    if (op->co_distance > 0)
    {
	clipArea.r_xbot *= locScale;
	clipArea.r_ybot *= locScale;
	clipArea.r_xtop *= locScale;
	clipArea.r_ytop *= locScale;

	clipArea.r_xtop += op->co_distance;
	clipArea.r_xbot -= op->co_distance;
	clipArea.r_ytop += op->co_distance;
	clipArea.r_ybot -= op->co_distance;
    }

    while (!StackEmpty(BloatStack))
    {
	Rect cifarea;
	TileType tt;

	POPTILE(t, dinfo, BloatStack);
	if (TiGetClientINT(t) != CIF_PENDING) continue;
        TiSetClientINT(t, CIF_PROCESSED);

	/* Get the tile into CIF coordinates. */

	TiToRect(t, &area);
	area.r_xbot *= locScale;
	area.r_ybot *= locScale;
	area.r_xtop *= locScale;
	area.r_ytop *= locScale;

	if (op->co_distance > 0)
	{
	    cifarea.r_xbot = area.r_xbot;
	    cifarea.r_ybot = area.r_ybot;
	    cifarea.r_xtop = area.r_xtop;
	    cifarea.r_ytop = area.r_ytop;

	    /* Note:  This is non-optimal, as it causes all tiles
	     * in the "bloat" group to be re-processed for each
	     * tile processed in the search group.  However, it
	     * is difficult to find an optimal method.
	     */
	    STACKPUSH(t, ResetStack);
	    STACKPUSH(dinfo, ResetStack);
	    GeoClip(&cifarea, &clipArea);
	    if (GEO_RECTNULL(&cifarea))
		continue;
	}

	/* Diagonal:  If expanding across the edge of a diagonal, then	*/
	/* just fill the whole tile.  Note that diagonal tiles are not	*/
	/* clipped;  the clipping only determines if the tile is	*/
	/* outside the clip area.					*/

	if (IsSplit(t))
	{
	    tt = TiGetTypeExact(t) | dinfo;
	    if ((tt & TT_SIDE) && (TTMaskHasType(connect, TiGetLeftType(t))))
		DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
	    else if (!(tt & TT_SIDE) && (TTMaskHasType(connect, TiGetRightType(t))))
		DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
	    else
		DBNMPaintPlane(cifPlane, TiGetTypeExact(t) | dinfo, &area,
			CIFPaintTable, (PaintUndoInfo *) NULL);
	}
	else
	{
	    tt = TiGetTypeExact(t);
	    if (op->co_distance > 0)
		DBNMPaintPlane(cifPlane, TiGetTypeExact(t), &cifarea,
			CIFPaintTable, (PaintUndoInfo *) NULL);
	    else
		DBNMPaintPlane(cifPlane, TiGetTypeExact(t), &area,
			CIFPaintTable, (PaintUndoInfo *) NULL);
	}

	/* Top */
	if ((op->co_distance == 0) || (area.r_ytop < clipArea.r_ytop))
	    for (tp = RT(t); RIGHT(tp) > LEFT(t); tp = BL(tp))
		if (TTMaskHasType(connect, TiGetBottomType(tp)))
		    PUSHTILE(tp, (SplitDirection(tp) == ((tt & TT_DIRECTION) ? 1 : 0)) ?
				(TileType)0 : (TileType)TT_SIDE, BloatStack);

	/* Left */
	if ((op->co_distance == 0) || (area.r_xbot > clipArea.r_xbot))
	    for (tp = BL(t); BOTTOM(tp) < TOP(t); tp = RT(tp))
		if (TTMaskHasType(connect, TiGetRightType(tp)))
		    PUSHTILE(tp, (TileType)TT_SIDE, BloatStack);

	/* Bottom */
	if ((op->co_distance == 0) || (area.r_ybot > clipArea.r_ybot))
	    for (tp = LB(t); LEFT(tp) < RIGHT(t); tp = TR(tp))
		if (TTMaskHasType(connect, TiGetTopType(tp)))
		    PUSHTILE(tp, (SplitDirection(tp) == ((tt & TT_DIRECTION) ? 1 : 0)) ?
				(TileType)TT_SIDE : (TileType)0, BloatStack);

	/* Right */
	if ((op->co_distance == 0) || (area.r_xtop < clipArea.r_xtop))
	    for (tp = TR(t); TOP(tp) > BOTTOM(t); tp = LB(tp))
		if (TTMaskHasType(connect, TiGetLeftType(tp)))
		    PUSHTILE(tp, (TileType)0, BloatStack);
    }

    /* Clear self */
    // TiSetClient(tile, CIF_UNPROCESSED);

    /* NOTE:  Tiles must be cleared after the bloat-all function has
     * completed.  However, for bloat-all with a limiting distance,
     * it is necessary to clear tiles after each tile processed,
     * because a processed tile that was partially or wholly outside
     * of the clip area may be inside another tile's clip area.
     * Those tiles that were not fully inside the clip area have all
     * been pushed to the ResetStack.
     */

    while (!StackEmpty(ResetStack))
    {
	POPTILE(t, dinfo, ResetStack);
	TiSetClient(t, CIF_UNPROCESSED);
    }

    if (SigInterruptPending) return 1;
    return 0;	/* Keep the search alive. . . */
}

/* Data structure to pass plane and minimum width to the callback function */
typedef struct _bridgeStruct {
    Plane       *plane;
    BridgeData	*bridge;
} BridgeStruct;

/* Bridge Check data structure */
typedef struct _bridgeCheckStruct {
   Tile *tile;		/* Tile that triggered search (ignore this tile) */
   Rect *area;		/* Area of search */
   int	direction;	/* What outside corner to look for */
   Tile *violator;	/* Return the violator tile in this space */
   TileType checktype;	/* Type to check for, either TT_SPACE or CIF_SOLIDTYPE */
} BridgeCheckStruct;

/* Direction flags */
#define BRIDGE_NW	1
#define BRIDGE_SW	2

/*
 * ----------------------------------------------------------------------------
 *
 * Function that returns the Euclidean distance corresponding to a manhattan
 * distance of the given width, at 45 degrees.
 *
 * This is used by the bridging method to keep the amount of extra material
 * added to a minimum.
 *
 * ----------------------------------------------------------------------------
 */

int
GetEuclideanWidthGrid(
    int width)
{
    int weuclid;
    int delta;
    int limit;

    limit = CIFCurStyle->cs_gridLimit;

    weuclid = (int)(ceil((double)width * 0.70711));
    if (CIFCurStyle && (limit > 1))
    {
	delta = weuclid % limit;
	if (delta > 0)
	{
	    weuclid -= delta;
	    weuclid += limit;
	}
    }
    return weuclid;
}

/*
 * ----------------------------------------------------------------------------
 *
 * GetExpandedAreaGrid --
 *
 *	Given a pointer "area" to a Rect and a minimum layer width "width",
 *	find the minimum size rectangle that expands "area" in all directions
 *	equally to ensure that the length of the diagonal of the expanded
 *	area is greater than or equal to "width".  This is used by the
 *	"bridge" operator to ensure that the bridging rectangle meets the
 *	minimum layer width requirement.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The Rect structure pointed to by "area" may be altered.
 *
 * Notes:
 *	The calculation needed depends on the geometry.  If there is a
 *	horizontal gap between the tiles, then the bridging shape must
 *	be as tall as the minimum width rule;  this is the vertical
 *	case (horiz = FALSE).  If there is a vertical gap between the
 *	tiles, then the bridging shape must be as wide as the minimum
 *	width rule;  this is the horizontal case (horiz = TRUE).  Then
 *	calculate the length of the bridging shape in the other direction
 *	needed to ensure that the join of the bridging shape and the
 *	preexisting shapes is always wide enough to satisfy the width
 *	rule.  Which calculation is needed depends on whether the shapes
 *	are catecorner (overlap = FALSE) or overlapping in one direction
 *	(overlap = TRUE).
 *
 * ----------------------------------------------------------------------------
 */

void
GetExpandedAreaGrid(
    int wrule,
    bool space,
    Rect *area)
{
    bool horiz;
    bool overlap;
    Rect r;

    int width, height, dx, dy, a, b, delta, dx2, dy2, limit;

    overlap = FALSE;
    if (area->r_xbot > area->r_xtop)
    {
	overlap = TRUE;
	horiz = (space) ? FALSE : TRUE;
    }
    if (area->r_ybot > area->r_ytop)
    {
	overlap = TRUE;
	horiz = (space) ? TRUE : FALSE;
    }

    GeoCanonicalRect(area, &r);
    width = r.r_xtop - r.r_xbot;
    height = r.r_ytop - r.r_ybot;

    if (overlap == FALSE)
	horiz = (width > height);

    if (horiz)
    {
	a = wrule - width;
	dx = (int)ceilf((float)a / 2.0);
	if (overlap || space)
	    b = wrule * wrule - (width + dx) * (width + dx);
	else
	    b = wrule * wrule - dx * dx;
	if (space && !overlap)
	    dy = (int)ceilf(sqrtf((float)b) - height);
	else
	    dy = (int)ceilf(sqrtf((float)b));

	b = wrule * wrule - width * width;
	dy2 = (int)ceilf((sqrtf((float)b) - height) / 2.0);
	if (dy2 > dy) dy = dy2;
    }
    else
    {
	a = wrule - height;
	dy = (int)ceilf((float)a / 2.0);
	if (overlap || space)
	    b = wrule * wrule - (height + dy) * (height + dy);
	else
	    b = wrule * wrule - dy * dy;
	if (space && !overlap)
	    dx = (int)ceilf(sqrtf((float)b) - width);
	else
	    dx = (int)ceilf(sqrtf((float)b));

	b = wrule * wrule - height * height;
	dx2 = (int)ceilf((sqrtf((float)b) - width) / 2.0);
	if (dx2 > dx) dx = dx2;
    }

    r.r_xbot -= dx;
    r.r_xtop += dx;
    r.r_ybot -= dy;
    r.r_ytop += dy;

    limit = CIFCurStyle->cs_gridLimit;

    if (CIFCurStyle && (limit > 1))
    {
	delta = r.r_xbot % limit;
	if (delta > 0)
	    r.r_xbot -= delta;
	else if (delta < 0)
	    r.r_xbot -= limit + delta;

	delta = r.r_xtop % limit;
	if (delta > 0)
	    r.r_xtop += limit - delta;
	else if (delta < 0)
	    r.r_xtop -= delta;

	delta = r.r_ybot % limit;
	if (delta > 0)
	    r.r_ybot -= delta;
	else if (delta < 0)
	    r.r_ybot -= limit + delta;

	delta = r.r_ytop % limit;
	if (delta > 0)
	    r.r_ytop += limit - delta;
	else if (delta < 0)
	    r.r_ytop -= delta;
    }

    *area = r;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifBridgeFunc1 --
 *
 * 	Called for each relevant tile during bridge operations.  The
 *	bridge operation is responsible for preventing a grow-shrink
 *	pair of operations from leaving width or spacing DRC errors,
 *	which happens when tiles are in a catecorner position from
 *	each other.  The bridge operation adds a bridge of material
 *	between the two tiles that solves spacing requirements between
 *      the two tiles while satisfying the minimum width requirements,
 *	adding a minimum amount of material to do so.
 *
 *	The current version of this routine adds material on a stair-step
 *	pattern;  preferably this should be extended to create material
 *	at allowed angles, where the allowed angles (90, 45, or any) are
 *	specified as an option to the bridge statement in the tech file.
 *
 *	GrowDistance is equal to the spacing rule distance needing to be
 *	satisfied.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	Paints into cifNewPlane.  Tiles in old plane are tagged with
 *	a static value in ClientData, which does not need to be reset
 *	since the old plane will be free'd.
 * ----------------------------------------------------------------------------
 */

int
cifBridgeFunc1(
    Tile *tile,
    TileType dinfo,
    BridgeStruct *brs)
{
    Plane *plane = brs->plane;
    Rect area;
    Tile *tp1, *tp2, *tpx;
    int width = brs->bridge->br_width;
    int spacing = growDistance;
    BridgeCheckStruct brcs;

    /* Forward reference */
    int cifBridgeCheckFunc(Tile *tile, TileType dinfo, BridgeCheckStruct *brcs);

    /* If tile is marked, then it has been handled, so ignore it */
    if (TiGetClient(tile) != CIF_UNPROCESSED) return 0;

    /* Find each tile outside corner (only two cases need to be checked) */

    /* Check for NE outside corner */
    tp1 = TR(tile);  /* Tile on right side at the top of this tile */
    tp2 = RT(tile);  /* Tile on top side at the right of this tile */
    if ((TiGetLeftType(tp1) == TT_SPACE) &&
	    (TiGetBottomType(tp2) == TT_SPACE))
    {
	/* Set search box */
	area.r_xbot = RIGHT(tile) - width;
	area.r_xtop = RIGHT(tile) + spacing;
	area.r_ybot = TOP(tile) - width;
	area.r_ytop = TOP(tile) + spacing;

	/* Find violator tiles */
	brcs.tile = tile;
	brcs.area = &area;
	brcs.direction = BRIDGE_SW;
	brcs.checktype = TT_SPACE;
	if (DBSrPaintArea((Tile *) NULL, plane, &area,
		    &CIFSolidBits, cifBridgeCheckFunc, (ClientData)&brcs) == 1)
	{
	    tpx = brcs.violator;

	    area.r_xbot = RIGHT(tile);
	    area.r_ybot = TOP(tile);
	    area.r_xtop = LEFT(tpx);
	    area.r_ytop = BOTTOM(tpx);

 	    GetExpandedAreaGrid(width, FALSE, &area);

	    /* Trivial implementation: fill box */
	    /* (to do: use stairstep to avoid filling unnecessary areas) */
	    DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
	}
    }

    /* Check for SE outside corner */
    for (tp1 = TR(tile); BOTTOM(tp1) > BOTTOM(tile); tp1 = LB(tp1));
    for (tp2 = LB(tile); RIGHT(tp1) < RIGHT(tile); tp2 = TR(tp2));
    if ((TiGetLeftType(tp1) == TT_SPACE) &&
	    (TiGetTopType(tp2) == TT_SPACE))
    {
	/* Set search box */
	area.r_xbot = RIGHT(tile) - width;
	area.r_xtop = RIGHT(tile) + spacing;
	area.r_ybot = BOTTOM(tile) - spacing;
	area.r_ytop = BOTTOM(tile) + width;

	/* Find violator tiles */
	brcs.tile = tile;
	brcs.area = &area;
	brcs.direction = BRIDGE_NW;
	brcs.checktype = TT_SPACE;
	if (DBSrPaintArea((Tile *) NULL, plane, &area,
		    &CIFSolidBits, cifBridgeCheckFunc, (ClientData)&brcs) == 1)
	{
	    tpx = brcs.violator;

	    area.r_xbot = RIGHT(tile);
	    area.r_ybot = TOP(tpx);
	    area.r_xtop = LEFT(tpx);
	    area.r_ytop = BOTTOM(tile);

 	    GetExpandedAreaGrid(width, FALSE, &area);

	    /* Trivial implementation: fill box */
	    /* (to do: use stairstep to avoid filling unnecessary areas) */
	    DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
	}
    }
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifBridgeFunc2 --
 *
 * 	Called for each relevant tile during bridge operations.  The
 *	bridge operation is responsible for preventing a grow-shrink
 *	pair of operations from leaving width or spacing DRC errors,
 *	which happens when tiles are overlapping at a corner with the
 *	overlap failing to meet the minimum width requirement.  The
 *	bridge operation adds a bridge of material over the pinch
 *	point that solves the minimum width requirement, adding a
 *	minimum amount of material to do so.
 *
 *	The current version of this routine adds material on a stair-step
 *	pattern;  preferably this should be extended to create material
 *	at allowed angles, where the allowed angles (90, 45, or any) are
 *	specified as an option to the bridge statement in the tech file.
 *
 *	growDistance is equal to the spacing rule distance needing to be
 *	satisfied.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	Paints into cifNewPlane.  Tiles in old plane are tagged with
 *	a static value in ClientData, which does not need to be reset
 *	since the old plane will be free'd.
 * ----------------------------------------------------------------------------
 */

int
cifBridgeFunc2(
    Tile *tile,
    TileType dinfo,
    BridgeStruct *brs)
{
    Plane *plane = brs->plane;
    Rect area;
    Tile *tp1, *tp2, *tpx;
    int width = brs->bridge->br_width;
    int wtest;
    int spacing = growDistance;
    BridgeCheckStruct brcs;

    /* Forward reference */
    int cifBridgeCheckFunc(Tile *tile, TileType dinfo, BridgeCheckStruct *brcs);

    /* If tile is marked, then it has been handled, so ignore it */
    if (TiGetClient(tile) != CIF_UNPROCESSED) return 0;

    /* Find each tile outside corner (only two cases need to be checked) */

    /* Check for NE outside corner */
    tp1 = TR(tile);  /* Tile on right side at the top of this tile */
    tp2 = RT(tile);  /* Tile on top side at the right of this tile */
    if ((TiGetLeftType(tp1) == CIF_SOLIDTYPE) &&
	    (TiGetBottomType(tp2) == CIF_SOLIDTYPE))
    {
	/* Set search box */
	area.r_xbot = RIGHT(tile) - spacing;
	area.r_xtop = RIGHT(tile) + width;
	area.r_ybot = TOP(tile) - spacing;
	area.r_ytop = TOP(tile) + width;

	/* Find violator tiles */
	brcs.tile = tile;
	brcs.area = &area;
	brcs.direction = BRIDGE_SW;
	brcs.checktype = CIF_SOLIDTYPE;
	if (DBSrPaintArea((Tile *) NULL, plane, &area,
		    &DBSpaceBits, cifBridgeCheckFunc, (ClientData)&brcs) == 1)
	{
	    tpx = brcs.violator;

	    area.r_xbot = RIGHT(tile);
	    area.r_ybot = TOP(tile);
	    area.r_xtop = LEFT(tpx);
	    area.r_ytop = BOTTOM(tpx);

	    GetExpandedAreaGrid(width, TRUE, &area);

	    /* Trivial implementation: fill box */
	    /* (to do: use stairstep to avoid filling unnecessary areas) */
	    DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
	}
    }

    /* Check for SE outside corner */
    for (tp1 = TR(tile); BOTTOM(tp1) > BOTTOM(tile); tp1 = LB(tp1));
    for (tp2 = LB(tile); RIGHT(tp2) < RIGHT(tile); tp2 = TR(tp2));
    if ((TiGetLeftType(tp1) == CIF_SOLIDTYPE) &&
	    (TiGetTopType(tp2) == CIF_SOLIDTYPE))
    {
	/* Set search box */
	area.r_xbot = RIGHT(tile) - spacing;
	area.r_xtop = RIGHT(tile) + width;
	area.r_ybot = BOTTOM(tile) - width;
	area.r_ytop = BOTTOM(tile) + spacing;

	/* Find violator tiles */
	brcs.tile = tile;
	brcs.area = &area;
	brcs.direction = BRIDGE_NW;
	brcs.checktype = CIF_SOLIDTYPE;
	if (DBSrPaintArea((Tile *) NULL, plane, &area,
		    &DBSpaceBits, cifBridgeCheckFunc, (ClientData)&brcs) == 1)
	{
	    tpx = brcs.violator;

	    area.r_xbot = RIGHT(tile);
	    area.r_ybot = TOP(tpx);
	    area.r_xtop = LEFT(tpx);
	    area.r_ytop = BOTTOM(tile);

	    GetExpandedAreaGrid(width, TRUE, &area);

	    /* Trivial implementation: fill box */
	    /* (to do: use stairstep to avoid filling unnecessary areas) */
	    DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
	}
    }
    return 0;
}

/*
 *-----------------------------------------------------------------------
 * Callback function for cifBridgeFunc1 and cifBridgeFunc2 to find if
 * there are violator cells in the search area.	 If a violator cell is
 * found, then put the tile pointer in the BridgeCheckStruct and return
 * value 1 to stop the search.	Otherwise return 0 to keep going.
 *-----------------------------------------------------------------------
 */

int
cifBridgeCheckFunc(
    Tile *tile,
    TileType dinfo,
    BridgeCheckStruct *brcs)
{
    int dir = brcs->direction;
    Tile *self = brcs->tile;
    Tile *tp1, *tp2;
    TileType checktype = brcs->checktype;

    if (self == tile) return 0;	    /* Ignore the triggering tile */

    switch (dir) {
	case BRIDGE_NW:
	    /* Ignore tile if NW corner is not in the search area */
	    for (tp1 = RT(tile); LEFT(tp1) > LEFT(tile); tp1 = BL(tp1));
	    if (LEFT(tile) <= brcs->area->r_xbot || TOP(tile) >= brcs->area->r_ytop)
		break;
	    /* Check that this is not a false corner */
	    else if (TiGetBottomType(tp1) == TiGetTopType(tile))
		break;
	    /* Ignore tile if split, and SE corner is clipped */
	    if (TiGetRightType(tile) == checktype || TiGetBottomType(tile) == checktype)
		break;
	    for (tp2 = BL(tile); TOP(tp2) < TOP(tile); tp2 = RT(tp2));
	    if ((TiGetBottomType(tp1) == checktype) &&
		    (TiGetRightType(tp2) == checktype))
	    {
		brcs->violator = tile;
		return 1;	/* Violator found */
	    }
	    break;
	case BRIDGE_SW:
	    /* Ignore tile if SW corner is not in the search area */
	    tp1 = LB(tile);
	    if (LEFT(tile) <= brcs->area->r_xbot || BOTTOM(tile) <= brcs->area->r_ybot)
		break;
	    /* Check that this is not a false corner */
	    else if (TiGetTopType(tp1) == TiGetBottomType(tile))
		break;
	    /* Ignore tile if split, and NE corner is clipped */
	    if (TiGetRightType(tile) == checktype || TiGetTopType(tile) == checktype)
		break;
	    tp2 = BL(tile);
	    if ((TiGetTopType(tp1) == checktype) ||
		    (TiGetRightType(tp2) == checktype))
	    {
		brcs->violator = tile;
		return 1;	/* Violator found */
	    }
	    break;
    }
    return 0;	/* Nothing found here, so keep going */
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifOrthogonalFunc --
 *
 * 	Called for each relevant tile during "orthogonal" operations.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	Paints into cifNewPlane.  Tiles in old plane are copied as-is
 *	if they are not split tiles.  Split tiles are either replaced
 *	with a rectangle of type "1" or "0" depending on whether the
 *	option is "fill" or "remove", respectively.
 * ----------------------------------------------------------------------------
 */

int
cifOrthogonalFunc(
    Tile *tile,
    TileType dinfo,
    const PaintResultType *table)		/* Table to be used for painting. */
{
    Rect area;
    TileType oldType = TiGetTypeExact(tile) | dinfo;

    TiToRect(tile, &area);

    /* In scaling the tile, watch out for infinities!!  If something
     * is already infinity, don't change it. */

    if (area.r_xbot > TiPlaneRect.r_xbot) area.r_xbot *= cifScale;
    if (area.r_ybot > TiPlaneRect.r_ybot) area.r_ybot *= cifScale;
    if (area.r_xtop < TiPlaneRect.r_xtop) area.r_xtop *= cifScale;
    if (area.r_ytop < TiPlaneRect.r_ytop) area.r_ytop *= cifScale;

    /* Diagonal tiles get replaced with non-diagonal tiles */

    if (oldType & TT_DIAGONAL)
	DBPaintPlane(cifPlane, &area, table, (PaintUndoInfo *) NULL);
    else
	DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);

    CIFTileOps += 1;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifCloseFunc --
 *
 * 	Called for each relevant tile during close operations.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	Paints into cifNewPlane.  Tiles in old plane are tagged with
 *	a static value in ClientData, which does not need to be reset
 *	since the old plane will be free'd.
 * ----------------------------------------------------------------------------
 */

#define CLOSE_SEARCH 0
#define CLOSE_FILL   1
#define CLOSE_DONE   2

int
cifCloseFunc(
    Tile *tile,
    TileType dinfo,
    Plane *plane)
{
    Rect area, newarea;
    int atotal;
    int cifGatherFunc(Tile *tile, TileType dinfo, int *atotal, int mode);

    /* If tile is marked, then it has been handled, so ignore it */
    if (TiGetClient(tile) != CIF_UNPROCESSED) return 0;

    atotal = 0;

    /* Search all sides for connected space tiles, and accumulate the	*/
    /* total area.  If any connected tile borders infinity, then stop	*/
    /* searching because the area is not enclosed.			*/

    cifGatherFunc(tile, dinfo, &atotal, CLOSE_SEARCH);

    /* If the total area is smaller than the rule area, then paint all	*/
    /* the tile areas into the destination plane.  A rule area of zero	*/
    /* indicates that any enclosed area, no matter how large, should be	*/
    /* filled.								*/

    if ((atotal != INFINITY) && ((atotal < growDistance) || (growDistance == 0)))
    {
	cifGatherFunc(tile, dinfo, &atotal, CLOSE_FILL);
    }
    else
    {
	cifGatherFunc(tile, dinfo, &atotal, CLOSE_DONE);
    }
    return 0;
}

int
cifGatherFunc(
    Tile *tile,
    TileType dinfo,
    int *atotal,
    int mode)
{
    Tile *tp;
    TileType type;
    dlong locarea;
    Rect area, newarea;
    ClientData cdata = (mode == CLOSE_SEARCH) ? CIF_UNPROCESSED :
	    CD2INT(CIF_PENDING);

    static Stack *GatherStack = (Stack *)NULL;

    if (GatherStack == (Stack *)NULL)
	GatherStack = StackNew(64);

    PUSHTILE(tile, dinfo, GatherStack);
    while (!StackEmpty(GatherStack))
    {
	TileType newdinfo;

	POPTILE(tile, dinfo, GatherStack);
	newdinfo = TiGetTypeExact(tile) | dinfo;

	/* Ignore if tile has already been processed */
	if (TiGetClient(tile) != cdata) continue;

	TiToRect(tile, &area);

	/* Boundary tiles indicate an unclosed area, so set the area total to 	*/
	/* INFINITY and don't try to run calculations on it.  NOTE:		*/
	/* TiPlaneRect is slightly smaller than the plane boundaries on all	*/
	/* sides.								*/

    	if ((area.r_xbot <= TiPlaneRect.r_xbot) ||
		(area.r_ybot <= TiPlaneRect.r_ybot) ||
	    	(area.r_xtop >= TiPlaneRect.r_xtop) ||
		(area.r_ytop >= TiPlaneRect.r_ytop))
	    *atotal = INFINITY;

    	/* Stop accumulating if already larger than growDistance to avoid the   */
    	/* possibility of integer overflow.					*/
    	if (mode == CLOSE_SEARCH)
    	{
	    if ((*atotal != INFINITY) && (*atotal < growDistance))
	    {
	    	locarea = (dlong)(area.r_xtop - area.r_xbot)
				* (dlong)(area.r_ytop - area.r_ybot);
	        if (IsSplit(tile)) locarea /= 2;
	        if (locarea > (dlong)INFINITY)
		    *atotal = INFINITY;
	        else
		    *atotal += (int)locarea;
	    }
        }
        else if (mode == CLOSE_FILL)
        {
	    /* The tile type cannot be depended on to have the TT_SIDE bit set	*/
	    /* for the side of the tile that is TT_SPACE.  So set the side bit	*/
	    /* manually.							*/

	    if (IsSplit(tile))
	    {
	        if (TiGetLeftType(tile) == TT_SPACE)
		    newdinfo &= ~TT_SIDE;
	        else
		    newdinfo |= TT_SIDE;
	    }

	    DBNMPaintPlane(cifPlane, newdinfo, &area, CIFPaintTable,
			(PaintUndoInfo *)NULL);
	    CIFTileOps++;
        }

        if (mode == CLOSE_SEARCH)
	    TiSetClientINT(tile, CIF_PENDING);
        else
	    TiSetClientINT(tile, CIF_PROCESSED);

        /* Look for additional neighboring space tiles */
        /* Check top */
        if (area.r_ytop != TiPlaneRect.r_ytop)
            for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
	        if (TiGetClient(tp) == cdata && TiGetBottomType(tp) == TT_SPACE)
		{
		    PUSHTILEALWAYS(tp,
				((newdinfo & TT_DIRECTION) != (TiGetTypeExact(tp) & TT_DIRECTION)) ?
				(TileType)TT_SIDE : (TileType)0,
				GatherStack);
		}

    	/* Check bottom */
    	if (area.r_ybot != TiPlaneRect.r_ybot)
            for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
		if (TiGetClient(tp) == cdata && TiGetTopType(tp) == TT_SPACE)
		{
		    PUSHTILEALWAYS(tp,
				((newdinfo & TT_DIRECTION) == (TiGetTypeExact(tp) & TT_DIRECTION)) ?
				(TileType)TT_SIDE : (TileType)0,
				GatherStack);
		}

    	/* Check left */
    	if (area.r_xbot != TiPlaneRect.r_xbot)
	    for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
		if (TiGetClient(tp) == cdata && TiGetRightType(tp) == TT_SPACE)
		{
		    PUSHTILEALWAYS(tp, (TileType)TT_SIDE, GatherStack);
		}

    	/* Check right */
    	if (area.r_xtop != TiPlaneRect.r_xtop)
	    for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
		if (TiGetClient(tp) == cdata && TiGetLeftType(tp) == TT_SPACE)
		{
		    PUSHTILEALWAYS(tp, (TileType)0, GatherStack);
		}
    }
    return 0;
}

/*--------------------------------------------------------------*/
/* Support routines and definitions for cifSquaresFillArea	*/
/*--------------------------------------------------------------*/

/*------------------------------------------------------*/
/* Data structure used for identifying contact strips	*/
/*------------------------------------------------------*/

typedef struct _linkedStrip {
    Rect	area;
    bool	vertical;	/* Tile is vertical */
    bool	shrink_ld;	/* Shrink left or down before creating cuts */
    bool	shrink_ur;	/* Shrink right or up before creating cuts */
    struct _linkedStrip *strip_next;
} linkedStrip;

typedef struct
{
   int size;
   int pitch;
   linkedStrip *strips;
} StripsData;

/*
 *-------------------------------------------------------
 *
 * cifSquaresInitFunc --
 *
 *	Find the first unprocessed tile in the plane.
 *
 * Results:
 *	Return 1 to stop the search and process.
 *	Otherwise, return 0 to keep the search going.
 *
 *-------------------------------------------------------
 */

int
cifSquaresInitFunc(tile, dinfo, clientData)
    Tile *tile;
    TileType dinfo;		/* (unused) */
    ClientData clientData;	/* (unused) */
{
    if (TiGetClient(tile) == CIF_UNPROCESSED)
	return 1;
    else
	return 0;
}

/*
 *-------------------------------------------------------
 *
 * cifSquaresStripFunc --
 *
 *	Find vertical or horizontal strips of contact
 * material that is between 1 and 2 contact cuts wide.
 * Generate and return a list of all such strips.
 *
 * Results:  Return 0 to keep the search going.
 *
 *-------------------------------------------------------
 */

int
cifSquaresStripFunc(
    Tile *tile,
    TileType dinfo,		/* (unused) */
    StripsData *stripsData)
{
    bool vertical;
    int width, height;
    linkedStrip *newStrip;
    Tile *tp, *tp2;
    Rect bbox;

    if (IsSplit(tile))
	return 0;
    TiToRect(tile, &bbox);

    /* Check if the tile is wide enough for exactly one cut */

    width = bbox.r_xtop - bbox.r_xbot;
    height = bbox.r_ytop - bbox.r_ybot;

    if (height > width)
    {
	vertical = TRUE;
	height = width;
    }
    else
	vertical = FALSE;

    if ((height < stripsData->size) || (height >=
		(stripsData->size + stripsData->pitch)))
	return 0;

    /* Ignore strips that are part of a larger	*/
    /* collection of non-manhattan geometry.	*/

    for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
	if (IsSplit(tp))
	    break;
    if (BOTTOM(tp) < TOP(tile))
	return 0;

    /* Note that the max horizontal stripes rule guarantees	*/
    /* that a tile is always bounded on left and right by	*/
    /* TT_SPACE.  Thus, we only need to search the top and	*/
    /* bottom boundaries of horizontal tiles.			*/

    if (vertical)
    {
	newStrip = (linkedStrip *)mallocMagic(sizeof(linkedStrip));
	newStrip->area = bbox;
	newStrip->vertical = TRUE;
	newStrip->shrink_ur = (TTMaskHasType(&CIFSolidBits,
		TiGetBottomType(RT(tile)))) ? TRUE : FALSE;
	newStrip->shrink_ld = (TTMaskHasType(&CIFSolidBits,
		TiGetTopType(LB(tile)))) ? TRUE : FALSE;
	newStrip->strip_next = stripsData->strips;
	stripsData->strips = newStrip;
    }
    else
    {
	int segstart, segend;
	int matchstart, matchend;

	tp = RT(tile);
	segend = RIGHT(tile);
	while (RIGHT(tp) > LEFT(tile))
	{
	    /* Isolate segments of space along the top of the tile */

	    while ((RIGHT(tp) > LEFT(tile)) &&
			TTMaskHasType(&CIFSolidBits, TiGetBottomType(tp)))
		tp = BL(tp);
	    segend = MIN(segend, RIGHT(tp));
	    while ((RIGHT(tp) > LEFT(tile)) &&
			TTMaskHasType(&DBSpaceBits, TiGetBottomType(tp)))
		tp = BL(tp);
	    segstart = MAX(LEFT(tile), RIGHT(tp));
	    if (segend <= segstart) break;

	    /* Find matching segments along the bottom of the  tile */

	    for (tp2 = LB(tile); RIGHT(tp2) < segstart; tp2 = TR(tp2));

	    while (LEFT(tp2) < segend)
	    {
		while (RIGHT(tp2) < segstart) tp2 = TR(tp2);
		while ((LEFT(tp2) < segend) &&
				TTMaskHasType(&CIFSolidBits, TiGetTopType(tp2)))
		    tp2 = TR(tp2);
		matchstart = MAX(LEFT(tp2), segstart);
		while ((LEFT(tp2) < segend) &&
				TTMaskHasType(&DBSpaceBits, TiGetTopType(tp2)))
		    tp2 = TR(tp2);
		matchend = MIN(LEFT(tp2), segend);
		if (matchend <= matchstart) break;

		/* Process the strip */

		newStrip = (linkedStrip *)mallocMagic(sizeof(linkedStrip));
		newStrip->area = bbox;
		newStrip->area.r_xbot = matchstart;
		newStrip->area.r_xtop = matchend;
		newStrip->vertical = FALSE;
		newStrip->strip_next = stripsData->strips;
		newStrip->shrink_ur = (matchend != RIGHT(tile)) ? TRUE : FALSE;
		newStrip->shrink_ld = (matchstart != LEFT(tile)) ? TRUE : FALSE;

		stripsData->strips = newStrip;
	    }
	}
    }
    return 0;
}

/*
 *-------------------------------------------------------
 *
 * cifUnconnectFunc --
 *
 *	Find space tiles inside a possible contact area
 *
 * Results:	Return 1 to stop the ongoing search.
 *
 *-------------------------------------------------------
 */


int
cifUnconnectFunc(
    Tile *tile,
    TileType dinfo,		/* (unused) */
    ClientData clientData)	/* (unused) */
{
    TileType t = TiGetTypeExact(tile);
    if (t == TT_SPACE) return 1;
    else if (t & TT_DIAGONAL) return 1;
    else if (TiGetClientINT(tile) != CIF_PROCESSED) return 1;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifRectBoundingBox --
 *
 * 	Fill regions of the current CIF plane with rectangles that represent
 *	the bounding box of each unconnected area.  This function "cleans
 *	up" areas processed by multiple rules and removes notches and
 *	cut-outs.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *
 * ----------------------------------------------------------------------------
 */

void
cifRectBoundingBox(
    CIFOp *op,
    CellDef *cellDef,
    Plane *plane)
{
    Tile *tile = NULL, *t, *tp;
    Rect bbox, area, *maxr;
    int i, j, savecount;
    TileType type;
    bool simple;
    static Stack *BoxStack = (Stack *)NULL;

    if (BoxStack == (Stack *)NULL)
	BoxStack = StackNew(64);

    while (DBSrPaintArea((Tile *)tile, plane, &TiPlaneRect, &CIFSolidBits,
		cifSquaresInitFunc, (ClientData)NULL) != 0)
    {
	/* Now, search for (nontrivially) connected tiles in all	*/
	/* directions.  Mark the tiles, and record the bounding box.	*/
	/* (Largely copied from cifSquaresFillArea)			*/

	simple = TRUE;
	tile = PlaneGetHint(plane);
	TiToRect(tile, &bbox);

	PUSHTILEONLY(tile, BoxStack);
	while (!StackEmpty(BoxStack))
	{
	    POPTILEONLY(t, BoxStack);
	    if (TiGetClientINT(t) != CIF_PENDING) continue;
            TiSetClientINT(t, CIF_PROCESSED);

	    /* Adjust bounding box */
	    TiToRect(t, &area);
	    GeoInclude(&area, &bbox);

	    if (IsSplit(t)) simple = FALSE;

	    /* Top */
	    for (tp = RT(t); RIGHT(tp) > LEFT(t); tp = BL(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetBottomType(tp)))
		{
		    simple = FALSE;
		    PUSHTILEONLY(tp, BoxStack);
		}

	    /* Left */
	    for (tp = BL(t); BOTTOM(tp) < TOP(t); tp = RT(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetRightType(tp)))
		{
		    simple = FALSE;
		    PUSHTILEONLY(tp, BoxStack);
		}

	    /* Bottom */
	    for (tp = LB(t); LEFT(tp) < RIGHT(t); tp = TR(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetTopType(tp)))
		{
		    simple = FALSE;
		    PUSHTILEONLY(tp, BoxStack);
		}

	    /* Right */
	    for (tp = TR(t); TOP(tp) > BOTTOM(t); tp = LB(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetLeftType(tp)))
		{
		    simple = FALSE;
		    PUSHTILEONLY(tp, BoxStack);
		}
	}

	if (op->co_client == (ClientData)1)	/* external */
	{
	    DBPaintPlane(cifPlane, &bbox, CIFPaintTable, (PaintUndoInfo *)NULL);
	    CIFTileOps++;
	}
	else	/* internal */
	{
	    if (simple)
	    {
		DBPaintPlane(cifPlane, &bbox, CIFPaintTable, (PaintUndoInfo *)NULL);
		CIFTileOps++;
	    }
	    else
	    {
		maxr = FindMaxRectangle2(&bbox, tile, plane, NULL);
		DBPaintPlane(cifPlane, maxr, CIFPaintTable, (PaintUndoInfo *)NULL);
		CIFTileOps++;
	    }
	}

	/* Clear the tiles that were processed in this set */

	TiSetClientINT(tile, CIF_IGNORE);
	PUSHTILEONLY(tile, BoxStack);
	while (!StackEmpty(BoxStack))
	{
	    POPTILEONLY(t, BoxStack);

	    /* Top */
	    for (tp = RT(t); RIGHT(tp) > LEFT(t); tp = BL(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, BoxStack);
		}

	    /* Left */
	    for (tp = BL(t); BOTTOM(tp) < TOP(t); tp = RT(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, BoxStack);
		}

	    /* Bottom */
	    for (tp = LB(t); LEFT(tp) < RIGHT(t); tp = TR(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, BoxStack);
		}

	    /* Right */
	    for (tp = TR(t); TOP(tp) > BOTTOM(t); tp = LB(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, BoxStack);
		}
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifSquaresFillArea --
 *
 *	Fill areas in the current CIF output plane with contact cuts based on
 *	the SQUARES operation passed in "op".  This differs from the original
 *	tile-based contact cut generation by collecting all connected tiles
 *	in an area, and placing cuts relative to that area's bounding box.
 *	A tile search is used to select the parts of any non-rectangular area
 *	inside the bounding box that allow contact cuts, which lets cuts
 *	be placed across tile boundaries and inside non-manhattan tiles.
 *	It also allows contacts to be placed inside complex structures such
 *	as (possibly intersecting) guardrings.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *
 * ----------------------------------------------------------------------------
 */

void
cifSquaresFillArea(
    CIFOp *op,
    CellDef *cellDef,
    Plane *plane)
{
    Tile *tile, *t, *tp;
    Rect bbox, area, square, cut, llcut;
    int nAcross, nUp, left, pitch, size, diff, right;
    int i, j, k, savecount;
    TileType type;
    SquaresData *squares = (SquaresData *)op->co_client;
    StripsData stripsData;
    linkedStrip *stripList;
    bool simple;
    static Stack *CutStack = (Stack *)NULL;

    pitch = squares->sq_size + squares->sq_sep;
    size = squares->sq_size + 2 * squares->sq_border;
    diff = squares->sq_sep - 2 * squares->sq_border;

    if (CutStack == (Stack *)NULL)
	CutStack = StackNew(64);

    /* Two-pass algorithm */

    /* Search the plane for "strips", sections of contact that are only	*/
    /* wide enough for 1 cut.  Process them separately, then erase the	*/
    /* processed areas.	  The purpose of this is to avoid applying the	*/
    /* contact matrix algorithm on non-convex spaces, such as guard	*/
    /* rings, where centering the matrix on the bounding box of the	*/
    /* contact area may produce a matrix misaligned with the contact	*/
    /* strips, preventing any contacts from being drawn!  Due to the	*/
    /* maximum horizontal stripes rule for corner stitched tiles, we	*/
    /* need only identify vertical contact strips.  After processing	*/
    /* and erasing them, all remaining areas will be convex.  This	*/
    /* algorithm allows contacts to be drawn in any arbitrary geometry.	*/

    stripsData.size = size;
    stripsData.pitch = pitch;
    stripsData.strips = NULL;
    DBSrPaintArea((Tile *)NULL, plane, &TiPlaneRect, &CIFSolidBits,
		cifSquaresStripFunc, (ClientData)&stripsData);

    /* Generate cuts in each strip found, then erase the strip */

    stripList = stripsData.strips;
    while (stripList != NULL)
    {
	Rect stripLess = stripList->area;

	if (diff > 0)
	{
	    if (stripList->vertical)
	    {
		if (stripList->shrink_ur) stripLess.r_ytop -= diff;
		if (stripList->shrink_ld) stripLess.r_ybot += diff;
	    }
	    else
	    {
		if (stripList->shrink_ur) stripLess.r_xtop -= diff;
		if (stripList->shrink_ld) stripLess.r_xbot += diff;
	    }
	}

	/* Create the cuts */

	if (op->co_opcode == CIFOP_SQUARES)
	    cifSquareFunc(&stripLess, op, &nUp, &nAcross, &llcut);
	else if (op->co_opcode == CIFOP_SQUARES_G)
	    cifSquareGridFunc(&stripLess, op, &nUp, &nAcross, &llcut);

	cut.r_ybot = llcut.r_ybot;
	cut.r_ytop = llcut.r_ytop;

	for (i = 0; i < nUp; i++)
	{
	    cut.r_xbot = llcut.r_xbot;
	    cut.r_xtop = llcut.r_xtop;
	    for (j = 0; j < nAcross; j++)
	    {
		DBPaintPlane(cifPlane, &cut, CIFPaintTable, (PaintUndoInfo *)NULL);
		cut.r_xbot += pitch;
		cut.r_xtop += pitch;
	    }
	    cut.r_ybot += pitch;
	    cut.r_ytop += pitch;
	}
	if (nUp == 0)
	{
	    if (stripList->shrink_ur == 0 && stripList->shrink_ld == 0)
	    {
		/* The following code is backwardly-compatible with the	 */
		/* original behavior of allowing cuts that do not have	 */
		/* sufficient border.  Here, we restrict that to contact */
		/* areas exactly matching the cut size.  There should be */
		/* a flag to allow contacts to be generated this way.	 */

		if ((stripList->area.r_xtop - stripList->area.r_xbot
			== squares->sq_size) && (stripList->area.r_ytop
			- stripList->area.r_ybot == squares->sq_size))
		{
		    DBPaintPlane(cifPlane, &stripList->area, CIFPaintTable,
				(PaintUndoInfo *)NULL);
		    CIFTileOps++;
		}
		else
		    CIFError(&stripList->area, "no room for contact cuts in area!");
	    }

	    /* Ad hoc rule catches problems due to contact areas of the proper	*/
	    /* size not fitting cuts because the grid offset prohibits it.	*/
	    else if (((stripList->area.r_ur.p_x - stripList->area.r_ll.p_x) *
			(stripList->area.r_ur.p_y - stripList->area.r_ll.p_y)) >
			2 * (pitch * pitch))
		CIFError(&stripList->area, "contact strip with no room for cuts!");
	}

	DBPaintPlane(plane, &stripList->area, CIFEraseTable,
		(PaintUndoInfo *) NULL);
	free_magic1_t mm1 = freeMagic1_init();
	freeMagic1(&mm1, stripList);
	stripList = stripList->strip_next;
	freeMagic1_end(&mm1);
    }

    /* 2nd pass:  Search the plane for unmarked tiles */

    while (DBSrPaintArea((Tile *)NULL, plane, &TiPlaneRect, &CIFSolidBits,
		cifSquaresInitFunc, (ClientData)NULL) != 0)
    {
	/* Now, search for (nontrivially) connected tiles in all	*/
	/* directions.  Mark the tiles, and record the bounding box.	*/
	/* (Largely copied from cifBloatAllFunc)			*/

	simple = TRUE;
	tile = PlaneGetHint(plane);
	TiToRect(tile, &bbox);

	PUSHTILEONLY(tile, CutStack);
	while (!StackEmpty(CutStack))
	{
	    POPTILEONLY(t, CutStack);
	    if (TiGetClientINT(t) != CIF_PENDING) continue;
            TiSetClientINT(t, CIF_PROCESSED);

	    /* Adjust bounding box */
	    TiToRect(t, &area);
	    GeoInclude(&area, &bbox);

	    if (IsSplit(t)) simple = FALSE;

	    /* Top */
	    for (tp = RT(t); RIGHT(tp) > LEFT(t); tp = BL(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetBottomType(tp)))
		{
		    simple = FALSE;
		    PUSHTILEONLY(tp, CutStack);
		}

	    /* Left */
	    for (tp = BL(t); BOTTOM(tp) < TOP(t); tp = RT(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetRightType(tp)))
		{
		    simple = FALSE;
		    PUSHTILEONLY(tp, CutStack);
		}

	    /* Bottom */
	    for (tp = LB(t); LEFT(tp) < RIGHT(t); tp = TR(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetTopType(tp)))
		{
		    simple = FALSE;
		    PUSHTILEONLY(tp, CutStack);
		}

	    /* Right */
	    for (tp = TR(t); TOP(tp) > BOTTOM(t); tp = LB(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetLeftType(tp)))
		{
		    simple = FALSE;
		    PUSHTILEONLY(tp, CutStack);
		}
	}

	savecount = CIFTileOps;

	for (k = 0; k < 3; k++)	/* prepare for up to 3 passes */
	{
	    /* Determine the contact cut offsets from the bounding box */

	    if (op->co_opcode == CIFOP_SQUARES)
		cifSquareFunc(&bbox, op, &nUp, &nAcross, &llcut);
	    else if (op->co_opcode == CIFOP_SQUARES_G)
		cifSquareGridFunc(&bbox, op, &nUp, &nAcross, &llcut);

	    cut.r_ybot = llcut.r_ybot;
	    cut.r_ytop = llcut.r_ytop;

	    /* For each contact cut area, check that there is	*/
	    /* no whitespace					*/

	    for (i = 0; i < nUp; i++)
	    {
		cut.r_xbot = llcut.r_xbot;
		cut.r_xtop = llcut.r_xtop;

		square.r_ybot = cut.r_ybot - squares->sq_border;
		square.r_ytop = cut.r_ytop + squares->sq_border;

		for (j = 0; j < nAcross; j++)
		{
		    square.r_xbot = cut.r_xbot - squares->sq_border;
		    square.r_xtop = cut.r_xtop + squares->sq_border;

    		    /* If there is only one simple rectangle in the	*/
		    /* area, the area is convex, so we don't have to	*/
		    /* check for unconnected regions.			*/

		    if (simple || DBSrPaintArea((Tile *)tile, plane, &square,
				&DBAllTypeBits, cifUnconnectFunc,
				(ClientData)NULL) == 0)
		    {
			DBPaintPlane(cifPlane, &cut, CIFPaintTable,
				(PaintUndoInfo *)NULL);
			CIFTileOps++;
		    }
		    cut.r_xbot += pitch;
		    cut.r_xtop += pitch;
		}
		cut.r_ybot += pitch;
		cut.r_ytop += pitch;
	    }
	    if (savecount != CIFTileOps) break;

	    /* In non-Manhattan regions with beveled corners, where	*/
	    /* the bounding box has space for 2 contacts, there may not	*/
	    /* be space in the shape itself for more than one.  We will	*/
	    /* attempt to shrink X, Y, and/or both to fit (up to 3	*/
	    /* passes may be necessary).				*/

	    if (nUp == 2)
	    {
		int bdiff = 1 + (bbox.r_ytop - bbox.r_ybot) -
			(2 * (squares->sq_size + squares->sq_border) +
			squares->sq_sep);
		if (bdiff & 0x1) bdiff++;	/* bdiff must be even	*/
		bdiff >>= 1;			/* take half		*/
		bbox.r_ytop -= bdiff;
		bbox.r_ybot += bdiff;
	    }
	    else if (nAcross == 2)
	    {
		int bdiff = 1 + (bbox.r_xtop - bbox.r_xbot) -
			(2 * (squares->sq_size + squares->sq_border) +
			squares->sq_sep);
		if (bdiff & 0x1) bdiff++;	/* bdiff must be even	*/
		bdiff >>= 1;			/* take half		*/
		bbox.r_xtop -= bdiff;
		bbox.r_xbot += bdiff;
	    }
	    else
		break;
	}
	if (savecount == CIFTileOps)
	    CIFError(&bbox, "no room for contacts in area!");

	/* Clear the tiles that were processed */

	TiSetClientINT(tile, CIF_IGNORE);
	PUSHTILEONLY(tile, CutStack);
	while (!StackEmpty(CutStack))
	{
	    POPTILEONLY(t, CutStack);

	    /* Top */
	    for (tp = RT(t); RIGHT(tp) > LEFT(t); tp = BL(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, CutStack);
		}

	    /* Left */
	    for (tp = BL(t); BOTTOM(tp) < TOP(t); tp = RT(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, CutStack);
		}

	    /* Bottom */
	    for (tp = LB(t); LEFT(tp) < RIGHT(t); tp = TR(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, CutStack);
		}

	    /* Right */
	    for (tp = TR(t); TOP(tp) > BOTTOM(t); tp = LB(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, CutStack);
		}
	}
    }

    /* Clear all the tiles that were processed */
    DBSrPaintArea((Tile *)NULL, plane, &TiPlaneRect, &CIFSolidBits,
		cifProcessResetFunc, (ClientData)NULL);
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifSlotsFillArea --
 *
 *	Fill areas in the current CIF output plane with contact cuts based on
 *	the SLOTS operation passed in "op".  This routine is like
 *	cifSquaresFillArea but handles X and Y dimensions independently.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *
 * ----------------------------------------------------------------------------
 */

void
cifSlotsFillArea(
    CIFOp *op,
    CellDef *cellDef,
    Plane *plane)
{
    Tile *tile, *t, *tp;
    Rect bbox, area, square, cut, llcut;
    int nAcross, nUp, left, spitch, lpitch, ssize, lsize, offset;
    int diff, right;
    int xpitch, ypitch, xborder, yborder, xdiff, ydiff;
    int i, j, k, savecount;
    TileType type;
    SlotsData *slots = (SlotsData *)op->co_client;
    StripsData stripsData;
    linkedStrip *stripList;
    bool simple, vertical;
    static Stack *CutStack = (Stack *)NULL;

    spitch = slots->sl_ssize + slots->sl_ssep;
    lpitch = slots->sl_lsize + slots->sl_lsep;
    ssize = slots->sl_ssize + 2 * slots->sl_sborder;
    lsize = slots->sl_lsize + 2 * slots->sl_lborder;

    /* This may not be good in all cases!  Amount to shorten a strip	*/
    /* depends on the orientation of the cuts on either side of the	*/
    /* connected strip edge. . .					*/
    diff = slots->sl_lsep - slots->sl_lborder - slots->sl_sborder;

    if (CutStack == (Stack *)NULL)
	CutStack = StackNew(64);

    /* Two-pass algorithm */

    /* Search the plane for "strips", sections of contact that are only	*/
    /* wide enough for 1 cut.  Process them separately, then erase the	*/
    /* processed areas.	  The purpose of this is to avoid applying the	*/
    /* contact matrix algorithm on non-convex spaces, such as guard	*/
    /* rings, where centering the matrix on the bounding box of the	*/
    /* contact area may produce a matrix misaligned with the contact	*/
    /* strips, preventing any contacts from being drawn!  Due to the	*/
    /* maximum horizontal stripes rule for corner stitched tiles, we	*/
    /* need only identify vertical contact strips.  After processing	*/
    /* and erasing them, all remaining areas will be convex.  This	*/
    /* algorithm allows contacts to be drawn in any arbitrary geometry.	*/

    stripsData.size = ssize;
    stripsData.pitch = spitch;
    stripsData.strips = NULL;
    DBSrPaintArea((Tile *)NULL, plane, &TiPlaneRect, &CIFSolidBits,
		cifSquaresStripFunc, (ClientData)&stripsData);

    /* Generate cuts in each strip found, then erase the strip */

    stripList = stripsData.strips;
    while (stripList != NULL)
    {
	Rect stripLess = stripList->area;

	if (diff > 0)
	{
	    if (stripList->vertical)
	    {
		if (stripList->shrink_ur) stripLess.r_ytop -= diff;
		if (stripList->shrink_ld) stripLess.r_ybot += diff;
	    }
	    else
	    {
		if (stripList->shrink_ur) stripLess.r_xtop -= diff;
		if (stripList->shrink_ld) stripLess.r_xbot += diff;
	    }
	}

	/* Create the cuts */

	cifSlotFunc(&stripLess, op, &nUp, &nAcross, &llcut, stripList->vertical);

	cut = llcut;

	if (stripList->vertical)
	{
	    for (i = 0; i < nUp; i++)
	    {
		DBPaintPlane(cifPlane, &cut, CIFPaintTable, (PaintUndoInfo *)NULL);
		cut.r_ybot += lpitch;
		cut.r_ytop += lpitch;
	    }
	}
	else
	{
	    for (i = 0; i < nAcross; i++)
	    {
		DBPaintPlane(cifPlane, &cut, CIFPaintTable, (PaintUndoInfo *)NULL);
		cut.r_xbot += lpitch;
		cut.r_xtop += lpitch;
	    }
	}

	if (nUp == 0 || nAcross == 0)
	{
	    if (stripList->shrink_ur == 0 && stripList->shrink_ld == 0)
	    {
		/* The following code is backwardly-compatible with the	 */
		/* original behavior of allowing cuts that do not have	 */
		/* sufficient border.  Here, we restrict that to contact */
		/* areas exactly matching the cut size.  There should be */
		/* a flag to allow contacts to be generated this way.	 */

		if (((stripList->area.r_xtop - stripList->area.r_xbot
			== slots->sl_ssize) && (stripList->area.r_ytop
			- stripList->area.r_ybot == slots->sl_lsize)) ||
			((stripList->area.r_xtop - stripList->area.r_xbot
			== slots->sl_lsize) && (stripList->area.r_ytop
			- stripList->area.r_ybot == slots->sl_ssize)))
		{
		    DBPaintPlane(cifPlane, &stripList->area, CIFPaintTable,
				(PaintUndoInfo *)NULL);
		    CIFTileOps++;
		}
		else
		    CIFError(&stripList->area, "no room for contact cuts in area!");
	    }

	    /* Ad hoc rule catches problems due to contact areas of the proper	*/
	    /* size not fitting cuts because the grid offset prohibits it.	*/
	    else if (((stripList->area.r_ur.p_x - stripList->area.r_ll.p_x) *
			(stripList->area.r_ur.p_y - stripList->area.r_ll.p_y)) >
			2 * (lpitch * spitch))
		CIFError(&stripList->area, "contact strip with no room for cuts!");
	}

	DBPaintPlane(plane, &stripList->area, CIFEraseTable,
		(PaintUndoInfo *) NULL);
	free_magic1_t mm1 = freeMagic1_init();
	freeMagic1(&mm1, stripList);
	stripList = stripList->strip_next;
	freeMagic1_end(&mm1);
    }

    /* 2nd pass:  Search the plane for unmarked tiles */

    while (DBSrPaintArea((Tile *)NULL, plane, &TiPlaneRect, &CIFSolidBits,
		cifSquaresInitFunc, (ClientData)NULL) != 0)
    {
	/* Now, search for (nontrivially) connected tiles in all	*/
	/* directions.  Mark the tiles, and record the bounding box.	*/
	/* (Largely copied from cifBloatAllFunc)			*/

	simple = TRUE;
	tile = PlaneGetHint(plane);
	TiToRect(tile, &bbox);

	PUSHTILEONLY(tile, CutStack);
	while (!StackEmpty(CutStack))
	{
	    POPTILEONLY(t, CutStack);
	    if (TiGetClientINT(t) != CIF_PENDING) continue;
            TiSetClientINT(t, CIF_PROCESSED);

	    /* Adjust bounding box */
	    TiToRect(t, &area);
	    GeoInclude(&area, &bbox);

	    if (IsSplit(t)) simple = FALSE;

	    /* Top */
	    for (tp = RT(t); RIGHT(tp) > LEFT(t); tp = BL(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetBottomType(tp)))
		{
		    simple = FALSE;
		    PUSHTILEONLY(tp, CutStack);
		}

	    /* Left */
	    for (tp = BL(t); BOTTOM(tp) < TOP(t); tp = RT(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetRightType(tp)))
		{
		    simple = FALSE;
		    PUSHTILEONLY(tp, CutStack);
		}

	    /* Bottom */
	    for (tp = LB(t); LEFT(tp) < RIGHT(t); tp = TR(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetTopType(tp)))
		{
		    simple = FALSE;
		    PUSHTILEONLY(tp, CutStack);
		}

	    /* Right */
	    for (tp = TR(t); TOP(tp) > BOTTOM(t); tp = LB(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetLeftType(tp)))
		{
		    simple = FALSE;
		    PUSHTILEONLY(tp, CutStack);
		}
	}

	/* May want to attempt a second pass with the orthogonal alignment? */
	if ((bbox.r_xtop - bbox.r_xbot) > (bbox.r_ytop - bbox.r_ybot))
	    vertical = FALSE;
	else
	    vertical = TRUE;

	if (vertical)
	{
	    xpitch = spitch;
	    ypitch = lpitch;
	    xborder = slots->sl_sborder;
	    yborder = slots->sl_lborder;
	    xdiff = 2 * (slots->sl_ssize + slots->sl_sborder) + slots->sl_ssep;
	    ydiff = 2 * (slots->sl_lsize + slots->sl_lborder) + slots->sl_lsep;
	}
	else
	{
	    xpitch = lpitch;
	    ypitch = spitch;
	    xborder = slots->sl_lborder;
	    yborder = slots->sl_sborder;
	    xdiff = 2 * (slots->sl_lsize + slots->sl_lborder) + slots->sl_lsep;
	    ydiff = 2 * (slots->sl_ssize + slots->sl_sborder) + slots->sl_ssep;
	}

	savecount = CIFTileOps;

	for (k = 0; k < 3; k++)	/* prepare for up to 3 passes */
	{
	    /* Determine the contact cut offsets from the bounding box */

	    cifSlotFunc(&bbox, op, &nUp, &nAcross, &llcut, vertical);

	    cut.r_ybot = llcut.r_ybot + slots->sl_start;
	    cut.r_ytop = llcut.r_ytop + slots->sl_start;

	    /* For each contact cut area, check that there is	*/
	    /* no whitespace					*/

	    offset = slots->sl_start;
	    for (i = 0; i < nUp; i++)
	    {
		cut.r_xbot = llcut.r_xbot + offset;
		cut.r_xtop = llcut.r_xtop + offset;

		square.r_ybot = cut.r_ybot - yborder;
		square.r_ytop = cut.r_ytop + yborder;

		for (j = 0; j < nAcross; j++)
		{
		    square.r_xbot = cut.r_xbot - xborder;
		    square.r_xtop = cut.r_xtop + xborder;

    		    /* If there is only one simple rectangle in the	*/
		    /* area, the area is convex, so we don't have to	*/
		    /* check for unconnected regions.			*/

		    if (simple || DBSrPaintArea((Tile *)tile, plane, &square,
				&DBAllTypeBits, cifUnconnectFunc,
				(ClientData)NULL) == 0)
		    {
			DBPaintPlane(cifPlane, &cut, CIFPaintTable,
				(PaintUndoInfo *)NULL);
			CIFTileOps++;
		    }
		    cut.r_xbot += xpitch;
		    cut.r_xtop += xpitch;
		}
		cut.r_ybot += ypitch;
		cut.r_ytop += ypitch;
		offset += slots->sl_offset;
		if (offset >= xpitch) offset -= xpitch;
	    }
	    if (savecount != CIFTileOps) break;

	    /* In non-Manhattan regions with beveled corners, where	*/
	    /* the bounding box has space for 2 contacts, there may not	*/
	    /* be space in the shape itself for more than one.  We will	*/
	    /* attempt to shrink X, Y, and/or both to fit (up to 3	*/
	    /* passes may be necessary).				*/

	    if (nUp == 2)
	    {
		int bdiff = 1 + (bbox.r_ytop - bbox.r_ybot) - ydiff;
		if (bdiff & 0x1) bdiff++;	/* bdiff must be even	*/
		bdiff >>= 1;			/* take half		*/
		bbox.r_ytop -= bdiff;
		bbox.r_ybot += bdiff;
	    }
	    else if (nAcross == 2)
	    {
		int bdiff = 1 + (bbox.r_xtop - bbox.r_xbot) - xdiff;
		if (bdiff & 0x1) bdiff++;	/* bdiff must be even	*/
		bdiff >>= 1;			/* take half		*/
		bbox.r_xtop -= bdiff;
		bbox.r_xbot += bdiff;
	    }
	    else
		break;
	}
	if (savecount == CIFTileOps)
	    CIFError(&bbox, "no room for contacts in area!");

	/* Clear the tiles that were processed */

	TiSetClientINT(tile, CIF_IGNORE);
	PUSHTILEONLY(tile, CutStack);
	while (!StackEmpty(CutStack))
	{
	    POPTILEONLY(t, CutStack);

	    /* Top */
	    for (tp = RT(t); RIGHT(tp) > LEFT(t); tp = BL(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, CutStack);
		}

	    /* Left */
	    for (tp = BL(t); BOTTOM(tp) < TOP(t); tp = RT(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, CutStack);
		}

	    /* Bottom */
	    for (tp = LB(t); LEFT(tp) < RIGHT(t); tp = TR(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, CutStack);
		}

	    /* Right */
	    for (tp = TR(t); TOP(tp) > BOTTOM(t); tp = LB(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, CutStack);
		}
	}
    }

    /* Clear all the tiles that were processed */
    DBSrPaintArea((Tile *)NULL, plane, &TiPlaneRect, &CIFSolidBits,
		cifProcessResetFunc, (ClientData)NULL);
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifBloatMaxFunc --
 *
 * 	Called once for each tile to be selectively bloated or
 *	shrunk using the CIFOP_BLOATMAX or CIFOP_BLOATMIN operation.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	Uses the bloat table in the current CIFOp to expand or shrink
 *	the tile, depending on which tiles it abuts.  The rectangular
 *	area of the tile is modified by moving each side in or out
 *	by the maximum bloat distance for any of its neighbors on
 *	that side.  The result is a new rectangle, which is OR'ed
 *	into the result plane.  Note:  any edge between two tiles of
 *	same type is given a zero bloat factor.
 *
 * ----------------------------------------------------------------------------
 */

int
cifBloatMaxFunc(
    Tile *tile,			/* The tile to be processed. */
    TileType dinfo,		/* Split tile information */
    CIFOp *op)			/* Describes the operation to be performed
				 * (all we care about is the opcode and
				 * bloat table).
				 */
{
    Rect area;
    int bloat, tmp;
    Tile *t;
    TileType type, otherType;
    BloatData *bloats = (BloatData *)op->co_client;

    /* Get the tile into CIF coordinates. */

    type = TiGetType(tile);
    TiToRect(tile, &area);
    area.r_xbot *= cifScale;
    area.r_ybot *= cifScale;
    area.r_xtop *= cifScale;
    area.r_ytop *= cifScale;

    /* See how much to adjust the left side of the tile. */

    if (op->co_opcode == CIFOP_BLOATMAX) bloat = -10000000;
    else bloat = 10000000;
    for (t = BL(tile); BOTTOM(t) < TOP(tile); t = RT(t))
    {
	otherType = TiGetType(t);
	if (otherType == type) continue;
	tmp = bloats->bl_distance[otherType];
	if (op->co_opcode == CIFOP_BLOATMAX)
	{
	    if (tmp > bloat) bloat = tmp;
	}
	else if (tmp < bloat) bloat = tmp;
    }
    if ((bloat < 10000000) && (bloat > -10000000))
	area.r_xbot -= bloat;

    /* Now figure out how much to adjust the top side of the tile. */

    if (op->co_opcode == CIFOP_BLOATMAX) bloat = -10000000;
    else bloat = 10000000;
    for (t = RT(tile); RIGHT(t) > LEFT(tile); t = BL(t))
    {
	otherType = TiGetType(t);
	if (otherType == type) continue;
	tmp = bloats->bl_distance[otherType];
	if (op->co_opcode == CIFOP_BLOATMAX)
	{
	    if (tmp > bloat) bloat = tmp;
	}
	else if (tmp < bloat) bloat = tmp;
    }
    if ((bloat < 10000000) && (bloat > -10000000))
	area.r_ytop += bloat;

    /* Now the right side. */

    if (op->co_opcode == CIFOP_BLOATMAX) bloat = -10000000;
    else bloat = 10000000;
    for (t = TR(tile); TOP(t) > BOTTOM(tile); t = LB(t))
    {
	otherType = TiGetType(t);
	if (otherType == type) continue;
	tmp = bloats->bl_distance[otherType];
	if (op->co_opcode == CIFOP_BLOATMAX)
	{
	    if (tmp > bloat) bloat = tmp;
	}
	else if (tmp < bloat) bloat = tmp;
    }
    if ((bloat < 10000000) && (bloat > -10000000))
	area.r_xtop += bloat;

    /* Lastly, do the bottom side. */

    if (op->co_opcode == CIFOP_BLOATMAX) bloat = -10000000;
    else bloat = 10000000;
    for (t = LB(tile); LEFT(t) < RIGHT(tile); t = TR(t))
    {
	otherType = TiGetType(t);
	if (otherType == type) continue;
	tmp = bloats->bl_distance[otherType];
	if (op->co_opcode == CIFOP_BLOATMAX)
	{
	    if (tmp > bloat) bloat = tmp;
	}
	else if (tmp < bloat) bloat = tmp;
    }
    if ((bloat < 10000000) && (bloat > -10000000))
	area.r_ybot -= bloat;

    /* Make sure the tile didn't shrink into negativity.  If it's
     * ok, paint it into the new plane being built.
     */

    if ((area.r_xbot > area.r_xtop) || (area.r_ybot > area.r_ytop))
    {
	TiToRect(tile, &area);
	area.r_xbot *= cifScale;
	area.r_xtop *= cifScale;
	area.r_ybot *= cifScale;
	area.r_ytop *= cifScale;
	CIFError(&area, "tile inverted by shrink");
    }
    else
	DBNMPaintPlane(cifPlane, TiGetTypeExact(tile) | dinfo, &area,
		CIFPaintTable, (PaintUndoInfo *) NULL);

    CIFTileOps += 1;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * inside_triangle --
 *
 *	Test if the specified rectangle is completely inside the tile.
 *	Tile is presumed to be a split tile.
 *
 * Results:
 *	TRUE if inside, FALSE if outside or overlapping.
 *
 * Side Effects:
 *	None.
 *
 * Notes:
 *	This is an optimized version of the code in DBtiles.c.  It
 *	assumes that the square is never infinite.
 *
 * ----------------------------------------------------------------------------
 */

bool
inside_triangle(
    Rect *rect,
    Tile *tile)
{
    int theight, twidth;
    dlong f1, f2, f3, f4;

    theight = TOP(tile) - BOTTOM(tile);
    twidth = RIGHT(tile) - LEFT(tile);

    f1 = (dlong)(TOP(tile) - rect->r_ybot) * twidth;
    f2 = (dlong)(rect->r_ytop - BOTTOM(tile)) * twidth;

    if (SplitLeftType(tile) != TT_SPACE)
    {
        /* Inside-of-triangle check */
        f4 = (dlong)(rect->r_xbot - LEFT(tile)) * theight;
        if (SplitDirection(tile) ? (f1 > f4) : (f2 > f4))
	    return TRUE;
    }
    else
    {
        /* Inside-of-triangle check */
        f3 = (dlong)(RIGHT(tile) - rect->r_xtop) * theight;
        if (SplitDirection(tile) ? (f2 > f3) : (f1 > f3))
	    return TRUE;
    }
    return FALSE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifContactFunc --
 *
 *	Called for each relevant tile when chopping up contacts into
 *	square cuts using the procedure of using a pre-defined cell
 *	definition of a single contact and arraying the cell.
 *	Technically, this is not a CIF function because CIF does not
 *	have an array method for cells, so there is no advantage to
 *	drawing lots of subcells in place of drawing lots of boxes.
 *	In GDS, however, the array method is much more compact than
 *	drawing individual cuts when the array size is larger than 1.
 *
 * Results:
 *	Normally returns 0 to keep the search going.  Return 1 if
 *	the contact type cannot be found, which halts the search.
 *
 * Side effects:
 *	Stuff is painted into cifPlane.
 *
 * ----------------------------------------------------------------------------
 */

int
cifContactFunc(
    Tile *tile,			/* Tile to be diced up. */
    TileType dinfo,		/* Split tile information (unused) */
    CIFSquaresInfo *csi) 	/* Describes how to generate squares. */
{
    Rect area;
    int i, nAcross, j, nUp, left, bottom, pitch, halfsize;
    bool result;
    SquaresData *squares = csi->csi_squares;

    /* For now, don't allow squares on non-manhattan tiles */
    if (IsSplit(tile))
	return 0;

    TiToRect(tile, &area);
    pitch = squares->sq_size + squares->sq_sep;

    nAcross = (area.r_xtop - area.r_xbot + squares->sq_sep
	    - (2 * squares->sq_border)) / pitch;
    if (nAcross == 0)
    {
	left = (area.r_xbot + area.r_xtop - squares->sq_size) / 2;
	if (left >= area.r_xbot) nAcross = 1;
    }
    else
	left = (area.r_xbot + area.r_xtop + squares->sq_sep
		- (nAcross * pitch)) / 2;

    nUp = (area.r_ytop - area.r_ybot + squares->sq_sep
	    - (2 * squares->sq_border)) / pitch;
    if (nUp == 0)
    {
	bottom = (area.r_ybot + area.r_ytop - squares->sq_size) / 2;
	if (bottom >= area.r_ybot) nUp = 1;
    }
    else
	bottom = (area.r_ybot + area.r_ytop + squares->sq_sep
		- (nUp * pitch)) / 2;

    /* Lower-left coordinate should be centered on the contact cut, as
     * contact definitions are centered on the origin.
     */
    halfsize = (squares->sq_size / 2);
    left += halfsize;
    bottom += halfsize;

#ifdef HAVE_ZLIB
    if (CalmaCompression > 0)
	result = CalmaGenerateArrayZ((FILETYPE)csi->csi_client, csi->csi_type,
		left, bottom, pitch, nAcross, nUp);
    else
#endif
	result = CalmaGenerateArray((FILE *)csi->csi_client, csi->csi_type,
		left, bottom, pitch, nAcross, nUp);

    return (result == TRUE) ? 0 : 1;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifSlotFunc --
 *
 * 	Called for each relevant tile area when chopping areas up into
 *	slots (rectangles).  Determines how to divide up the area into
 *	slots, and generates the number of rows, columns, and the lower-
 *	left-most cut rectangle.
 *
 * Results:
 *	Always return 0
 *
 * Side effects:
 *	Pointers "rows", "columns", and "cut" are filled with
 *	appropriate values.
 *
 * ----------------------------------------------------------------------------
 */

int
cifSlotFunc(
    Rect  *area,		/* Area to be diced up			*/
    CIFOp *op,			/* Describes how to generate squares.	*/
    int *numY,
    int *numX,			/* Return values: # rows and # columns 	*/
    Rect *cut,			/* initial (lower left) cut area 	*/
    bool vertical)		/* if TRUE, slot is aligned vertically	*/
{
    int i, j, xpitch, ypitch, delta, limit;
    int *axtop, *axbot, *aytop, *aybot;
    int *sxtop, *sxbot, *sytop, *sybot;
    int *rows, *columns;
    SlotsData *slots = (SlotsData *)op->co_client;

    /* Orient to the short/long orientation of the tile */

    /* Assume a vertical tile;  if not, reorient area and remember */
    if (vertical)
    {
	axbot = &area->r_xbot;
	aybot = &area->r_ybot;
	axtop = &area->r_xtop;
	aytop = &area->r_ytop;
	sxbot = &cut->r_xbot;
	sybot = &cut->r_ybot;
	sxtop = &cut->r_xtop;
	sytop = &cut->r_ytop;
	rows = numY;
	columns = numX;
    }
    else
    {
	axbot = &area->r_ybot;
	aybot = &area->r_xbot;
	axtop = &area->r_ytop;
	aytop = &area->r_xtop;
	sxbot = &cut->r_ybot;
	sybot = &cut->r_xbot;
	sxtop = &cut->r_ytop;
	sytop = &cut->r_xtop;
	rows = numX;
	columns = numY;
    }

    xpitch = slots->sl_ssize + slots->sl_ssep;

calcX:
    *columns = (*axtop - *axbot + slots->sl_ssep - (2 * slots->sl_sborder)) / xpitch;
    if (*columns == 0)
    {
	*rows = 0;
	return 0;
	// *sxbot = (*axbot + *axtop - slots->sl_ssize) / 2;
	// if (*sxbot >= *axbot) *columns = 1;
    }
    else
	*sxbot = (*axbot + *axtop + slots->sl_ssep - (*columns * xpitch)) / 2;
    *sxtop = *sxbot + slots->sl_ssize;

    /* Check that we are not violating any gridlimit */

    limit = CIFCurStyle->cs_gridLimit;

    if (CIFCurStyle && (limit > 1))
    {
	delta = abs(*sxbot) % limit;
	if (delta > 0)
	{
	    if (*sxbot >= 0)
		*axtop -= 2 * delta;
	    else
		*axtop += 2 * delta;
	    goto calcX;
	}
    }

    if (slots->sl_lsize > 0)
    {
	ypitch = slots->sl_lsize + slots->sl_lsep;
calcY:
	*rows = (*aytop - *aybot + slots->sl_lsep - (2 * slots->sl_lborder)) / ypitch;
	if (*rows == 0)
	{
	    return 0;
	    // *sybot = (*aybot + *aytop - slots->sl_lsize) / 2;
	    // if (*sybot >= *aybot) *rows = 1;
	}
	else
	    *sybot = (*aybot + *aytop + slots->sl_lsep - (*rows * ypitch)) / 2;
	*sytop = *sybot + slots->sl_lsize;

	/* Check that we are not violating any gridlimit */

	if (CIFCurStyle && (limit > 1))
	{
	    delta = abs(*sybot) % limit;
	    if (delta > 0)
	    {
		if (*sybot >= 0)
		    *aytop -= 2 * delta;
		else
		    *aytop += 2 * delta;
		goto calcY;
	    }
	}
    }
    else
    {
	*rows = 1;
	*sybot = *aybot + slots->sl_lborder;
	*sytop = *aytop - slots->sl_lborder;
	/* There's no space to fit a slot */
	if (*sytop - *sybot <= 0)
	    return 0;
    }

    /* To be done---slot offsets */

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifSquareFunc --
 *
 * 	Called for each relevant rectangle when chopping areas up into
 *	squares.   Determines the number of rows and columns in the area
 *	and returns these and the area of the lower-left cut.
 *
 * Results:
 *	Always return 0
 *
 * Side effects:
 *	Results filled in the pointer positions passed to the function
 *
 * ----------------------------------------------------------------------------
 */

int
cifSquareFunc(
    Rect *area,			/* Area to be diced up */
    CIFOp *op,			/* Describes how to generate squares.	*/
    int *rows,
    int *columns,		/* Return values: # rows and # columns,	*/
    Rect *cut)			/* initial (lower left) cut area.	*/
{
    int pitch, delta, limit;
    bool glimit;
    SquaresData *squares = (SquaresData *)op->co_client;

    limit = CIFCurStyle->cs_gridLimit;

    glimit = (CIFCurStyle && (limit > 1)) ? TRUE : FALSE;
    pitch = squares->sq_size + squares->sq_sep;

    /* Compute the real border to leave around the sides.  If only
     * one square will fit in a particular direction, center it
     * regardless of the requested border size.  If more than one
     * square will fit, then fit it in extras only if at least the
     * requested border size can be left.  Also center things in the
     * rectangle, so that the box's exact size doesn't matter. This
     * trickiness is done so that coincident contacts from overlapping
     * cells always have their squares line up, regardless of the
     * orientation of the cells.
     *
     * Update 1/13/09:  Removed the "feature" that allows contact
     * cuts that violate the border requirement.  The "slots" rule
     * can be used if necessary to generate cuts with different
     * border allowances.
     */

sqX:
    *columns = (area->r_xtop - area->r_xbot + squares->sq_sep
	    - (2 * squares->sq_border)) / pitch;
    if (*columns == 0)
    {
	*rows = 0;
	return 0;

	// cut->r_xbot = (area->r_xbot + area->r_xtop - squares->sq_size) / 2;
	// if (cut->r_xbot >= area->r_xbot) *columns = 1;
    }
    else
    {
	cut->r_xbot = (area->r_xbot + area->r_xtop + squares->sq_sep
		- (*columns * pitch)) / 2;
    }

    /* Check for any gridlimit violation */

    if (glimit)
    {
	delta = abs(cut->r_xbot) % limit;
	if (delta > 0)
	{
	    area->r_xtop -= 2 * delta;
	    goto sqX;
	}
    }

sqY:
    *rows = (area->r_ytop - area->r_ybot + squares->sq_sep
	    - (2 * squares->sq_border)) / pitch;
    if (*rows == 0)
    {
	return 0;

	// cut->r_ybot = (area->r_ybot + area->r_ytop - squares->sq_size) / 2;
	// if (cut->r_ybot >= area->r_ybot) *rows = 1;
    }
    else
    {
	cut->r_ybot = (area->r_ybot + area->r_ytop + squares->sq_sep
		- (*rows * pitch)) / 2;
    }

    /* Check for any gridlimit violation */

    if (glimit)
    {
	delta = abs(cut->r_ybot) % limit;
	if (delta > 0)
	{
	    area->r_ytop -= 2 * delta;
	    goto sqY;
	}
    }

    cut->r_xtop = cut->r_xbot + squares->sq_size;
    cut->r_ytop = cut->r_ybot + squares->sq_size;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifSquareGridFunc --
 *
 * 	Called for each relevant rectangle when chopping areas up into
 *	squares.   Determines the number of rows and columns in the area
 *	and returns these and the area of the lower-left cut.
 *
 * Results:
 *	Always return 0
 *
 * Side effects:
 *	Results filled in the pointer positions passed to the function
 *
 * ----------------------------------------------------------------------------
 */

int
cifSquareGridFunc(
    Rect *area,			/* Area to be diced up */
    CIFOp *op,			/* Describes how to generate squares.	*/
    int *rows,
    int *columns,		/* Return values: # rows and # columns,	*/
    Rect *cut)			/* initial (lower left) cut area.	*/
{
    Rect locarea;
    int left, bottom, right, top, pitch;
    int gridx, gridy, margin;
    SquaresData *squares = (SquaresData *)op->co_client;

    /*
     * It may be necessary to generate contacts on a specific grid; e.g.,
     * to avoid placing contacts at half-lambda.  If this is the case,
     * then the DRC section of the techfile should specify "no overlap"
     * for these types.
     *
     * This routine has been simplified.  It flags a warning when there
     * is not enough space for a contact cut, rather than attempting to
     * draw a cut without enough border area.  The routine has also been
     * extended to allow different x and y grids to be specified.  This
     * allows certain tricks, such as generating offset contact grids on
     * a pad, as required by some processes.
     */

    pitch = squares->sq_size + squares->sq_sep;
    gridx = squares->sq_gridx;
    gridy = squares->sq_gridy;

    locarea.r_xtop = area->r_xtop - squares->sq_border;
    locarea.r_ytop = area->r_ytop - squares->sq_border;
    locarea.r_xbot = area->r_xbot + squares->sq_border;
    locarea.r_ybot = area->r_ybot + squares->sq_border;

    left = locarea.r_xbot / gridx;
    left *= gridx;
    if (left < locarea.r_xbot) left += gridx;

    bottom = locarea.r_ybot / gridy;
    bottom *= gridy;
    if (bottom < locarea.r_ybot) bottom += gridy;

    *columns = (locarea.r_xtop - left + squares->sq_sep) / pitch;
    if (*columns == 0)
    {
	*rows = 0;
	return 0;
    }

    *rows = (locarea.r_ytop - bottom + squares->sq_sep) / pitch;
    if (*rows == 0) return 0;

    /* Center the contacts while remaining on-grid */
    right = left + *columns * squares->sq_size + (*columns - 1) * squares->sq_sep;
    top = bottom + *rows * squares->sq_size + (*rows - 1) * squares->sq_sep;
    margin = ((locarea.r_xtop - right) - (left - locarea.r_xbot)) / (gridx * 2);
    locarea.r_xbot = left + margin * gridx;
    margin = ((locarea.r_ytop - top) - (bottom - locarea.r_ybot)) / (gridy * 2);
    locarea.r_ybot = bottom + margin * gridy;

    cut->r_ybot = locarea.r_ybot;
    cut->r_ytop = cut->r_ybot + squares->sq_size;
    cut->r_xbot = locarea.r_xbot;
    cut->r_xtop = cut->r_xbot + squares->sq_size;
    return 0;
}


/*
 * ----------------------------------------------------------------------------
 *
 * cifSrTiles --
 *
 * 	This is a utility procedure that just calls DBSrPaintArea
 *	one or more times for the planes being used in processing
 *	one CIFOp.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	This procedure itself has no side effects.  For each of the
 *	paint or temporary planes indicated in cifOp, we call
 *	DBSrPaintArea to find the desired tiles in the desired
 *	area for the operation.  DBSrPaintArea is given func as a
 *	search function, and cdArg as ClientData.
 *
 * ----------------------------------------------------------------------------
 */

void
cifSrTiles(
    CIFOp *cifOp,		/* Geometric operation being processed. */
    const Rect *area,		/* Area of Magic paint to consider. */
    CellDef *cellDef,		/* CellDef to search for paint. */
    Plane *temps[],		/* Planes to use for temporaries. */
    int (*func)(),		/* Search function to pass to DBSrPaintArea. */
    ClientData cdArg)		/* Client data for func. */
{
    TileTypeBitMask maskBits;
    TileType t;
    Tile *tp;
    int i;
    BloatData *bloats;

    /* When reading data from a cell, it must first be scaled to
     * CIF units.  Check for CIFCurStyle, as we don't want to
     * crash while reading CIF/GDS just becuase the techfile
     * "cifoutput" section was blank.
     */

    cifScale = (CIFCurStyle) ? CIFCurStyle->cs_scaleFactor : 1;

    /* Bloat operations (except bloat-all) have to be in a single plane */

    switch (cifOp->co_opcode) {
	case CIFOP_BLOAT:
	case CIFOP_BLOATMIN:
	case CIFOP_BLOATMAX:
	    bloats = (BloatData *)cifOp->co_client;
	    i = bloats->bl_plane;
	    maskBits = DBPlaneTypes[i];
	    TTMaskAndMask(&maskBits, &cifOp->co_paintMask);
	    if (!TTMaskEqual(&maskBits, &DBZeroTypeBits))
		DBSrPaintArea((Tile *) NULL, cellDef->cd_planes[i],
				area, &cifOp->co_paintMask, func, cdArg);
	    break;

	default:
	    for (i = PL_DRC_CHECK; i < DBNumPlanes; i++)
	    {
		maskBits = DBPlaneTypes[i];
		TTMaskAndMask(&maskBits, &cifOp->co_paintMask);
		if (!TTMaskEqual(&maskBits, &DBZeroTypeBits))
		    DBSrPaintArea((Tile *) NULL, cellDef->cd_planes[i],
				area, &cifOp->co_paintMask, func, cdArg);
	    }
	    break;
    }

    /* When processing CIF data, use everything in the plane. */

    cifScale = 1;
    for (t = 0; t < TT_MAXTYPES; t++, temps++)
	if (TTMaskHasType(&cifOp->co_cifMask, t))
	    DBSrPaintArea((Tile *) NULL, *temps, &TiPlaneRect,
			&CIFSolidBits, func, (ClientData) cdArg);
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifSrTiles2 --
 *
 * 	This is a utility procedure that just calls DBSrPaintArea
 *	one or more times for the planes being used in processing
 *	where the CIF search should be conducted over "area" scaled
 *	to CIF units, rather than the entire plane.  Currently used 
 *	only for operators CIFOP_INTERACT and CIFOP_TAGGED.
 *
 * Results:
 *	Returns the value returned by the function.
 *
 * Side effects:
 *	This procedure itself has no side effects.  For each of the
 *	paint or temporary planes indicated in cifOp, we call
 *	DBSrPaintArea to find the desired tiles in the desired
 *	area for the operation.  DBSrPaintArea is given func as a
 *	search function, and cdArg as ClientData.
 *
 * ----------------------------------------------------------------------------
 */

int
cifSrTiles2(
    CIFOp *cifOp,		/* Geometric operation being processed. */
    Rect *area,			/* Area of Magic paint to consider. */
    CellDef *cellDef,		/* CellDef to search for paint. */
    Plane *temps[],		/* Planes to use for temporaries. */
    int (*func)(),		/* Search function to pass to DBSrPaintArea. */
    ClientData cdArg)		/* Client data for func. */
{
    TileTypeBitMask maskBits;
    Rect r;
    TileType t;
    Tile *tp;
    int i;

    /* When reading data from a cell, it must first be scaled to
     * CIF units.  Check for CIFCurStyle, as we don't want to
     * crash while reading CIF/GDS just becuase the techfile
     * "cifoutput" section was blank.
     */

    cifScale = (CIFCurStyle) ? CIFCurStyle->cs_scaleFactor : 1;

    /* When processing local database, rescale area */
    r.r_xbot = area->r_xbot / cifScale;
    r.r_xtop = area->r_xtop / cifScale;
    r.r_ybot = area->r_ybot / cifScale;
    r.r_ytop = area->r_ytop / cifScale;
    for (i = PL_DRC_CHECK; i < DBNumPlanes; i++)
    {
	maskBits = DBPlaneTypes[i];
	TTMaskAndMask(&maskBits, &cifOp->co_paintMask);
	if (!TTMaskEqual(&maskBits, &DBZeroTypeBits))
	    if (DBSrPaintArea((Tile *) NULL, cellDef->cd_planes[i],
			&r, &cifOp->co_paintMask, func, cdArg))
		return 1;
    }

    cifScale = 1;
    if (TTMaskIsZero(&cifOp->co_cifMask) && TTMaskIsZero(&cifOp->co_paintMask))
    {
	/* Current CIF plane is in *temps */
	if (DBSrPaintArea((Tile *)NULL, *temps, area,
			&CIFSolidBits, func, (ClientData)cdArg))
	    return 1;
    }
    else
    {
	for (t = 0; t < TT_MAXTYPES; t++, temps++)
	    if (TTMaskHasType(&cifOp->co_cifMask, t))
		if (DBSrPaintArea((Tile *)NULL, *temps, area,
			&CIFSolidBits, func, (ClientData)cdArg))
		    return 1;
    }
    return 0;
}

/* Data structure to pass plane and minimum width to the callback function */

typedef struct _bridgeLimStruct {
    Plane       *plane;
    BridgeData	*bridge;
    CellDef     *def;
    Plane       **temps;
    TileTypeBitMask co_paintMask;/* Zero or more paint layers to consider. */
    TileTypeBitMask co_cifMask;  /* Zero or more other CIF layers. */
} BridgeLimStruct;

static int xOverlap, yOverlap;

/* Bridge-lim Check data structure */

typedef struct _bridgeLimCheckStruct {
   Tile *tile;		/* Tile that triggered search (ignore this tile) */
   int	direction;	/* What outside corner to look for */
   Tile *violator;	/* Return the violator tile in this space */
   TileType checktype;	/* Type to check for, either TT_SPACE or CIF_SOLIDTYPE */
   long sqdistance;     /* Square of the minimum distance */
} BridgeLimCheckStruct;

/*
 *-----------------------------------------------------------------------
 * Callback function for bridgeLimSrTiles used when a limiting layer tile
 * was found in the specified area. If calcOverlap is TRUE then the xOverlap
 * and yOverlap are updated and return 0 to continue searching. Otherwise
 * return 1 to stop the search.
 *-----------------------------------------------------------------------
 */

int
bridgeLimFound(
    Tile *tile,
    TileType dinfo,	/* (unused) */
    bool calcOverlap)
{
    if (calcOverlap)
    {
        if (RIGHT(tile) > xOverlap)
                xOverlap = RIGHT(tile);
        if (TOP(tile) > yOverlap)
                yOverlap = TOP(tile);
        return 0;       // continue searching
    } else
        return 1;       // tile found, stop the search
}

/*
 *------------------------------------------------------------------------
 * Callback function used in bridge-lim operations to find if the specified
 * area contains tiles of the limiting layers.
 *------------------------------------------------------------------------
 */

int
bridgeLimSrTiles(
    BridgeLimStruct *brlims,    /* Bridge-Lim structure. */
    Rect *area,                 /* Area of Magic paint to consider. */
    bool calcOverlap)           /* TRUE to calculate the overlap of the limiting tiles in the specified area. */
{
    TileTypeBitMask maskBits;
    TileType t;
    int i;
    Plane **temps = brlims->temps;
    xOverlap = area->r_xbot;
    yOverlap = area->r_ybot;

    for (i = PL_DRC_CHECK; i < DBNumPlanes; i++)
    {
	maskBits = DBPlaneTypes[i];
	TTMaskAndMask(&maskBits, &brlims->co_paintMask);
	if (!TTMaskEqual(&maskBits, &DBZeroTypeBits))
	    if (DBSrPaintArea((Tile *) NULL, brlims->def->cd_planes[i], area, &brlims->co_paintMask, bridgeLimFound, calcOverlap))
		return 0;
    }

    for (t = 0; t < TT_MAXTYPES; t++, temps++)
        if (TTMaskHasType(&brlims->co_cifMask, t))
           if (DBSrPaintArea((Tile *) NULL, *temps, area, &CIFSolidBits, bridgeLimFound, calcOverlap))
                return 0;

    if (( xOverlap != area->r_xbot) || (yOverlap != area->r_ybot))
        return 0;       //Constraint tile found
    else
        return 1;       //Nothing found
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifBridgeLimFunc0 --
 *
 * 	Called for each relevant tile during bridge-lim operations.  The
 *	bridge-lim operation is similar to the bridge operation with the
 *	difference that the material created to meet the minimum spacing/width
 *	rules do not overlap the limiting layers.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	May paint into cifNewPlane
 *
 * Algorithm (based on maximum horizontal stripes rule):
 * This function grows dimentions of tiles to meet a minimimum width rule (only
 * applies to vertical or horizontal directions). This is done in two steps:
 * 	1. Adjust tile width to accomplish the minimimum width rule, limited by
 *	the constraint layers.
 *	2. Search top and bottom tiles of same type, for each segment verify if
 *	height meets the minimimum value, if not, adjust the segment height
 *	but do not paint over limiting layer tiles.
 * ----------------------------------------------------------------------------
 */

int
cifBridgeLimFunc0(
    Tile *tile,
    TileType dinfo,		/* (unused) */
    BridgeLimStruct *brlims)
{
    Plane *plane = brlims->plane;
    Rect area, parea;
    int minDistance = brlims->bridge->br_width;
    int width, height, ybot0, tp2lim;
    Tile *tp, *tp2;

    TiToRect(tile, &area);

    /* Check whole tile for minimum width */
    width = area.r_xtop - area.r_xbot;
    if (width < minDistance)
    {
	area.r_xbot = area.r_xtop - minDistance;
        if (!bridgeLimSrTiles(brlims, &area, TRUE))
	{
            area.r_xbot = MIN(LEFT(tile), xOverlap);
	    area.r_xtop = area.r_xbot + minDistance;
	}
    }

	/* If there is another tile on top or bottom, and the height is	*/
	/* less than minimum, then extend height in the direction of	*/
	/* the bordering tile.  Otherwise, if the height is less than	*/
	/* minimum, then grow considering the limiting layers.	*/

	height = area.r_ytop - area.r_ybot;
	if (height < minDistance)
	{
         for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
            {
            tp2lim = MAX(LEFT(tp), area.r_xbot);
            for (tp2 = RT(tile); RIGHT(tp2) > tp2lim; tp2 = BL(tp2))
                if (LEFT(tp2) < RIGHT(tp))
                {
                   parea.r_xbot = MAX(LEFT(tp2), tp2lim);
                   parea.r_xtop = MIN(MIN(RIGHT(tp2), RIGHT(tp)), area.r_xtop);
                   if (TiGetBottomType(tp2) == TiGetTopType(tile))
                        parea.r_ytop = TOP(tp2);
                   else
                        parea.r_ytop = area.r_ytop;
                   if (TiGetTopType(tp) == TiGetBottomType(tile))
                        ybot0 = BOTTOM(tp);
                   else
                        ybot0 = area.r_ybot;
                   height = parea.r_ytop - ybot0;
                   if (height < minDistance)
                   {
                        parea.r_ybot = parea.r_ytop - minDistance;
                        if (!bridgeLimSrTiles(brlims, &parea, TRUE))
                        {
                           parea.r_ybot = MIN(ybot0 , yOverlap);
                           parea.r_ytop = parea.r_ybot + minDistance;
                        }
                        DBPaintPlane(cifPlane, &parea, CIFPaintTable, (PaintUndoInfo *) NULL);
                   }
                }
            }
        }
    DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
    return 0;
}

/*
 *-----------------------------------------------------------------------
 * Callback function for cifBridgeLimFunc1 and cifBridgeLimFunc2 to find if
 * there are violator cells in the search area.	 If a violator cell is
 * found, then put the tile pointer in the BridgeCheckStruct and return
 * value 1 to stop the search.	Otherwise return 0 to keep going.
 *-----------------------------------------------------------------------
 */

int
bridgeLimCheckFunc(
    Tile *tile,
    TileType dinfo,			/* (unused) */
    BridgeLimCheckStruct *brlimcs)
{
    int dir = brlimcs->direction;
    Tile *self = brlimcs->tile;
    Tile *tp1, *tp2;
    TileType checktype = brlimcs->checktype;
    long sqdistance = brlimcs->sqdistance;
    int dx, dy;
    long sqcheck;

    if (self == tile) return 0;	    /* Ignore the triggering tile */

    switch (dir) {
	case BRIDGE_NW:
	    /* Ignore tile if split, and SE corner is clipped */
	    if (IsSplit(tile))
	    if (TiGetRightType(tile) == checktype || TiGetBottomType(tile) == checktype)
		break;
	    for (tp1 = RT(tile); LEFT(tp1) > LEFT(tile); tp1 = BL(tp1));
	    for (tp2 = BL(tile); TOP(tp2) < TOP(tile); tp2 = RT(tp2));
	    if ((TiGetBottomType(tp1) == checktype) &&
		    (TiGetRightType(tp2) == checktype))
	    {
		dx = LEFT(tile) - RIGHT(self);
		dy = BOTTOM(self) - TOP(tile);
		if ((dx > 0) && (dy > 0))
		{
		   sqcheck = (long)dx*dx + (long)dy*dy; 
		   if (sqcheck >= sqdistance)
			return 0;
		}
		brlimcs->violator = tile;
		return 1;	/* Violator found */
	    }
	    break;
	case BRIDGE_SW:
	    /* Ignore tile if split, and NE corner is clipped */
	    if (IsSplit(tile))
	    if (TiGetRightType(tile) == checktype || TiGetTopType(tile) == checktype)
		break;
	    tp1 = LB(tile);
	    tp2 = BL(tile);
	    if ((TiGetTopType(tp1) == checktype) &&
		    (TiGetRightType(tp2) == checktype))
	    {
		dx = LEFT(tile) - RIGHT(self);
		dy = BOTTOM(tile) - TOP(self);
		if ((dx > 0) && (dy > 0))
		{
		   sqcheck = (long)dx*dx + (long)dy*dy; 
		   if (sqcheck >= sqdistance)
			return 0;
		}
		brlimcs->violator = tile;
		return 1;	/* Violator found */
	    }
	    break;
    }
    return 0;	/* Nothing found here, so keep going */
}

/*
 *-----------------------------------------------------------------------
 * Callback function used in bridge-lim operations to do not paint areas 
 * where new bridge overlaps the limiting layer tiles.
 *-----------------------------------------------------------------------
 */

int
bridgeErase(
    BridgeLimStruct *brlims,    /* Bridge-lim structure. */
    Rect *area)                 /* Area of Magic paint to consider. */
{
    TileTypeBitMask maskBits;
    TileType t;
    int i;
    Plane **temps = brlims->temps;

    for (i = PL_DRC_CHECK; i < DBNumPlanes; i++)
    {
	maskBits = DBPlaneTypes[i];
	TTMaskAndMask(&maskBits, &brlims->co_paintMask);
	if (!TTMaskEqual(&maskBits, &DBZeroTypeBits))
	    if (DBSrPaintArea((Tile *) NULL, brlims->def->cd_planes[i],
			area, &brlims->co_paintMask, cifPaintFunc, CIFEraseTable))
		return 0;
    }

    for (t = 0; t < TT_MAXTYPES; t++, temps++)
    {
        if (TTMaskHasType(&brlims->co_cifMask, t))
           if (DBSrPaintArea((Tile *) NULL, *temps, area, &CIFSolidBits,
			cifPaintFunc, CIFEraseTable))
                return 0;
    }

    return 1;       //Nothing found
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifBridgeLimFunc1 --
 *
 * 	Called for each relevant tile during bridge-lim operations.  The
 *	bridge-lim operation is similar to the bridge operation with the
 *	difference that the material created to meet the minimum spacing/width
 *	rules do not overlap the limiting layers.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	May paint into cifNewPlane
 * ----------------------------------------------------------------------------
 */

int
cifBridgeLimFunc1(
    Tile *tile,
    TileType dinfo,		/* (unused) */
    BridgeLimStruct *brlims)
{
    Plane *plane = brlims->plane;
    Rect area;
    Tile *tp1, *tp2, *tpx;
    int width = brlims->bridge->br_width;
    int spacing = growDistance;
    BridgeLimCheckStruct brlimcs;
    brlimcs.sqdistance = (long) spacing * spacing;

    /* If tile is marked, then it has been handled, so ignore it */
    if (TiGetClient(tile) != CIF_UNPROCESSED) return 0;

    /* Find each tile outside corner (up to four) */

    /* Check for NE outside corner */
    tp1 = TR(tile);  /* Tile on right side at the top of this tile */
    tp2 = RT(tile);  /* Tile on top side at the right of this tile */
    if ((TiGetLeftType(tp1) == TT_SPACE) &&
	    (TiGetBottomType(tp2) == TT_SPACE))
    {
	/* Set search box */
	area.r_xbot = RIGHT(tile);
	area.r_xtop = RIGHT(tile) + spacing;
	area.r_ybot = TOP(tile);
	area.r_ytop = TOP(tile) + spacing;

	/* Find violator tiles */
	brlimcs.tile = tile;
	brlimcs.direction = BRIDGE_SW;
	brlimcs.checktype = TT_SPACE;
	if (DBSrPaintArea((Tile *) NULL, plane, &area,
		    &CIFSolidBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1)
	{
	    tpx = brlimcs.violator;
	    area.r_xtop = MAX(RIGHT(tile), LEFT(tpx) + width);
	    area.r_ytop = MAX(TOP(tile), BOTTOM(tpx));
	    area.r_xbot = MIN(LEFT(tpx), RIGHT(tile));
	    area.r_ybot = MIN(BOTTOM(tpx), TOP(tile) - width);
	    if (bridgeLimSrTiles(brlims, &area, FALSE))
	    {
		/* First option of bridge can be implemented (there are not limiting tiles in the area)*/
		area.r_ytop = TOP(tile);
		DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
		area.r_xbot = LEFT(tpx);
		area.r_ytop = MAX(TOP(tile), BOTTOM(tpx));
		DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
	    }
	    else
	    {
	    area.r_xtop = MAX(RIGHT(tile), LEFT(tpx));
	    area.r_ytop = MAX(TOP(tile), BOTTOM(tpx) + width);
	    area.r_xbot = MIN(LEFT(tpx), RIGHT(tile) - width);
	    area.r_ybot = MIN(BOTTOM(tpx), TOP(tile));
	    if (bridgeLimSrTiles(brlims, &area, FALSE))
		{
		/* Second option of bridge can be implemented (there are not limiting tiles in the area)*/
		area.r_ybot = BOTTOM(tpx);
		DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
		area.r_xtop = RIGHT(tile);
	    	area.r_ybot = MIN(BOTTOM(tpx), TOP(tile));
		DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
		}
		else
		{
		/* Last option (Limiting tiles are present on both sides, those areas must be excluded from the bridge)*/
	    	area.r_xtop = MAX(RIGHT(tile), LEFT(tpx) + width);
	    	area.r_ytop = MAX(TOP(tile), BOTTOM(tpx) + width);
	    	area.r_xbot = MIN(LEFT(tpx), RIGHT(tile) - width);
	    	area.r_ybot = MIN(BOTTOM(tpx), TOP(tile) - width);
		DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
	    	bridgeErase(brlims, &area);
		}
	    }
	}
    }

    /* Check for SE outside corner */
    for (tp1 = TR(tile); BOTTOM(tp1) > BOTTOM(tile); tp1 = LB(tp1));
    for (tp2 = LB(tile); RIGHT(tp1) < RIGHT(tile); tp2 = TR(tp2));
    if ((TiGetLeftType(tp1) == TT_SPACE) &&
	    (TiGetTopType(tp2) == TT_SPACE))
    {
	/* Set search box */
	area.r_xbot = RIGHT(tile);
	area.r_xtop = RIGHT(tile) + spacing;
	area.r_ybot = BOTTOM(tile) - spacing;
	area.r_ytop = BOTTOM(tile);

	/* Find violator tiles */
	brlimcs.tile = tile;
	brlimcs.direction = BRIDGE_NW;
	brlimcs.checktype = TT_SPACE;
	if (DBSrPaintArea((Tile *) NULL, plane, &area,
		    &CIFSolidBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1)
	{
	    tpx = brlimcs.violator;
	    area.r_xtop = MAX(RIGHT(tile), LEFT(tpx));
	    area.r_ybot = MIN(BOTTOM(tile), TOP(tpx) - width);
	    area.r_xbot = MIN(LEFT(tpx), RIGHT(tile) - width);
	    area.r_ytop = MAX(TOP(tpx), BOTTOM(tile));
	    if (bridgeLimSrTiles(brlims, &area, FALSE))
	    {
		/* First option of bridge can be implemented (there are not limiting tiles in the area)*/
		area.r_xtop = RIGHT(tile);
		DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
	    	area.r_xtop = MAX(RIGHT(tile), LEFT(tpx));
		area.r_ytop = TOP(tpx);
		DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
	    }
	    else
	    {
	    area.r_xtop = MAX(RIGHT(tile), LEFT(tpx) + width);
	    area.r_ybot = MIN(BOTTOM(tile), TOP(tpx));
	    area.r_xbot = MIN(LEFT(tpx), RIGHT(tile));
	    area.r_ytop = MAX(TOP(tpx), BOTTOM(tile) + width);
	    if (bridgeLimSrTiles(brlims, &area, FALSE))
		{
		/* Second option of bridge can be implemented (there are not limiting tiles in the area)*/
		area.r_xbot = LEFT(tpx);
		DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
	    	area.r_xbot = MIN(LEFT(tpx), RIGHT(tile));
		area.r_ybot = BOTTOM(tile);
		DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
		}
		else
		{
		/* Last option (Limiting tiles are present on both sides, those areas must be excluded from the bridge)*/
		area.r_xtop = MAX(RIGHT(tile), LEFT(tpx) + width);
		area.r_ybot = MIN(BOTTOM(tile), TOP(tpx) - width);
		area.r_xbot = MIN(LEFT(tpx), RIGHT(tile) - width);
		area.r_ytop = MAX(TOP(tpx), BOTTOM(tile) + width);
		DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
	    	bridgeErase(brlims, &area);
		}
	    }
	}
    }

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 * cifBridgeLimFunc2 --
 *
 * 	Called for each relevant tile during bridge-lim operations.  The
 *	bridge-lim operation is similar to the bridge operation with the
 *	difference that the material created to meet the minimum spacing/width
 *	rules do not overlap the limiting layers.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	May paint into cifNewPlane
 * ----------------------------------------------------------------------------
 */

int
cifBridgeLimFunc2(
    Tile *tile,
    TileType dinfo,		/* (unused) */
    BridgeLimStruct *brlims)
{
    Plane *plane = brlims->plane;
    Rect area;
    Tile *tp1, *tp2, *tpx;
    int width = brlims->bridge->br_width;
    int thirdOption;
    int spacing = growDistance;
    BridgeLimCheckStruct brlimcs;
    brlimcs.sqdistance = (long) width * width;

    /* If tile is marked, then it has been handled, so ignore it */
    if (TiGetClient(tile) != CIF_UNPROCESSED) return 0;

    /* Find each tile outside corner (up to four) */

    /* Check for NE outside corner */
    tp1 = TR(tile);  /* Tile on right side at the top of this tile */
    tp2 = RT(tile);  /* Tile on top side at the right of this tile */
    if ((TiGetLeftType(tp1) == CIF_SOLIDTYPE) &&
	    (TiGetBottomType(tp2) == CIF_SOLIDTYPE))
    {
	/* Set search box */
	area.r_xbot = RIGHT(tile);
	area.r_xtop = RIGHT(tile) + width;
	area.r_ybot = TOP(tile);
	area.r_ytop = TOP(tile) + width;

	/* Find violator tiles */
	brlimcs.tile = tile;
	brlimcs.direction = BRIDGE_SW;
	brlimcs.checktype = CIF_SOLIDTYPE;
	if (DBSrPaintArea((Tile *) NULL, plane, &area,
		    &DBSpaceBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1)
	{
	    tpx = brlimcs.violator;
	    area.r_xtop = RIGHT(tile);
	    area.r_ytop = TOP(tile);
	    area.r_xbot = LEFT(tpx) - width;
	    area.r_ybot = BOTTOM(tpx) - width;

	    thirdOption = 0;
	    if (!bridgeLimSrTiles(brlims, &area, FALSE))
	    {
	    	area.r_xtop = RIGHT(tile) + width;
	    	area.r_ytop = TOP(tile) + width;
	    	area.r_xbot = LEFT(tpx);
	    	area.r_ybot = BOTTOM(tpx);
	        if (!bridgeLimSrTiles(brlims, &area, FALSE))
		{
	    	   area.r_xbot = LEFT(tpx) - width;
		   area.r_ybot = BOTTOM(tpx) - width;
	    	   thirdOption = 1;
		}
	    }
	    DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
	    if (thirdOption)
	    	bridgeErase(brlims, &area);
	}
    }

    /* Check for SE outside corner */
    for (tp1 = TR(tile); BOTTOM(tp1) > BOTTOM(tile); tp1 = LB(tp1));
    for (tp2 = LB(tile); RIGHT(tp2) < RIGHT(tile); tp2 = TR(tp2));
    if ((TiGetLeftType(tp1) == CIF_SOLIDTYPE) &&
	    (TiGetTopType(tp2) == CIF_SOLIDTYPE))
    {
	/* Set search box */
	area.r_xbot = RIGHT(tile);
	area.r_xtop = RIGHT(tile) + width;
	area.r_ybot = BOTTOM(tile) - width;
	area.r_ytop = BOTTOM(tile);

	/* Find violator tiles */
	brlimcs.tile = tile;
	brlimcs.direction = BRIDGE_NW;
	brlimcs.checktype = CIF_SOLIDTYPE;
	if (DBSrPaintArea((Tile *) NULL, plane, &area,
		    &DBSpaceBits, bridgeLimCheckFunc, (ClientData)&brlimcs) == 1)
	{
	    tpx = brlimcs.violator;
	    area.r_xtop = RIGHT(tile) + width;
	    area.r_ytop = TOP(tpx);
	    area.r_xbot = LEFT(tpx);
	    area.r_ybot = BOTTOM(tile) - width;

	    thirdOption = 0;
	    if (!bridgeLimSrTiles(brlims, &area, FALSE))
	    {
	    	area.r_xtop = RIGHT(tile);
	    	area.r_ytop = TOP(tpx) + width;
	    	area.r_xbot = LEFT(tpx) - width;
	    	area.r_ybot = BOTTOM(tile);
	        if (!bridgeLimSrTiles(brlims, &area, FALSE))
		{
	    	   area.r_xtop = RIGHT(tile) + width;
		   area.r_ybot = BOTTOM(tile) - width;
	    	   thirdOption = 1;
		}
	    }
	    DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
	    if (thirdOption)
	    	bridgeErase(brlims, &area);
	}
    }

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifNotSquareFunc --
 *
 *	Process each tile and remove those which are square and not
 *	connected to any other tile of the same type.  This operator aids
 *	in the detection of bar contacts to distinguish them from regular
 *	(square) contact cuts.  Because of the special nature of the
 *	operator, only the negative-sense operator "not-square" is
 *	implemented, as the positive-sense operator is not especially
 *	useful (and can be implemented if needed with "not-square" and
 *	"and-not"). 
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies the CIF planes.
 *
 * ----------------------------------------------------------------------------
 */

int
cifNotSquareFunc(
    Tile *tile,
    TileType dinfo,
    ClientData clientData)		/* (unused) */
{
    Tile *tp;
    TileType ttype;
    Rect area;
    int width, height;
    bool isolated = TRUE;

    if (IsSplit(tile)) return 0;	/* Non-Manhattan tiles are never square */
    ttype = TiGetType(tile);
    if (ttype == TT_SPACE) return 0;	/* Don't handle space tiles */

    /* Search all four sides of the tile.  Tiles are only considered square
     * for the purposes of this operator if they are also unconnected to any
     * other tile.
     */

    /* Top */
    for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
	if (TiGetBottomType(tp) == ttype)
	{
	    isolated = FALSE;
	    break;
	}

    /* Left */
    if (isolated)
	for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
	    if (TiGetBottomType(tp) == ttype)
	    {
		isolated = FALSE;
		break;
	    }

    /* Bottom */
    if (isolated)
	for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
	    if (TiGetBottomType(tp) == ttype)
	    {
		isolated = FALSE;
		break;
	    }

    /* Right */
    if (isolated)
	for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
	    if (TiGetBottomType(tp) == ttype)
	    {
		isolated = FALSE;
		break;
	    }

    TiToRect(tile, &area);

    if (isolated)
    {
	width = area.r_xtop - area.r_xbot;
	height = area.r_ytop - area.r_ybot;
	if (width == height) return 0;		/* Square and isolated */
    }

    area.r_xbot *= cifScale;
    area.r_ybot *= cifScale;
    area.r_xtop *= cifScale;
    area.r_ytop *= cifScale;

    DBPaintPlane(cifPlane, &area, CIFPaintTable, (PaintUndoInfo *)NULL);

    CIFTileOps += 1;
    return 0;
}


/*
 * ----------------------------------------------------------------------------
 *
 * cifInteractingRegions --
 *
 *	Process each disjoint region and copy the entire content of each
 *	region for which any part of the region overlaps with co_cifMask
 *	or co_paintMask to the current CIF plane.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies the CIF planes.
 *
 * ----------------------------------------------------------------------------
 */

void
cifInteractingRegions(
    CIFOp *op,
    const Rect *area,		/* Area of interest to check */
    CellDef *cellDef,
    Plane *temps[],		/* Planes to use for temporaries. */
    Plane *plane)
{
    Tile *tile = NULL, *t, *tp;
    Rect rect;
    int i;
    TileType type;
    bool interacts;
    static Stack *RegStack = (Stack *)NULL;

    if (RegStack == (Stack *)NULL)
	RegStack = StackNew(64);

    while (DBSrPaintArea((Tile *)tile, plane, area, &CIFSolidBits,
		cifSquaresInitFunc, (ClientData)NULL) != 0)
    {
	/* Now, search for (nontrivially) connected tiles in all	*/
	/* directions.  Mark the tiles, and record the bounding box.	*/
	/* This is the same method used for cifRectBoundingBox(),	*/
	/* except that the contents of each disjoint region is copied	*/
	/* to the destination plane or not depending on whether any	*/
	/* part of the region overlapped the specified type(s).		*/

	/* NOTE: Currently reusing code from cifSquares, but cifSquares	*/
	/* does not handle non-Manhattan geometry, whereas overlap and	*/
	/* interaction detection must.					*/

	interacts = FALSE;
	tile = PlaneGetHint(plane);
	PUSHTILEONLY(tile, RegStack);
	while (!StackEmpty(RegStack))
	{
	    POPTILEONLY(t, RegStack);
	    if (TiGetClientINT(t) != CIF_PENDING) continue;
            TiSetClientINT(t, CIF_PROCESSED);

	    /* Get tile area for interaction search */
	    TiToRect(t, &rect);

	    /* "interacting" includes touching as well as overlapping, so expand
	     * search by one unit in every direction and then check overlap.
	     * NOTE:  This catches catecorner-touching material, which is
	     * assumed not to be an issue.
	     */
	    if ((pointertype)op->co_client & CIFOP_INT_TOUCHING)
	    {
		rect.r_xbot -= 1;
		rect.r_xtop += 1;
		rect.r_ybot -= 1;
		rect.r_ytop += 1;
	    }

	    /* Check if this tile interacts with the rule's types, or skip	*/
	    /* if already known to be interacting.				*/

	    if (!interacts)
	    {
		if (cifSrTiles2(op, &rect, cellDef, temps, cifInteractFunc, (ClientData)NULL))
		    interacts = TRUE;
	    }

	    /* Top */
	    for (tp = RT(t); RIGHT(tp) > LEFT(t); tp = BL(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetBottomType(tp)))
		{
		    PUSHTILEONLY(tp, RegStack);
		}

	    /* Left */
	    for (tp = BL(t); BOTTOM(tp) < TOP(t); tp = RT(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetRightType(tp)))
		{
		    PUSHTILEONLY(tp, RegStack);
		}

	    /* Bottom */
	    for (tp = LB(t); LEFT(tp) < RIGHT(t); tp = TR(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetTopType(tp)))
		{
		    PUSHTILEONLY(tp, RegStack);
		}

	    /* Right */
	    for (tp = TR(t); TOP(tp) > BOTTOM(t); tp = LB(tp))
		if (TTMaskHasType(&CIFSolidBits, TiGetLeftType(tp)))
		{
		    PUSHTILEONLY(tp, RegStack);
		}
	}

	/* op->co_client has an invert bit indicating the rule is "not interacting"
	 * or "not-overlapping", so invert the sense of "interacts".  Then the non-
	 * interacting/overlapping regions will be kept and the interacting/
	 * overlapping regions will be discarded.
	 */
	if ((pointertype)op->co_client & CIFOP_INT_NOT)
	    interacts = (interacts) ? FALSE : TRUE;

	/* Clear the tiles that were processed in this set, first copying them	*/
	/* to the destination if this region was found to be interacting with	*/
	/* op->co_paintMask (op->co_cifMask)					*/ 

	TiSetClientINT(tile, CIF_IGNORE);
	PUSHTILEONLY(tile, RegStack);
	while (!StackEmpty(RegStack))
	{
	    POPTILEONLY(t, RegStack);

	    if (interacts)
	    {
		TiToRect(t, &rect);
		DBPaintPlane(cifPlane, &rect, CIFPaintTable, (PaintUndoInfo *)NULL);
	    }

	    /* Top */
	    for (tp = RT(t); RIGHT(tp) > LEFT(t); tp = BL(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, RegStack);
		}

	    /* Left */
	    for (tp = BL(t); BOTTOM(tp) < TOP(t); tp = RT(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, RegStack);
		}

	    /* Bottom */
	    for (tp = LB(t); LEFT(tp) < RIGHT(t); tp = TR(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, RegStack);
		}

	    /* Right */
	    for (tp = TR(t); TOP(tp) > BOTTOM(t); tp = LB(tp))
		if (TiGetClientINT(tp) == CIF_PROCESSED)
		{
		    TiSetClientINT(tp, CIF_IGNORE);
		    PUSHTILEONLY(tp, RegStack);
		}
	}

	/* Allow interrupts here */
	if (SigInterruptPending) break;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifCopyPropPlaneFunc --
 *
 * 	Copy the contents of a plane saved as a plane-type property into the
 *	current CIF plane.  The property plane is in magic internal
 *	coordinates, so each tile needs to be scaled and redrawn into the
 *	current CIF plane.
 *
 * Results:
 *	Zero to keep the search going
 *
 * Side effects:
 *	Copies translated geometry into the target plane.
 *
 * ----------------------------------------------------------------------------
 */

int
cifCopyPropPlaneFunc(Tile *tile,
    TileType dinfo,
    Plane *curPlane)
{
    Rect bbox;

    TiToRect(tile, &bbox);

    cifScale = (CIFCurStyle) ? CIFCurStyle->cs_scaleFactor : 1;

    bbox.r_xbot *= cifScale;
    bbox.r_ybot *= cifScale;
    bbox.r_xtop *= cifScale;
    bbox.r_ytop *= cifScale;

    cifScale = 1;
    DBNMPaintPlane(curPlane, CIF_SOLIDTYPE, &bbox,
		CIFPaintTable, (PaintUndoInfo *)NULL);
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFGenLayer --
 *
 *	This routine will generate one CIF layer.
 *	It provides the core of the CIF generator.
 *
 * Results:
 *	Returns a malloc'ed plane with tiles of type CIF_SOLIDTYPE
 *	marking the area of this CIF layer as built up by op.
 *
 * Side effects:
 *	None, except to create a new plane holding the CIF for the layer.
 *	The CIF that's generated may fall outside of area... it's what
 *	results from considering everything in area.  In most cases the
 *	caller will clip the results down to the desired area.
 *
 * ----------------------------------------------------------------------------
 */

Plane *
CIFGenLayer(
    CIFOp *op,			/* List of CIFOps telling how to make layer. */
    const Rect *area,		/* Area to consider when generating CIF.  Only
				 * material in this area will be considered, so
				 * the caller should usually expand his desired
				 * area by one CIF radius.
				 */
    CellDef *cellDef,		/* CellDef to search when paint layers are
				 * needed for operation.
				 */
    CellDef *origDef,		/* Original CellDef for which output is being
				 * generated (cellDef may be derived from this).
				 */
    Plane *temps[],		/* Temporary layers to be used when needed
				 * for operation.
				 */
    bool hier,			/* TRUE if called from CIFGenSubcells or CIFGenArrays */
    ClientData clientdata)	/*
				 * Data that may be passed to the CIF operation
				 * function.
				 */
{
    Plane *temp;
    static Plane *nextPlane, *curPlane;
    Rect bbox;
    CIFOp *tempOp;
    CIFSquaresInfo csi;
    SearchContext scx;
    TileType ttype;
    char *netname, *text;
    Label *label;
    BloatStruct bls;
    BridgeStruct brs;
    BridgeLimStruct brlims;
    BridgeData *bridge;
    BloatData *bloats, locbloat;
    bool hstop = FALSE;
    PropertyRecord *proprec;
    char *propvalue;
    bool found;

    int (*cifGrowFuncPtr)() = (CIFCurStyle->cs_flags & CWF_GROW_EUCLIDEAN) ?
		cifGrowEuclideanFunc : cifGrowFunc;

    /* Set up temporary planes used during computation.  One of these
     * will be returned as the result (whichever is curPlane at the
     * end of the computation).  The other is saved for later use.
     */

    if (nextPlane == NULL)
	nextPlane = DBNewPlane((ClientData) TT_SPACE);
    curPlane = DBNewPlane((ClientData) TT_SPACE);

    /* Go through the geometric operations and process them one
     * at a time.
     *
     * NOTE:  Some opcodes (and whatever follows them) should never
     * be run during hierarchical processing.  That includes BOUNDARY,
     * SQUARES/SLOTS, BBOX, and NET.
     *
     * Caveat:  SQUARES/SLOTS followed by GROW can be used to define
     * an etch area around a contact;  this should be considered a
     * special case that requires hierarchical processing on SQUARES/
     * SLOTS.
     */

    for ( ; op != NULL; op = op->co_next)
    {
	switch (op->co_opcode)
	{
	    /* For AND, first collect all the stuff to be anded with
	     * plane in a temporary plane.  Then find all the places
	     * where there isn't any stuff, and erase from the
	     * current plane.
	     */

	    case CIFOP_AND:
		DBClearPaintPlane(nextPlane);
		cifPlane = nextPlane;
		cifSrTiles(op, area, cellDef, temps, cifPaintFunc,
		    (ClientData) CIFPaintTable);
		cifPlane = curPlane;
		cifScale = 1;
		(void) DBSrPaintArea((Tile *) NULL, nextPlane, &TiPlaneRect,
		    &DBSpaceBits, cifPaintFunc,
		    (ClientData) CIFEraseTable);
		break;

	    /* For OR, just use cifPaintFunc to OR the areas of all
	     * relevant tiles into plane.  HOWEVER, if the co_client
	     * record is non-NULL and CalmaContactArrays is TRUE,
	     * then for each contact type, we do the paint function
	     * separately, then call the contact array generation
	     * procedure.
	     */

	    case CIFOP_OR:
		cifPlane = curPlane;
		cifScale = (CIFCurStyle) ? CIFCurStyle->cs_scaleFactor : 1;

		if ((op->co_client != (ClientData)NULL)
				&& (CalmaContactArrays == TRUE))
		{
		    TileTypeBitMask paintMask, errMask, *rMask;
		    TileType i, j;

		    TTMaskZero(&errMask);
		    TTMaskZero(&paintMask);
 		    TTMaskSetMask(&paintMask, &op->co_paintMask);
		    for (i = TT_TECHDEPBASE; i < DBNumUserLayers; i++)
		    {
			if (TTMaskHasType(&paintMask, i))
			{
			    TTMaskSetOnlyType(&op->co_paintMask, i);
			    for (j = DBNumUserLayers; j < DBNumTypes; j++)
			    {
				rMask = DBResidueMask(j);
				if (TTMaskHasType(rMask, i))
				    TTMaskSetType(&op->co_paintMask, j);
			    }

			    cifSrTiles(op, area, cellDef, temps, cifPaintFunc,
			    		(ClientData) CIFPaintTable);

			    csi.csi_squares = (SquaresData *)op->co_client;
			    csi.csi_type = i;
			    csi.csi_client = clientdata;

			    if (DBSrPaintArea((Tile *) NULL, curPlane,
					&TiPlaneRect, &CIFSolidBits,
					cifContactFunc,	(ClientData) &csi))
			    {
				/* Failure of DBSrPaintArea() (returns 1)
				 * indicates that a contact cell type
				 * could not be found for magic layer i.
				 * Record the error for subsequent handling.
				 */
				TTMaskSetType(&errMask, i);
			    }
			    DBClearPaintPlane(curPlane);
			}
		    }
		    if (!TTMaskIsZero(&errMask))
		    {
			/*
			 * Handle layers for which a contact cell could
			 * not be found in the default manner.
			 */
			TTMaskZero(&op->co_paintMask);
 			TTMaskSetMask(&op->co_paintMask, &errMask);
			cifSrTiles(op, area, cellDef, temps, cifPaintFunc,
		    		(ClientData) CIFPaintTable);
		    }

		    /* Recover the original magic layer mask for the cifop */

		    TTMaskZero(&op->co_paintMask);
 		    TTMaskSetMask(&op->co_paintMask, &paintMask);
		}
		else
		{
		    cifSrTiles(op, area, cellDef, temps, cifPaintFunc,
		    	(ClientData) CIFPaintTable);
		}
		break;

	    /* For ANDNOT, do exactly the same thing as OR, except erase
	     * instead of paint.
	     */

	    case CIFOP_ANDNOT:
		cifPlane = curPlane;
		cifSrTiles(op, area, cellDef, temps, cifPaintFunc,
		    (ClientData) CIFEraseTable);
		break;

	    /* For GROW, just find all solid tiles in the current plane,
	     * and paint a larger version into a new plane.  The switch
	     * the current and new planes.
	     */

	    case CIFOP_GROW:
		growDistance = op->co_distance;
		DBClearPaintPlane(nextPlane);
		cifPlane = nextPlane;
		cifScale = 1;
		(void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect,
		    &CIFSolidBits, *cifGrowFuncPtr, (ClientData) CIFPaintTable);
		temp = curPlane;
		curPlane = nextPlane;
		nextPlane = temp;
		break;

	    /* GROWMIN grows non-uniformly to ensure minimum dimensions */

	    case CIFOP_GROWMIN:
		growDistance = op->co_distance;
		DBClearPaintPlane(nextPlane);
		cifPlane = nextPlane;
		cifScale = 1;
		(void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect,
		    &CIFSolidBits, cifGrowMinFunc, (ClientData)CIFPaintTable);
		temp = curPlane;
		curPlane = nextPlane;
		nextPlane = temp;
		break;

	    /* GROW_G grows non-uniformly to the indicated grid. */

	    case CIFOP_GROW_G:
		growDistance = op->co_distance;
		DBClearPaintPlane(nextPlane);
		cifPlane = nextPlane;
		cifScale = 1;
		(void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect,
		    &CIFSolidBits, cifGrowGridFunc, (ClientData) CIFPaintTable);
		temp = curPlane;
		curPlane = nextPlane;
		nextPlane = temp;
		break;

	    /* SHRINK is just like grow except work from the space tiles. */

	    case CIFOP_SHRINK:
		growDistance = op->co_distance;
		DBClearPaintPlane(nextPlane);
		DBPaintPlane(nextPlane, &TiPlaneRect, CIFPaintTable,
		    (PaintUndoInfo *) NULL);
		cifPlane = nextPlane;
		cifScale = 1;
		(void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect,
		    &DBSpaceBits, *cifGrowFuncPtr, (ClientData) CIFEraseTable);
		temp = curPlane;
		curPlane = nextPlane;
		nextPlane = temp;
		break;

	    case CIFOP_CLOSE:
		growDistance = op->co_distance;
		DBClearPaintPlane(nextPlane);
		cifPlane = nextPlane;
		cifScale = 1;
		/* First copy the existing paint into the target plane */
		DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect,
		    	&CIFSolidBits, cifPaintFunc, (ClientData)CIFPaintTable);
		DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect,
		    	&DBSpaceBits, cifCloseFunc, (ClientData)&curPlane);

		temp = curPlane;
		curPlane = nextPlane;
		nextPlane = temp;
		break;

	    case CIFOP_BRIDGE:
		growDistance = op->co_distance;
		DBClearPaintPlane(nextPlane);
		cifPlane = nextPlane;
		cifScale = 1;
		/* First copy the existing paint into the target plane */
		(void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect,
		    &CIFSolidBits, cifPaintFunc, (ClientData)CIFPaintTable);

		brs.plane = curPlane;
		brs.bridge = (BridgeData *)op->co_client;
		(void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect,
		    &CIFSolidBits, cifBridgeFunc1, (ClientData)&brs);
		(void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect,
		    &DBSpaceBits, cifBridgeFunc2, (ClientData)&brs);
		temp = curPlane;
		curPlane = nextPlane;
		nextPlane = temp;
		break;

	    case CIFOP_BRIDGELIM:
		growDistance = op->co_distance;
		DBClearPaintPlane(nextPlane);
		cifPlane = nextPlane;
		cifScale = 1;
		
                brlims.bridge = (BridgeData *)op->co_client;
                brlims.def = cellDef;
                brlims.temps = temps;
		brlims.co_paintMask = op->co_paintMask;
		brlims.co_cifMask = op->co_cifMask;

		(void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect,
		    &CIFSolidBits, cifBridgeLimFunc0, (ClientData)&brlims);
		temp = curPlane;
                curPlane = nextPlane;
		brlims.plane = curPlane;

		(void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect,
		    &CIFSolidBits, cifBridgeLimFunc1, (ClientData)&brlims);
		(void) DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect,
		    &DBSpaceBits, cifBridgeLimFunc2, (ClientData)&brlims);
		curPlane = nextPlane;
		nextPlane = temp;
		break;

	    case CIFOP_BLOAT:
		cifPlane = curPlane;
		cifSrTiles(op, area, cellDef, temps,
		    cifBloatFunc, op->co_client);
		break;

	    case CIFOP_BLOATMAX:
	    case CIFOP_BLOATMIN:
		cifPlane = curPlane;
		cifSrTiles(op, area, cellDef, temps,
		    cifBloatMaxFunc, (ClientData) op);
		break;

	    case CIFOP_BLOATALL:
		cifPlane = curPlane;
		bls.op = op;
		bls.def = cellDef;
		bls.temps = temps;

	        /* Create a mask of all connecting types (these must be in a single
	         * plane), then call a search function to find all connecting material
	         * of these types (if the bloat types are temp layers, then this mask
	         * is not used).
	         */

	        bloats = (BloatData *)op->co_client;
	        if (bloats->bl_plane < 0)
	        {
		   /* bl_plane == -1 indicates bloating into a CIF templayer,	*/
		   /* so the only connecting type should be CIF_SOLIDTYPE.	*/
		   TTMaskSetOnlyType(&bls.connect, CIF_SOLIDTYPE);
	        }
	        else
	        {
		    int i;
		    TTMaskZero(&bls.connect);
		    for (i = 0; i < TT_MAXTYPES; i++)
			if (bloats->bl_distance[i] != 0)
			  TTMaskSetType(&bls.connect, i);
	        }

		cifSrTiles(op, area, cellDef, temps, cifBloatAllFunc, (ClientData)&bls);

	        /* Reset marked tiles */

	        if (bloats->bl_plane < 0)   /* Bloat types are CIF types */
	        {
		    for (ttype = 0; ttype < TT_MAXTYPES; ttype++, bls.temps++)
			if (bloats->bl_distance[ttype] > 0)
			    (void) DBSrPaintArea((Tile *)NULL, *bls.temps, &TiPlaneRect,
				    &CIFSolidBits, cifProcessResetFunc,
				    (ClientData)NULL);
	        }
	        else
		{
		    TTMaskSetMask(&bls.connect, &op->co_paintMask);
		    DBSrPaintArea((Tile *)NULL, cellDef->cd_planes[bloats->bl_plane],
			    &TiPlaneRect, &bls.connect, cifProcessResetFunc,
			    (ClientData)NULL);
		}

		break;

	    case CIFOP_SQUARES:
		if (hier)
		{
		    if ((op->co_next == NULL) || (op->co_next->co_opcode != CIFOP_GROW))
		    {
		    	hstop = TRUE;	/* Stop hierarchical processing */
		    	break;
		    }
		}
		if (CalmaContactArrays == FALSE)
		{
		    DBClearPaintPlane(nextPlane);
		    cifPlane = nextPlane;
		    cifSquaresFillArea(op, cellDef, curPlane);
		    temp = curPlane;
		    curPlane = nextPlane;
		    nextPlane = temp;
		}
		break;

	    case CIFOP_SQUARES_G:
		if (hier)
		{
		    if ((op->co_next == NULL) || (op->co_next->co_opcode != CIFOP_GROW))
		    {
		    	hstop = TRUE;	/* Stop hierarchical processing */
		    	break;
		    }
		}
		if (CalmaContactArrays == FALSE)
		{
		    DBClearPaintPlane(nextPlane);
		    cifPlane = nextPlane;
		    cifSquaresFillArea(op, cellDef, curPlane);
		    temp = curPlane;
		    curPlane = nextPlane;
		    nextPlane = temp;
		}
		break;

	    case CIFOP_SLOTS:
		if (hier)
		{
		    if ((op->co_next == NULL) || (op->co_next->co_opcode != CIFOP_GROW))
		    {
		    	hstop = TRUE;	/* Stop hierarchical processing */
		    	break;
		    }
		}
		if (CalmaContactArrays == FALSE)
		{
		    DBClearPaintPlane(nextPlane);
		    cifPlane = nextPlane;
		    cifSlotsFillArea(op, cellDef, curPlane);
		    temp = curPlane;
		    curPlane = nextPlane;
		    nextPlane = temp;
		}
		break;

	    case CIFOP_MANHATTAN:
		DBClearPaintPlane(nextPlane);
		cifPlane = nextPlane;
		cifScale = 1;
		DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect,
		    	&CIFSolidBits, cifOrthogonalFunc,
			(op->co_client == (ClientData)1) ?
			(ClientData) CIFPaintTable : 
			(ClientData) CIFEraseTable);

		temp = curPlane;
		curPlane = nextPlane;
		nextPlane = temp;
		break;

	    case CIFOP_MAXRECT:
		cifPlane = curPlane;

		DBClearPaintPlane(nextPlane);
		cifPlane = nextPlane;
		cifRectBoundingBox(op, cellDef, curPlane);
		temp = curPlane;
		curPlane = nextPlane;
		nextPlane = temp;
		break;

	    case CIFOP_INTERACT:
		cifPlane = curPlane;

		DBClearPaintPlane(nextPlane);
		cifPlane = nextPlane;
		cifInteractingRegions(op, area, cellDef, temps, curPlane);
		temp = curPlane;
		curPlane = nextPlane;
		nextPlane = temp;
		break;

	    case CIFOP_NOTSQUARE:
		DBClearPaintPlane(nextPlane);
		cifPlane = nextPlane;
		cifScale = 1;
		DBSrPaintArea((Tile *) NULL, curPlane, &TiPlaneRect,
		    	&CIFSolidBits, cifNotSquareFunc,
			(ClientData)NULL);

		temp = curPlane;
		curPlane = nextPlane;
		nextPlane = temp;
		break;

	    case CIFOP_NET:
		if (hier)
		{
		    hstop = TRUE;	/* Stop hierarchical processing */
		    break;
		}
		netname = (char *)op->co_client;
		cifPlane = curPlane;
		ttype = CmdFindNetProc(netname, CIFDummyUse, &bbox, FALSE, NULL);
		if (ttype != TT_SPACE)
		{
		    UndoDisable();
		    DBCellClearDef(Select2Def);
		    scx.scx_area = bbox;
		    scx.scx_use = CIFDummyUse;
		    scx.scx_trans = GeoIdentityTransform;
		    DBTreeCopyConnect(&scx, &DBConnectTbl[ttype], 0,
				DBConnectTbl, &TiPlaneRect, SEL_NO_LABELS, Select2Use);
		    cifSrTiles(op, area, Select2Def, temps, cifPaintFunc,
				(ClientData) CIFPaintTable);
		    DBCellClearDef(Select2Def);
		    UndoEnable();
		}
		break;

	    case CIFOP_TAGGED:
		if (hier)
		{
		    hstop = TRUE;	/* Stop hierarchical processing */
		    break;
		}

		/*
		 * Find all relevant labels by text matching and then continue
		 * like CIFOP_BLOATALL.  CIFOP_BLOATALL uses a BloatData record
		 * which is not part of CIFOP_TAGGED.  Create a BloatData record
		 * on the fly for each tagged area based on type, and swap it for
		 * the text, so that cifBloatAllFunc believes this is actually a
		 * CIFOP_BLOATALL operation.  Note that we don't actually care
		 * what layer the label is attached to (lab_type).  We are looking
		 * for labels whose lab_rect values overlap the types that are given
		 * in the rule.
		 */

		cifPlane = curPlane;
		bls.op = op;
		bls.def = cellDef;
		bls.temps = temps;

		text = (char *)op->co_client;

	        bloats = &locbloat;
		if (!TTMaskIsZero(&op->co_cifMask))
		{
		    bloats->bl_plane = -1;
		    for (ttype = 0; ttype < TT_MAXTYPES; ttype++)
		    {
			if (TTMaskHasType(&op->co_cifMask, ttype))
		    	    bloats->bl_distance[ttype] = 1;
			else
		    	    bloats->bl_distance[ttype] = 0;
		    }
		}
		else if (!TTMaskIsZero(&op->co_paintMask))
		{
		    int plane, pmask;
		    pmask = DBTechTypesToPlanes(&op->co_paintMask);
		    for (plane = PL_TECHDEPBASE; plane < DBNumPlanes; plane++)
			if (PlaneMaskHasPlane(pmask, plane))
			    break;
		    bloats->bl_plane = plane;
		    for (ttype = 0; ttype < TT_MAXTYPES; ttype++)
		    {
			if (TTMaskHasType(&op->co_paintMask, ttype))
		    	    bloats->bl_distance[ttype] = 1;
			else
		    	    bloats->bl_distance[ttype] = 0;
		    }
		}
		else
		{
		    /* Operate on the existing plane. */

		    bloats->bl_distance[0] = 1;
		    for (ttype = 1; ttype < TT_MAXTYPES; ttype++)
			bloats->bl_distance[ttype] = 0;

		    bloats->bl_plane = -1;
		    bls.temps = &curPlane;

		    DBClearPaintPlane(nextPlane);
		    cifPlane = nextPlane;
		}

		/* Replace the client data with the bloat record */
		op->co_client = (ClientData)bloats;

	        if (bloats->bl_plane < 0)
	        {
		   /* bl_plane == -1 indicates bloating into a CIF templayer,	*/
		   /* so the only connecting type should be CIF_SOLIDTYPE.	*/
		   TTMaskSetOnlyType(&bls.connect, CIF_SOLIDTYPE);
	        }
	        else
	        {
		    int i;
		    TTMaskZero(&bls.connect);
		    for (i = 0; i < TT_MAXTYPES; i++)
			if (bloats->bl_distance[i] != 0)
			  TTMaskSetType(&bls.connect, i);
	        }

		for (label = cellDef->cd_labels; label; label = label->lab_next)
		{
		    if (!strcmp(label->lab_text, text))
		    {
			Rect labr = label->lab_rect;

			/* Since cifSrTiles2() searches over an area, the
			 * area must not be degenerate.
			 */
			if (labr.r_xbot == labr.r_xtop)
			{
			    labr.r_xbot--;
			    labr.r_xtop++;
			}
			if (labr.r_ybot == labr.r_ytop)
			{
			    labr.r_ybot--;
			    labr.r_ytop++;
			}
			cifSrTiles2(op, &labr, cellDef, bls.temps,
				cifBloatAllFunc, (ClientData)&bls);
		    }
		}

	        /* Reset marked tiles */

	        if (bloats->bl_plane < 0)   /* Bloat types are CIF types */
	        {
		    for (ttype = 0; ttype < TT_MAXTYPES; ttype++, bls.temps++)
			if (bloats->bl_distance[ttype] > 0)
			    (void) DBSrPaintArea((Tile *)NULL, *bls.temps, &TiPlaneRect,
				    &CIFSolidBits, cifProcessResetFunc,
				    (ClientData)NULL);
	        }
	        else
		{
		    TTMaskSetMask(&bls.connect, &op->co_paintMask);
		    DBSrPaintArea((Tile *)NULL, cellDef->cd_planes[bloats->bl_plane],
			    &TiPlaneRect, &bls.connect, cifProcessResetFunc,
			    (ClientData)NULL);
		}

		/* Replace the client data */
		op->co_client = (ClientData)text;

		/* If operating on the current plane, swap the current
		 * and next planes.
		 */
		if (TTMaskIsZero(&op->co_cifMask) && TTMaskIsZero(&op->co_paintMask))
		{
		    temp = curPlane;
		    curPlane = nextPlane;
		    nextPlane = temp;
		}
		break;

	    case CIFOP_BOUNDARY:
		if (hier)
		{
		    hstop = TRUE;	/* Stop hierarchical processing */
		    break;
		}
		cifPlane = curPlane;
		/* This function for cifoutput only.  cifinput handled separately. */

		if (origDef && (origDef->cd_flags & CDFIXEDBBOX))
		{
		    proprec = DBPropGet(origDef, "FIXED_BBOX", &found);
		    if (!found) break;

		    if ((proprec->prop_type == PROPERTY_TYPE_DIMENSION) &&
				(proprec->prop_len == 4))
		    {
			bbox.r_xbot = proprec->prop_value.prop_integer[0];
			bbox.r_ybot = proprec->prop_value.prop_integer[1];
			bbox.r_xtop = proprec->prop_value.prop_integer[2];
			bbox.r_ytop = proprec->prop_value.prop_integer[3];
		    }

		    cifScale = (CIFCurStyle) ? CIFCurStyle->cs_scaleFactor : 1;
		    bbox.r_xbot *= cifScale;
		    bbox.r_xtop *= cifScale;
		    bbox.r_ybot *= cifScale;
		    bbox.r_ytop *= cifScale;
		    cifScale = 1;
		    DBNMPaintPlane(cifPlane, CIF_SOLIDTYPE, &bbox,
				CIFPaintTable, (PaintUndoInfo *)NULL);
		}
		break;

	    case CIFOP_BBOX:
		if (hier)
		{
		    hstop = TRUE;	/* Stop hierarchical processing */
		    break;
		}
		if (CIFErrorDef == NULL) break;

		/* co_client contains the flag (1) for top-level only */
		if ((int)CD2INT(op->co_client) == 1)
		{
		    /* Only generate output for the top-level cell */
		    int found = 0;
		    CellUse *celluse;
		    for (celluse = CIFErrorDef->cd_parents;
				celluse != (CellUse *) NULL;
				celluse = celluse->cu_nextuse)
		    {
			if (celluse->cu_parent != (CellDef *) NULL)
			    if ((celluse->cu_parent->cd_flags & CDINTERNAL)
					!= CDINTERNAL)
			    {
			 	found = 1;
				break;
			    }
		    }
		    if (found != 0) break;
		}
		cifPlane = curPlane;
		bbox = CIFErrorDef->cd_bbox;
		cifScale = (CIFCurStyle) ? CIFCurStyle->cs_scaleFactor : 1;
		bbox.r_xbot *= cifScale;
		bbox.r_xtop *= cifScale;
		bbox.r_ybot *= cifScale;
		bbox.r_ytop *= cifScale;
		cifScale = 1;
		DBNMPaintPlane(curPlane, CIF_SOLIDTYPE, &bbox,
			CIFPaintTable, (PaintUndoInfo *)NULL);
		break;

	    /* The CIFOP_MASKHINTS operator checks the current cell for	*/
	    /* a property with "MASKHINTS_" followed by the name saved	*/
	    /* in the co_client record.  If there is such a property,	*/
	    /* then the property value is parsed for geometry in	*/
	    /* internal units, grouped in sets of four values llx lly	*/
	    /* urx ury.							*/

	    case CIFOP_MASKHINTS:
		{
		    int n;
		    char propname[512];
		    char *layername = (char *)op->co_client;
		    Tile *t;

		    snprintf(propname, 512, "MASKHINTS_%s", layername);
		    
		    if (cellDef == (CellDef *)NULL) break;
		    proprec = DBPropGet(cellDef, propname, &found);
		    if (!found) break;	    /* No mask hints available */

		    ASSERT (proprec->prop_type == PROPERTY_TYPE_PLANE, "CIFGenLayer");
		    t = PlaneGetHint(proprec->prop_value.prop_plane);
		    DBSrPaintArea(t, proprec->prop_value.prop_plane,
				&TiPlaneRect, &CIFSolidBits,
				cifCopyPropPlaneFunc, (ClientData)curPlane);
		}
		break;

	    default:
		continue;
	}
	if (hstop) break;	/* Don't process any further rules */
    }

    return curPlane;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFGen --
 *
 * 	This procedure generates a complete set of CIF layers for
 *	a particular area of a particular cell.  NOTE: if the argument
 *	genAllPlanes is FALSE, only planes for those layers having
 *	a bit set in 'layers' are generated; the others are set
 *	to NULL.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The parameters realPlanes and tempPlanes are modified
 *	to hold the CIF and temporary layers for area of cellDef,
 *	as determined by the current CIF generation rules.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFGen(
    CellDef *cellDef,		/* Cell for which CIF is to be generated. */
    CellDef *origDef,		/* Original cell, if different from cellDef */
    const Rect *area,		/* Any CIF overlapping this area (in coords
				 * of cellDef) will be generated.  The CIF
				 * will be clipped to this area.
				 */
    Plane **planes,		/* Pointer to array of pointers to planes
				 * to hold "real" CIF layers that are
				 * generated.  Pointers may initially be
				 * NULL.
				 */
    TileTypeBitMask *layers,	/* CIF layers to generate. */
    bool replace,		/* TRUE means that the new CIF is to replace
				 * anything that was previously in planes.
				 * FALSE means that the new CIF is to be
				 * OR'ed in with the current contents of
				 * planes.
				 */
    bool genAllPlanes,		/* If TRUE, generate a tile plane even for
				 * those layers not specified as being
				 * generated in the 'layers' mask above.
				 */
    bool hier,			/* TRUE if called from CIFGenSubcells or CIFGenArrays */
    ClientData clientdata)	/* Data that may be passed along to the
				 * CIF operation functions.
				 */
{
    int i;
    Plane *new[MAXCIFLAYERS];
    Rect expanded, clip;

    /*
     * Generate the area in magic coordinates to search, and the area in
     * cif coordinates to use in clipping the results of CIFGenLayer().
     */
    cifGenClip(area, &expanded, &clip);

    /*
     * Generate all of the new layers in a temporary place.
     * If a layer isn't being generated, leave new[i] set to
     * NULL to indicate this fact.
     */
    for (i = 0; i < CIFCurStyle->cs_nLayers; i++)
    {
	if (TTMaskHasType(layers,i))
	{
	    CIFErrorLayer = i;
	    new[i] = CIFGenLayer(CIFCurStyle->cs_layers[i]->cl_ops,
		    &expanded, cellDef, origDef, new, hier, clientdata);

	    /* Clean up the non-manhattan geometry in the plane */
	    if (CIFUnfracture) DBMergeNMTiles(new[i], &expanded,
			(PaintUndoInfo *)NULL);
	}
	else if (genAllPlanes) new[i] = DBNewPlane((ClientData) TT_SPACE);
	else new[i] = (Plane *) NULL;
    }

    /*
     * Now mask off all the unwanted material in the new layers, and
     * either OR them into the existing layers or replace the existing
     * material with them.
     */
    for (i = 0; i < CIFCurStyle->cs_nLayers; i += 1)
    {
	if (new[i])
	    cifClipPlane(new[i], &clip);

	if (replace)
	{
	    if (planes[i])
	    {
		DBFreePaintPlane(planes[i]);
		TiFreePlane(planes[i]);
	    }
	    planes[i] = new[i];
	    continue;
	}

	if (planes[i])
	{
	    if (new[i])
	    {
		cifPlane = planes[i];
		cifScale = 1;
		(void) DBSrPaintArea((Tile *) NULL, new[i], &TiPlaneRect,
			&CIFSolidBits, cifPaintFunc,
			(ClientData) CIFPaintTable);
		DBFreePaintPlane(new[i]);
		TiFreePlane(new[i]);
	    }
	}
	else planes[i] = new[i];
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifClipPlane --
 *
 * Erase the portions of the plane 'plane' that lie outside of 'clip'.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	See above.
 *
 * ----------------------------------------------------------------------------
 */

void
cifClipPlane(
    Plane *plane,
    Rect *clip)
{
    Rect r;

    if (clip->r_xtop < TiPlaneRect.r_xtop)
    {
	r = TiPlaneRect;
	r.r_xbot = clip->r_xtop;
	DBPaintPlane(plane, &r, CIFEraseTable, (PaintUndoInfo *) NULL);
    }
    if (clip->r_ytop < TiPlaneRect.r_ytop)
    {
	r = TiPlaneRect;
	r.r_ybot = clip->r_ytop;
	DBPaintPlane(plane, &r, CIFEraseTable, (PaintUndoInfo *) NULL);
    }
    if (clip->r_xbot > TiPlaneRect.r_xbot)
    {
	r = TiPlaneRect;
	r.r_xtop = clip->r_xbot;
	DBPaintPlane(plane, &r, CIFEraseTable, (PaintUndoInfo *) NULL);
    }
    if (clip->r_ybot > TiPlaneRect.r_ybot)
    {
	r = TiPlaneRect;
	r.r_ytop = clip->r_ybot;
	DBPaintPlane(plane, &r, CIFEraseTable, (PaintUndoInfo *) NULL);
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifGenClip --
 *
 * Compute two new areas from the original area:  one ('expanded')
 * is expanded by a CIF halo and is used to determine how much of
 * the database to search to find what's relevant for CIF generation;
 * the other ('clip') is the CIF equivalent of area and is used to
 * clip the resulting CIF.  This code is tricky because area may run
 * off to infinity, and we have to be careful not to expand past infinity.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Sets *expanded and *clip.
 *
 * ----------------------------------------------------------------------------
 */

void
cifGenClip(
    const Rect *area,		/* Any CIF overlapping this area (in coords
				 * of cellDef) will be generated.  The CIF
				 * will be clipped to this area.
				 */
    Rect *expanded,
    Rect *clip)
{
    if (area->r_xbot > TiPlaneRect.r_xbot)
    {
	clip->r_xbot = area->r_xbot * CIFCurStyle->cs_scaleFactor;
	expanded->r_xbot = area->r_xbot - CIFCurStyle->cs_radius;
    }
    else clip->r_xbot = expanded->r_xbot = area->r_xbot;
    if (area->r_ybot > TiPlaneRect.r_ybot)
    {
	clip->r_ybot = area->r_ybot * CIFCurStyle->cs_scaleFactor;
	expanded->r_ybot = area->r_ybot - CIFCurStyle->cs_radius;
    }
    else clip->r_ybot = expanded->r_ybot = area->r_ybot;
    if (area->r_xtop < TiPlaneRect.r_xtop)
    {
	clip->r_xtop = area->r_xtop * CIFCurStyle->cs_scaleFactor;
	expanded->r_xtop = area->r_xtop + CIFCurStyle->cs_radius;
    }
    else clip->r_xtop = expanded->r_xtop = area->r_xtop;
    if (area->r_ytop < TiPlaneRect.r_ytop)
    {
	clip->r_ytop = area->r_ytop * CIFCurStyle->cs_scaleFactor;
	expanded->r_ytop = area->r_ytop + CIFCurStyle->cs_radius;
    }
    else clip->r_ytop = expanded->r_ytop = area->r_ytop;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFClearPlanes --
 *
 * 	This procedure clears out a collection of CIF planes.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Each of the planes in "planes" is re-initialized to point to
 *	an empty paint plane.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFClearPlanes(
    Plane **planes)		/* Pointer to an array of MAXCIFLAYERS
				 * planes.
				 */
{
    int i;

    for (i = 0; i < MAXCIFLAYERS; i++)
    {
	if (planes[i] == NULL)
	{
	    planes[i] = DBNewPlane((ClientData) TT_SPACE);
	}
	else
	{
	    DBClearPaintPlane(planes[i]);
	}
    }
}
