magic/cif/CIFhier.c

1588 lines
46 KiB
C

/* 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;
} MaskHintsData;
/*
* ----------------------------------------------------------------------------
*
* cifMaskHints --
*
* Copy a mask hint into a target cell by adding it to the
* property list of the target cell. If the target cell already
* has the same mask hint key, then the mask hint value is
* appended to the property in the target cell def.
*
* Returns:
* 0 to keep the search going.
*
* Side effects:
* Modifies properties of the target cell def.
*
* ----------------------------------------------------------------------------
*/
/* DEPRECATED */
int
cifMaskHints(
char *name,
char *value,
CellDef *targetDef)
{
char *propvalue, *newval;
bool propfound;
if (!strncmp(name, "MASKHINTS_", 10))
{
/* Check if name exists already in the flattened cell */
propvalue = (char *)DBPropGet(targetDef, name, &propfound);
if (propfound)
{
/* Append value to the property */
newval = mallocMagic(strlen(value) + strlen(propvalue) + 2);
sprintf(newval, "%s %s", propvalue, value);
}
else
newval = StrDup((char **)NULL, value);
DBPropPut(targetDef, name, newval);
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* cifFlatMaskHints --
*
* Copy a mask hint into a flattened cell by transforming it into the
* coordinate system of the flattened cell, and adding it to the
* property list 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,
char *value,
MaskHintsData *mhd)
{
Rect r, newr;
char *vptr, *newval, *lastval, *propvalue;
bool propfound;
int lastlen, numvals;
if (!strncmp(name, "MASKHINTS_", 10))
{
newval = (char *)NULL;
vptr = value;
while (*vptr != '\0')
{
numvals = sscanf(vptr, "%d %d %d %d", &r.r_xbot, &r.r_ybot,
&r.r_xtop, &r.r_ytop);
if (numvals == 4)
{
/* Transform rectangle to top level coordinates */
GeoTransRect(mhd->mh_trans, &r, &newr);
lastval = newval;
lastlen = (lastval) ? strlen(lastval) : 0;
newval = mallocMagic(40 + lastlen);
if (lastval)
strcpy(newval, lastval);
else
*newval = '\0';
sprintf(newval + lastlen, "%s%d %d %d %d", (lastval) ? " " : "",
newr.r_xbot, newr.r_ybot, newr.r_xtop, newr.r_ytop);
if (lastval) freeMagic(lastval);
/* Parse through the four values and check if there's more */
while (*vptr && isspace(*vptr)) vptr++;
while (*vptr && !isspace(*vptr)) vptr++;
while (*vptr && isspace(*vptr)) vptr++;
while (*vptr && !isspace(*vptr)) vptr++;
while (*vptr && isspace(*vptr)) vptr++;
while (*vptr && !isspace(*vptr)) vptr++;
while (*vptr && isspace(*vptr)) vptr++;
while (*vptr && !isspace(*vptr)) vptr++;
while (*vptr && isspace(*vptr)) vptr++;
}
else
{
TxError("MASKHINTS_%s: Expected 4 values, found only %d\n",
name + 10, numvals);
break;
}
}
/* Check if name exists already in the flattened cell */
propvalue = (char *)DBPropGet(mhd->mh_def, name, &propfound);
if (propfound)
{
/* Append newval to the property */
lastval = newval;
newval = mallocMagic(strlen(lastval) + strlen(propvalue) + 2);
sprintf(newval, "%s %s", propvalue, lastval);
freeMagic(lastval);
}
DBPropPut(mhd->mh_def, name, newval);
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* CIFCopyMaskHints --
*
* Callback function to copy mask hints from one cell into another.
*
* Results:
* None.
*
* Side effects:
* May modify properties in the target cell.
*
* ----------------------------------------------------------------------------
*/
void
CIFCopyMaskHints(
SearchContext *scx,
CellDef *targetDef)
{
MaskHintsData mhd;
CellDef *sourceDef = scx->scx_use->cu_def;
mhd.mh_trans = &scx->scx_trans;
mhd.mh_def = targetDef;
DBPropEnum(sourceDef, cifFlatMaskHints, &mhd);
}
/*
* ----------------------------------------------------------------------------
*
* cifHierCopyMaskHints --
*
* Callback function to copy mask hints from a subcell into a flattened
* cell, which is passed in the clientData record.
*
* Results:
* Always returns 0 to keep the search alive.
*
* Side effects:
* May modify properties in the flattened cell.
*
* ----------------------------------------------------------------------------
*/
int
cifHierCopyMaskHints(
SearchContext *scx,
ClientData clientData)
{
MaskHintsData mhd;
mhd.mh_trans = &scx->scx_trans;
mhd.mh_def = (CellDef *)clientData;
DBPropEnum(scx->scx_use->cu_def, 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. */
TreeContext *cxp) /* Describes context of search, including
* transform and client data.
*/
{
TileType type = TiGetTypeExact(tile);
Rect sourceRect, targetRect;
int pNum;
CellDef *def = (CellDef *) cxp->tc_filter->tf_arg;
int dinfo = 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))
{
dinfo = DBTransformDiagonal(type, &cxp->tc_scx->scx_trans);
type = (SplitSide(tile)) ? 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], dinfo, &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, cifHierCopyMaskHints,
(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. */
Rect *checkArea) /* Intersection of this and tile is error. */
{
Rect area;
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) && !SplitSide(tile)) ||
((area.r_xtop == checkArea->r_xtop) && SplitSide(tile)))
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. */
Plane *plane) /* Plane to check against and modify. */
{
Rect area;
TiToRect(tile, &area);
if (IsSplit(tile))
{
DBSrPaintNMArea((Tile *)NULL, plane, TiGetTypeExact(tile),
&area, &DBSpaceBits, cifHierErrorFunc, (ClientData) &area);
DBNMPaintPlane(plane, TiGetTypeExact(tile), &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. */
Plane *plane) /* Plane to check against and modify. */
{
Rect area;
TiToRect(tile, &area);
if (IsSplit(tile))
DBNMPaintPlane(plane, TiGetTypeExact(tile), &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,
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, &area);
if (IsSplit(tile))
DBNMPaintPlane(plane, TiGetTypeExact(tile), &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)
*/
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, cifHierCopyMaskHints,
(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 = DISPLAY_IDLE;
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, cifHierCopyMaskHints,
(ClientData)CIFTotalDef);
DBCellClearDef(CIFComponentDef);
(void) DBTreeSrTiles(&scx, &CIFCurStyle->cs_yankLayers, 0,
cifHierCopyFunc, (ClientData) CIFComponentDef);
CIFCopyMaskHints(&scx, CIFComponentDef);
DBTreeSrCells(&scx, 0, cifHierCopyMaskHints,
(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,
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)
{
Rect area;
int i, j, xbot, xtop;
TiToRect(tile, &area);
if (CIFCurStyle->cs_flags & CWF_GROW_SLIVERS) cifGrowSliver(tile, &area);
xbot = area.r_xbot;
xtop = area.r_xtop;
for (i=0; i<cifHierYCount; i++)
{
for (j=0; j<cifHierXCount; j++)
{
DBPaintPlane(cifHierCurPlane, &area, CIFPaintTable,
(PaintUndoInfo *) NULL);
CIFTileOps += 1;
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();
}