1703 lines
41 KiB
C
1703 lines
41 KiB
C
/* CIFreadutils.c -
|
|
*
|
|
* This file contains routines that parse a file in CIF
|
|
* format. This file contains the top-level routine for
|
|
* reading CIF files, plus a bunch of utility routines
|
|
* for skipping white space, parsing numbers and points, etc.
|
|
*
|
|
* *********************************************************************
|
|
* * 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/CIFrdutils.c,v 1.4 2010/06/24 12:37:15 tim Exp $";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
|
|
/*
|
|
* C99 compat
|
|
* Mind: tcltk/tclmagic.h must be included prior to all the other headers
|
|
*/
|
|
#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/CIFread.h"
|
|
#include "cif/cif.h"
|
|
#include "textio/textio.h"
|
|
#include "utils/signals.h"
|
|
#include "utils/undo.h"
|
|
#include "utils/malloc.h"
|
|
|
|
/* C99 compat */
|
|
#include "lef/lef.h"
|
|
#include "drc/drc.h"
|
|
#include "extract/extract.h"
|
|
#include "wiring/wiring.h"
|
|
#include "router/router.h"
|
|
#include "mzrouter/mzrouter.h"
|
|
#include "irouter/irouter.h"
|
|
#include "plow/plow.h"
|
|
|
|
/* The following variables are used to provide one character of
|
|
* lookahead. cifParseLaAvail is TRUE if cifParseLaChar contains
|
|
* a valid character, FALSE otherwise. The PEEK and TAKE macros
|
|
* are used to manipulate this stuff.
|
|
*/
|
|
|
|
bool cifParseLaAvail = FALSE;
|
|
int cifParseLaChar = EOF;
|
|
|
|
/* Below is a variable pointing to the CIF input file. It's used
|
|
* by the PEEK and TAKE macros. The other stuff is used to keep
|
|
* track of our location in the CIF file for error reporting
|
|
* purposes.
|
|
*/
|
|
|
|
FILE *cifInputFile;
|
|
FILE *cifErrorFile;
|
|
int cifLineNumber; /* Number of current line. */
|
|
int cifTotalWarnings; /* Number of warnings detected */
|
|
int cifTotalErrors; /* Number of errors detected */
|
|
bool cifSeenSnapWarning; /* Track this to prevent excessive messaging */
|
|
|
|
/* The variables used below hold general information about what
|
|
* we're currently working on.
|
|
*/
|
|
|
|
int cifReadScale1; /* Scale factor: multiply by Scale1 */
|
|
int cifReadScale2; /* then divide by Scale2. */
|
|
int CIFRescaleLimit = CIFMAXRESCALE; /* Don't increase cifReadScale1 by more
|
|
* than this amount; internal units
|
|
* finer than this will be rounded.
|
|
*/
|
|
bool CIFRescaleAllow = TRUE; /* Don't subdivide the magic internal
|
|
* grid if this is FALSE.
|
|
*/
|
|
bool CIFNoDRCCheck = FALSE; /* If TRUE, then cell is marked DRC clean
|
|
* and not DRC checked.
|
|
*/
|
|
char *CIFErrorFilename; /* Name of file for error redirection */
|
|
|
|
int CifPolygonCount; /* Count of generated subcells
|
|
* containing polygons. This number
|
|
* is used to create a unique cell name.
|
|
*/
|
|
bool CIFSubcellPolygons = FALSE; /* If TRUE, each non-Manhattan polygon
|
|
* will be put in a separate subcell
|
|
* to avoid too much tile splitting
|
|
*/
|
|
|
|
Plane *cifReadPlane; /* Plane into which to paint material
|
|
* NULL means no layer command has
|
|
* been seen for the current cell.
|
|
*/
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFReadError --
|
|
*
|
|
* This procedure is called to print out error messages during
|
|
* CIF file reading.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* An error message is printed.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/* VARARGS1 */
|
|
void
|
|
CIFReadError(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
cifTotalErrors++;
|
|
if (CIFWarningLevel == CIF_WARN_NONE) return;
|
|
if ((cifTotalErrors < 100) || (CIFWarningLevel != CIF_WARN_LIMIT))
|
|
{
|
|
if (cifLineNumber > 0)
|
|
TxError("Error at line %d of CIF file: ", cifLineNumber);
|
|
else
|
|
TxError("CIF file read error: ");
|
|
va_start(args, format);
|
|
Vfprintf(stderr, format, args);
|
|
va_end(args);
|
|
}
|
|
else if ((cifTotalErrors == 100) && (CIFWarningLevel == CIF_WARN_LIMIT))
|
|
{
|
|
TxError("Error limit set: Remaining errors will not be reported.\n");
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
CIFReadWarning(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
cifTotalWarnings++;
|
|
if (CIFWarningLevel == CIF_WARN_NONE) return;
|
|
if ((cifTotalWarnings < 100) || (CIFWarningLevel != CIF_WARN_LIMIT))
|
|
{
|
|
if (cifLineNumber > 0)
|
|
TxError("Warning at line %d of CIF file: ", cifLineNumber);
|
|
else
|
|
TxError("CIF file read warning: ");
|
|
va_start(args, format);
|
|
Vfprintf(stderr, format, args);
|
|
va_end(args);
|
|
}
|
|
else if ((cifTotalWarnings == 100) && (CIFWarningLevel == CIF_WARN_LIMIT))
|
|
{
|
|
TxError("Warning limit set: Remaining warnings will not be reported.\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFScaleCoord
|
|
*
|
|
* This procedure does rounding and division to convert from
|
|
* CIF units back into Magic units.
|
|
*
|
|
* "snap_type" may be one of:
|
|
* COORD_EXACT: result must be an exact integer. If not, the
|
|
* magic grid spacing is changed such that the result will
|
|
* be an integer.
|
|
* COORD_HALF_U: twice the result must be an exact integer. If
|
|
* not, the magic grid spacing is changed as above. If the
|
|
* result is 1/2, it is rounded up to the nearest integer.
|
|
* COORD_HALF_L: same as above, but result is rounded down.
|
|
* COORD_ANY: result may be fractional, and will be snapped to
|
|
* the nearest magic grid. Generally, this is used for
|
|
* labels whose position need not be exact.
|
|
*
|
|
* Results:
|
|
* The result is the Magic unit equivalent to cifCoord.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
CIFScaleCoord(
|
|
int cifCoord, /* A coordinate in CIF units. */
|
|
int snap_type) /* How to deal with fractional results */
|
|
{
|
|
int result, scale, remain, denom;
|
|
int mult, mfactor;
|
|
|
|
/* If internal grid subdivision is disallowed, always round to the */
|
|
/* nearest grid unit. */
|
|
|
|
if (!CIFRescaleAllow)
|
|
snap_type = COORD_ANY;
|
|
|
|
scale = cifCurReadStyle->crs_scaleFactor;
|
|
mult = cifCurReadStyle->crs_multiplier;
|
|
|
|
/* Check for non-integer result and warn of fractional-lambda violation */
|
|
|
|
if ((remain = (cifCoord % scale)) != 0)
|
|
{
|
|
int lgcf = FindGCF(abs(cifCoord), scale);
|
|
|
|
remain = abs(remain) / lgcf;
|
|
denom = scale / lgcf;
|
|
|
|
if (CIFReadTechLimitScale(1, denom)) snap_type = COORD_ANY;
|
|
|
|
switch (snap_type)
|
|
{
|
|
case COORD_EXACT:
|
|
if (!cifSeenSnapWarning)
|
|
CIFReadWarning("Input off lambda grid by %d/%d; grid redefined.\n",
|
|
remain, denom);
|
|
cifSeenSnapWarning = TRUE;
|
|
|
|
CIFTechInputScale(1, denom, FALSE);
|
|
CIFTechOutputScale(1, denom);
|
|
DRCTechScale(1, denom);
|
|
ExtTechScale(1, denom);
|
|
WireTechScale(1, denom);
|
|
#ifdef LEF_MODULE
|
|
LefTechScale(1, denom);
|
|
#endif
|
|
#ifdef ROUTE_MODULE
|
|
RtrTechScale(1, denom);
|
|
MZAfterTech();
|
|
IRAfterTech();
|
|
#endif
|
|
DBScaleEverything(denom, 1);
|
|
DBLambda[1] *= denom;
|
|
ReduceFraction(&DBLambda[0], &DBLambda[1]);
|
|
scale = cifCurReadStyle->crs_scaleFactor;
|
|
result = cifCoord / scale;
|
|
break;
|
|
case COORD_HALF_U: case COORD_HALF_L:
|
|
if (denom > 2)
|
|
{
|
|
if (!cifSeenSnapWarning)
|
|
CIFReadWarning("Input off lambda grid by %d/%d; "
|
|
"grid redefined.\n", remain, denom);
|
|
cifSeenSnapWarning = TRUE;
|
|
|
|
/* scale to nearest half-lambda */
|
|
if (!(denom & 0x1)) denom >>= 1;
|
|
|
|
CIFTechInputScale(1, denom, FALSE);
|
|
CIFTechOutputScale(1, denom);
|
|
DRCTechScale(1, denom);
|
|
PlowAfterTech();
|
|
ExtTechScale(1, denom);
|
|
WireTechScale(1, denom);
|
|
#ifdef ROUTE_MODULE
|
|
MZAfterTech();
|
|
IRAfterTech();
|
|
#endif
|
|
#ifdef LEF_MODULE
|
|
LefTechScale(1, denom);
|
|
#endif
|
|
#ifdef ROUTE_MODULE
|
|
RtrTechScale(1, denom);
|
|
#endif
|
|
DBScaleEverything(denom, 1);
|
|
DBLambda[1] *= denom;
|
|
ReduceFraction(&DBLambda[0], &DBLambda[1]);
|
|
scale = cifCurReadStyle->crs_scaleFactor;
|
|
}
|
|
|
|
if (snap_type == COORD_HALF_U)
|
|
result = cifCoord + (scale >> 1);
|
|
else
|
|
result = cifCoord - (scale >> 1);
|
|
result /= scale;
|
|
|
|
break;
|
|
case COORD_ANY:
|
|
if (!cifSeenSnapWarning)
|
|
CIFReadWarning("Input off lambda grid by %d/%d; snapped to grid.\n",
|
|
abs(remain), abs(denom));
|
|
cifSeenSnapWarning = TRUE;
|
|
|
|
/* Careful: must round down a bit more for negative numbers, in
|
|
* order to ensure that a point exactly halfway between Magic units
|
|
* always gets rounded down, rather than towards zero (this would
|
|
* result in different treatment of the same paint, depending on
|
|
* where it is in the coordinate system.
|
|
*/
|
|
|
|
if (cifCoord < 0)
|
|
result = cifCoord - ((scale)>>1);
|
|
else
|
|
result = cifCoord + ((scale-1)>>1);
|
|
result /= scale;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
result = cifCoord / scale;
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cifIsBlank --
|
|
*
|
|
* Figures out whether a character qualifies as a blank in CIF.
|
|
* A blank is anything except a digit, an upper-case character,
|
|
* or the symbols "-", "(", "(", and ";".
|
|
*
|
|
* Results:
|
|
* Returns TRUE if ch is a CIF blank, FALSE otherwise.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
cifIsBlank(
|
|
int ch)
|
|
{
|
|
|
|
if ( isdigit(ch) || isupper(ch)
|
|
|| (ch == '-') || (ch == ';')
|
|
|| (ch == '(') || (ch == ')')
|
|
|| (ch == EOF))
|
|
{
|
|
return FALSE;
|
|
}
|
|
else return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFSkipBlanks --
|
|
*
|
|
* This procedure skips over whitespace in the CIF file,
|
|
* keeping track of the line number and other information
|
|
* for error reporting.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Advances through the CIF file.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
CIFSkipBlanks(void)
|
|
{
|
|
|
|
while (cifIsBlank(PEEK())) {
|
|
if (TAKE() == '\n')
|
|
{
|
|
cifLineNumber++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFSkipSep --
|
|
*
|
|
* Skip over separators in the CIF file. Blanks and upper-case
|
|
* characters are separators.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Advances through the CIF file.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
CIFSkipSep(void)
|
|
{
|
|
int ch;
|
|
|
|
for (ch = PEEK() ; isupper(ch) || cifIsBlank(ch) ; ch = PEEK()) {
|
|
if (TAKE() == '\n')
|
|
{
|
|
cifLineNumber++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFSkipToSemi --
|
|
*
|
|
* This procedure is called after errors. It skips everything
|
|
* in the CIF file up to the next semi-colon.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Advances through the CIF file.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
CIFSkipToSemi(void)
|
|
{
|
|
int ch;
|
|
|
|
for (ch = PEEK() ; ((ch != ';') && (ch != EOF)) ; ch = PEEK()) {
|
|
if (TAKE() == '\n')
|
|
{
|
|
cifLineNumber++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFSkipSemi --
|
|
*
|
|
* Skips a semi-colon, including blanks around the semi-colon.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Advances through the CIF file.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
CIFSkipSemi(void)
|
|
{
|
|
|
|
CIFSkipBlanks();
|
|
if (PEEK() != ';') {
|
|
CIFReadError("`;\' expected.\n");
|
|
return;
|
|
}
|
|
TAKE();
|
|
CIFSkipBlanks();
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFParseSInteger --
|
|
*
|
|
* This procedure parses a signed integer from the CIF file.
|
|
*
|
|
* Results:
|
|
* TRUE is returned if the parse completed without error,
|
|
* FALSE otherwise.
|
|
*
|
|
* Side effects:
|
|
* The integer pointed to by valuep is modified with the
|
|
* value of the signed integer.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
CIFParseSInteger(
|
|
int *valuep)
|
|
{
|
|
bool is_signed;
|
|
char buffer[ BUFSIZ ];
|
|
char *bufferp;
|
|
|
|
*valuep = 0;
|
|
CIFSkipSep();
|
|
if (PEEK() == '-')
|
|
{
|
|
TAKE();
|
|
is_signed = TRUE;
|
|
}
|
|
else is_signed = FALSE;
|
|
bufferp = &buffer[0];
|
|
while (isdigit(PEEK()))
|
|
*bufferp++ = TAKE();
|
|
if (bufferp == &buffer[0])
|
|
return FALSE;
|
|
*bufferp = '\0';
|
|
*valuep = atoi(&buffer[0]);
|
|
if (is_signed)
|
|
*valuep = -(*valuep);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFParseInteger --
|
|
*
|
|
* Parses a positive integer from the CIF file.
|
|
*
|
|
* Results:
|
|
* TRUE is returned if the parse was completed successfully,
|
|
* FALSE otherwise.
|
|
*
|
|
* Side effects:
|
|
* The value pointed to by valuep is modified to hold the integer.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
CIFParseInteger(
|
|
int *valuep)
|
|
{
|
|
|
|
if (!CIFParseSInteger(valuep))
|
|
return FALSE;
|
|
if (*valuep < 0)
|
|
CIFReadError("negative integer not permitted.\n");
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFParsePoint --
|
|
*
|
|
* Parse a point from a CIF file. A point is two integers
|
|
* separated by CIF separators.
|
|
* parameter "iscale" (internal scale factor) is usually 1, but
|
|
* can be 2 to deal with half-lambda entries in the CIF by
|
|
* returning double the result.
|
|
*
|
|
* Results:
|
|
* TRUE is returned if the point was parsed correctly, otherwise
|
|
* FALSE is returned.
|
|
*
|
|
* Side effects:
|
|
* The parameter pointp is filled in with the coordinates of
|
|
* the point.
|
|
*
|
|
* If the CIF scalefactors are such that the result would be a
|
|
* fractional value, the definition of the CIF scale is altered
|
|
* such that the result is integer, and all geometry read so far
|
|
* is altered to match. This does not immediately affect the geometry
|
|
* in the magic database; if that also appears to have fractional
|
|
* units, it will be discovered by CIFScaleCoord and corrected.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
CIFParsePoint(
|
|
Point *pointp,
|
|
int iscale)
|
|
{
|
|
int rescale;
|
|
|
|
pointp->p_x = 0;
|
|
pointp->p_y = 0;
|
|
if (!CIFParseSInteger(&pointp->p_x))
|
|
return FALSE;
|
|
pointp->p_x *= (cifReadScale1 * iscale);
|
|
if (pointp->p_x % cifReadScale2 != 0)
|
|
{
|
|
rescale = cifReadScale2 / FindGCF(cifReadScale2, abs(pointp->p_x));
|
|
if ((cifReadScale1 * rescale) > CIFRescaleLimit)
|
|
{
|
|
CIFReadWarning("CIF units at maximum scale; value is rounded\n");
|
|
/* prepare for nearest-integer rounding */
|
|
if (pointp->p_x < 0)
|
|
pointp->p_x -= ((cifReadScale2 - 1) >> 1);
|
|
else
|
|
pointp->p_x += (cifReadScale2 >> 1);
|
|
}
|
|
else
|
|
{
|
|
cifReadScale1 *= rescale;
|
|
CIFInputRescale(rescale, 1);
|
|
pointp->p_x *= rescale;
|
|
}
|
|
}
|
|
pointp->p_x /= cifReadScale2;
|
|
if (!CIFParseSInteger(&pointp->p_y))
|
|
return FALSE;
|
|
pointp->p_y *= (cifReadScale1 * iscale);
|
|
if (pointp->p_y % cifReadScale2 != 0)
|
|
{
|
|
rescale = cifReadScale2 / FindGCF(cifReadScale2, abs(pointp->p_y));
|
|
if ((cifReadScale1 * rescale) > CIFRescaleLimit)
|
|
{
|
|
CIFReadWarning("CIF units at maximum scale; value is rounded\n");
|
|
/* prepare for nearest-integer rounding */
|
|
if (pointp->p_y < 0)
|
|
pointp->p_y -= ((cifReadScale2 - 1) >> 1);
|
|
else
|
|
pointp->p_y += (cifReadScale2 >> 1);
|
|
}
|
|
else
|
|
{
|
|
cifReadScale1 *= rescale;
|
|
CIFInputRescale(rescale, 1);
|
|
pointp->p_x *= rescale;
|
|
pointp->p_y *= rescale;
|
|
}
|
|
}
|
|
pointp->p_y /= cifReadScale2;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFParsePath --
|
|
*
|
|
* This procedure parses a CIF path, which is sequence of
|
|
* one or more points.
|
|
*
|
|
* Results:
|
|
* non-NULL CIFPath* the caller takes ownership of
|
|
* if the path was parsed successfully, otherwise NULL.
|
|
*
|
|
* Side effects:
|
|
* None
|
|
*
|
|
* Corrections:
|
|
* CIF coordinates are multiplied by 2 to cover the case where
|
|
* the path centerline lies on the half lambda grid but the line
|
|
* itself is on-grid. This can't be done for polygons, so a
|
|
* parameter "iscale" (internal scale) is added, and set to 1 for
|
|
* polygons, 2 for wires when calling CIFParsePath().
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
CIFPath *
|
|
CIFParsePath(
|
|
int iscale)
|
|
{
|
|
CIFPath *pathheadp, *pathtailp, *newpathp;
|
|
bool nonManhattan = FALSE; /* diagnostic only */
|
|
CIFPath path;
|
|
int savescale;
|
|
|
|
pathheadp = NULL;
|
|
pathtailp = NULL;
|
|
path.cifp_next = NULL;
|
|
while (TRUE)
|
|
{
|
|
CIFSkipSep();
|
|
if (PEEK() == ';')
|
|
break;
|
|
|
|
savescale = cifReadScale1;
|
|
if (!CIFParsePoint(&path.cifp_point, iscale))
|
|
{
|
|
CIFFreePath(pathheadp);
|
|
return NULL;
|
|
}
|
|
if (savescale != cifReadScale1)
|
|
{
|
|
CIFPath *phead = pathheadp;
|
|
int newscale = cifReadScale1 / savescale;
|
|
while (phead != NULL)
|
|
{
|
|
phead->cifp_x *= newscale;
|
|
phead->cifp_y *= newscale;
|
|
phead = phead->cifp_next;
|
|
}
|
|
}
|
|
newpathp = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
|
|
*newpathp = path;
|
|
if (pathheadp)
|
|
{
|
|
/*
|
|
* Check that this segment is Manhattan. If not, remember the
|
|
* fact and later introduce extra stair-steps to make the path
|
|
* Manhattan. We don't do the stair-step introduction here for
|
|
* two reasons: first, the same code is also used by the Calma
|
|
* module, and second, it is important to know which side of
|
|
* the polygon is the outside when generating the stair steps.
|
|
*/
|
|
if (pathtailp->cifp_x != newpathp->cifp_x
|
|
&& pathtailp->cifp_y != (newpathp->cifp_y))
|
|
{
|
|
nonManhattan = TRUE;
|
|
}
|
|
pathtailp->cifp_next = newpathp;
|
|
}
|
|
else pathheadp = newpathp;
|
|
pathtailp = newpathp;
|
|
}
|
|
return pathheadp;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* test_insideness --
|
|
*
|
|
* Determine if a point is inside a rectangle defined by the
|
|
* first three points in the given CIF path.
|
|
*
|
|
* Results:
|
|
* TRUE if point is inside, FALSE if outside or on the border
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
test_insideness(
|
|
CIFPath *start,
|
|
Point *tpoint)
|
|
{
|
|
Rect tmprect, irect;
|
|
|
|
tmprect.r_xbot = start->cifp_x;
|
|
tmprect.r_ybot = start->cifp_y;
|
|
tmprect.r_xtop = start->cifp_next->cifp_next->cifp_x;
|
|
tmprect.r_ytop = start->cifp_next->cifp_next->cifp_y;
|
|
|
|
GeoCanonicalRect(&tmprect, &irect);
|
|
|
|
return ((tpoint->p_x > irect.r_xbot)
|
|
&& (tpoint->p_x < irect.r_xtop)
|
|
&& (tpoint->p_y > irect.r_ybot)
|
|
&& (tpoint->p_y < irect.r_ytop)) ? TRUE : FALSE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* seg_intersect --
|
|
*
|
|
* Determine if two line segments intersect or touch
|
|
* Expects first line to be manhattan.
|
|
*
|
|
* Results:
|
|
* returns TRUE if segments intersect, FALSE otherwise
|
|
*
|
|
* Side effects:
|
|
* value of respt contains point to which segment will be
|
|
* truncated.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
seg_intersect(
|
|
CIFPath *tstart,
|
|
Point *bf,
|
|
Point *bs,
|
|
Point *respt)
|
|
{
|
|
int afx = tstart->cifp_x;
|
|
int afy = tstart->cifp_y;
|
|
int asx = tstart->cifp_next->cifp_x;
|
|
int asy = tstart->cifp_next->cifp_y;
|
|
int adx, ady;
|
|
|
|
if (afx == asx) /* "a" is a vertical line */
|
|
{
|
|
adx = afx + ((tstart->cifp_next->cifp_next->cifp_x > afx) ? 1 : -1);
|
|
|
|
/* Ignore if b does not cross the x boundary of ad */
|
|
if ((bf->p_x > adx && bs->p_x > adx) ||
|
|
(bf->p_x < adx && bs->p_x < adx))
|
|
return FALSE;
|
|
|
|
if (bs->p_x == bf->p_x) /* nonintersecting vertical lines */
|
|
return FALSE;
|
|
|
|
respt->p_x = afx;
|
|
respt->p_y = bf->p_y + (int)
|
|
(((dlong)(bs->p_y - bf->p_y) * (dlong)(afx - bf->p_x)) /
|
|
(dlong)(bs->p_x - bf->p_x));
|
|
if (((respt->p_y > afy) && (respt->p_y < asy)) ||
|
|
((respt->p_y < afy) && (respt->p_y > asy)))
|
|
return TRUE;
|
|
}
|
|
else /* (afy == asy), "a" is a horizontal line */
|
|
{
|
|
ady = afy + ((tstart->cifp_next->cifp_next->cifp_y > afy) ? 1 : -1);
|
|
|
|
/* Ignore if b does not cross the y boundary of ad */
|
|
if ((bf->p_y > ady && bs->p_y > ady) ||
|
|
(bf->p_y < ady && bs->p_y < ady))
|
|
return FALSE;
|
|
|
|
if (bs->p_y == bf->p_y) /* nonintersecting horizontal lines */
|
|
return FALSE;
|
|
|
|
respt->p_y = afy;
|
|
respt->p_x = bf->p_x + (int)
|
|
(((dlong)(bs->p_x - bf->p_x) * (dlong)(afy - bf->p_y)) /
|
|
(dlong)(bs->p_y - bf->p_y));
|
|
if (((respt->p_x > afx) && (respt->p_x < asx)) ||
|
|
((respt->p_x < afx) && (respt->p_x > asx)))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* path_intersect --
|
|
*
|
|
* Determine if a path intersects the given line segment.
|
|
* A path sharing a portion of the segment is not an intersection.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
path_intersect(
|
|
CIFPath *pathHead,
|
|
CIFPath *start,
|
|
Point *respt)
|
|
{
|
|
CIFPath *path, *segcrossed, *new;
|
|
Point tmppt;
|
|
bool does_cross = FALSE, diagonal = FALSE;
|
|
int tdist, newdist;
|
|
|
|
tdist = newdist = INFINITY;
|
|
for (path = pathHead; path->cifp_next; path = path->cifp_next)
|
|
{
|
|
/* don't compare with self */
|
|
if (path == start || path == start->cifp_next) continue;
|
|
|
|
/* Does the path intersect the first line of the */
|
|
/* right triangle, continuing in the direction of */
|
|
/* the last point on the triangle? */
|
|
|
|
if (seg_intersect(start, &path->cifp_point,
|
|
&path->cifp_next->cifp_point, &tmppt))
|
|
{
|
|
newdist = (start->cifp_x - tmppt.p_x) + (start->cifp_y - tmppt.p_y);
|
|
diagonal = TRUE;
|
|
}
|
|
|
|
/* Is the point inside the triangle, and the path is Manhattan? */
|
|
/* (Note that *both* tests can be true, in which case the one */
|
|
/* with the smaller absolute distance takes precedence.) */
|
|
|
|
if (test_insideness(start, &path->cifp_point)) {
|
|
int tmpdist = abs(newdist); /* save this value */
|
|
if (path->cifp_x == path->cifp_next->cifp_x ||
|
|
path->cifp_y == path->cifp_next->cifp_y)
|
|
{
|
|
if (start->cifp_x == start->cifp_next->cifp_x)
|
|
{
|
|
newdist = path->cifp_y - start->cifp_y;
|
|
if (abs(newdist) < tmpdist)
|
|
{
|
|
tmppt.p_x = start->cifp_x;
|
|
tmppt.p_y = path->cifp_y;
|
|
diagonal = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newdist = path->cifp_x - start->cifp_x;
|
|
if (abs(newdist) < tmpdist)
|
|
{
|
|
tmppt.p_y = start->cifp_y;
|
|
tmppt.p_x = path->cifp_x;
|
|
diagonal = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else if (diagonal == FALSE)
|
|
continue;
|
|
}
|
|
else if (diagonal == FALSE)
|
|
continue;
|
|
|
|
newdist = abs(newdist);
|
|
if ((!does_cross) || (newdist < tdist))
|
|
{
|
|
does_cross = TRUE;
|
|
respt->p_x = tmppt.p_x;
|
|
respt->p_y = tmppt.p_y;
|
|
tdist = newdist;
|
|
segcrossed = (diagonal) ? path : NULL;
|
|
}
|
|
}
|
|
|
|
/* If we're limited by another side of the polygon, then we're */
|
|
/* guaranteed that we'll have to add another point there. By */
|
|
/* doing it here, we avoid problems due to roundoff errors. */
|
|
|
|
if (does_cross && segcrossed)
|
|
{
|
|
new = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
|
|
new->cifp_next = segcrossed->cifp_next;
|
|
segcrossed->cifp_next = new;
|
|
new->cifp_x = respt->p_x;
|
|
new->cifp_y = respt->p_y;
|
|
}
|
|
return does_cross;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* is_clockwise --
|
|
*
|
|
* Determine if a path is clockwise or counterclockwise.
|
|
*
|
|
* Results:
|
|
* TRUE if the path is clockwise, FALSE otherwise.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
is_clockwise(
|
|
CIFPath *pathHead)
|
|
{
|
|
CIFPath *path, *midx = NULL, *last;
|
|
Point *p1, *p2, *p3;
|
|
dlong sdir;
|
|
int minx = INFINITY;
|
|
|
|
/* Find out if this is a clockwise or counterclockwise path by */
|
|
/* finding the (a) leftmost point and assuming the polygon to fill */
|
|
/* is to the right. */
|
|
|
|
for (path = pathHead; path->cifp_next; path = path->cifp_next)
|
|
{
|
|
if (path->cifp_next->cifp_x < minx)
|
|
{
|
|
minx = path->cifp_next->cifp_x;
|
|
midx = path->cifp_next;
|
|
last = path;
|
|
}
|
|
}
|
|
|
|
if (!midx) return TRUE; /* one-point polygon? */
|
|
|
|
/* Rare case of colinear points (implies degenerate polygon) requires */
|
|
/* moving along pointlist until points are not colinear and repeating */
|
|
/* the search for the minimum. */
|
|
|
|
if (last->cifp_x == midx->cifp_x)
|
|
{
|
|
for (path = pathHead; path && path->cifp_x == minx;
|
|
path = path->cifp_next);
|
|
if (!path) return TRUE; /* completely degenerate; direc. irrelevant */
|
|
minx = INFINITY;
|
|
for (; path->cifp_next; path = path->cifp_next)
|
|
{
|
|
if (path->cifp_next->cifp_x < minx)
|
|
{
|
|
minx = path->cifp_next->cifp_x;
|
|
midx = path->cifp_next;
|
|
last = path;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(midx->cifp_next)) midx = pathHead;
|
|
|
|
/* p2 is the (a) leftmost point; p1 and p3 are the points before */
|
|
/* and after in the CIF path, respectively. */
|
|
|
|
p1 = &(last->cifp_point);
|
|
p2 = &(midx->cifp_point);
|
|
p3 = &(midx->cifp_next->cifp_point);
|
|
|
|
/* Find which side p3 falls on relative to the line p1-p2. This */
|
|
/* determines whether the path is clockwise or counterclockwise. */
|
|
/* Use type dlong to avoid integer overflow. */
|
|
|
|
sdir = ((dlong)(p2->p_x - p1->p_x) * (dlong)(p3->p_y - p1->p_y) -
|
|
(dlong)(p2->p_y - p1->p_y) * (dlong)(p3->p_x - p1->p_x));
|
|
|
|
return (sdir < 0) ? TRUE : FALSE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFMakeManhattanPath --
|
|
*
|
|
* Convert a non-Manhattan path into a Manhattan one by
|
|
* breaking out triangles and leaving all Manhattan edges.
|
|
* Additional points are added which reroute the CIF path
|
|
* around the triangle. In the simplest case, each non-Manhattan
|
|
* edge becomes a split tile bounding the edge endpoints.
|
|
* However, if that split tile would extend beyond the boundary
|
|
* of the CIF path, the edge is subdivided into as many
|
|
* triangles as are necessary to complete the path while remaining
|
|
* within the polygon boundary. Unfortunately, for non-45-degree
|
|
* edges, the edge subdivision might not fall on an integer lambda
|
|
* value, so the resulting edge could be off by as much as 1/2
|
|
* lambda. In this case, flag a warning.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* May insert additional points in the path.
|
|
* May alter the intended geometry of a non-manhattan edge by as
|
|
* much as 1/2 lambda.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
CIFMakeManhattanPath(
|
|
CIFPath *pathHead,
|
|
Plane *plane,
|
|
const PaintResultType *resultTbl,
|
|
PaintUndoInfo *ui)
|
|
{
|
|
CIFPath *new, *new2, *next, *path;
|
|
int xinit, xdiff, xincr, xlast, x;
|
|
int yinit, ydiff, yincr, ylast, y;
|
|
|
|
bool clockwise;
|
|
CIFPath *first, *last;
|
|
Rect tt, tr;
|
|
TileType type;
|
|
|
|
clockwise = is_clockwise(pathHead);
|
|
|
|
for (path = pathHead; path->cifp_next; path = path->cifp_next)
|
|
{
|
|
Point clipbase;
|
|
int edir;
|
|
next = path->cifp_next;
|
|
|
|
/* No work if this segment is Manhattan */
|
|
if (path->cifp_x == next->cifp_x || path->cifp_y == next->cifp_y)
|
|
continue;
|
|
|
|
/* Otherwise, break out the triangle, then adjust as necessary */
|
|
|
|
new = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
|
|
path->cifp_next = new;
|
|
new->cifp_next = next;
|
|
|
|
/* Generate split tiles as necessary to reach next->cifp_y */
|
|
|
|
if (clockwise)
|
|
{
|
|
first = next;
|
|
last = path;
|
|
}
|
|
else
|
|
{
|
|
first = path;
|
|
last = next;
|
|
}
|
|
edir = CIFEdgeDirection(first, last);
|
|
if (edir == CIF_DIAG_DL || edir == CIF_DIAG_UR)
|
|
{
|
|
new->cifp_x = first->cifp_x;
|
|
new->cifp_y = last->cifp_y;
|
|
}
|
|
else /* edir == CIF_DIAG_DR || edir == CIF_DIAG_UL */
|
|
{
|
|
new->cifp_x = last->cifp_x;
|
|
new->cifp_y = first->cifp_y;
|
|
}
|
|
|
|
/* Check if the segment from first to base intersects */
|
|
/* the polygon edge */
|
|
|
|
if (path_intersect(pathHead, path, &clipbase))
|
|
{
|
|
new->cifp_x = clipbase.p_x;
|
|
new->cifp_y = clipbase.p_y;
|
|
|
|
new2 = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
|
|
new->cifp_next = new2;
|
|
new2->cifp_next = next;
|
|
|
|
/* Use double long for the multiplication and */
|
|
/* division, or else integer overflow can occur. */
|
|
|
|
if (path->cifp_x == new->cifp_x) /* vertical line */
|
|
{
|
|
new2->cifp_y = new->cifp_y;
|
|
new2->cifp_x = path->cifp_x + (int)
|
|
((dlong)(new2->cifp_y - path->cifp_y)
|
|
* (dlong)(next->cifp_x - path->cifp_x)
|
|
/ (dlong)(next->cifp_y - path->cifp_y));
|
|
}
|
|
else
|
|
{
|
|
new2->cifp_x = new->cifp_x;
|
|
new2->cifp_y = path->cifp_y + (int)
|
|
((dlong)(new2->cifp_x - path->cifp_x)
|
|
* (dlong)(next->cifp_y - path->cifp_y)
|
|
/ (dlong)(next->cifp_x - path->cifp_x));
|
|
}
|
|
}
|
|
|
|
/* Break out the diagonal tile from the polygon and paint it. */
|
|
|
|
type = (edir == CIF_DIAG_UR || edir == CIF_DIAG_UL) ? 0 : TT_SIDE;
|
|
type |= (edir == CIF_DIAG_UR || edir == CIF_DIAG_DL) ? 0 : TT_DIRECTION;
|
|
type |= TT_DIAGONAL;
|
|
|
|
tt.r_ll = path->cifp_point;
|
|
tt.r_ur = path->cifp_next->cifp_next->cifp_point;
|
|
GeoCanonicalRect(&tt, &tr);
|
|
|
|
// TxPrintf("CIF read: Triangle %s %c at (%d, %d) plane %x\n",
|
|
// (type & TT_SIDE) ? "right" : "left", (type & TT_DIRECTION)
|
|
// ? '\\' : '/', tt.r_xbot, tt.r_ybot, plane);
|
|
|
|
/* Final check---ensure that rectangle is not degenerate */
|
|
|
|
if (plane && (tr.r_xtop - tr.r_xbot > 0) && (tr.r_ytop - tr.r_ybot > 0))
|
|
{
|
|
DBNMPaintPlane(plane, type, &tr, resultTbl, ui);
|
|
GEO_EXPAND(&tr, 1, &tr);
|
|
DBMergeNMTiles(plane, &tr, ui);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFEdgeDirection --
|
|
*
|
|
* This procedure assigns a direction to the given edge.
|
|
*
|
|
* Results:
|
|
* CIF_ZERO if the two points are the same
|
|
* CIF_LEFT if the edge goes left
|
|
* CIF_UP if the edge goes up
|
|
* CIF_RIGHT if the edge goes right
|
|
* CIF_DOWN if the edge goes down
|
|
* CIF_DIAG if the edge is non-manhattan
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
CIFEdgeDirection(
|
|
CIFPath *first,
|
|
CIFPath *last) /* Edge to be categorized. */
|
|
{
|
|
|
|
if (first->cifp_x < last->cifp_x)
|
|
{
|
|
if (first->cifp_y < last->cifp_y)
|
|
return CIF_DIAG_UR;
|
|
if (first->cifp_y > last->cifp_y)
|
|
return CIF_DIAG_DR;
|
|
return CIF_RIGHT;
|
|
}
|
|
if (first->cifp_x > last->cifp_x)
|
|
{
|
|
if (first->cifp_y < last->cifp_y)
|
|
return CIF_DIAG_UL;
|
|
if (first->cifp_y > last->cifp_y)
|
|
return CIF_DIAG_DL;
|
|
return CIF_LEFT;
|
|
}
|
|
if (first->cifp_y < last->cifp_y)
|
|
return CIF_UP;
|
|
if (first->cifp_y > last->cifp_y)
|
|
return CIF_DOWN;
|
|
return CIF_ZERO;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFCleanPath --
|
|
*
|
|
* Removes a edge in a path if it has zero length (repeated points).
|
|
* Combines two consecutive edges if their direction is the same,
|
|
* and their direction is manhattan.
|
|
* CIFCleanPath assumes that the path is closed, and will eliminate
|
|
* the last edge if its direction is the same as the first.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* May delete points in the path.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
CIFCleanPath(
|
|
CIFPath *pathHead)
|
|
{
|
|
CIFPath *next, *path, *prev, *last;
|
|
int dir1, dir2;
|
|
|
|
if (!pathHead) return;
|
|
prev = pathHead;
|
|
path = prev->cifp_next;
|
|
if (!path) return;
|
|
while((dir1 = CIFEdgeDirection(prev, path)) == CIF_ZERO)
|
|
{
|
|
/* This is a repeated point. */
|
|
next = path->cifp_next;
|
|
prev->cifp_next = next;
|
|
freeMagic((char *) path);
|
|
path = next;
|
|
if (!path) return;
|
|
}
|
|
|
|
while ((next = path->cifp_next))
|
|
{
|
|
if ((dir2 = CIFEdgeDirection(path, next)) == CIF_ZERO)
|
|
{
|
|
/* This is a repeated point. */
|
|
path->cifp_next = next->cifp_next;
|
|
freeMagic((char *) next);
|
|
continue;
|
|
}
|
|
|
|
/* Skip any non-manhattan (diagonal) edges. */
|
|
if (dir2 >= CIF_DIAG)
|
|
goto path_inc;
|
|
|
|
if (dir1 == dir2)
|
|
{
|
|
/* The middle point must go. */
|
|
prev->cifp_next = next;
|
|
freeMagic((char *) path);
|
|
path = next;
|
|
dir1 = CIFEdgeDirection(prev, path);
|
|
continue;
|
|
}
|
|
path_inc:
|
|
dir1 = dir2;
|
|
prev = path;
|
|
path = next;
|
|
}
|
|
|
|
/* Ensure that the path has more than one point. */
|
|
if (!pathHead->cifp_next)
|
|
{
|
|
/* Ensure that the resulting path is closed. */
|
|
if ((pathHead->cifp_x != path->cifp_x) ||
|
|
(pathHead->cifp_y != path->cifp_y))
|
|
{
|
|
next = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
|
|
next->cifp_x = pathHead->cifp_x;
|
|
next->cifp_y = pathHead->cifp_y;
|
|
next->cifp_next = (CIFPath *) 0;
|
|
path->cifp_next = next;
|
|
prev = path;
|
|
path = next;
|
|
dir1 = CIFEdgeDirection(prev, path);
|
|
}
|
|
if ((dir2 = CIFEdgeDirection(pathHead, pathHead->cifp_next)) <
|
|
CIF_DIAG)
|
|
{
|
|
/* We have at least two edges in the path. We have to */
|
|
/* fix the first edge and eliminate the last edge if */
|
|
/* the first and last edge have the same direction. */
|
|
if (dir1 == dir2)
|
|
{
|
|
pathHead->cifp_x = prev->cifp_x;
|
|
pathHead->cifp_y = prev->cifp_y;
|
|
prev->cifp_next = (CIFPath *) 0;
|
|
freeMagic((char *) path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFFreePath --
|
|
*
|
|
* This procedure frees up a path once it has been used.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* All the elements of path are returned to the storage allocator.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
CIFFreePath(
|
|
CIFPath *path) /* Path to be freed. */
|
|
{
|
|
while (path != NULL)
|
|
{
|
|
freeMagic((char *) path);
|
|
path = path->cifp_next;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cifCommandError --
|
|
*
|
|
* This procedure is called when unknown CIF commands are found
|
|
* in CIF files. It skips the command and advances to the next
|
|
* command.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
cifCommandError(void)
|
|
{
|
|
CIFReadError("unknown command `%c'; ignored.\n" , PEEK());
|
|
CIFSkipToSemi();
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cifParseEnd --
|
|
*
|
|
* This procedure processes the "end" statement in a CIF file
|
|
* (it ignores it).
|
|
*
|
|
* Results:
|
|
* Always returns TRUE.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
cifParseEnd(void)
|
|
{
|
|
TAKE();
|
|
CIFSkipBlanks();
|
|
if (PEEK() != EOF)
|
|
{
|
|
CIFReadError("End command isn't at end of file.\n");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cifParseComment --
|
|
*
|
|
* This command skips over user comments in CIF files.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
cifParseComment(void)
|
|
{
|
|
int opens;
|
|
int ch;
|
|
|
|
/*
|
|
* take the '('
|
|
*/
|
|
TAKE();
|
|
opens = 1;
|
|
do
|
|
{
|
|
ch = TAKE();
|
|
if (ch == '(')
|
|
opens++;
|
|
else if (ch == ')')
|
|
opens--;
|
|
else if (ch == '\n')
|
|
{
|
|
cifLineNumber++;
|
|
}
|
|
else if (ch == EOF)
|
|
{
|
|
CIFReadError("(comment) extends to end of file.\n");
|
|
return FALSE;
|
|
}
|
|
} while (opens > 0);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFDirectionToTrans --
|
|
*
|
|
* This procedure is used to convert from a direction vector
|
|
* to a Magic transformation. The direction vector is a point
|
|
* giving a direction from the origin. It better be along
|
|
* one of the axes.
|
|
*
|
|
* Results:
|
|
* The return value is the transformation corresponding to
|
|
* the direction, or the identity transform if the direction
|
|
* isn't along one of the axes.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
const Transform *
|
|
CIFDirectionToTrans(
|
|
const Point *point) /* Direction vector from origin. */
|
|
{
|
|
if ((point->p_x != 0) && (point->p_y == 0))
|
|
{
|
|
if (point->p_x > 0)
|
|
return &GeoIdentityTransform;
|
|
else return &Geo180Transform;
|
|
}
|
|
else if ((point->p_y != 0) && (point->p_x == 0))
|
|
{
|
|
if (point->p_y > 0)
|
|
return &Geo270Transform;
|
|
else return &Geo90Transform;
|
|
}
|
|
CIFReadError("non-manhattan direction vector (%d, %d); ignored.\n",
|
|
point->p_x, point->p_y);
|
|
return &GeoIdentityTransform;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFParseTransform --
|
|
*
|
|
* This procedure is called to read in a transform from a
|
|
* CIF file.
|
|
*
|
|
* Results:
|
|
* TRUE is returned if the parse completed successfully, and
|
|
* FALSE is returned otherwise.
|
|
*
|
|
* Side effects:
|
|
* The parameter pointed to by transformp is modified to
|
|
* contain the transform indicated by the CIF file.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
CIFParseTransform(
|
|
Transform *transformp)
|
|
{
|
|
char ch;
|
|
Point point;
|
|
Transform tmp;
|
|
int savescale;
|
|
|
|
*transformp = GeoIdentityTransform;
|
|
CIFSkipBlanks();
|
|
for (ch = PEEK() ; ch != ';' ; ch = PEEK())
|
|
{
|
|
switch (ch)
|
|
{
|
|
case 'T':
|
|
TAKE();
|
|
if (!CIFParsePoint(&point, 1))
|
|
{
|
|
CIFReadError("translation, but no point.\n");
|
|
CIFSkipToSemi();
|
|
return FALSE;
|
|
}
|
|
GeoTranslateTrans(transformp, point.p_x, point.p_y, &tmp);
|
|
*transformp = tmp;
|
|
break;
|
|
case 'M':
|
|
TAKE();
|
|
CIFSkipBlanks();
|
|
ch = PEEK();
|
|
if (ch == 'X')
|
|
GeoTransTrans(transformp, &GeoSidewaysTransform, &tmp);
|
|
else if (ch == 'Y')
|
|
GeoTransTrans(transformp, &GeoUpsideDownTransform,
|
|
&tmp);
|
|
else
|
|
{
|
|
CIFReadError("mirror, but not in X or Y.\n");
|
|
CIFSkipToSemi();
|
|
return FALSE;
|
|
}
|
|
TAKE();
|
|
*transformp = tmp;
|
|
break;
|
|
case 'R':
|
|
TAKE();
|
|
if (!CIFParseSInteger(&point.p_x) ||
|
|
!CIFParseSInteger(&point.p_y))
|
|
{
|
|
CIFReadError("rotation, but no direction.\n");
|
|
CIFSkipToSemi();
|
|
return FALSE;
|
|
}
|
|
GeoTransTrans(transformp, CIFDirectionToTrans(&point),
|
|
&tmp);
|
|
*transformp = tmp;
|
|
break;
|
|
default:
|
|
CIFReadError("transformation expected.\n");
|
|
CIFSkipToSemi();
|
|
return FALSE;
|
|
}
|
|
CIFSkipBlanks();
|
|
}
|
|
|
|
/* Before returning, we must scale the transform into Magic units. */
|
|
|
|
transformp->t_c = CIFScaleCoord(transformp->t_c, COORD_EXACT);
|
|
savescale = cifCurReadStyle->crs_scaleFactor;
|
|
transformp->t_f = CIFScaleCoord(transformp->t_f, COORD_EXACT);
|
|
if (savescale != cifCurReadStyle->crs_scaleFactor)
|
|
transformp->t_c *= (savescale / cifCurReadStyle->crs_scaleFactor);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CIFParseCommand --
|
|
*
|
|
* Parse one CIF command and farm it out to a routine to handle
|
|
* that command.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* May modify the contents of cifReadCellDef by painting or adding
|
|
* new uses or labels. May also create new CellDefs.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
CIFReadFile(
|
|
FILE *file) /* File from which to read CIF. */
|
|
{
|
|
/* We will use 1-word CIF numbers as keys in this hash table */
|
|
CIFReadCellInit(1);
|
|
|
|
if (cifCurReadStyle == NULL)
|
|
{
|
|
TxError("Don't know how to read CIF: nothing in tech file.\n");
|
|
return;
|
|
}
|
|
TxPrintf("Warning: CIF reading is not undoable! I hope that's OK.\n");
|
|
UndoDisable();
|
|
|
|
cifTotalWarnings = 0;
|
|
cifTotalErrors = 0;
|
|
CifPolygonCount = 0;
|
|
cifSeenSnapWarning = FALSE;
|
|
|
|
cifInputFile = file;
|
|
cifReadScale1 = 1;
|
|
cifReadScale2 = 1;
|
|
cifParseLaAvail = FALSE;
|
|
cifLineNumber = 1;
|
|
cifReadPlane = (Plane *) NULL;
|
|
cifCurLabelType = TT_SPACE;
|
|
while (PEEK() != EOF)
|
|
{
|
|
if (SigInterruptPending) goto done;
|
|
CIFSkipBlanks();
|
|
switch (PEEK())
|
|
{
|
|
case EOF:
|
|
break;
|
|
case ';':
|
|
break;
|
|
case 'B':
|
|
(void) CIFParseBox();
|
|
break;
|
|
case 'C':
|
|
(void) CIFParseCall();
|
|
break;
|
|
case 'D':
|
|
TAKE();
|
|
CIFSkipBlanks();
|
|
switch (PEEK())
|
|
{
|
|
case 'D':
|
|
(void) CIFParseDelete();
|
|
break;
|
|
case 'F':
|
|
(void) CIFParseFinish();
|
|
break;
|
|
case 'S':
|
|
(void) CIFParseStart();
|
|
break;
|
|
default:
|
|
cifCommandError();
|
|
break;
|
|
}
|
|
break;
|
|
case 'E':
|
|
(void) cifParseEnd();
|
|
goto done;
|
|
case 'L':
|
|
(void) CIFParseLayer();
|
|
break;
|
|
case 'P':
|
|
(void) CIFParsePoly();
|
|
break;
|
|
case 'R':
|
|
(void) CIFParseFlash();
|
|
break;
|
|
case 'W':
|
|
(void) CIFParseWire();
|
|
break;
|
|
case '(':
|
|
(void) cifParseComment();
|
|
break;
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
(void) CIFParseUser();
|
|
break;
|
|
default:
|
|
cifCommandError();
|
|
break;
|
|
}
|
|
CIFSkipSemi();
|
|
}
|
|
|
|
CIFReadError("no \"End\" statement.\n");
|
|
|
|
done:
|
|
CIFReadCellCleanup(FILE_CIF);
|
|
UndoEnable();
|
|
}
|