/* CIFhier.c -
 *
 *	This module handles hierarchy as part of the CIF generator.
 *	Because of the nature of the geometrical operations used
 *	to generate CIF, such as grow and shrink, the CIF representing
 *	two nearby subcells (or elements of an array) may require
 *	more than just the combined CIF of the two subcells considered
 *	separately.  This module computes the extra CIF that may be
 *	needed.
 *
 *     *********************************************************************
 *     * 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/CIFhier.c,v 1.3 2010/06/24 12:37:15 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "cif/CIFint.h"
#include "cif/cif.h"
#include "drc/drc.h"
#include "graphics/graphics.h"
#include "textio/textio.h"
#include "utils/undo.h"
#include "utils/malloc.h"
#include "utils/signals.h"

/* To compute CIF where there are interaction areas we do two things:
 * 1. Compute the CIF by combining all the material in all the interacting
 *    cells together.  CIFTotalUse and CIFTotalDef and CIFTotalPlanes
 *    are used to hold the flattened material and resulting CIF.
 * 2. Compute the CIF that results by considering the material in each
 *    subtree separately and also the material in the parent separately.
 *    The paint for each subtree is first flattened into CIFComponentUse
 *    and CIFComponentDef, and the resulting CIF from each subtree is
 *    OR'ed together into CIFComponentPlanes.
 */

CellUse *CIFTotalUse = NULL;
CellDef *CIFTotalDef;
CellUse *CIFComponentUse;
CellDef *CIFComponentDef;
Plane *CIFTotalPlanes[MAXCIFLAYERS];
Plane *CIFComponentPlanes[MAXCIFLAYERS];

/* The following use is just used to turn a def into a use for calling
 * procedures that want a use.
 */

CellUse *CIFDummyUse;

/* The following local variables are used to share information
 * between top-level procedures and search functions.
 */

/* Used in cifHierPaintArrayFunc: */

Plane *cifHierCurPlane;			/* Current plane. */
static int cifHierXSpacing, cifHierYSpacing, cifHierXCount, cifHierYCount;

/* Used in cifGrowSliver */
static CIFLayer *CurCifLayer;

/* Macro for scaling boxes into CIF coordinates: */
#define SCALE(src, scale, dst) \
    (dst)->r_xbot = (src)->r_xbot * scale; \
    (dst)->r_ybot = (src)->r_ybot * scale; \
    (dst)->r_xtop = (src)->r_xtop * scale; \
    (dst)->r_ytop = (src)->r_ytop * scale;


/*
 * ----------------------------------------------------------------------------
 *
 * CIFInitCells --
 *
 * 	This procedure just sets up cell definitions and uses needed
 *	for hierarchical checking and other CIF uses.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	DRCUse, DRCDef, and DRCDummyUse are set up if they're not
 *	there already.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFInitCells(void)
{
    int i;

    if (CIFTotalUse != NULL) return;

    CIFTotalDef = DBCellLookDef("__CIF__");
    if (CIFTotalDef == (CellDef *) NULL)
    {
	CIFTotalDef = DBCellNewDef("__CIF__");
	ASSERT(CIFTotalDef != (CellDef *) NULL, "cifMakeCell");
	DBCellSetAvail(CIFTotalDef);
	CIFTotalDef->cd_flags |= CDINTERNAL;
    }
    CIFTotalUse = DBCellNewUse (CIFTotalDef, (char *) NULL);
    DBSetTrans (CIFTotalUse, &GeoIdentityTransform);
    CIFTotalUse->cu_expandMask = CU_DESCEND_SPECIAL;	/* This is always expanded. */

    CIFComponentDef = DBCellLookDef("__CIF2__");
    if (CIFComponentDef == (CellDef *) NULL)
    {
	CIFComponentDef = DBCellNewDef("__CIF2__");
	ASSERT(CIFComponentDef != (CellDef *) NULL, "cifMakeCell");
	DBCellSetAvail(CIFComponentDef);
	CIFComponentDef->cd_flags |= CDINTERNAL;
    }
    CIFComponentUse = DBCellNewUse (CIFComponentDef, (char *) NULL);
    DBSetTrans (CIFComponentUse, &GeoIdentityTransform);
    CIFComponentUse->cu_expandMask = CU_DESCEND_SPECIAL; /* This is always expanded. */

    /* Clear out the planes used to collect hierarchical CIF. */

    for (i = 0; i < MAXCIFLAYERS; i++)
    {
	CIFComponentPlanes[i] = NULL;
	CIFTotalPlanes[i] = NULL;
    }

    /* Also create a dummy cell use to use for passing to
     * procedures that need a use when all we've got is a def.
     */

    CIFDummyUse = DBCellNewUse(CIFTotalDef, (char *) NULL);
    DBSetTrans (CIFDummyUse, &GeoIdentityTransform);
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifHierCleanup --
 *
 * 	This procedure is called after CIF hierarchical processing
 *	to clean up the cells and tile planes use for hierarchy and
 *	release storage.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	All the CIF-related uses and planes are cleaned up.
 *
 * ----------------------------------------------------------------------------
 */

void
cifHierCleanup(void)
{
    int i;

    /* We can't afford for this clearing out to be interrupted, or
     * it could cause the next CIF to be generated wrong.
     */

    SigDisableInterrupts();

    DBCellClearDef(CIFTotalDef);
    DBCellClearDef(CIFComponentDef);
    for (i = 0; i < MAXCIFLAYERS; i++)
    {
	if (CIFTotalPlanes[i] != NULL)
	{
	    DBFreePaintPlane(CIFTotalPlanes[i]);
	    TiFreePlane(CIFTotalPlanes[i]);
	    CIFTotalPlanes[i] = NULL;
	}
	if (CIFComponentPlanes[i] != NULL)
	{
	    DBFreePaintPlane(CIFComponentPlanes[i]);
	    TiFreePlane(CIFComponentPlanes[i]);
	    CIFComponentPlanes[i] = NULL;
	}
    }

    SigEnableInterrupts();
}

/* Structure used by cifFlatMaskHints, below */

typedef struct _maskHintsData
{
    Transform *mh_trans;
    CellDef *mh_def;
    Plane *mh_plane;
} MaskHintsData;

