Files
magic/plot/plotGremln.c
T
Patrick Kurth ea03883829 Fixed unused-parameters/unused-variables warnings
Added a ARG_UNUSED macro in utils/magic.h, which
produces a simple cast to void to silence the warnings.
The reasons this is used as opposed to an attribute is
that most compilers should support this.
Besides this, the macro can be easily searched for in
the future to potentially remove (where possible) the
unused parameters.
2025-10-10 22:12:19 +02:00

838 lines
23 KiB
C

/*
* plotGremlin.c --
*
* This file contains procedures that generate Gremlin-format files
* to describe a section of layout.
*
* *********************************************************************
* * Copyright (C) 1985, 1990 Regents of the University of California. *
* * Permission to use, copy, modify, and distribute this *
* * software and its documentation for any purpose and without *
* * fee is hereby granted, provided that the above copyright *
* * notice appear in all copies. The University of California *
* * makes no representations about the suitability of this *
* * software for any purpose. It is provided "as is" without *
* * express or implied warranty. Export of this software outside *
* * of the United States of America may require an export license. *
* *********************************************************************
*/
#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/plot/plotGremln.c,v 1.2 2008/06/01 18:37:45 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "utils/tech.h"
#include "utils/malloc.h"
#include "utils/utils.h"
#include "windows/windows.h"
#include "commands/commands.h"
#include "dbwind/dbwind.h"
#include "textio/textio.h"
/* Records of the following type are used to describe how to generate
* Gremlin output for a particular set of mask layers. Each style
* describes the Gremlin figures to draw for a particular set of
* layers. A single layer may participate in several gremlin styles.
*/
#ifdef GREMLIN
typedef struct gremlinstyle
{
TileTypeBitMask grs_layers; /* Layers to plot in this style. */
int grs_stipple; /* Type of fill to use. See below. */
struct gremlinstyle *grs_next; /* Next style in chain. */
} GremlinStyle;
static GremlinStyle *plotGremlinStyles;
/* Most of the grs_stipple values are Gremlin stipple numbers. However,
* if a grs_stipple value is less than zero, it means something special.
* The definitions below give the possible alternatives:
*
* CROSS: Draw a thick outline around the tile with
* a cross through it (used for contacts).
* BORDER: Same as CROSS, except draw the outline with
* no cross through it.
*/
#define CROSS -1
#define BORDER -2
/* The definitions below give the integers used for various Gremlin
* line drawing styles (brushes).
*/
#define GREMLIN_DOTTED 1
#define GREMLIN_DASHED 4
#define GREMLIN_DOTDASH 2
#define GREMLIN_THIN 5
#define GREMLIN_MEDIUM 6
#define GREMLIN_THICK 3
/* The variables below are used to pass information from the top-level
* procedure PlotGremlin down to the lower-level search functions
* that are invoked for pieces of the layout.
*/
static FILE *file; /* File to use for output. */
static float scale; /* Multiply this by Magic units to get
* Gremlin units.
*/
static GremlinStyle *curStyle; /* Current style being output. */
static TileTypeBitMask curMask; /* Layers currently being searched: this
* is the AND of the mask from curStyle and
* the layers that the user specified.
*/
static Rect bbox; /* Bounding box, in root coordinates, of
* area being plotted.
*/
#endif /* GREMLIN */
/*
* ----------------------------------------------------------------------------
* PlotGremlinTechInit --
*
* Called once at beginning of technology file read-in to initialize
* data structures.
*
* Results:
* None.
*
* Side effects:
* Clears out the list of things to plot.
* ----------------------------------------------------------------------------
*/
#ifdef GREMLIN
void
PlotGremlinTechInit()
{
GremlinStyle *style;
for (style = plotGremlinStyles; style != NULL; style = style->grs_next)
{
freeMagic((char *) style);
}
plotGremlinStyles = NULL;
}
#else
void
PlotGremlinTechInit()
{}
#endif /* GREMLIN */
/*
* ----------------------------------------------------------------------------
* PlotGremlinTechLine --
*
* This procedure is invoked by the technology module once for
* each line in the "gremlin" subsection of the "plot" section
* of the technology file.
*
* Results:
* Always returns TRUE (otherwise the technology module would
* abort Magic with a fatal error).
*
* Side effects:
* Builds up the table of Gremlin styles.
* ----------------------------------------------------------------------------
*/
#ifdef GREMLIN
bool
PlotGremlinTechLine(sectionName, argc, argv)
char *sectionName; /* Name of this section (unused). */
int argc; /* Number of arguments on line. */
char *argv[]; /* Pointers to fields of line. */
{
GremlinStyle *new;
int stipple, i;
if (argc != 2)
{
TechError("\"gremlin\" lines must have exactly 2 arguments.\n");
return TRUE;
}
if (strcmp(argv[1], "X") == 0)
stipple = CROSS;
else if (strcmp(argv[1], "B") == 0)
stipple = BORDER;
else
{
if (!StrIsInt(argv[1]))
{
TechError("2nd field must be an integer or \"X\" or \"B\".\n");
return TRUE;
}
stipple = atoi(argv[1]);
}
new = (GremlinStyle *) mallocMagic(sizeof(GremlinStyle));
DBTechNoisyNameMask(argv[0], &new->grs_layers);
/* Replace non-primary contact images with primary images. */
for (i = TT_TECHDEPBASE; i < DBNumTypes; i++)
{
if TTMaskHasType(&new->grs_layers, i)
TTMaskSetMask(&new->grs_layers, &DBLayerTypeMaskTbl[i]);
}
TTMaskAndMask(&new->grs_layers, &DBUserLayerBits);
new->grs_stipple = stipple;
new->grs_next = plotGremlinStyles;
plotGremlinStyles = new;
return TRUE;
}
#else
bool
PlotGremlinTechLine(sectionName, argc, argv)
char *sectionName; /* Name of this section (unused). */
int argc; /* Number of arguments on line (unused). */
char *argv[]; /* Pointers to fields of line (unused). */
{
ARG_UNUSED(sectionName);
ARG_UNUSED(argc);
ARG_UNUSED(argv);
return TRUE;
}
#endif /* GREMLIN */
#ifdef GREMLIN
/*
* ----------------------------------------------------------------------------
*
* plotGremlinLine --
*
* Outputs a line into the current Gremlin file.
*
* Results:
* None.
*
* Side effects:
* I/O.
*
* ----------------------------------------------------------------------------
*/
void
plotGremlinLine(p1, p2, lineStyle)
Point *p1, *p2; /* Endpoints of line, given in root
* coordinates.
*/
int lineStyle; /* Gremlin line style to use for line. */
{
float x1, x2, y1, y2, limit;
/* Clip the line to the rectangular area being output. First,
* arrange for the first x-coordinate to be the smaller, then
* clip against vertical lines at the x-boundaries.
*/
if (p1->p_x <= p2->p_x)
{
x1 = p1->p_x - bbox.r_xbot;
x2 = p2->p_x - bbox.r_xbot;
y1 = p1->p_y - bbox.r_ybot;
y2 = p2->p_y - bbox.r_ybot;
}
else
{
x1 = p2->p_x - bbox.r_xbot;
x2 = p1->p_x - bbox.r_xbot;
y1 = p2->p_y - bbox.r_ybot;
y2 = p1->p_y - bbox.r_ybot;
}
limit = bbox.r_xtop - bbox.r_xbot;
if ((x1 > limit) || (x2 < 0)) return;
if (x1 < 0)
{
y1 += (-x1)*(y2-y1)/(x2-x1);
x1 = 0;
}
if (x2 > limit)
{
y2 -= (x2-limit)*(y2-y1)/(x2-x1);
x2 = limit;
}
/* Now clip against horizontal lines at the y-boundaries. */
if (y2 < y1)
{
float tmp;
tmp = y2; y2 = y1; y1 = tmp;
tmp = x2; x2 = x1; x1 = tmp;
}
limit = bbox.r_ytop - bbox.r_ybot;
if ((y1 > limit) || (y2 < 0)) return;
if (y1 < 0)
{
x1 += (-y1)*(x2-x1)/(y2-y1);
y1 = 0;
}
if (y2 > limit)
{
x2 -= (y2-limit)*(x2-x1)/(y2-y1);
y2 = limit;
}
/* Lastly, scale and generate Gremlin output. */
x1 *= scale;
x2 *= scale;
y1 *= scale;
y2 *= scale;
fprintf(file, "VECTOR\n");
fprintf(file, "%.3f %.3f\n%.3f %.3f\n", x1, y1, x2, y2);
fprintf(file, "*\n%d 0\n0 \n", lineStyle);
}
/*
* ----------------------------------------------------------------------------
*
* plotGremlinRect --
*
* Outputs Gremlin statements to draw a rectangular area as
* an outline with a given line style.
*
* Results:
* None.
*
* Side effects:
* Adds information to the current Gremlin file.
*
* ----------------------------------------------------------------------------
*/
void
plotGremlinRect(rect, lineStyle)
Rect *rect; /* Rectangle to be drawn, in root coords. */
int lineStyle; /* Gremlin line style to use for outline. */
{
Point p;
p.p_x = rect->r_xbot;
p.p_y = rect->r_ytop;
plotGremlinLine(&rect->r_ll, &p, lineStyle);
plotGremlinLine(&p, &rect->r_ur, lineStyle);
p.p_x = rect->r_xtop;
p.p_y = rect->r_ybot;
plotGremlinLine(&rect->r_ur, &p, lineStyle);
plotGremlinLine(&p, &rect->r_ll, lineStyle);
}
/*
* ----------------------------------------------------------------------------
*
* plotGremlinPaint --
*
* This procedure is invoked once for each paint rectangle in
* the area being plotted.
*
* Results:
* Always returns 0 to keep the search alive.
*
* Side effects:
* Outputs information for the tile, including stipple for its
* interior, and a solid line for any portion of the boundary
* of the tile that is adjacent to a tile NOT in this style.
*
* ----------------------------------------------------------------------------
*/
int
plotGremlinPaint(tile, cxp)
Tile *tile; /* Tile that's of type to be output. */
TreeContext *cxp; /* Describes search in progress. */
{
Rect tileArea, edge, rootArea;
float xbot, xtop, ybot, ytop;
Tile *neighbor;
/* First transform tile coords to root coords */
TiToRect(tile, &tileArea);
GeoTransRect(&cxp->tc_scx->scx_trans, &tileArea, &rootArea);
/* See if this tile gets special handling. */
if ((curStyle->grs_stipple == CROSS) || (curStyle->grs_stipple == BORDER))
{
/* Draw tile as a thick outline with a cross from corner
* to corner, and skip the rest of this procedure.
*/
Point ul, lr;
plotGremlinRect(&rootArea, GREMLIN_MEDIUM);
if (curStyle->grs_stipple == CROSS)
{
ul.p_x = rootArea.r_xbot;
ul.p_y = rootArea.r_ytop;
lr.p_x = rootArea.r_xtop;
lr.p_y = rootArea.r_ybot;
plotGremlinLine(&rootArea.r_ll, &rootArea.r_ur, GREMLIN_MEDIUM);
plotGremlinLine(&ul, &lr, GREMLIN_MEDIUM);
}
return 0;
}
/* This tile gets "normal" processing (i.e. stippling and outlining).
* Clip it to the plotting area and translate to Gremlin coords.
* Then output Gremlin information for stippled interior.
*/
GeoClip(&rootArea, &bbox);
xbot = (rootArea.r_xbot - bbox.r_xbot) * scale;
xtop = (rootArea.r_xtop - bbox.r_xbot) * scale;
ybot = (rootArea.r_ybot - bbox.r_ybot) * scale;
ytop = (rootArea.r_ytop - bbox.r_ybot) * scale;
fprintf(file, "POLYGON\n");
fprintf(file, "%.3f %.3f\n%.3f %.3f\n%.3f %.3f\n%.3f %.3f\n%.3f %.3f\n",
xbot, ybot, xtop, ybot, xtop, ytop, xbot, ytop, xbot, ybot);
fprintf(file, "*\n0 %d\n0 \n", curStyle->grs_stipple);
/* Now output lines for any edges between material of the type
* currently being drawn and material of other types. This is
* done by searching along the tile's borders for neighbors that
* have the wrong types. First, search the tile's bottom border
* (unless it is at infinity).
*/
if (tileArea.r_ybot > TiPlaneRect.r_ybot)
{
edge.r_ybot = edge.r_ytop = tileArea.r_ybot;
for (neighbor = LB(tile); LEFT(neighbor) < tileArea.r_xtop;
neighbor = TR(neighbor))
{
if (TTMaskHasType(&curMask, TiGetType(neighbor))) continue;
edge.r_xbot = LEFT(neighbor);
edge.r_xtop = RIGHT(neighbor);
if (edge.r_xbot < tileArea.r_xbot) edge.r_xbot = tileArea.r_xbot;
if (edge.r_xtop > tileArea.r_xtop) edge.r_xtop = tileArea.r_xtop;
GeoTransRect(&cxp->tc_scx->scx_trans, &edge, &rootArea);
plotGremlinLine(&rootArea.r_ll, &rootArea.r_ur, GREMLIN_THIN);
}
}
/* Now go along the tile's left border, doing the same thing. Ignore
* edges that are at infinity.
*/
if (tileArea.r_xbot > TiPlaneRect.r_xbot)
{
edge.r_xbot = edge.r_xtop = tileArea.r_xbot;
for (neighbor = BL(tile); BOTTOM(neighbor) < tileArea.r_ytop;
neighbor = RT(neighbor))
{
if (TTMaskHasType(&curMask, TiGetType(neighbor))) continue;
edge.r_ybot = BOTTOM(neighbor);
edge.r_ytop = TOP(neighbor);
if (edge.r_ybot < tileArea.r_ybot) edge.r_ybot = tileArea.r_ybot;
if (edge.r_ytop < tileArea.r_ytop) edge.r_ytop = tileArea.r_ytop;
GeoTransRect(&cxp->tc_scx->scx_trans, &edge, &rootArea);
plotGremlinLine(&rootArea.r_ll, &rootArea.r_ur, GREMLIN_THIN);
}
}
/* Same thing for the tile's top border. */
if (tileArea.r_ytop < TiPlaneRect.r_ytop)
{
edge.r_ybot = edge.r_ytop = tileArea.r_ytop;
for (neighbor = RT(tile); RIGHT(neighbor) > tileArea.r_xbot;
neighbor = BL(neighbor))
{
if (TTMaskHasType(&curMask, TiGetType(neighbor))) continue;
edge.r_xbot = LEFT(neighbor);
edge.r_xtop = RIGHT(neighbor);
if (edge.r_xbot < tileArea.r_xbot) edge.r_xbot = tileArea.r_xbot;
if (edge.r_xtop > tileArea.r_xtop) edge.r_xtop = tileArea.r_xtop;
GeoTransRect(&cxp->tc_scx->scx_trans, &edge, &rootArea);
plotGremlinLine(&rootArea.r_ll, &rootArea.r_ur, GREMLIN_THIN);
}
}
/* Finally, the right border. */
if (tileArea.r_xtop < TiPlaneRect.r_xtop)
{
edge.r_xbot = edge.r_xtop = tileArea.r_xtop;
for (neighbor = TR(tile); TOP(neighbor) > tileArea.r_ybot;
neighbor = LB(neighbor))
{
if (TTMaskHasType(&curMask, TiGetType(neighbor))) continue;
edge.r_ybot = BOTTOM(neighbor);
edge.r_ytop = TOP(neighbor);
if (edge.r_ybot < tileArea.r_ybot) edge.r_ybot = tileArea.r_ybot;
if (edge.r_ytop < tileArea.r_ytop) edge.r_ytop = tileArea.r_ytop;
GeoTransRect(&cxp->tc_scx->scx_trans, &edge, &rootArea);
plotGremlinLine(&rootArea.r_ll, &rootArea.r_ur, GREMLIN_THIN);
}
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* plotGremlinLabel --
*
* This procedure is invoked once for each label overlapping the
* area being plotted. It generates Gremlin output to describe
* the label.
*
* Results:
* Always returns 0 to keep the search from aborting.
*
* Side effects:
* Gremlin information is output.
*
* ----------------------------------------------------------------------------
*/
int
plotGremlinLabel(scx, label)
SearchContext *scx; /* Describes state of search when label
* was found.
*/
Label *label; /* Label that was found. */
{
Rect rootArea;
float x, y;
float delta;
int pos;
/* Mapping from our GEO_xxx positions to Gremlin object types: */
static char *gremlinPosition[] =
{
"CENTCENT",
"BOTCENT",
"BOTLEFT",
"CENTLEFT",
"TOPLEFT",
"TOPCENT",
"TOPRIGHT",
"CENTRIGHT",
"BOTRIGHT"
};
/* Compute a distance equal to 1/8th the size of a typical wire
* (max of thicknesses of routing layers). This is used to offset
* text from labels and to compute cross size for point labels.
*/
if (RtrMetalWidth > RtrPolyWidth)
delta = RtrMetalWidth*scale/8;
else delta = RtrPolyWidth*scale/8;
/* Translate the label's area and relative position to root
* coordinates, and figure out the point relative to which
* the label is to be positioned.
*/
GeoTransRect(&scx->scx_trans, &label->lab_rect, &rootArea);
pos = GeoTransPos(&scx->scx_trans, label->lab_just);
switch (pos)
{
case GEO_NORTH:
case GEO_NORTHEAST:
case GEO_NORTHWEST:
y = (rootArea.r_ytop - bbox.r_ybot) * scale;
y += delta;
break;
case GEO_CENTER:
case GEO_WEST:
case GEO_EAST:
y = (rootArea.r_ytop + rootArea.r_ybot - 2*bbox.r_ybot)*scale/2;
break;
case GEO_SOUTH:
case GEO_SOUTHEAST:
case GEO_SOUTHWEST:
y = (rootArea.r_ybot - bbox.r_ybot) * scale;
y -= delta;
break;
}
switch (pos)
{
case GEO_WEST:
case GEO_NORTHWEST:
case GEO_SOUTHWEST:
x = (rootArea.r_xbot - bbox.r_xbot) * scale;
x -= delta;
break;
case GEO_CENTER:
case GEO_NORTH:
case GEO_SOUTH:
x = (rootArea.r_xtop + rootArea.r_xbot - 2*bbox.r_xbot)*scale/2;
break;
case GEO_EAST:
case GEO_NORTHEAST:
case GEO_SOUTHEAST:
x = (rootArea.r_xtop - bbox.r_xbot) * scale;
x += delta;
break;
}
/* Output the text for the label, if the label is within delta
* of the area we're plotting (a large label could overlap a
* bit of the area but stick out way off-screen too).
*/
if ((x >= -delta) && (y >= -delta) &&
(x <= ((bbox.r_xtop - bbox.r_xbot) * scale) + delta) &&
(y <= ((bbox.r_ytop - bbox.r_ybot) * scale) + delta))
{
fprintf(file, "%s\n", gremlinPosition[pos]);
fprintf(file, "%.3f %.3f\n*\n", x, y);
fprintf(file, "1 1\n"); /* Roman font, small characters */
fprintf(file, "%d %s\n", strlen(label->lab_text), label->lab_text);
}
/* Output lines marking the label's area. Different things are
* done depending on whether the label is a point, a line, or an
* area.
*/
if ((rootArea.r_xbot == rootArea.r_xtop) &&
(rootArea.r_ybot == rootArea.r_ytop))
{
/* Point label. Output a cross. */
float top, bot;
x = (rootArea.r_xbot - bbox.r_xbot) * scale;
y = (rootArea.r_ybot - bbox.r_ybot) * scale;
top = y + delta;
bot = y - delta;
fprintf(file, "VECTOR\n%.3f %.3f\n%.3f %.3f\n*\n", x, bot, x, top);
fprintf(file, "6 1\n0 \n"); /* Medium thickness */
top = x + delta;
bot = x - delta;
fprintf(file, "VECTOR\n%.3f %.3f\n%.3f %.3f\n*\n", bot, y, top, y);
fprintf(file, "6 1\n0 \n"); /* Medium thickness */
}
else if ((rootArea.r_xbot == rootArea.r_xtop) ||
(rootArea.r_ybot == rootArea.r_ytop))
{
/* Line label. Just draw a medium-thickness line. */
plotGremlinLine(&rootArea.r_ll, &rootArea.r_ur, GREMLIN_MEDIUM);
}
else
{
/* Rectangular. Draw lines around the boundary. */
plotGremlinRect(&rootArea, GREMLIN_MEDIUM);
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* plotGremlinCell --
*
* This procedure is invoked once for each unexpanded cell that
* overlaps the area being plotted.
*
* Results:
* Always returns 0 to keep the search from aborting.
*
* Side effects:
* Gremlin information is output to describe the cell.
*
* ----------------------------------------------------------------------------
*/
int
plotGremlinCell(scx)
SearchContext *scx; /* Describes cell whose bbox is to
* be plotted.
*/
{
extern bool PlotShowCellNames;
char idName[100];
Rect rootArea;
CellDef *def;
float x, y;
/* Convert the cell's bounding box to root coordinates and then
* draw as a thick outline.
*/
def = scx->scx_use->cu_def;
GeoTransRect(&scx->scx_trans, &def->cd_bbox, &rootArea);
plotGremlinRect(&rootArea, GREMLIN_THICK);
if (!PlotShowCellNames)
return 0;
/* Output the cell definition's name in the top of the bounding box.
* Use a bold font (#3), in a medium size (#2). Make sure that the
* name's positioning point is within the area we're plotting.
*/
x = (rootArea.r_xtop + rootArea.r_xbot - 2*bbox.r_xbot)*scale/2;
y = (2*rootArea.r_ytop + rootArea.r_ybot - 3*bbox.r_ybot)*scale/3;
if ((x >= 0) && (y >= 0) &&
(x <= (bbox.r_xtop - bbox.r_xbot)*scale) &&
(y <= (bbox.r_ytop - bbox.r_ybot)*scale))
{
fprintf(file, "CENTCENT\n%.3f %.3f\n*\n", x, y);
fprintf(file, "3 2\n%d %s\n", strlen(def->cd_name), def->cd_name);
}
/* Output the cell id in the bottom of the bounding box.
* Use an italic font (#2) in a medium size (#2).
*/
x = (rootArea.r_xtop + rootArea.r_xbot - 2*bbox.r_xbot)*scale/2;
y = (rootArea.r_ytop + 2*rootArea.r_ybot - 3*bbox.r_ybot)*scale/3;
if ((x >= 0) && (y >= 0) &&
(x <= (bbox.r_xtop - bbox.r_xbot)*scale) &&
(y <= (bbox.r_ytop - bbox.r_ybot)*scale))
{
(void) DBPrintUseId(scx, idName, 100, TRUE);
fprintf(file, "CENTCENT\n%.3f %.3f\n*\n", x, y);
fprintf(file, "2 2\n%d %s\n", strlen(idName), idName);
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* PlotGremlin --
*
* This procedure generates a Gremlin file to describe an area of
* a layout.
*
* Results:
* None.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
void
PlotGremlin(fileName, scx, layers, xMask)
char *fileName; /* Name of Gremlin file to write. */
SearchContext *scx; /* The use and area and transformation
* in this describe what to plot.
*/
TileTypeBitMask *layers; /* Tells what layers to plot. Only
* paint layers in this mask, and also
* expanded according to xMask, are
* plotted. If L_LABELS is set, then
* labels on the layers are also
* plotted, if expanded according to
* xMask. If L_CELL is set, then
* subcells that are unexpanded
* according to xMask are plotted as
* bounding boxes.
*/
int xMask; /* An expansion mask, used to indicate
* the window whose expansion status
* will be used to determine
* visibility. Zero means treat
* everything as expanded.
*/
{
int size, tmp;
/* Compute a scale factor between our coordinates and Gremlin
* coordinates. Pick an even power of two that will make all
* the Gremlin units fall between 0 and 512.
*/
GeoTransRect(&scx->scx_trans, &scx->scx_area, &bbox);
size = bbox.r_xtop - bbox.r_xbot;
tmp = bbox.r_ytop - bbox.r_ybot;
if (size < tmp) size = tmp;
scale = 64.0;
while (scale*size > 512)
scale /= 2.0;
/* Open the Gremlin file and output header information. */
file = PaOpen(fileName, "w", (char *) NULL, ".", (char *) NULL,
(char **) NULL);
if (file == NULL)
{
TxError("Couldn't write Gremlin file \"%s\".\n", fileName);
return;
}
fprintf(file, "sungremlinfile\n");
fprintf(file, "1 0.00 0.00\n");
/* For each Gremlin style, find all the paint layers that belong
* to that style and put plot information into the file.
*/
for (curStyle = plotGremlinStyles; curStyle != NULL;
curStyle = curStyle->grs_next)
{
TTMaskAndMask3(&curMask, layers, &curStyle->grs_layers);
(void) DBTreeSrTiles(scx, &curMask, xMask, plotGremlinPaint,
(ClientData) NULL);
}
/* Output labels, if they are wanted. */
if (TTMaskHasType(layers, L_LABEL))
{
curMask = *layers;
TTMaskSetType(&curMask, TT_SPACE);
(void) DBTreeSrLabels(scx, &curMask, xMask, (TerminalPath *) NULL,
TF_LABEL_ATTACH, plotGremlinLabel, (ClientData) NULL);
}
/* Output subcell bounding boxes, if they are wanted. */
if (TTMaskHasType(layers, L_CELL))
{
(void) DBTreeSrCells(scx, xMask, plotGremlinCell, (ClientData) NULL);
}
/* Output trailer information into the file, and close it. */
fprintf(file, "-1\n");
fclose(file);
return;
}
#endif /* GREMLIN */