/* CIFreadpaint.c -
 *
 *	This file contains more routines to parse CIF files.  In
 *	particular, it contains the routines to handle paint,
 *	including rectangles, wires, flashes, and polygons.
 *
 *     *********************************************************************
 *     * 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/CIFrdpt.c,v 1.2 2010/06/24 12:37:15 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>		/* for wire path-to-poly path conversion */

#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "utils/malloc.h"
#include "database/database.h"
#include "windows/windows.h"
#include "utils/main.h"
#include "cif/CIFint.h"
#include "cif/CIFread.h"

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

/*
 * ----------------------------------------------------------------------------
 *
 * CIFParseBox --
 *
 * 	This procedure parses a CIF box command.
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	A box is added to the CIF information for this cell.  The
 *	box better not have corners that fall on half-unit boundaries.
 *
 * Correction:
 *	A box may be centered on a half lambda grid but have width
 *	and height such that the resulting box is entirely on the lambda
 *	grid.  So:  don't divide by 2 until the last step!
 *	---Tim Edwards, 4/20/00
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFParseBox(void)
{
    Point	center;
    Point	direction;
    Rect	rectangle, r2;
    int		savescale;

    /*	Take the 'B'. */

    TAKE();
    if (cifReadPlane == NULL)
    {
	CIFSkipToSemi();
	return FALSE;
    }

    /* Treat length and width as a point so we can make use of the code in */
    /* CIFParsePoint();  however, before moving on, check that both values */
    /* are strictly positive.						   */

    if (!CIFParsePoint(&rectangle.r_ur, 1))
    {
	CIFReadError("box, but no length and/or width; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }
    if (rectangle.r_xtop <= 0)
    {
	CIFReadError("box length not strictly positive; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }
    if (rectangle.r_ytop <= 0)
    {
	CIFReadError("box width not strictly positive; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }

    savescale = cifReadScale1;

    if (!CIFParsePoint(&center, 2))
    {
	CIFReadError("box, but no center; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }

    /* If reading the center causes a CIF input scale to be redefined,	*/
    /* then the length and width must also be changed.			*/

    if (savescale != cifReadScale1)
    {
	rectangle.r_xtop *= (cifReadScale1 / savescale);
	rectangle.r_ytop *= (cifReadScale1 / savescale);
    }

    rectangle.r_xbot = -rectangle.r_xtop;
    rectangle.r_ybot = -rectangle.r_ytop;

    /*	Optional direction vector:  have to build transform to do rotate. */

    if (CIFParseSInteger(&direction.p_x))
    {
	if (!CIFParseSInteger(&direction.p_y))
	{
	    CIFReadError("box, direction botched; box ignored.\n");
	    CIFSkipToSemi();
	    return FALSE;
	}
	GeoTransRect(CIFDirectionToTrans(&direction), &rectangle , &r2);
    }
    else r2 = rectangle;

    /* Offset by center only now that rotation is complete, and divide by two. */

    r2.r_xbot = (r2.r_xbot + center.p_x) / 2;
    r2.r_ybot = (r2.r_ybot + center.p_y) / 2;
    r2.r_xtop = (r2.r_xtop + center.p_x) / 2;
    r2.r_ytop = (r2.r_ytop + center.p_y) / 2;

    DBPaintPlane(cifReadPlane, &r2, CIFPaintTable, (PaintUndoInfo *) NULL);
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFParseFlash --
 *
 * 	This routine parses and processes a roundflash command.  The syntax is:
 *	roundflash ::= R diameter center
 *
 *	We approximate a roundflash by a box.
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	Paint is added to the current CIF plane.
 *
 * Corrections:  Incorrectly implemented.  Now CIFParsePoint returns the
 *	center coordinate doubled;  in this way, the center can be on the
 *	half-lambda grid but the resulting block on-grid, if the diameter
 *	is an odd number.
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFParseFlash(void)
{
    int		diameter;
    int		savescale;
    Point	center;
    Rect	rectangle;

    /* Take the 'R'. */

    TAKE();
    if (cifReadPlane == NULL)
    {
	CIFSkipToSemi();
	return FALSE;
    }
    if (!CIFParseInteger(&diameter))
    {
	CIFReadError("roundflash, but no diameter; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }
    diameter *= cifReadScale1;
    if (diameter % cifReadScale2 != 0)
	CIFReadWarning("Roundflash diameter snapped to nearest integer boundary.\n");

    diameter /= cifReadScale2;
    savescale = cifReadScale1;
    if (!CIFParsePoint(&center, 2))
    {
	CIFReadError("roundflash, but no center; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }
    if (savescale != cifReadScale1)
	diameter *= (cifReadScale1 / savescale);

    rectangle.r_xbot = (center.p_x - diameter) / 2;
    rectangle.r_ybot = (center.p_y - diameter) / 2;
    rectangle.r_xtop = (center.p_x + diameter) / 2;
    rectangle.r_ytop = (center.p_y + diameter) / 2;
    DBPaintPlane(cifReadPlane, &rectangle, CIFPaintTable,
	    (PaintUndoInfo *) NULL);
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFPropRecordPath --
 *
 * Generate a property in the current edit cell and set it to a string
 * containing the values in the list at pathheadp.
 *
 * If "iswire" is TRUE, then all values in the path are assumed to be
 * double the actual value (because path centerlines can be on half-
 * lambda).
 * ----------------------------------------------------------------------------
 */

void
CIFPropRecordPath(
    CellDef *def,
    CIFPath *pathheadp,
    bool iswire,
    char *propname)
{
    extern float CIFGetOutputScale(int convert);
    CIFPath *pathp;
    char *namestr = NULL;
    int components, i, x, y, mult, pathnum;
    PropertyRecord *proprec;
    bool propfound;

    /* If "name" is a property, then append a suffix to it to ensure uniqueness */
    DBPropGet(def, propname, &propfound);
    if (propfound)
    {
	pathnum = 0;
	namestr = mallocMagic(strlen(propname) + 10);
	while (propfound)
	{
	    sprintf(namestr, "%s_%d", propname, pathnum);
	    DBPropGet(def, namestr, &propfound);
	    pathnum++;
	}
    }

    mult = (iswire == TRUE) ? 1 : 0;

    /* Count the number of components in the path */
    pathp = pathheadp;
    components = 0;
    while (pathp != NULL)
    {
	components++;
	pathp = pathp->cifp_next;
    }
    /* Allocate enough space to hold 2 * N points. */
    proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
		((components - 1) * 2) * sizeof(int));
    proprec->prop_type = PROPERTY_TYPE_DIMENSION;
    proprec->prop_len = components * 2;

    pathp = pathheadp;
    i = 0;
    while (pathp != NULL)
    {
	x = pathp->cifp_x >> mult;
	y = pathp->cifp_y >> mult;
        
	proprec->prop_value.prop_integer[i] = x;
	proprec->prop_value.prop_integer[i + 1] = y;

	i += 2;
	pathp = pathp->cifp_next;
    }
    if (namestr)
    {
	DBPropPut(def, namestr, proprec);
	freeMagic(namestr);
    }
    else
	DBPropPut(def, propname, proprec);
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFPaintWirePath --
 *
 * Draw a "wire path" described by the endpoints of a centerline through
 * a series of segments, and a wire width.  We pass the plane and paint
 * table information so this routine can be used by the database for
 * painting paths from the command-line.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Paints layout into magic.  The original wire path is destroyed
 *	(memory free'd).
 *
 * Notes:
 *	Path coordinates for wires are always assumed to be twice the
 *	actual value to avoid roundoff errors, since the centerline of
 *	a path can be halfway between two coordinates of the layout grid
 *	and still describe a polygon whose endpoints are all on the grid.
 *
 * Warning:
 *	It is still possible to get roundoff problems with different
 *	values of segment width at different angles caused by snapping
 *	to grid points.  While this is "as it should be", it causes
 *	problems when process design rules demand geometry at 45 degrees
 *	only, and the algorithm produces points that are 1 unit off.
 *	A possible solution is to adjust "cwidth" to match the average
 *	value of "width" after snapping at entering and exiting angles.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFPaintWirePath(
    CIFPath *pathheadp,
    int width,
    bool endcap,
    Plane *plane,
    const PaintResultType *ptable,
    PaintUndoInfo *ui)
{
    CIFPath *pathp, *previousp, *nextp, *polypath;
    CIFPath *returnpath, *newpath, *savepath;
    LinkedRect	*rectp;
    double theta, phi, alpha, delta, cwidth, adjwidth, testmitre, savetheta;
    double xmaxoff, ymaxoff, xminoff, yminoff;
    double xmin, ymin, xmax, ymax, xnext, ynext;
    bool firstpoint;

    /* Get rid of any repeat points, which just screw up the algorithm */

    previousp = pathheadp;
    pathp = pathheadp->cifp_next;
    if (pathp != NULL)
    {
	free_magic1_t mm1 = freeMagic1_init();
	while (pathp->cifp_next != NULL)
	{
	    if (pathp->cifp_next->cifp_x == pathp->cifp_x &&
			pathp->cifp_next->cifp_y == pathp->cifp_y)
	    {
		previousp->cifp_next = pathp->cifp_next;
		freeMagic1(&mm1, pathp);
	    }
	    else
		previousp = pathp;
	    pathp = pathp->cifp_next;
	}
	freeMagic1_end(&mm1);
    }

    previousp = pathheadp;
    polypath = NULL;

    /* Single-point paths are okay; just set the endpoints equal */
    if (pathheadp->cifp_next == NULL)
	pathp = pathheadp;
    else
	pathp = pathheadp->cifp_next;

    firstpoint = TRUE;
    theta = 0;
    while (pathp != NULL)
    {
	/* Advance to the next point */
	xmin = (double)previousp->cifp_x;
	xmax = (double)pathp->cifp_x;
	ymin = (double)previousp->cifp_y;
	ymax = (double)pathp->cifp_y;

	/* Angle of this segment */
	savetheta = theta;
	theta = atan2(ymax - ymin, xmax - xmin);

	/* Look ahead to the next point */
	if (firstpoint)
	{
	    /* Back first point up by endcap amount (width,	*/
	    /* which is half the width of the route segment.)	*/

	    if (endcap)
	    {
		xmin -= (double)width * cos(theta);
		ymin -= (double)width * sin(theta);
	    }
	    xminoff = (double)width * cos(theta - 1.5708); /* 90 degrees */
	    yminoff = (double)width * sin(theta - 1.5708);
	    firstpoint = FALSE;

	    newpath = (CIFPath *)mallocMagic(sizeof(CIFPath));
	    newpath->cifp_next = polypath;
	    polypath = newpath;
	    returnpath = polypath;	/* returnpath is always at the end */
	    newpath->cifp_x = round((xmin + xminoff) / 2);
	    newpath->cifp_y = round((ymin + yminoff) / 2);

	    newpath = (CIFPath *)mallocMagic(sizeof(CIFPath));
	    newpath->cifp_next = polypath;
	    polypath = newpath;
	    newpath->cifp_x = round((xmin - xminoff) / 2);
	    newpath->cifp_y = round((ymin - yminoff) / 2);
	}

	nextp = pathp->cifp_next;
	if (nextp != NULL)
	{
	    xnext = (double)nextp->cifp_x;
	    ynext = (double)nextp->cifp_y;
	    phi = atan2(ynext - ymax, xnext - xmax);
	}
	else
	{
	    /* Endpoint:  create 1/2 width endcap */
	    phi = theta;
	    if (endcap)
	    {
		xmax += (double)width * cos(theta);
		ymax += (double)width * sin(theta);
	    }
	}

	alpha = 0.5 * (phi - theta);
	testmitre = fabs(cos(alpha));

	/* This routine does not (yet) do mitre limits, so for	*/
	/* now, we do a sanity check.  In the case of an	*/
	/* extremely acute angle, we generate a warning and	*/
	/* truncate the route.  The mitre limit is arbitrarily	*/
	/* set at 4 times the route width.  Such extreme bends	*/
	/* are usually DRC violations, anyway.  Tighter bends	*/
	/* than this tend to cause difficulties for the		*/
	/* CIFMakeManhattanPath() routine.			*/

	if (testmitre < 0.25) {
	    if (testmitre < 1.0e-10) {
		/* Wire reverses direction.  Break wire here,	*/
		/* draw, and start new polygon.			*/

		/* Check first if last point and current point	*/
		/* are the same, in which case a different	*/
		/* message should be issued (and possibly	*/
		/* should be handled differently?)		*/

		if (previousp && previousp->cifp_x == pathp->cifp_x
			&& previousp->cifp_y == pathp->cifp_y)
		{
		    TxError("Warning: duplicate point in path at (%d, %d).\n",
				pathp->cifp_x / 2, pathp->cifp_y / 2);
		}
		else
		{
		    TxError("Warning: direction reversal in path at (%d, %d).\n",
				pathp->cifp_x / 2, pathp->cifp_y / 2);
		}
		phi = theta;
		if (endcap)
		{
		    xmax += (double)width * cos(theta);
		    ymax += (double)width * sin(theta);
		}
		alpha = 0.5 * (phi - theta);
		firstpoint = TRUE;
	    }
	    else {
		TxError("Error: mitre limit exceeded at wire junction at (%d, %d).\n",
				pathp->cifp_x, pathp->cifp_y);
		TxError("Route has been truncated.\n");
		break;
	   }
	}

	delta = (0.5 * (phi + theta)) - 1.5708;
	cwidth = (double)width / cos(alpha);
	xmaxoff = cwidth * cos(delta);
	ymaxoff = cwidth * sin(delta);

	newpath = (CIFPath *)mallocMagic(sizeof(CIFPath));
	newpath->cifp_next = polypath;
	polypath = newpath;
	newpath->cifp_x = round((xmax - xmaxoff) / 2);
	newpath->cifp_y = round((ymax - ymaxoff) / 2);

	newpath = (CIFPath *)mallocMagic(sizeof(CIFPath));
	newpath->cifp_next = NULL;
	savepath = returnpath;
	returnpath->cifp_next = newpath;
	returnpath = newpath;
	newpath->cifp_x = round((xmax + xmaxoff) / 2);
	newpath->cifp_y = round((ymax + ymaxoff) / 2);

	if (firstpoint == TRUE || nextp == NULL)
	{
	    /* Slow draw for non-Manhattan paths:		*/
	    /* Break the area up into triangles and rectangles	*/

	    rectp = CIFPolyToRects(polypath, plane, ptable, ui, FALSE);
	    CIFFreePath(polypath);

	    free_magic1_t mm1 = freeMagic1_init();
	    for (; rectp != NULL ; rectp = rectp->r_next)
	    {
		DBPaintPlane(plane, &rectp->r_r, ptable, ui);
		freeMagic1(&mm1, (char *) rectp);
	    }
	    freeMagic1_end(&mm1);
	    polypath = NULL;
	}
	else
	{
	    Rect r;
	    double a1, a2, r2, d1;
	    Point newpt;

	    /* Check if either of the two new segments travels opposite	*/
	    /* to theta.  If so, then we need to find the intersection	*/
	    /* with the previous point, to avoid creating a cut-out	*/
	    /* wedge in the path.					*/

	    a1 = fabs(atan2(returnpath->cifp_y - savepath->cifp_y,
			returnpath->cifp_x - savepath->cifp_x) - theta);
	    a2 = fabs(atan2(polypath->cifp_y - polypath->cifp_next->cifp_y,
			polypath->cifp_x - polypath->cifp_next->cifp_x) - theta);
	    if (a1 > 0.1 && a1 < 6.1)
	    {
		/* Find new intersection point */
		d1 = cos(savetheta) * sin(phi) - sin(savetheta) * cos(phi);
		if (fabs(d1) > 1.0e-4)
		{
		    r2 = (sin(phi) * (returnpath->cifp_x - savepath->cifp_x)
		    	 - cos(phi) * (returnpath->cifp_y - savepath->cifp_y)) / d1;
		    savepath->cifp_x += round(r2 * cos(savetheta));
		    savepath->cifp_y += round(r2 * sin(savetheta));
		}
	    }
	    else if (a2 > 0.1 && a2 < 6.1)
	    {
		/* Find new intersection point */
		d1 = cos(savetheta) * sin(phi) - sin(savetheta) * cos(phi);
		if (fabs(d1) > 1.0e-4)
		{
		    r2 = (sin(phi) * (polypath->cifp_x - polypath->cifp_next->cifp_x)
		     	- cos(phi) * (polypath->cifp_y - polypath->cifp_next->cifp_y))
		     	/ d1;
		    polypath->cifp_next->cifp_x += round(r2 * cos(savetheta));
		    polypath->cifp_next->cifp_y += round(r2 * sin(savetheta));
		}
	    }
	}

	previousp = pathp;
	pathp = pathp->cifp_next;
    }
    CIFFreePath(pathheadp);
}

/*
 * ----------------------------------------------------------------------------
 *
 * PaintPolygon --
 *
 * Convert a list of points in the form of an array of type Point to a
 * CIFPath linked structure, and paint them into the database.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Paints tiles into the layout database.  Calling routine is
 *	responsible for free'ing memory of the pointlist, if necessary.
 *
 * Notes:
 *	This is a database routine, not a CIF routine.  However, it makes
 *	use of the CIFPath structure, so it is included here.
 *
 * ----------------------------------------------------------------------------
 */

LinkedRect *
PaintPolygon(
    Point *pointlist,		/* Array of Point structures */
    int number,			/* total number of points */
    Plane *plane,		/* Plane structure to paint into */
    PaintResultType *ptable,	/* Paint result table */
    PaintUndoInfo *ui,		/* Undo record */
    bool keep)			/* Return list of rects if true */
{
    LinkedRect	*rectp, *rectlist;
    CIFPath *newpath, *cifpath = (CIFPath *)NULL;
    int i;

    for (i = 0; i < number; i++)
    {
	newpath = (CIFPath *) mallocMagic((unsigned) sizeof (CIFPath));
	newpath->cifp_x = pointlist[i].p_x;
	newpath->cifp_y = pointlist[i].p_y;
	newpath->cifp_next = cifpath;
	cifpath = newpath;
    }

    rectlist = CIFPolyToRects(cifpath, plane, ptable, ui, FALSE);
    CIFFreePath(cifpath);

    free_magic1_t mm1 = freeMagic1_init();
    for (rectp = rectlist; rectp != NULL ; rectp = rectp->r_next)
    {
	DBPaintPlane(plane, &rectp->r_r, ptable, ui);
	if (!keep) freeMagic1(&mm1, (char *) rectp);
    }
    freeMagic1_end(&mm1);
    return (keep) ? rectlist : (LinkedRect *)NULL;
}

/*
 * ----------------------------------------------------------------------------
 *
 * PaintWireList --
 *
 * Convert a list of points in the form of an array of type Point to a
 * CIFPath linked structure, and paint them into the database.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Paints tiles into the layout database.  Calling routine is
 *	responsible for free'ing memory of the pointlist, if necessary.
 *
 * Notes:
 *	This is a database routine, not a CIF routine.  However, it makes
 *	use of the CIFPath structure, so it is included here.
 *
 * ----------------------------------------------------------------------------
 */

void
PaintWireList(
    Point *pointlist,		/* Array of Point structures */
    int number,			/* total number of points */
    int width,			/* Route width of path */
    bool endcap,		/* Whether or not to add 1/2 width endcaps */
    Plane *plane,		/* Plane structure to paint into */
    PaintResultType *ptable,	/* Paint result table */
    PaintUndoInfo *ui)		/* Undo record */
{
    CIFPath *newpath, *cifpath = (CIFPath *)NULL;
    int i;

    for (i = 0; i < number; i++)
    {
	newpath = (CIFPath *) mallocMagic((unsigned) sizeof (CIFPath));
	newpath->cifp_x = pointlist[i].p_x;
	newpath->cifp_y = pointlist[i].p_y;
	newpath->cifp_next = cifpath;
	cifpath = newpath;
    }
    CIFPaintWirePath(cifpath, width, endcap, plane, ptable, ui);
}


/*
 * ----------------------------------------------------------------------------
 *
 * CIFParseWire --
 *
 * 	This procedure parses CIF wire commands, and adds paint
 *	to the current CIF cell.  A wire command consists of
 *	an integer width, then a path.
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	The current CIF planes are modified.
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFParseWire(void)
{
    int		width;
    CIFPath	*pathheadp, *polypath;
    int		savescale;

    /* Take the 'W'. */

    TAKE();
    if (cifReadPlane == NULL)
    {
	CIFSkipToSemi();
	return FALSE;
    }
    if (!CIFParseInteger(&width))
    {
	CIFReadError("wire, but no width; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }
    width *= cifReadScale1;
    if (width % cifReadScale2 != 0)
	CIFReadWarning("Wire width snapped to nearest integer boundary.\n");

    width /= cifReadScale2;
    savescale = cifReadScale1;
    pathheadp = CIFParsePath(2);
    if (pathheadp == NULL)
    {
	CIFReadError("wire, but improper path; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }
    if (savescale != cifReadScale1)
	width *= (cifReadScale1 / savescale);

    CIFPaintWirePath(pathheadp, width, TRUE, cifReadPlane, CIFPaintTable,
		(PaintUndoInfo *)NULL);
    return TRUE;
}


/*
 * ----------------------------------------------------------------------------
 *
 * CIFParseLayer --
 *
 * 	This procedure parses layer changes.  The syntax is:
 *	layer ::= L { blank } processchar layerchars
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	Switches the CIF plane where paint is being saved.
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFParseLayer(void)
{
#define MAXCHARS 4
    char	name[MAXCHARS+1];
    char	c;
    int		i;
    TileType	type;

    /* Take the 'L'. */

    TAKE();
    CIFSkipBlanks();

    /* Get the layer name. */

    for (i=0; i<=MAXCHARS; i++)
    {
	c = PEEK();
	if (isdigit(c) || isupper(c))
	    name[i] = TAKE();
	else break;
    }
    name[i] = '\0';

    /* Set current plane for use by the routines that parse geometric
     * elements.
     */

    type = CIFReadNameToType(name, FALSE);
    if (type < 0)
    {
	cifReadPlane = NULL;
	cifCurLabelType = TT_SPACE;
	CIFReadError("layer %s isn't known in the current style.\n",
		name);
    } else {
	cifCurLabelType = cifCurReadStyle->crs_labelLayer[type];
	cifReadPlane = cifCurReadPlanes[type];
    }

    CIFSkipToSemi();
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFParsePoly --
 *
 * 	This procedure reads and processes a polygon command.  The syntax is:
 *	polygon ::= path
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	Paint is added to the current CIF plane.
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFParsePoly(void)
{
    CIFPath	*pathheadp;
    LinkedRect	*rectp;

    /* Take the 'P'. */

    TAKE();
    if (cifReadPlane == NULL)
    {
	CIFSkipToSemi();
	return FALSE;
    }
    pathheadp = CIFParsePath(1);
    if (pathheadp == NULL)
    {
	CIFReadError("polygon, but improper path; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }

    /* Convert the polygon to rectangles. */

    rectp = CIFPolyToRects(pathheadp, cifReadPlane, CIFPaintTable,
		(PaintUndoInfo *)NULL, FALSE);
    CIFFreePath(pathheadp);
    if (rectp == NULL)
    {
        /* The non-Manhattan geometry polygon parsing algorithm */
        /* typically leaves behind degenerate paths, so they    */
        /* should not be considered erroneous.                  */
	CIFSkipToSemi();
	return FALSE;
    }
    free_magic1_t mm1 = freeMagic1_init();
    for (; rectp != NULL ; rectp = rectp->r_next)
    {
	DBPaintPlane(cifReadPlane, &rectp->r_r, CIFPaintTable,
		(PaintUndoInfo *) NULL);
	freeMagic1(&mm1, (char *) rectp);
    }
    freeMagic1_end(&mm1);
    return TRUE;
}