/*
 * ----------------------------------------------------------------------------
 *
 * cifCopyMaskHintFunc --
 *
 *	Callback function used by cifFlatMaskHints.  Transforms a tile
 *	from the original plane and paints it into the target plane,
 *	both of which are properties.
 *
 * Results:
 *	Zero to keep the search going.
 *
 * Side effects:
 *	Paints geometry into the target plane.
 *
 * ----------------------------------------------------------------------------
 */

int
cifCopyMaskHintFunc(Tile *tile,
    TileType dinfo,
    ClientData cdata)
{
    MaskHintsData *mhd = (MaskHintsData *)cdata;
    Rect r, newr;

    TiToRect(tile, &r);

    /* Transform tile area to coordinates of mhd->mh_plane and paint */
    GeoTransRect(mhd->mh_trans, &r, &newr);
    DBPaintPlane(mhd->mh_plane, &newr, CIFPaintTable, (PaintUndoInfo *)NULL);

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifFlatMaskHints --
 *
 *	Copy a mask hint into a flattened cell by transforming it into the
 *	coordinate system of the flattened cell, and painting it into the
 *	property plane of the flattened cell.
 *
 * Returns:
 *	0 to keep the search going.
 *
 * Side effects:
 *	Modifies properties of the cell def passed in MaskHintsData.
 *
 * ----------------------------------------------------------------------------
 */

int
cifFlatMaskHints(
    char *name,
    PropertyRecord *proprec,
    MaskHintsData *mhd)
{
    Rect r, newr;
    char *vptr, *newval, *lastval, *propvalue;
    bool propfound;
    int i, lastlen, numvals;
    PropertyRecord *newproprec, *oldproprec;
    Plane *plane;

    if (!strncmp(name, "MASKHINTS_", 10))
    {
	/* Check if name exists already in the flattened cell */
	oldproprec = (PropertyRecord *)DBPropGet(mhd->mh_def, name, &propfound);
	if (propfound)
	{
	    ASSERT(oldproprec->prop_type == PROPERTY_TYPE_PLANE,
			"cifFlatMaskHints");
	    plane = oldproprec->prop_value.prop_plane;
	}
	else
	{
	    newproprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord));
	    newproprec->prop_len = 0; 	/* (unused) */
	    newproprec->prop_type = PROPERTY_TYPE_PLANE;
	    plane = DBNewPlane((ClientData)TT_SPACE);
	    newproprec->prop_value.prop_plane = plane;
	    DBPropPut(mhd->mh_def, name, newproprec);
	}

	mhd->mh_plane = plane;
	DBSrPaintArea((Tile *)NULL, proprec->prop_value.prop_plane,
			&TiPlaneRect, &CIFSolidBits,
			cifCopyMaskHintFunc, (ClientData)mhd);
    }
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFCopyMaskHints --
 *
 *	Callback function to copy mask hints from one cell into another.
 *	(Occasionally called as a standalone function, not as a callback.)
 * 
 * Results:
 *	Return 0 to keep the search going.
 *
 * Side effects:
 *	May modify properties in the target cell.
 *
 * ----------------------------------------------------------------------------
 */

