magic/cif/CIFrdutils.c

1700 lines
42 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 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(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: ", cifLineNumber);
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(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(cifCoord, snap_type)
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);
MZAfterTech();
IRAfterTech();
#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(ch)
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()
{
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()
{
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()
{
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()
{
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(valuep)
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(valuep)
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(pointp, iscale)
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:
* TRUE is returned if the path was parsed successfully,
* FALSE otherwise.
*
* Side effects:
* Modifies the parameter pathheadpp to point to the path
* that is constructed.
*
* 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().
*
* ----------------------------------------------------------------------------
*/
bool
CIFParsePath(pathheadpp, iscale)
CIFPath **pathheadpp;
int iscale;
{
CIFPath *pathtailp, *newpathp;
bool nonManhattan = FALSE; /* diagnostic only */
CIFPath path;
int savescale;
*pathheadpp = NULL;
pathtailp = NULL;
path.cifp_next = NULL;
while (TRUE)
{
CIFSkipSep();
if (PEEK() == ';')
break;
savescale = cifReadScale1;
if (!CIFParsePoint(&path.cifp_point, iscale))
{
CIFFreePath(*pathheadpp);
return FALSE;
}
if (savescale != cifReadScale1)
{
CIFPath *phead = *pathheadpp;
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 (*pathheadpp)
{
/*
* 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 *pathheadpp = newpathp;
pathtailp = newpathp;
}
return (*pathheadpp != NULL);
}
/*
* ----------------------------------------------------------------------------
*
* 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(start, tpoint)
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(tstart, bf, bs, respt)
CIFPath *tstart;
Point *bf, *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(pathHead, start, respt)
CIFPath *pathHead, *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(pathHead)
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(pathHead, plane, resultTbl, ui)
CIFPath *pathHead;
Plane *plane;
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(first, last)
CIFPath *first, *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(pathHead)
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(path)
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()
{
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()
{
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()
{
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.
*
* ----------------------------------------------------------------------------
*/
Transform *
CIFDirectionToTrans(point)
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(transformp)
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; /* 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();
}