/* 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[] = "$Header: /ufs/repository/magic/cif/CIFrdutils.c,v 1.6 2001/06/15 17:53:20 tim Exp $"; #endif #include #include #include "misc/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 "textio/textio.h" #include "signals/signals.h" #include "undo/undo.h" #include "utils/malloc.h" #define CIF_ZERO 0 #define CIF_LEFT 1 #define CIF_UP 2 #define CIF_RIGHT 3 #define CIF_DOWN 4 #define CIF_DIAG 5 /* Specific diagonal directions */ #define CIF_DIAG_UL 5 #define CIF_DIAG_UR 6 #define CIF_DIAG_DL 7 #define CIF_DIAG_DR 8 /* 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; int cifLineNumber; /* Number of current line. */ /* 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. */ 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. * * Note: * You can add more arguments if three turns out not to be enough. * * ---------------------------------------------------------------------------- */ /* VARARGS1 */ void CIFReadError(format, arg1, arg2, arg3) char *format; /* Text format string. */ char *arg1, *arg2, *arg3; /* Additional arguments, if needed. */ { TxError("Error at line %d of CIF file: ", cifLineNumber); TxError(format, arg1, arg2, arg3); } /* * ---------------------------------------------------------------------------- * * CIFgcf * * Your basic greatest-common-factor routine. * * ---------------------------------------------------------------------------- */ int CIFgcf(a, b) int a, b; { register int mod; if ((mod = a % b) == 0) return (b); else return (CIFgcf(b, mod)); } /* * ---------------------------------------------------------------------------- * * CIFScaleCoord * * This procedure does rounding and division to convert from * CIF units back into Magic units. * * Results: * The result is the Magic unit equivalent to cifCoord. * * Side effects: * None. * * ---------------------------------------------------------------------------- */ int CIFScaleCoord(cifCoord) int cifCoord; /* A coordinate in CIF units. */ { int result, scale, remain; scale = cifCurReadStyle->crs_scaleFactor; /* 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; /* Check for non-integer result and warn of fractional-lambda violation */ if ((remain = cifCoord % scale) != 0) { int lgcf = CIFgcf(cifCoord, scale); remain /= lgcf; scale /= lgcf; CIFReadError("Input off lambda grid by %d/%d; snapped to grid.\n", abs(remain), abs(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. Coordinate is *not* divided by cifReadScale2 * if boolean "norm" is True. This allows correct parsing of * off-lambda coordinates on boxes where the final result of all * transformations falls on the lambda grid. * * ---------------------------------------------------------------------------- */ bool CIFParsePoint(pointp, iscale) Point *pointp; int iscale; { pointp->p_x = 0; pointp->p_y = 0; if (!CIFParseSInteger(&pointp->p_x)) return FALSE; pointp->p_x = (pointp->p_x * cifReadScale1 * iscale * cifCurReadStyle->crs_divider) / cifReadScale2; if (!CIFParseSInteger(&pointp->p_y)) return FALSE; pointp->p_y = (pointp->p_y * cifReadScale1 * iscale * cifCurReadStyle->crs_divider) / cifReadScale2; return TRUE; } /* * ---------------------------------------------------------------------------- * * CIFParsePath -- * * This procedure parses a CIF path, which is sequence of * one or more points. * * If the path is non-Manhattan, we introduce additional * stair-steps a minimum of cifCurReadStyle->crs_scaleFactor * high and wide along each non-Manhattan segment. * * 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; { register CIFPath *pathtailp, *newpathp; bool nonManhattan = FALSE; CIFPath path; *pathheadpp = NULL; pathtailp = NULL; path.cifp_next = NULL; while (TRUE) { CIFSkipSep(); if (PEEK() == ';') break; if (!CIFParsePoint(&path.cifp_point, iscale)) { CIFFreePath(*pathheadpp); return FALSE; } MALLOC(CIFPath *, newpathp, sizeof (CIFPath)); *newpathp = path; if (*pathheadpp) { #ifndef NONMANHATTAN /* * 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)) { if (!nonManhattan) CIFReadError("non-Manhattan path; using stairstep.\n"); nonManhattan = TRUE; } #endif pathtailp->cifp_next = newpathp; } else *pathheadpp = newpathp; pathtailp = newpathp; } #ifndef NONMANHATTAN if (nonManhattan) CIFMakeManhattanPath(*pathheadpp); #endif return (*pathheadpp != NULL); } #ifdef NONMANHATTAN /* * ---------------------------------------------------------------------------- * * 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 1 if segments intersect, 0 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 + ((bs->p_y - bf->p_y) * (afx - bf->p_x)) / (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 + ((bs->p_x - bf->p_x) * (afy - bf->p_y)) / (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; 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; } /* Otherwise, is the point inside the triangle (path must */ /* be Manhattan in this case, unless the path crosses itself, */ /* which would be extremely bad CIF to begin with). */ else if (test_insideness(start, &path->cifp_point)) { if (start->cifp_x == start->cifp_next->cifp_x) { newdist = path->cifp_y - start->cifp_y; tmppt.p_x = start->cifp_x; tmppt.p_y = path->cifp_y; } else { newdist = path->cifp_x - start->cifp_x; tmppt.p_y = start->cifp_y; tmppt.p_x = path->cifp_x; } diagonal = FALSE; } else 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) { MALLOC(CIFPath *, new, 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, *last; Point *p1, *p2, *p3; long 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->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. */ sdir = ((p2->p_x - p1->p_x) * (p3->p_y - p1->p_y) - (p2->p_y - p1->p_y) * (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) CIFPath *pathHead; Plane *plane; { register 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 */ MALLOC(CIFPath *, new, 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_DR || edir == CIF_DIAG_UL) { new->cifp_x = first->cifp_x; new->cifp_y = last->cifp_y; } else /* edir == CIF_DIAG_UR || edir == CIF_DIAG_DL */ { 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; MALLOC(CIFPath *, new2, sizeof (CIFPath)); new->cifp_next = new2; new2->cifp_next = next; if (path->cifp_x == new->cifp_x) /* vertical line */ { new2->cifp_y = new->cifp_y; new2->cifp_x = path->cifp_x + (new2->cifp_y - path->cifp_y) * (next->cifp_x - path->cifp_x) / (next->cifp_y - path->cifp_y); } else { new2->cifp_x = new->cifp_x; new2->cifp_y = path->cifp_y + (new2->cifp_x - path->cifp_x) * (next->cifp_y - path->cifp_y) / (next->cifp_x - path->cifp_x); } } /* Break out the diagonal tile from the polygon and paint it. */ type = (edir == CIF_DIAG_UL || edir == CIF_DIAG_DL) ? 0 : TT_SIDE; type |= (edir == CIF_DIAG_UL || edir == CIF_DIAG_DR) ? 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); */ if (plane) DBNMPaintPlane(plane, type, &tr, CIFPaintTable, (PaintUndoInfo *)NULL); } } #else /* * ---------------------------------------------------------------------------- * * CIFMakeManhattanPath -- * * Convert a non-Manhattan path into a Manhattan one by adding * additional points. These points are added using a simple * scan-conversion algorithm that generates a series of stair * steps that are at least cifCurReadStyle->crs_scaleFactor * units high and wide (but which may be higher or wider). * * Results: * None. * * Side effects: * May insert additional points in the path. * * ---------------------------------------------------------------------------- */ CIFMakeManhattanPath(pathHead) CIFPath *pathHead; { register CIFPath *new, *next, *path; int xinit, xdiff, xincr, xlast, x; int yinit, ydiff, yincr, ylast, y; for (path = pathHead; path->cifp_next; path = path->cifp_next) { 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; /* * The major loop will be over whichever difference (x or y) * is the SMALLER of the two; for each iteration over the smaller * dimension, we will add the number of units of the larger * dimension per unit of the smaller dimension to the larger * dimension. */ xdiff = next->cifp_x - path->cifp_x; ydiff = next->cifp_y - path->cifp_y; xinit = path->cifp_x; yinit = path->cifp_y; if (ABS(xdiff) > ABS(ydiff)) { /* Iterate over y, stopping before next->cifp_y */ yincr = cifCurReadStyle->crs_scaleFactor; ylast = yinit; if (ydiff < 0) yincr = -yincr; for (y = yinit + yincr, x = xinit; (yincr > 0 && y < next->cifp_y) || (yincr < 0 && y > next->cifp_y); y += yincr) { /* Move by one in y first */ MALLOC(CIFPath *, new, sizeof (CIFPath)); new->cifp_x = x; new->cifp_y = y; path->cifp_next = new; path = new; /* * Now move in x. * Note that ((y - yinit) / ydiff) >= 0 always. * Also, as long as y has not reached next->cifp_y, * this quantity will be < 1, so x will range from * path->cifp_x up to but not reaching next->cifp_x. */ x = xinit + (xdiff * (y - yinit)) / ydiff; MALLOC(CIFPath *, new, sizeof (CIFPath)); new->cifp_x = x; new->cifp_y = y; new->cifp_next = next; path->cifp_next = new; path = new; ylast = y; } /* * The last y processed was short of next->cifp_y. * If x was not yet at next->cifp_x, add one more point * to bridge the gap. */ if (x != next->cifp_x) { MALLOC(CIFPath *, new, sizeof (CIFPath)); new->cifp_x = next->cifp_x; new->cifp_y = ylast;; new->cifp_next = next; path->cifp_next = new; path = new; } } else { /* Iterate over x, stopping before next->cifp_x */ xincr = cifCurReadStyle->crs_scaleFactor; xlast = xinit; if (xdiff < 0) xincr = -xincr; for (x = xinit + xincr, y = yinit; (xincr > 0 && x < next->cifp_x) || (xincr < 0 && x > next->cifp_x); x += xincr) { /* Move by one in x first */ MALLOC(CIFPath *, new, sizeof (CIFPath)); new->cifp_x = x; new->cifp_y = y; path->cifp_next = new; path = new; /* * Now move in y. * Note that ((x - xinit) / xdiff) >= 0 always. * Also, as long as x has not reached next->cifp_x, * this quantity will be < 1, so y will range from * path->cifp_y up to but not reaching next->cifp_y. */ y = yinit + (ydiff * (x - xinit)) / xdiff; MALLOC(CIFPath *, new, sizeof (CIFPath)); new->cifp_x = x; new->cifp_y = y; new->cifp_next = next; path->cifp_next = new; path = new; xlast = x; } /* * The last x processed was short of next->cifp_x. * If y was not yet at next->cifp_y, add one more point * to bridge the gap. */ if (y != next->cifp_y) { MALLOC(CIFPath *, new, sizeof (CIFPath)); new->cifp_x = xlast; new->cifp_y = next->cifp_y; new->cifp_next = next; path->cifp_next = new; path = new; } } } } #endif /* * ---------------------------------------------------------------------------- * * 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_UL; if (first->cifp_y > last->cifp_y) return CIF_DIAG_UR; return CIF_UP; } if (first->cifp_x > last->cifp_x) { if (first->cifp_y < last->cifp_y) return CIF_DIAG_DL; if (first->cifp_y > last->cifp_y) return CIF_DIAG_DR; return CIF_DOWN; } if (first->cifp_y < last->cifp_y) return CIF_RIGHT; if (first->cifp_y > last->cifp_y) return CIF_LEFT; 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; { register 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)) { MALLOC(CIFPath *, next, 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. * * ---------------------------------------------------------------------------- */ 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; *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); transformp->t_f = CIFScaleCoord(transformp->t_f); 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(); 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(); UndoEnable(); }