int
CIFCopyMaskHints(
    SearchContext *scx,
    CellDef *targetDef)
{
    MaskHintsData mhd;

    CellDef *sourceDef = scx->scx_use->cu_def;
    mhd.mh_trans = &scx->scx_trans;
    mhd.mh_def = targetDef;
    mhd.mh_plane = (Plane *)NULL;

    DBPropEnum(sourceDef, cifFlatMaskHints, &mhd);
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifHierCopyFunc --
 *
 * 	This procedure is called to copy paint from the database into
 *	flattened areas for CIF generation.  It's important to use
 *	this procedure rather than calling DBCellCopyAllPaint.  The
 *	reason is that DBCellCopyAllPaint clips the tiles to the
 *	edges of the search area.  When generating CIF for layers like
 *	contacts, the exact location of the edge of the tile is
 *	important.  Thus, this procedure always copies WHOLE tiles.
 *	Information will be clipped to the edge of the CIF generation
 *	area later.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	The tile is copied into the definition indicated by the
 *	client data.
 *
 * ----------------------------------------------------------------------------
 */

int
cifHierCopyFunc(
    Tile *tile,			/* Pointer to tile to copy. */
    TileType dinfo,		/* Split tile information */
    TreeContext *cxp)		/* Describes context of search, including
				 * transform and client data.
				 */
{
    TileType type = TiGetTypeExact(tile) | dinfo;
    Rect sourceRect, targetRect;
    int pNum;
    CellDef *def = (CellDef *) cxp->tc_filter->tf_arg;
    TileType newdinfo = 0;

    /* Ignore tiles in vendor GDS, unless this is specifically	*/
    /* overridden by the "see-vendor" option.			*/

    if (cxp->tc_scx->scx_use->cu_def->cd_flags & CDVENDORGDS)
    {
	if (!CIFCurStyle)
	    if (!(CIFCurStyle->cs_flags & CWF_SEE_NO_VENDOR))
		return 0;
    }

    /* Ignore space tiles, since they won't do anything anyway. */

    if (IsSplit(tile))
    {
	newdinfo = DBTransformDiagonal(type, &cxp->tc_scx->scx_trans);
	type = (dinfo & TT_SIDE) ? SplitRightType(tile) :
		SplitLeftType(tile);
    }

    if (type == TT_SPACE) return 0;

    /* Get the rectangular area, and transform to final coords. */

    TiToRect(tile, &sourceRect);
    GeoTransRect(&cxp->tc_scx->scx_trans, &sourceRect, &targetRect);

    for (pNum = PL_SELECTBASE; pNum < DBNumPlanes; pNum++)
    {
	if (DBPaintOnPlane(type, pNum))
	{
	    DBNMPaintPlane(def->cd_planes[pNum], newdinfo, &targetRect,
		DBStdPaintTbl(type, pNum), (PaintUndoInfo *) NULL);
	}
    }
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifHierCellFunc --
 *
 * 	This procedure is invoked once for each subcell overlapping
 *	an interaction area.  It flattens the subcell and its children
 *	in the area of the overlap, generates CIF for that area, and
 *	saves it in cifHierPieces.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	A new collection of CIF planes is added to cifHierPieces.
 *
 * ----------------------------------------------------------------------------
 */

int
cifHierCellFunc(
    SearchContext *scx)		/* Describes cell and area in cell. */
{
    SearchContext newscx;
    Rect rootArea;

    /* Do not call this function on cells that are vendor GDS */
    /* (handled in cifHierCopyFunc())	*/
    /* if (scx->scx_use->cu_def->cd_flags & CDVENDORGDS) return 0; */

    /* In order to generate CIF safely in the interaction area, we
     * have to yank material in a larger area:  bloats and shrinks
     * may cause this material to affect CIF in the interaction area.
     * This may actually be over-conservative, but it's safe.  Think
     * carefully before trying to optimize!
     */

    DBCellClearDef(CIFComponentDef);
    newscx = *scx;
    GEO_EXPAND(&scx->scx_area, CIFCurStyle->cs_radius, &newscx.scx_area);
    (void) DBTreeSrTiles(&newscx, &CIFCurStyle->cs_yankLayers, 0,
	    cifHierCopyFunc, (ClientData) CIFComponentDef);

    /* Flatten mask hints in the area of interest */
    CIFCopyMaskHints(scx, CIFComponentDef);
    DBTreeSrCells(&newscx, 0, CIFCopyMaskHints,
		(ClientData)CIFComponentDef);

    /* Set CIFErrorDef to NULL to ignore errors here... these will
     * get reported anyway when the cell is CIF'ed non-hierarchically,
     * so there's no point in making a second report here.
     */

    CIFErrorDef = (CellDef *) NULL;
    GeoTransRect(&scx->scx_trans, &scx->scx_area, &rootArea);
    CIFGen(CIFComponentDef, scx->scx_use->cu_def, &rootArea,
	    CIFComponentPlanes, &CIFCurStyle->cs_hierLayers, FALSE,
	    TRUE, TRUE, (ClientData)NULL);
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifHierErrorFunc --
 *
 * 	This function is invoked when the combined CIF in a parent
 *	is LESS than the individual CIFs of the children.  This means
 *	there are bogus rules in the CIF rule set.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	Error information is output.
 *
 * ----------------------------------------------------------------------------
 */

int
cifHierErrorFunc(
    Tile *tile,			/* Tile that covers area it shouldn't. */
    TileType dinfo,		/* Split tile information */
    Rect *checkArea)		/* Intersection of this and tile is error. */
{
    Rect area;
    bool side = (dinfo & TT_SIDE) ? TRUE : FALSE;

    TiToRect(tile, &area);

    /* Space in a diagonal tile is not an error if the corner containing
     * space bounds the checkArea.
     */
    if (IsSplit(tile))
	if (((area.r_xbot == checkArea->r_xbot) && !side) ||
		((area.r_xtop == checkArea->r_xtop) && side))
	    return 0;

    GeoClip(&area, checkArea);
    CIFError(&area, "parent and child disagree on CIF");
    return 0;
}


/*
 * ----------------------------------------------------------------------------
 *
 * cifHierCheckFunc --
 *
 * 	This function is invoked once for each CIF tile coming from
 *	a subcell piece.  It makes sure there are no space tiles over
 *	its area in "plane", then deletes everything from that area
 *	in "plane".
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	Error messages may be output.
 *
 * ----------------------------------------------------------------------------
 */

int
cifHierCheckFunc(
    Tile *tile,			/* Tile containing CIF. */
    TileType dinfo,		/* Split tile information */
    Plane *plane)		/* Plane to check against and modify. */
{
    Rect area;

    TiToRect(tile, &area);

    if (IsSplit(tile))
    {
        DBSrPaintNMArea((Tile *)NULL, plane, TiGetTypeExact(tile) | dinfo,
		&area, &DBSpaceBits, cifHierErrorFunc, (ClientData) &area);

	DBNMPaintPlane(plane, TiGetTypeExact(tile) | dinfo, &area, CIFEraseTable,
		(PaintUndoInfo *) NULL);
    }
    else
    {
        DBSrPaintArea((Tile *) NULL, plane, &area,
		&DBSpaceBits, cifHierErrorFunc, (ClientData) &area);
        DBPaintPlane(plane, &area, CIFEraseTable, (PaintUndoInfo *) NULL);
    }

    CIFTileOps++;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifHierTempCheckFunc --
 *
 * 	This function is like cifHierCheckFunc() (see above), but is used
 *	for "templayers", where any parent/child disagreement should be
 *	considered a non-issue as far as output is concerned.  Only the
 *	actual mask layer will report any problems.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	Error messages may be output.
 *
 * ----------------------------------------------------------------------------
 */

int
cifHierTempCheckFunc(
    Tile *tile,			/* Tile containing CIF. */
    TileType dinfo,		/* Information about split tiles */
    Plane *plane)		/* Plane to check against and modify. */
{
    Rect area;

    TiToRect(tile, &area);

    if (IsSplit(tile))
	DBNMPaintPlane(plane, TiGetTypeExact(tile) | dinfo, &area, CIFEraseTable,
		(PaintUndoInfo *) NULL);
    else
        DBPaintPlane(plane, &area, CIFEraseTable, (PaintUndoInfo *) NULL);

    CIFTileOps++;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifHierPaintFunc --
 *
 * 	Called to transfer information from one CIF plane to another.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	The area of tile is painted into plane.
 *
 * ----------------------------------------------------------------------------
 */

int
cifHierPaintFunc(
    Tile *tile,
    TileType dinfo,		/* Information about split tiles */
    Plane *plane)		/* Plane in which to paint CIF over tile's
				 * area.
				 */
{
    Rect area;

    TiToRect(tile, &area);
    if (CIFCurStyle->cs_flags & CWF_GROW_SLIVERS) cifGrowSliver(tile, dinfo, &area);
    if (IsSplit(tile))
	DBNMPaintPlane(plane, TiGetTypeExact(tile) | dinfo, &area, CIFPaintTable,
		(PaintUndoInfo *) NULL);
    else
	DBPaintPlane(plane, &area, CIFPaintTable, (PaintUndoInfo *) NULL);
    CIFTileOps += 1;
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifCheckAndErase --
 *
 * 	This utility procedure processes the hierarchical pieces
 *	in two ways.  First, it makes sure that all the CIF in
 *	CIFComponentPlanes is present in CIFTotalPlanes.  Second,
 *	it erases from CIFTotalPlanes any information that's in
 *	CIFComponentPlanes.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The information in CIFTotalPlanes is reduced, and error messages
 *	may be output.
 *
 * ----------------------------------------------------------------------------
 */

void
cifCheckAndErase(
    CIFStyle *style)		/* Describes how to make CIF. */
{
    int i;

    /* Process all of the bits of CIF in CIFComponentPlanes. */

    for (i=0; i<style->cs_nLayers; i++)
    {
	CIFErrorLayer = i;
	if (CIFComponentPlanes[i] == NULL) continue;
	if (CIFCurStyle->cs_layers[i]->cl_flags & CIF_TEMP)
	    (void) DBSrPaintArea((Tile *) NULL, CIFComponentPlanes[i],
	    	&TiPlaneRect, &CIFSolidBits, cifHierTempCheckFunc,
	    	(ClientData) CIFTotalPlanes[i]);
	else
	    (void) DBSrPaintArea((Tile *) NULL, CIFComponentPlanes[i],
	    	&TiPlaneRect, &CIFSolidBits, cifHierCheckFunc,
	    	(ClientData) CIFTotalPlanes[i]);
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFGenSubcells --
 *
 * 	This procedure computes all of the CIF that must be added to
 *	a given area to compensate for interactions between subcells.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The parameter "output" is modified (by OR'ing) to hold all
 *	the CIF that was generated for subcells.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFGenSubcells(
    CellDef *def,		/* Parent cell for which CIF is computed. */
    Rect *area,			/* All CIF in this area is interesting. */
    Plane **output)		/* Array of pointers to planes into which
				 * the CIF output will be OR'ed (real CIF
				 * only).
				 */
{
    int stepSize, x, y, i, radius, oldTileOps, oldTileOps2;
    Rect totalArea, square, interaction;
    SearchContext scx;
    int cuts, totcuts;
    float pdone, plast;

    UndoDisable();
    CIFInitCells();
    radius = CIFCurStyle->cs_radius;
    stepSize = CIFCurStyle->cs_stepSize;
    if (stepSize <= 0)
    {
	stepSize = 20*radius;
	if (stepSize < 50) stepSize = 50;
    }
    CIFDummyUse->cu_def = def;
    scx.scx_use = CIFDummyUse;
    scx.scx_trans = GeoIdentityTransform;

    /* This routine can take a long time, so use the display
     * timer to force a 5-second progress check (like is done
     * with extract).  Save and restore GrDisplayStatus so that
     * a headless (DISPLAY_SUSPEND) build isn't left in DISPLAY_IDLE.
     */
    unsigned char savedDisplayStatus = GrDisplayStatus;
    GrDisplayStatus = DISPLAY_IN_PROGRESS;
    SigSetTimer(5);			/* Print at 5-second intervals */
    cuts = 0;
    pdone = 0.0;
    plast = 0.0;

    /* Any tile operations processed here get billed to hierarchy
     * in addition to being added to the total.
     */

    oldTileOps = CIFTileOps;

    /* Divide the area of the cell up into squares, and step through
     * the chunks.  In each chunk, the first thing to do is to find
     * out if there are any subcell interactions within one
     * radius of the square.
     */

    totalArea = *area;
    GeoClip(&totalArea, &def->cd_bbox);

    totcuts = (totalArea.r_ytop - totalArea.r_ybot + stepSize - 1)
			/ stepSize;
    totcuts *= ((totalArea.r_xtop - totalArea.r_xbot + stepSize - 1)
			/ stepSize);

    for (y = totalArea.r_ybot; y < totalArea.r_ytop; y += stepSize)
	for (x = totalArea.r_xbot; x < totalArea.r_xtop; x += stepSize)
	{
	    square.r_xbot = x;
	    square.r_ybot = y;
	    square.r_xtop = x + stepSize;
	    square.r_ytop = y + stepSize;
	    if (square.r_xtop > totalArea.r_xtop)
		square.r_xtop = totalArea.r_xtop;
	    if (square.r_ytop > totalArea.r_ytop)
		square.r_ytop = totalArea.r_ytop;
	    GEO_EXPAND(&square, radius, &square);
	    if (DRCFindInteractions(def, &square, radius,
		&interaction) <= 0) continue;

	    /* We've found an interaction.  Flatten it into CIFTotalUse, then
	     * make CIF from what's flattened.  Yank extra material to
	     * ensure that CIF is generated correctly.
	     */

	    GEO_EXPAND(&interaction, CIFCurStyle->cs_radius, &scx.scx_area);
	    (void) DBTreeSrTiles(&scx, &CIFCurStyle->cs_yankLayers, 0,
		cifHierCopyFunc, (ClientData) CIFTotalDef);
	    /* Flatten mask hints in the area of interest */
    	    CIFCopyMaskHints(&scx, CIFTotalDef);
	    DBTreeSrCells(&scx, 0, CIFCopyMaskHints,
                	(ClientData)CIFTotalDef);

	    CIFErrorDef = def;
	    CIFGen(CIFTotalDef, def, &interaction, CIFTotalPlanes,
		    &CIFCurStyle->cs_hierLayers, TRUE, TRUE, TRUE,
		    (ClientData)NULL);

	    /* Now go through all the subcells overlapping the area
	     * and generate CIF for each subcell individually.  OR this
	     * combined CIF together into CIFComponentPlanes.
	     */

	    scx.scx_area = interaction;
	    (void) DBCellSrArea(&scx, cifHierCellFunc, (ClientData) NULL);

	    /* Also generate CIF for the paint in the parent alone.  Ignore
	     * CIF generation errors here, since they will already have been
	     * recorded when the parent was CIF'ed by itself.
	     */

	    CIFErrorDef = (CellDef *) NULL;
	    CIFGen(def, def, &interaction, CIFComponentPlanes,
		    &CIFCurStyle->cs_hierLayers, FALSE, TRUE, TRUE,
		    (ClientData)NULL);

	    /* Make sure everything in the pieces is also in the overall
	     * CIF, then erase the piece stuff from the overall, and
	     * throw away the pieces.
	     */

	    CIFErrorDef = def;
	    cifCheckAndErase(CIFCurStyle);

	    /* Lastly, paint everything from the pieces into the result
	     * planes.
	     */

	    oldTileOps2 = CIFTileOps;
	    for (i=0; i<CIFCurStyle->cs_nLayers; i++)
	    {
		CurCifLayer = CIFCurStyle->cs_layers[i]; /* for growSliver */
		(void) DBSrPaintArea((Tile *) NULL, CIFTotalPlanes[i],
		    &TiPlaneRect, &CIFSolidBits, cifHierPaintFunc,
		    (ClientData) output[i]);
	    }
	    CIFHierRects += CIFTileOps - oldTileOps2;

	    cifHierCleanup();

	    cuts++;
	    pdone = 100.0 * ((float)cuts / (float)totcuts);
	    if ((((pdone - plast) > 1.0) || (cuts == totcuts)) && (cuts > 1))
	    {
		/* Only print something if the 5-second timer has expired */
		if (GrDisplayStatus == DISPLAY_BREAK_PENDING)
		{
		    TxPrintf("Completed %d%%\n", (int)(pdone + 0.5));
		    plast = pdone;
		    TxFlushOut();

		    GrDisplayStatus = DISPLAY_IN_PROGRESS;
		    SigSetTimer(5);
		}
#ifdef MAGIC_WRAPPER
		/* We need to let Tk paint the console display */
		while (Tcl_DoOneEvent(TCL_DONT_WAIT) != 0);
#endif
	    }
	}

    CIFHierTileOps += CIFTileOps - oldTileOps;

    GrDisplayStatus = savedDisplayStatus;
    SigRemoveTimer();

    UndoEnable();
}

/*
 * ----------------------------------------------------------------------------
 *
 * ----------------------------------------------------------------------------
 */

int
cifHierElementFuncLow(
    CellUse *use,			/* CellUse being array-checked. */
    Transform *transform,		/* Transform from this instance to
					 * the parent.
					 */
    int x,
    int y,				/* Indices of this instance. */
    Rect *checkArea)			/* Area (in parent coords) to be
					 * CIF-generated.
					 */
{
    if (((x - use->cu_xlo) < 2) && ((y - use->cu_ylo) < 2))
	return cifHierElementFunc(use, transform, x, y, checkArea);
    else
	return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * ----------------------------------------------------------------------------
 */

int
cifHierElementFuncHigh(
    CellUse *use,			/* CellUse being array-checked. */
    Transform *transform,		/* Transform from this instance to
					 * the parent.
					 */
    int x,
    int y,				/* Indices of this instance. */
    Rect *checkArea)			/* Area (in parent coords) to be
					 * CIF-generated.
					 */
{
    if (((use->cu_xhi - x) < 2) && ((use->cu_yhi - y) < 2))
	return cifHierElementFunc(use, transform, x, y, checkArea);
    else
	return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifHierElementFunc --
 *
 * 	This function is called once for each time an array element
 *	overlaps one of the four areas A, B, C, or D in cifHierArrayFunc
 *	(see below).  Its job is to yank the relevant piece of this
 *	cell into CIFTotalDef, and also to yank the same piece into
 *	CIFComponentDef and generate a piece of CIF from it.  The CIF
 *	from the piece is OR'ed into CIFComponentPlanes for later comparison
 *	with the parent.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	The cell CIFTotalUse is modified, as is CIFComponentUse and
 *	CIFComponentPlanes.
 *
 * ----------------------------------------------------------------------------
 */

    /* ARGSUSED */
int
cifHierElementFunc(
    CellUse *use,			/* CellUse being array-checked. */
    Transform *transform,		/* Transform from this instance to
					 * the parent.
					 */
    int x,
    int y,				/* Indices of this instance. */
    Rect *checkArea)			/* Area (in parent coords) to be
					 * CIF-generated.
					 */
{
    Rect defArea;
    Transform tinv;
    SearchContext scx;

    GeoInvertTrans(transform, &tinv);
    GeoTransRect(&tinv, checkArea, &defArea);

    /* Yank extra material to ensure that CIF is generated correctly. */

    GEO_EXPAND(&defArea, CIFCurStyle->cs_radius, &scx.scx_area);
    scx.scx_trans = *transform;
    scx.scx_use = use;
    (void) DBTreeSrTiles(&scx, &CIFCurStyle->cs_yankLayers, 0,
	cifHierCopyFunc, (ClientData) CIFTotalDef);
    CIFCopyMaskHints(&scx, CIFTotalDef);
    DBTreeSrCells(&scx, 0, CIFCopyMaskHints,
                (ClientData)CIFTotalDef);

    DBCellClearDef(CIFComponentDef);
    (void) DBTreeSrTiles(&scx, &CIFCurStyle->cs_yankLayers, 0,
	cifHierCopyFunc, (ClientData) CIFComponentDef);
    CIFCopyMaskHints(&scx, CIFComponentDef);
    DBTreeSrCells(&scx, 0, CIFCopyMaskHints,
                (ClientData)CIFComponentDef);

    CIFErrorDef = (CellDef *) NULL;
    CIFGen(CIFComponentDef, use->cu_def, checkArea, CIFComponentPlanes,
	    &CIFCurStyle->cs_hierLayers, FALSE, TRUE, TRUE,
	    (ClientData)NULL);

    return 0;
}


/*
 * ---------------------------------------------------------------------------
 * cifGrowSliver() --
 *
 * This function is passed the address of a "sliver" rectangle.  It then
 * grows the shortest dimension of the sliver so that it is at least minimum
 * width.  The minimum width is found through the variable CurCifLayer which
 * identifies the current layer being generated.  A "minwidth" command has been
 * added to the cifoutput section for which the minimum width of each layer
 * can be specified.
 *
 * Results:
 *	Always return 0
 *
 * Side effects:
 *	Returns the modified rectangle in "area" (pointer)
 * ---------------------------------------------------------------------------
 */

int
cifGrowSliver(
    Tile *tile,
    TileType dinfo,		/* Split tile information, needs to be handled */
    Rect *area)
{
    int height, width, expand_up, expand_side;

    TiToRect(tile, area);
    expand_up = (TiGetType(BL(tile)) != TT_SPACE) ||
		(TiGetType(TR(tile)) != TT_SPACE);
    expand_side = (TiGetType(LB(tile)) != TT_SPACE) ||
		(TiGetType(RT(tile)) != TT_SPACE);
    if (CurCifLayer->min_width == 0)
	return 0;

    height = area->r_ytop - area->r_ybot;
    width = area->r_xtop - area->r_xbot;
    printf("got sliver %d %d %d %d [%d,%d]\n",
	area->r_xtop, area->r_xbot, area->r_ytop,
	area->r_ybot, expand_up,expand_side);

    if ((height < width) || expand_up)
    {
	if (height >= CurCifLayer->min_width) return 0;
	area->r_ytop += (CurCifLayer->min_width - height)/2;
	area->r_ybot -= (CurCifLayer->min_width - height)/2;
    }
    if ((width < height) || expand_side)
    {
	if(width >= CurCifLayer->min_width) return 0;
        area->r_xtop += (CurCifLayer->min_width - width)/2;
        area->r_xbot -= (CurCifLayer->min_width - width)/2;
    }
    printf("created sliver %d %d %d %d \n",
	area->r_xtop, area->r_xbot, area->r_ytop, area->r_ybot);

    return 0;
}


/*
 * ----------------------------------------------------------------------------
 *
 * cifHierPaintArrayFunc --
 *
 * 	This function is used to paint in interaction tiles from an
 *	array.  It is called once for each tile to be copied, and
 *	paints the tile into cifHierCurPlane.  Then the tile is
 *	offset by cifHierXSpacing and cifHierYSpacing, and copied
 *	again and again, cifHierCount times in all.  The caller must
 *	set up these shared variables.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	The plane pointed to by cifHierCurPlane gets modified.
 *
 * ----------------------------------------------------------------------------
 */

int
cifHierPaintArrayFunc(
    Tile *tile,
    TileType dinfo,
    ClientData clientdata)	/* (unused) */
{
    Rect area;
    int i, j, xbot, xtop;

    TiToRect(tile, &area);
    if (CIFCurStyle->cs_flags & CWF_GROW_SLIVERS) cifGrowSliver(tile, dinfo, &area);
    xbot = area.r_xbot;
    xtop = area.r_xtop;
    for (i=0; i<cifHierYCount; i++)
    {
	for (j = 0; j < cifHierXCount; j++)
	{
	    DBNMPaintPlane(cifHierCurPlane, TiGetTypeExact(tile) | dinfo,
			&area, CIFPaintTable, (PaintUndoInfo *) NULL);
	    CIFTileOps++;
	    area.r_xbot += cifHierXSpacing;
	    area.r_xtop += cifHierXSpacing;
	}
	area.r_xbot = xbot;
	area.r_xtop = xtop;
	area.r_ybot += cifHierYSpacing;
	area.r_ytop += cifHierYSpacing;
    }
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifHierArrayFunc --
 *
 * 	This procedure is called once for each array that we
 *	are to generate interaction CIF for.  It computes all
 *	the CIF that must be present in the parent to compensate
 *	for the interactions between array elements.
 *
 * Results:
 *	Always returns 2, to skip the remaining instances in the
 *	current array.
 *
 * Side effects:
 *	CIF is added to the set of planes indicated by output.
 *
 * Design:
 *	This procedure is something like a cross between drcArrayFunc
 *	and DRCGenSubcells.  It computes interaction CIF in the four
 *	areas of the array shown below, then replicates	it over all
 *	the other elements of the array.
 *
 *	------------------------------CCCCC--------------
 *	|               |             CCCCC             |
 *	|               |             CCCCC             |
 *	|               |             CCCCC             |
 *	|               |             CCCCCDDDDDDDDDDDDDD
 *	------------------------------CCCCCDDDDDDDDDDDDDD
 *	|               |             CCCCCDDDDDDDDDDDDDD
 *	|               |               |               |
 *	|               |               |               |
 *	AAAAAAAAAAAAAAAAAAA             |               |
 *	AAAAAAAAAAAAAAAAAAA------------------------------
 *	AAAAAAAAAAAAAAAAAAA             |               |
 *	|             BBBBB             |               |
 *	|             BBBBB             |               |
 *	|             BBBBB             |               |
 *	--------------BBBBB------------------------------
 * ----------------------------------------------------------------------------
 */

int
cifHierArrayFunc(
    SearchContext *scx,		/* Information about the search. */
    Plane **output)		/* Array of planes to hold results. */
{
    Rect childArea, parentArea, A, B, C, D, expandedArea;
    CellUse *use;
    int radius, xsep, ysep, xsize, ysize, nx, ny, i, oldTileOps;
    bool anyInteractions = FALSE;

    use = scx->scx_use;
    radius = CIFCurStyle->cs_radius;
    if ((use->cu_xlo == use->cu_xhi) && (use->cu_ylo == use->cu_yhi))
	return 2;

    /* We only want interactions between neighboring cells, so only	*/
    /* look at the bottom-left 2x2 set when calculating A and B, and	*/
    /* only look at the top-right 2x2 set when calculating C and D.	*/

    /* Compute the sizes and separations of elements, in coordinates
     * of the parent.  If the array is 1-dimensional, we set the
     * corresponding spacing to an impossibly large distance.
     */

    childArea.r_xbot = 0;
    childArea.r_ybot = 0;

    /* Ensure that array width or height 1 corresponds to a separation	*/
    /* large enough not to satisfy any conditions A, B, C, or D.	*/

    if (use->cu_xlo == use->cu_xhi)
	childArea.r_xtop = radius +
		(use->cu_def->cd_bbox.r_xtop - use->cu_def->cd_bbox.r_xbot);
    else childArea.r_xtop = use->cu_xsep;
    if (use->cu_ylo == use->cu_yhi)
	childArea.r_ytop = radius +
		(use->cu_def->cd_bbox.r_ytop - use->cu_def->cd_bbox.r_ybot);

    else childArea.r_ytop = use->cu_ysep;
    GeoTransRect(&use->cu_transform, &childArea, &parentArea);
    xsep = parentArea.r_xtop - parentArea.r_xbot;
    ysep = parentArea.r_ytop - parentArea.r_ybot;
    GeoTransRect(&use->cu_transform, &use->cu_def->cd_bbox, &parentArea);
    xsize = parentArea.r_xtop - parentArea.r_xbot;
    ysize = parentArea.r_ytop - parentArea.r_ybot;
    nx = (use->cu_bbox.r_xtop - use->cu_bbox.r_xbot - xsize)/xsep;
    nx += 1;
    ny = (use->cu_bbox.r_ytop - use->cu_bbox.r_ybot - ysize)/ysep;
    ny += 1;

    /* Process each of the four areas A, B, C, and D.  For each one,
     * do three things:  yank the relevant material into cell
     * __CIF__, and generate a piece of CIF corresponding to each
     * child area contributing to the overlap.  This is all handled
     * by the procedure cifHierElementFunc.  Then generate the CIF
     * for the combined information that was yanked into __CIF__.
     * Collect all of the combined CIF from all four areas together
     * into CIFTotalPlanes.  Note:  in each case we have to yank a larger
     * area than we check, in order to include material that will be
     * bloated or shrunk.
     *
     * (Updated 6/7/2021) A and B should be calculated and processed
     * completely independently of C and D, or else if the array is
     * small and the radius is large, then results from one will get
     * picked up when making copies of the other, resulting in things
     * getting painted out-of-bounds.
     */

    /* A */

    if (ysep < ysize + radius)
    {
	A.r_xbot = use->cu_bbox.r_xbot - radius;
	A.r_xtop = use->cu_bbox.r_xbot + xsize + radius;
	A.r_ybot = use->cu_bbox.r_ybot + ysep - radius;
	A.r_ytop = use->cu_bbox.r_ybot + ysize + radius;
	GEO_EXPAND(&A, CIFCurStyle->cs_radius, &expandedArea);
	(void) DBArraySr(use, &expandedArea, cifHierElementFuncLow,
		(ClientData) &A);
	CIFErrorDef = use->cu_parent;
	CIFGen(CIFTotalDef, use->cu_def, &A, CIFTotalPlanes,
		&CIFCurStyle->cs_hierLayers, FALSE, TRUE, TRUE,
		(ClientData)NULL);
	anyInteractions = TRUE;
    }

    if ((xsep < xsize + radius) && (ysep < ysize + radius))
    {
	/* B */

	B.r_xbot = use->cu_bbox.r_xbot + xsep - radius;
	B.r_xtop = use->cu_bbox.r_xbot + xsize + radius;
	B.r_ybot = use->cu_bbox.r_ybot - radius;
	B.r_ytop = use->cu_bbox.r_ybot + ysep - radius;
	GEO_EXPAND(&B, CIFCurStyle->cs_radius, &expandedArea);
	(void) DBArraySr(use, &expandedArea, cifHierElementFuncLow,
		(ClientData) &B);
	CIFErrorDef = use->cu_parent;
	CIFGen(CIFTotalDef, use->cu_def, &B, CIFTotalPlanes,
		&CIFCurStyle->cs_hierLayers, FALSE, TRUE, TRUE,
		(ClientData)NULL);
    }

    if (anyInteractions)
    {
	/* Remove redundant CIF that's already in the children, and
	 * make sure everything in the kids is in the parent too.
	 */

	CIFErrorDef = use->cu_parent;
	cifCheckAndErase(CIFCurStyle);

	/* Lastly, paint everything back from our local planes into
	 * the planes of the caller.  In doing this, stuff has to
	 * be replicated many times over to cover each of the array
	 * interaction areas.
	 */

	oldTileOps = CIFTileOps;
	for (i=0; i<CIFCurStyle->cs_nLayers; i++)
	{
	    int scale = CIFCurStyle->cs_scaleFactor;
	    Rect cifArea;

	    cifHierCurPlane = output[i];
	    CurCifLayer = CIFCurStyle->cs_layers[i]; /* for growSliver */

	    /* The left edge of the array (from A). */

	    if ((ny > 1) && (ysep < ysize + radius))
	    {
		cifHierYSpacing = ysep * scale;
		cifHierXSpacing = 0;
		cifHierXCount = 1;
		cifHierYCount = ny-1;
		SCALE(&A, scale, &cifArea);
		(void) DBSrPaintArea((Tile *) NULL, CIFTotalPlanes[i],
			&cifArea, &CIFSolidBits, cifHierPaintArrayFunc,
			(ClientData) NULL);
	    }

	    if ((nx > 1) && (ny > 1) && (xsep < xsize + radius)
	    		&& (ysep < ysize + radius))
	    {
		/* The bottom edge of the array (from B). */

		cifHierXSpacing = xsep * scale;
		cifHierYSpacing = 0;
		cifHierXCount = nx-1;
		cifHierYCount = 1;
		SCALE(&B, scale, &cifArea);
		(void) DBSrPaintArea((Tile *) NULL, CIFTotalPlanes[i],
			&cifArea, &CIFSolidBits, cifHierPaintArrayFunc,
			(ClientData) NULL);

		/* The core of the array (copied from A and B).  Copy area
		 * from A, but not to exceed 1/2 Y array separation, into the
		 * inside area of the array.  If the expanded area is > 1/2
		 * the array separation, then there is no need to copy from
		 * area B.  Otherwise, copy area from B, extending up to
		 * the bottom of the area just copied, and not to exceed
		 * 1/2 the X array separation.
		 */

		cifHierXSpacing = xsep * scale;
		cifHierYSpacing = ysep * scale;
		cifHierXCount = nx-1;
		cifHierYCount = ny-1;

		/* Find top edge of cell */
		A.r_xbot = use->cu_bbox.r_xbot;
		A.r_xtop = use->cu_bbox.r_xbot + xsep;
		A.r_ybot = use->cu_bbox.r_ybot + ysep;
		A.r_ytop = use->cu_bbox.r_ybot + ysize;
		/* Expand up/down but not by more than 1/2 ysep */
		if ((2 * radius) > ysep)
		{
		    A.r_ybot -= (ysep >> 1);
		    A.r_ytop += (ysep >> 1);
		}
		else
		{
		    A.r_ybot -= radius;
		    A.r_ytop += radius;
		}
		SCALE(&A, scale, &cifArea);
		(void) DBSrPaintArea((Tile *) NULL, CIFTotalPlanes[i],
			&cifArea, &CIFSolidBits, cifHierPaintArrayFunc,
			(ClientData) NULL);

		/* If the radius is more than half of ysep then there	*/
		/* is nothing left that needs to be copied.		*/

		if ((2 * radius) < ysep)
		{
		    /* Find right edge of cell */
		    B.r_ybot = use->cu_bbox.r_ybot;
		    B.r_ytop = use->cu_bbox.r_ybot + ysep;

		    if (B.r_ytop > A.r_ybot) B.r_ytop = A.r_ybot;

		    B.r_xbot = use->cu_bbox.r_xbot + xsep;
		    B.r_xtop = use->cu_bbox.r_xbot + xsize;
		    /* Expand left/right but not by more than 1/2 xsep */
		    if ((2 * radius) > xsep)
		    {
			B.r_xbot -= (xsep >> 1);
			B.r_xtop += (xsep >> 1);
		    }
		    else
		    {
			B.r_xbot -= radius;
			B.r_xtop += radius;
		    }
		    SCALE(&B, scale, &cifArea);
		    (void) DBSrPaintArea((Tile *) NULL, CIFTotalPlanes[i],
				&cifArea, &CIFSolidBits, cifHierPaintArrayFunc,
				(ClientData) NULL);
		}
	    }
	}
	CIFHierRects += CIFTileOps - oldTileOps;
    }

    /* Clean up from areas A and B */
    cifHierCleanup();
    anyInteractions = FALSE;

    /* Now do areas C and D */

    /* C */

    if (xsep < xsize + radius)
    {
	C.r_xbot = use->cu_bbox.r_xtop - xsize - radius;
	C.r_xtop = use->cu_bbox.r_xtop - xsep + radius;
	C.r_ybot = use->cu_bbox.r_ytop - ysize - radius;
	C.r_ytop = use->cu_bbox.r_ytop + radius;
	GEO_EXPAND(&C, CIFCurStyle->cs_radius, &expandedArea);
	(void) DBArraySr(use, &expandedArea, cifHierElementFuncHigh,
		(ClientData) &C);
	CIFErrorDef = use->cu_parent;
	CIFGen(CIFTotalDef, use->cu_def, &C, CIFTotalPlanes,
		&CIFCurStyle->cs_hierLayers, FALSE, TRUE, TRUE,
		(ClientData)NULL);
	anyInteractions = TRUE;
    }

    if ((xsep < xsize + radius) && (ysep < ysize + radius))
    {
	/* D */

	D.r_xbot = use->cu_bbox.r_xtop - xsep + radius;
	D.r_xtop = use->cu_bbox.r_xtop + radius;
	D.r_ybot = use->cu_bbox.r_ytop - ysize - radius;
	D.r_ytop = use->cu_bbox.r_ytop - ysep + radius;
	GEO_EXPAND(&D, CIFCurStyle->cs_radius, &expandedArea);
	(void) DBArraySr(use, &expandedArea, cifHierElementFuncHigh,
		(ClientData) &D);
	CIFErrorDef = use->cu_parent;
	CIFGen(CIFTotalDef, use->cu_def, &D, CIFTotalPlanes,
		&CIFCurStyle->cs_hierLayers, FALSE, TRUE, TRUE,
		(ClientData)NULL);
    }

    if (anyInteractions)
    {

	/* Remove redundant CIF that's already in the children, and
	 * make sure everything in the kids is in the parent too.
	 */

	CIFErrorDef = use->cu_parent;
	cifCheckAndErase(CIFCurStyle);

	/* Lastly, paint everything back from our local planes into
	 * the planes of the caller.  In doing this, stuff has to
	 * be replicated many times over to cover each of the array
	 * interaction areas.
	 */

	oldTileOps = CIFTileOps;
	for (i=0; i<CIFCurStyle->cs_nLayers; i++)
	{
	    int scale = CIFCurStyle->cs_scaleFactor;
	    Rect cifArea;

	    cifHierCurPlane = output[i];
	    CurCifLayer = CIFCurStyle->cs_layers[i]; /* for growSliver */

	    /* The top edge of the array (from C). */

	    if ((nx > 1) && (xsep < xsize + radius))
	    {
		cifHierXSpacing = -xsep * scale;
		cifHierYSpacing = 0;
		cifHierXCount = nx-1;
		cifHierYCount = 1;
		SCALE(&C, scale, &cifArea);
		(void) DBSrPaintArea((Tile *) NULL, CIFTotalPlanes[i],
			&cifArea, &CIFSolidBits, cifHierPaintArrayFunc,
			(ClientData) NULL);
	    }

	    if ((nx > 1) && (ny > 1) && (xsep < xsize + radius)
	    		&& (ysep < ysize + radius))
	    {
		/* The right edge of the array (from D). */

		cifHierXSpacing = 0;
		cifHierYSpacing = -ysep * scale;
		cifHierXCount = 1;
		cifHierYCount = ny-1;
		SCALE(&D, scale, &cifArea);
		(void) DBSrPaintArea((Tile *) NULL, CIFTotalPlanes[i],
			&cifArea, &CIFSolidBits, cifHierPaintArrayFunc,
			(ClientData) NULL);
	    }
	}
	CIFHierRects += CIFTileOps - oldTileOps;
    }

    cifHierCleanup();
    return 2;
}


/*
 * ----------------------------------------------------------------------------
 *
 * CIFGenArrays --
 *
 * 	This procedure computes all of the CIF that must be added to
 *	a given area of a parent to compensate for interactions between
 *	elements of arrays in that area.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The parameter output is modified (by OR'ing) to hold all
 *	the CIF that was generated for array interactions.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFGenArrays(
    CellDef *def,		/* Parent cell for which CIF is computed. */
    Rect *area,			/* All CIF in this area is interesting. */
    Plane **output)		/* Array of pointers to planes into which
				 * the CIF output will be OR'ed (real CIF
				 * only, temp layers won't appear).  If
				 * output is NULL, then CIF is stored in
				 * CIFPlanes, and the planes are initially
				 * cleared.
				 */
{
    SearchContext scx;
    int i, oldTileOps;

    UndoDisable();
    CIFInitCells();

    /* Bill all tile operations here to hierarchical processing in
     * addition to adding to the total.
     */

    oldTileOps = CIFTileOps;

    if (output == NULL)
    {
	output = CIFPlanes;
	for (i=0; i<CIFCurStyle->cs_nLayers; i++)
	{
	    if (output[i] == NULL)
		output[i] = DBNewPlane((ClientData) TT_SPACE);
	    else DBClearPaintPlane(output[i]);
	}
    }

    scx.scx_area = *area;
    scx.scx_use = CIFDummyUse;
    CIFDummyUse->cu_def = def;
    (void) DBCellSrArea(&scx, cifHierArrayFunc, (ClientData) output);

    CIFHierTileOps += CIFTileOps - oldTileOps;
    UndoEnable();
}
