/*-------------------------------------------------------------------------*/ /* functions.c */ /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */ /*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/ /* written by Tim Edwards, 8/13/93 */ /*-------------------------------------------------------------------------*/ #include #include #include #include #include #ifndef _MSC_VER #include #include #endif /*-------------------------------------------------------------------------*/ /* Local includes */ /*-------------------------------------------------------------------------*/ #ifdef TCL_WRAPPER #include #endif #include "colordefs.h" #include "xcircuit.h" /*----------------------------------------------------------------------*/ /* Function prototype declarations */ /*----------------------------------------------------------------------*/ #include "prototypes.h" /*-------------------------------------------------------------------------*/ /* External Variable definitions */ /*-------------------------------------------------------------------------*/ extern Display *dpy; extern Pixmap STIPPLE[8]; extern short eventmode; extern Clientdata areastruct; extern Globaldata xobjs; extern int *appcolors; extern short textpos, textend; /*------------------------------------------------------------------------*/ /* find the squared length of a wire (or distance between two points in */ /* user space). */ /*------------------------------------------------------------------------*/ long sqwirelen(XPoint *userpt1, XPoint *userpt2) { long xdist, ydist; xdist = (long)userpt2->x - (long)userpt1->x; ydist = (long)userpt2->y - (long)userpt1->y; return (xdist * xdist + ydist * ydist); } /*------------------------------------------------------------------------*/ /* floating-point version of the above */ /*------------------------------------------------------------------------*/ float fsqwirelen(XfPoint *userpt1, XfPoint *userpt2) { float xdist, ydist; xdist = userpt2->x - userpt1->x; ydist = userpt2->y - userpt1->y; return (xdist * xdist + ydist * ydist); } /*------------------------------------------------------------------------*/ /* Find absolute distance between two points in user space */ /*------------------------------------------------------------------------*/ int wirelength(XPoint *userpt1, XPoint *userpt2) { u_long xdist, ydist; xdist = (long)(userpt2->x) - (long)(userpt1->x); ydist = (long)(userpt2->y) - (long)(userpt1->y); return (int)sqrt((double)(xdist * xdist + ydist * ydist)); } /*------------------------------------------------------------------------*/ /* Find the closest (squared) distance from a point to a line */ /*------------------------------------------------------------------------*/ long finddist(XPoint *linept1, XPoint *linept2, XPoint *userpt) { long a, b, c, frac; float protod; c = sqwirelen(linept1, linept2); a = sqwirelen(linept1, userpt); b = sqwirelen(linept2, userpt); frac = a - b; if (frac >= c) return b; /* "=" is important if c = 0 ! */ else if (-frac >= c) return a; else { protod = (float)(c + a - b); return (a - (long)((protod * protod) / (float)(c << 2))); } } /*------------------------------------------------------------------------*/ /* Calculate points for an arc */ /*------------------------------------------------------------------------*/ void calcarc(arcptr thearc) { short idx; int sarc; float theta, delta; /* assume that angle2 > angle1 always: must be guaranteed by other routines */ sarc = (int)(thearc->angle2 - thearc->angle1) * RSTEPS; thearc->number = (sarc / 360) + 1; if (sarc % 360 != 0) thearc->number++; delta = RADFAC * ((float)(thearc->angle2 - thearc->angle1) / (thearc->number - 1)); theta = thearc->angle1 * RADFAC; for (idx = 0; idx < thearc->number - 1; idx++) { thearc->points[idx].x = (float)thearc->position.x + fabs((float)thearc->radius) * cos(theta); thearc->points[idx].y = (float)thearc->position.y + (float)thearc->yaxis * sin(theta); theta += delta; } /* place last point exactly to avoid roundoff error */ theta = thearc->angle2 * RADFAC; thearc->points[thearc->number - 1].x = (float)thearc->position.x + fabs((float)thearc->radius) * cos(theta); thearc->points[thearc->number - 1].y = (float)thearc->position.y + (float)thearc->yaxis * sin(theta); if (thearc->radius < 0) reversefpoints(thearc->points, thearc->number); } /*------------------------------------------------------------------------*/ /* Create a Bezier curve approximation from control points */ /* (using PostScript formula for Bezier cubic curve) */ /*------------------------------------------------------------------------*/ float par[INTSEGS]; float parsq[INTSEGS]; float parcb[INTSEGS]; void initsplines() { float t; short idx; for (idx = 0; idx < INTSEGS; idx++) { t = (float)(idx + 1) / (INTSEGS + 1); par[idx] = t; parsq[idx] = t * t; parcb[idx] = parsq[idx] * t; } } /*------------------------------------------------------------------------*/ /* Compute spline coefficients */ /*------------------------------------------------------------------------*/ void computecoeffs(splineptr thespline, float *ax, float *bx, float *cx, float *ay, float *by, float *cy) { *cx = 3.0 * (float)(thespline->ctrl[1].x - thespline->ctrl[0].x); *bx = 3.0 * (float)(thespline->ctrl[2].x - thespline->ctrl[1].x) - *cx; *ax = (float)(thespline->ctrl[3].x - thespline->ctrl[0].x) - *cx - *bx; *cy = 3.0 * (float)(thespline->ctrl[1].y - thespline->ctrl[0].y); *by = 3.0 * (float)(thespline->ctrl[2].y - thespline->ctrl[1].y) - *cy; *ay = (float)(thespline->ctrl[3].y - thespline->ctrl[0].y) - *cy - *by; } /*------------------------------------------------------------------------*/ void calcspline(splineptr thespline) { float ax, bx, cx, ay, by, cy; short idx; computecoeffs(thespline, &ax, &bx, &cx, &ay, &by, &cy); for (idx = 0; idx < INTSEGS; idx++) { thespline->points[idx].x = ax * parcb[idx] + bx * parsq[idx] + cx * par[idx] + (float)thespline->ctrl[0].x; thespline->points[idx].y = ay * parcb[idx] + by * parsq[idx] + cy * par[idx] + (float)thespline->ctrl[0].y; } } /*------------------------------------------------------------------------*/ /* Find the (x,y) position and tangent rotation of a point on a spline */ /*------------------------------------------------------------------------*/ void findsplinepos(splineptr thespline, float t, XPoint *retpoint, int *retrot) { float ax, bx, cx, ay, by, cy; float tsq = t * t; float tcb = tsq * t; double dxdt, dydt; computecoeffs(thespline, &ax, &bx, &cx, &ay, &by, &cy); retpoint->x = (short)(ax * tcb + bx * tsq + cx * t + (float)thespline->ctrl[0].x); retpoint->y = (short)(ay * tcb + by * tsq + cy * t + (float)thespline->ctrl[0].y); if (retrot != NULL) { dxdt = (double)(3 * ax * tsq + 2 * bx * t + cx); dydt = (double)(3 * ay * tsq + 2 * by * t + cy); *retrot = (int)(INVRFAC * atan2(dxdt, dydt)); /* reversed y, x */ if (*retrot < 0) *retrot += 360; } } /*------------------------------------------------------------------------*/ /* floating-point version of the above */ /*------------------------------------------------------------------------*/ void ffindsplinepos(splineptr thespline, float t, XfPoint *retpoint) { float ax, bx, cx, ay, by, cy; float tsq = t * t; float tcb = tsq * t; computecoeffs(thespline, &ax, &bx, &cx, &ay, &by, &cy); retpoint->x = ax * tcb + bx * tsq + cx * t + (float)thespline->ctrl[0].x; retpoint->y = ay * tcb + by * tsq + cy * t + (float)thespline->ctrl[0].y; } /*------------------------------------------------------------------------*/ /* Find the closest distance between a point and a spline and return the */ /* fractional distance along the spline of this point. */ /*------------------------------------------------------------------------*/ float findsplinemin(splineptr thespline, XPoint *upoint) { XfPoint *spt, flpt, newspt; float minval = 1000000, tval, hval, ndist; short j, ival; flpt.x = (float)(upoint->x); flpt.y = (float)(upoint->y); /* get estimate from precalculated spline points */ for (spt = thespline->points; spt < thespline->points + INTSEGS; spt++) { ndist = fsqwirelen(spt, &flpt); if (ndist < minval) { minval = ndist; ival = (short)(spt - thespline->points); } } tval = (float)(ival + 1) / (INTSEGS + 1); hval = 0.5 / (INTSEGS + 1); /* short fixed iterative loop to converge on minimum t */ for (j = 0; j < 5; j++) { tval += hval; ffindsplinepos(thespline, tval, &newspt); ndist = fsqwirelen(&newspt, &flpt); if (ndist < minval) minval = ndist; else { tval -= hval * 2; ffindsplinepos(thespline, tval, &newspt); ndist = fsqwirelen(&newspt, &flpt); if (ndist < minval) minval = ndist; else tval += hval; } hval /= 2; } if (tval < 0.1) { if ((float)sqwirelen(&(thespline->ctrl[0]), upoint) < minval) tval = 0; } else if (tval > 0.9) { if ((float)sqwirelen(&(thespline->ctrl[3]), upoint) < minval) tval = 1; } return tval; } /*----------------------------------------------------------------------------*/ /* Find closest point of a polygon to the cursor */ /*----------------------------------------------------------------------------*/ short closepointdistance(polyptr curpoly, XPoint *cursloc, short *mindist) { short curdist; XPoint *curpt, *savept; curpt = savept = curpoly->points; *mindist = wirelength(curpt, cursloc); while (++curpt < curpoly->points + curpoly->number) { curdist = wirelength(curpt, cursloc); if (curdist < *mindist) { *mindist = curdist; savept = curpt; } } return (short)(savept - curpoly->points); } /*----------------------------------------------------------------------------*/ /* Find closest point of a polygon to the cursor */ /*----------------------------------------------------------------------------*/ short closepoint(polyptr curpoly, XPoint *cursloc) { short mindist; return closepointdistance(curpoly, cursloc, &mindist); } /*----------------------------------------------------------------------------*/ /* Find the distance to the closest point of a polygon to the cursor */ /*----------------------------------------------------------------------------*/ short closedistance(polyptr curpoly, XPoint *cursloc) { short mindist; closepointdistance(curpoly, cursloc, &mindist); return mindist; } /*----------------------------------------------------------------------------*/ /* Coordinate system transformations */ /*----------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------*/ /* Check screen bounds: minimum, maximum scale and translation is determined */ /* by values which fit in an X11 type XPoint (short int). If the window */ /* extremes exceed type short when mapped to user space, or if the page */ /* bounds exceed type short when mapped to X11 window space, return error. */ /*------------------------------------------------------------------------------*/ short checkbounds() { XPoint testpt; long lval; /* check window-to-user space */ lval = 2 * (long)((float) (areastruct.width) / (*areastruct.vscale)) + (long)areastruct.pcorner->x; if (lval != (long)((short)lval)) return -1; lval = 2 * (long)((float) (areastruct.height) / (*areastruct.vscale)) + (long)areastruct.pcorner->y; if (lval != (long)((short)lval)) return -1; /* check user-to-window space */ lval = (long)((float)(topobject->bbox.lowerleft.x - areastruct.pcorner->x) * (*areastruct.vscale)); if (lval != (long)((short)lval)) return -1; lval = (long)areastruct.height - (long)((float)(topobject->bbox.lowerleft.y - areastruct.pcorner->y) * (*areastruct.vscale)); if (lval != (long)((short)lval)) return -1; UTransformbyCTM(DCTM, &(topobject->bbox.lowerleft), &testpt, 1); lval = (long)((float)(topobject->bbox.lowerleft.x + topobject->bbox.width - areastruct.pcorner->x) * (*areastruct.vscale)); if (lval != (long)((short)lval)) return -1; lval = (long)areastruct.height - (long)((float)(topobject->bbox.lowerleft.y + topobject->bbox.height - areastruct.pcorner->y) * (*areastruct.vscale)); if (lval != (long)((short)lval)) return -1; return 0; } /*------------------------------------------------------------------------*/ /* Transform X-window coordinate to xcircuit coordinate system */ /*------------------------------------------------------------------------*/ void window_to_user(short xw, short yw, XPoint *upt) { float tmpx, tmpy; tmpx = (float)xw / (*areastruct.vscale) + (float)areastruct.pcorner->x; tmpy = (float)(areastruct.height - yw) / (*areastruct.vscale) + (float)areastruct.pcorner->y; tmpx += (tmpx > 0) ? 0.5 : -0.5; tmpy += (tmpy > 0) ? 0.5 : -0.5; upt->x = (short)tmpx; upt->y = (short)tmpy; } /*------------------------------------------------------------------------*/ /* Transform xcircuit coordinate back to X-window coordinate system */ /*------------------------------------------------------------------------*/ void user_to_window(XPoint upt, XPoint *wpt) { float tmpx, tmpy; tmpx = (float)(upt.x - areastruct.pcorner->x) * (*areastruct.vscale); tmpy = (float)areastruct.height - (float)(upt.y - areastruct.pcorner->y) * (*areastruct.vscale); tmpx += (tmpx > 0) ? 0.5 : -0.5; tmpy += (tmpy > 0) ? 0.5 : -0.5; wpt->x = (short)tmpx; wpt->y = (short)tmpy; } /*------------------------------------------------------------------------*/ /* Transformations in the object hierarchy */ /*------------------------------------------------------------------------*/ float UTopScale() { Matrix *ctm = DCTM; return (float)(sqrt((double)(ctm->a * ctm->a + ctm->d * ctm->d))); } /*------------------------------------------------------------------------*/ float UTopTransScale(float length) { return (float)(length * UTopScale()); } /*----------------------------------------------------------------------*/ /* Get the cursor position */ /*----------------------------------------------------------------------*/ XPoint UGetCursor() { Window nullwin; int nullint, xpos, ypos; u_int nullui; XPoint newpos; #ifdef TCL_WRAPPER /* Don't use areastruct.areawin; if called from inside an object */ /* (e.g., "here" in a Tcl expression), areastruct.areawin will be */ /* an off-screen pixmap, and cause a crash. */ #ifndef _MSC_VER XQueryPointer(dpy, Tk_WindowId(areastruct.area), &nullwin, &nullwin, &nullint, &nullint, &xpos, &ypos, &nullui); #else XQueryPointer_TkW32(dpy, Tk_WindowId(areastruct.area), &nullwin, &nullwin, &nullint, &nullint, &xpos, &ypos, &nullui); #endif #else XQueryPointer(dpy, areastruct.areawin, &nullwin, &nullwin, &nullint, &nullint, &xpos, &ypos, &nullui); #endif newpos.x = xpos; newpos.y = ypos; return newpos; } /*----------------------------------------------------------------------*/ /* Get the cursor position and translate to user coordinates */ /*----------------------------------------------------------------------*/ XPoint UGetCursorPos() { XPoint winpos, userpos; winpos = UGetCursor(); window_to_user(winpos.x, winpos.y, &userpos); return userpos; } /*----------------------------------------------------------------------*/ /* Translate a point to the nearest snap-to grid point */ /*----------------------------------------------------------------------*/ /* user coordinates to user coordinates version */ void u2u_snap(XPoint *uvalue) { float tmpx, tmpy; float tmpix, tmpiy; if (areastruct.snapto) { tmpx = (float)uvalue->x / xobjs.pagelist[areastruct.page]->snapspace; if (tmpx > 0) tmpix = (float)((int)(tmpx + 0.5)); else tmpix = (float)((int)(tmpx - 0.5)); tmpy = (float)uvalue->y / xobjs.pagelist[areastruct.page]->snapspace; if (tmpy > 0) tmpiy = (float)((int)(tmpy + 0.5)); else tmpiy = (float)((int)(tmpy - 0.5)); tmpix *= xobjs.pagelist[areastruct.page]->snapspace; tmpix += (tmpix > 0) ? 0.5 : -0.5; tmpiy *= xobjs.pagelist[areastruct.page]->snapspace; tmpiy += (tmpiy > 0) ? 0.5 : -0.5; uvalue->x = (int)tmpix; uvalue->y = (int)tmpiy; } } /*------------------------------------------------------------------------*/ /* window coordinates to user coordinates version */ /*------------------------------------------------------------------------*/ void snap(short valuex, short valuey, XPoint *returnpt) { window_to_user(valuex, valuey, returnpt); u2u_snap(returnpt); } /*------------------------------------------------------------------------*/ /* Transform object coordinates through scale, translation, and rotation */ /* This routine attempts to match the PostScript definition of trans- */ /* formation matrices. */ /*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/ /* Current transformation matrix manipulation routines */ /*------------------------------------------------------------------------*/ void UResetCTM(Matrix *ctm) { ctm->a = ctm->e = 1; ctm->b = ctm->d = 0; ctm->c = ctm->f = 0; /* 0.5 for nearest-int real->int conversion? */ } /*------------------------------------------------------------------------*/ void InvertCTM(Matrix *ctm) { float det = ctm->a * ctm->e - ctm->b * ctm->d; float tx = ctm->b * ctm->f - ctm->c * ctm->e; float ty = ctm->d * ctm->c - ctm->a * ctm->f; float tmpa = ctm->a; ctm->b = -ctm->b / det; ctm->d = -ctm->d / det; ctm->a = ctm->e / det; ctm->e = tmpa / det; ctm->c = tx / det; ctm->f = ty / det; } /*------------------------------------------------------------------------*/ void UCopyCTM(fctm, tctm) Matrix *fctm, *tctm; { tctm->a = fctm->a; tctm->b = fctm->b; tctm->c = fctm->c; tctm->d = fctm->d; tctm->e = fctm->e; tctm->f = fctm->f; } /*-------------------------------------------------------------------------*/ /* Multiply CTM by current screen position and scale to get transformation */ /* matrix from a user point to the X11 window */ /*-------------------------------------------------------------------------*/ void UMakeWCTM(Matrix *ctm) { ctm->a *= (*areastruct.vscale); ctm->b *= (*areastruct.vscale); ctm->c = (ctm->c - (float)areastruct.pcorner->x) * (*areastruct.vscale); ctm->d *= -(*areastruct.vscale); ctm->e *= -(*areastruct.vscale); ctm->f = (float)areastruct.height + ((float)areastruct.pcorner->y - ctm->f) * (*areastruct.vscale); } /*------------------------------------------------------------------------*/ void UMultCTM(Matrix *ctm, XPoint position, float scale, short rotate) { float tmpa, tmpb, tmpd, tmpe, yscale; float mata, matb, matc; double drot = (double)rotate * RADFAC; yscale = abs(scale); /* -scale implies flip in x direction only */ tmpa = scale * cos(drot); tmpb = yscale * sin(drot); tmpd = -scale * sin(drot); tmpe = yscale * cos(drot); mata = ctm->a * tmpa + ctm->d * tmpb; matb = ctm->b * tmpa + ctm->e * tmpb; matc = ctm->c * tmpa + ctm->f * tmpb + position.x; ctm->d = ctm->d * tmpe + ctm->a * tmpd; ctm->e = ctm->e * tmpe + ctm->b * tmpd; ctm->f = ctm->f * tmpe + ctm->c * tmpd + position.y; ctm->a = mata; ctm->b = matb; ctm->c = matc; } /*----------------------------------------------------------------------*/ /* Slanting function x' = x + beta * y, y' = y */ /*----------------------------------------------------------------------*/ void USlantCTM(Matrix *ctm, float beta) { ctm->b += ctm->a * beta; ctm->e += ctm->d * beta; } #define EPS 1e-9 /*----------------------------------------------------------------------*/ /* Transform text to make it right-side up within 90 degrees of page */ /* NOTE: This is not yet resolved, as xcircuit does not agree with */ /* PostScript in a few cases! */ /*----------------------------------------------------------------------*/ void UPreScaleCTM(Matrix *ctm) { /* negative X scale (-1, +1) */ if ((ctm->a < -EPS) || ((ctm->a < EPS) && (ctm->a > -EPS) && ((ctm->d * ctm->b) < 0))) { ctm->a = -ctm->a; ctm->d = -ctm->d; } /* negative Y scale (+1, -1) */ if (ctm->e > EPS) { ctm->e = -ctm->e; ctm->b = -ctm->b; } /* At 90, 270 degrees need special attention to avoid discrepencies */ /* with the PostScript output due to roundoff error. This code */ /* matches what PostScript produces. */ } /*----------------------------------------------------------------------*/ /* Adjust justification and CTM as necessary for flip invariance */ /*----------------------------------------------------------------------*/ short flipadjust(short justify) { short tmpjust = justify & (~FLIPINV); if (justify & FLIPINV) { if (((DCTM)->a < -EPS) || (((DCTM)->a < EPS) && ((DCTM)->a > -EPS) && (((DCTM)->d * (DCTM)->b) < 0))) { if ((tmpjust & (RIGHT | NOTLEFT)) != NOTLEFT) tmpjust ^= (RIGHT | NOTLEFT); } if ((DCTM)->e > EPS) { if ((tmpjust & (TOP | NOTBOTTOM)) != NOTBOTTOM) tmpjust ^= (TOP | NOTBOTTOM); } UPreScaleCTM(DCTM); } return tmpjust; } /*------------------------------------------------------------------------*/ void UPreMultCTM(Matrix *ctm, XPoint position, float scale, short rotate) { float tmpa, tmpb, tmpd, tmpe, yscale; float mata, matd; double drot = (double)rotate * RADFAC; yscale = abs(scale); /* negative scale value implies flip in x only */ tmpa = scale * cos(drot); tmpb = yscale * sin(drot); tmpd = -scale * sin(drot); tmpe = yscale * cos(drot); ctm->c += ctm->a * position.x + ctm->b * position.y; ctm->f += ctm->d * position.x + ctm->e * position.y; mata = ctm->a * tmpa + ctm->b * tmpd; ctm->b = ctm->a * tmpb + ctm->b * tmpe; matd = ctm->d * tmpa + ctm->e * tmpd; ctm->e = ctm->d * tmpb + ctm->e * tmpe; ctm->a = mata; ctm->d = matd; } /*----------------------------------------------------------------------*/ /* Direct Matrix-Matrix multiplication */ /*----------------------------------------------------------------------*/ void UPreMultCTMbyMat(Matrix *ctm, Matrix *pre) { float mata, matd; mata = pre->a * ctm->a + pre->d * ctm->b; ctm->c += pre->c * ctm->a + pre->f * ctm->b; ctm->b = pre->b * ctm->a + pre->e * ctm->b; ctm->a = mata; matd = pre->a * ctm->d + pre->d * ctm->e; ctm->f += pre->c * ctm->d + pre->f * ctm->e; ctm->e = pre->b * ctm->d + pre->e * ctm->e; ctm->d = matd; } /*------------------------------------------------------------------------*/ void UTransformbyCTM(Matrix *ctm, XPoint *ipoints, XPoint *points, short number) { pointlist current, ptptr = points; float fx, fy; short tmpx; for (current = ipoints; current < ipoints + number; current++, ptptr++) { fx = ctm->a * (float)current->x + ctm->b * (float)current->y + ctm->c; fy = ctm->d * (float)current->x + ctm->e * (float)current->y + ctm->f; ptptr->x = (fx >= 0) ? (short)(fx + 0.5) : (short)(fx - 0.5); ptptr->y = (fy >= 0) ? (short)(fy + 0.5) : (short)(fy - 0.5); } } /*------------------------------------------------------------------------*/ /* (same as above routine but using type (float) for point values; this */ /* is for calculation of Bezier curve internal points. */ /*------------------------------------------------------------------------*/ void UfTransformbyCTM(Matrix *ctm, XfPoint *fpoints, XPoint *points, short number) { fpointlist current; pointlist new = points; float fx, fy; for (current = fpoints; current < fpoints + number; current++, new++) { fx = ctm->a * current->x + ctm->b * current->y + ctm->c; fy = ctm->d * current->x + ctm->e * current->y + ctm->f; new->x = (fx >= 0) ? (short)(fx + 0.5) : (short)(fx - 0.5); new->y = (fy >= 0) ? (short)(fy + 0.5) : (short)(fy - 0.5); } } /*------------------------------------------------------------------------*/ void UPopCTM() { Matrixptr lastmatrix; if (areastruct.MatStack == NULL) { Wprintf("Matrix stack pop error"); return; } lastmatrix = areastruct.MatStack->nextmatrix; free(areastruct.MatStack); areastruct.MatStack = lastmatrix; } /*------------------------------------------------------------------------*/ void UPushCTM() { Matrixptr nmatrix; nmatrix = (Matrixptr)malloc(sizeof(Matrix)); if (areastruct.MatStack == NULL) UResetCTM(nmatrix); else UCopyCTM(areastruct.MatStack, nmatrix); nmatrix->nextmatrix = areastruct.MatStack; areastruct.MatStack = nmatrix; } /*------------------------------------------------------------------------*/ void UTransformPoints(XPoint *points, XPoint *newpoints, short number, XPoint atpt, float scale, short rotate) { Matrix LCTM; UResetCTM(&LCTM); UMultCTM(&LCTM, atpt, scale, rotate); UTransformbyCTM(&LCTM, points, newpoints, number); } /*----------------------------------------------------*/ /* Transform points inward to next hierarchical level */ /*----------------------------------------------------*/ void InvTransformPoints(XPoint *points, XPoint *newpoints, short number, XPoint atpt, float scale, short rotate) { Matrix LCTM; UResetCTM(&LCTM); UPreMultCTM(&LCTM, atpt, scale, rotate); InvertCTM(&LCTM); UTransformbyCTM(&LCTM, points, newpoints, number); } /*------------------------------------------------------------------------*/ /* Translate wire coords to force wire to horizontal or vertical position */ /*------------------------------------------------------------------------*/ void manhattanize(XPoint *pospt, polyptr newwire) { short deltax, deltay; XPoint *tpoint = newwire->points + newwire->number - 2; deltax = abs(tpoint->x - pospt->x); deltay = abs(tpoint->y - pospt->y); if (deltay > deltax) pospt->x = tpoint->x; else pospt->y = tpoint->y; } /*----------------------------------------------------------------------*/ /* Bounding box calculation routines */ /*----------------------------------------------------------------------*/ void bboxcalc(short testval, short *lowerval, short *upperval) { if (testval < *lowerval) *lowerval = testval; if (testval > *upperval) *upperval = testval; } /*----------------------------------------------------------------------*/ /* Bounding box calculation for elements which can be part of a path */ /*----------------------------------------------------------------------*/ void calcextents(genericptr *bboxgen, short *llx, short *lly, short *urx, short *ury) { switch (ELEMENTTYPE(*bboxgen)) { case(POLYGON): { pointlist bboxpts; for (bboxpts = TOPOLY(bboxgen)->points; bboxpts < TOPOLY(bboxgen)->points + TOPOLY(bboxgen)->number; bboxpts++) { bboxcalc(bboxpts->x, llx, urx); bboxcalc(bboxpts->y, lly, ury); } } break; case(SPLINE): { fpointlist bboxpts; bboxcalc(TOSPLINE(bboxgen)->ctrl[0].x, llx, urx); bboxcalc(TOSPLINE(bboxgen)->ctrl[0].y, lly, ury); bboxcalc(TOSPLINE(bboxgen)->ctrl[3].x, llx, urx); bboxcalc(TOSPLINE(bboxgen)->ctrl[3].y, lly, ury); for (bboxpts = TOSPLINE(bboxgen)->points; bboxpts < TOSPLINE(bboxgen)->points + INTSEGS; bboxpts++) { bboxcalc((short)(bboxpts->x), llx, urx); bboxcalc((short)(bboxpts->y), lly, ury); } } break; case (ARC): { fpointlist bboxpts; for (bboxpts = TOARC(bboxgen)->points; bboxpts < TOARC(bboxgen)->points + TOARC(bboxgen)->number; bboxpts++) { bboxcalc((short)(bboxpts->x), llx, urx); bboxcalc((short)(bboxpts->y), lly, ury); } } break; } } /*----------------------------------------------------------------------*/ /* Calculate the bounding box of an object instance */ /*----------------------------------------------------------------------*/ void objinstbbox(objinstptr obbox, XPoint *npoints, Boolean expand) { XPoint points[4]; int extend = (expand) ? 4 : 0; points[0].x = points[1].x = obbox->bbox.lowerleft.x - extend; points[1].y = points[2].y = obbox->bbox.lowerleft.y + obbox->bbox.height + extend; points[2].x = points[3].x = obbox->bbox.lowerleft.x + obbox->bbox.width + extend; points[0].y = points[3].y = obbox->bbox.lowerleft.y - extend; UTransformPoints(points, npoints, 4, obbox->position, obbox->scale, obbox->rotation); } /*----------------------------------------------------------------------*/ /* Calculate the bounding box of a label */ /*----------------------------------------------------------------------*/ void labelbbox(labelptr labox, XPoint *npoints, objinstptr callinst) { XPoint points[4]; TextExtents tmpext; short j; tmpext = ULength(labox->string, callinst, 0.0, 0, NULL); points[0].x = points[1].x = (labox->justify & NOTLEFT ? (labox->justify & RIGHT ? -tmpext.width : -tmpext.width / 2) : 0); points[2].x = points[3].x = points[0].x + tmpext.width; points[0].y = points[3].y = (labox->justify & NOTBOTTOM ? (labox->justify & TOP ? -tmpext.ascent : -(tmpext.ascent + tmpext.base) / 2) : -tmpext.base) + tmpext.descent; points[1].y = points[2].y = points[0].y + tmpext.ascent - tmpext.descent; /* separate bounding box for pinlabels and infolabels */ if (labox->pin) for (j = 0; j < 4; j++) pinadjust(labox->justify, &points[j].x, &points[j].y, 1); UTransformPoints(points, npoints, 4, labox->position, labox->scale, labox->rotation); } /*----------------------------------------------------------------------*/ /* Calculate the bounding box of a graphic image */ /*----------------------------------------------------------------------*/ void graphicbbox(graphicptr gp, XPoint *npoints) { XPoint points[4]; int hw = gp->source->width >> 1; int hh = gp->source->height >> 1; points[1].x = points[2].x = hw; points[0].x = points[3].x = -hw; points[0].y = points[1].y = -hh; points[2].y = points[3].y = hh; UTransformPoints(points, npoints, 4, gp->position, gp->scale, gp->rotation); } /*--------------------------------------------------------------*/ /* Wrapper for single call to calcbboxsingle() in the netlister */ /*--------------------------------------------------------------*/ void calcinstbbox(genericptr *bboxgen, short *llx, short *lly, short *urx, short *ury) { *llx = *lly = 32767; *urx = *ury = -32768; calcbboxsingle(bboxgen, areastruct.topinstance, llx, lly, urx, ury); } /*----------------------------------------------------------------------*/ /* Bounding box calculation for a single generic element */ /*----------------------------------------------------------------------*/ void calcbboxsingle(genericptr *bboxgen, objinstptr thisinst, short *llx, short *lly, short *urx, short *ury) { XPoint npoints[4]; short j; /* For each screen element, compute the extents and revise bounding */ /* box points, if necessary. */ switch(ELEMENTTYPE(*bboxgen)) { case(OBJINST): objinstbbox(TOOBJINST(bboxgen), npoints, False); for (j = 0; j < 4; j++) { bboxcalc(npoints[j].x, llx, urx); bboxcalc(npoints[j].y, lly, ury); } break; case(LABEL): /* because a pin is offset from its position point, include */ /* that point in the bounding box. */ if (TOLABEL(bboxgen)->pin) { bboxcalc(TOLABEL(bboxgen)->position.x, llx, urx); bboxcalc(TOLABEL(bboxgen)->position.y, lly, ury); } labelbbox(TOLABEL(bboxgen), npoints, thisinst); for (j = 0; j < 4; j++) { bboxcalc(npoints[j].x, llx, urx); bboxcalc(npoints[j].y, lly, ury); } break; case(GRAPHIC): graphicbbox(TOGRAPHIC(bboxgen), npoints); for (j = 0; j < 4; j++) { bboxcalc(npoints[j].x, llx, urx); bboxcalc(npoints[j].y, lly, ury); } break; case(PATH): { genericptr *pathc; for (pathc = TOPATH(bboxgen)->plist; pathc < TOPATH(bboxgen)->plist + TOPATH(bboxgen)->parts; pathc++) calcextents(pathc, llx, lly, urx, ury); } break; default: calcextents(bboxgen, llx, lly, urx, ury); } } /*------------------------------------------------------*/ /* Find if an object is in the specified library */ /*------------------------------------------------------*/ Boolean object_in_library(short libnum, objectptr thisobject) { short i; for (i = 0; i < xobjs.userlibs[libnum].number; i++) { if (*(xobjs.userlibs[libnum].library + i) == thisobject) return True; } return False; } /*-----------------------------------------------------------*/ /* Find if an object is in the hierarchy of the given object */ /* Returns the number (position in plist) or -1 if not found */ /*-----------------------------------------------------------*/ short find_object(objectptr pageobj, objectptr thisobject) { short i, j; genericptr *pelem; for (i = 0; i < pageobj->parts; i++) { pelem = pageobj->plist + i; if (IS_OBJINST(*pelem)) { if ((TOOBJINST(pelem))->thisobject == thisobject) return i; else if ((j = find_object((TOOBJINST(pelem))->thisobject, thisobject)) >= 0) return i; /* was j---is this the right fix? */ } } return -1; } /*------------------------------------------------------*/ /* Find all pages and libraries containing this object */ /* and update accordingly. If this object is a page, */ /* just update the page directory. */ /*------------------------------------------------------*/ void updatepagebounds(objectptr thisobject) { short i, j; objectptr pageobj; if ((i = is_page(thisobject)) >= 0) { if (xobjs.pagelist[i]->background.name != (char *)NULL) backgroundbbox(i); updatepagelib(PAGELIB, i); } else { for (i = 0; i < xobjs.pages; i++) { if (xobjs.pagelist[i]->pageinst != NULL) { pageobj = xobjs.pagelist[i]->pageinst->thisobject; if ((j = find_object(pageobj, thisobject)) >= 0) { calcbboxvalues(xobjs.pagelist[i]->pageinst, (genericptr *)(pageobj->plist + j)); updatepagelib(PAGELIB, i); } } } for (i = 0; i < xobjs.numlibs; i++) if (object_in_library(i, thisobject)) composelib(i + LIBRARY); } } /*--------------------------------------------------------------*/ /* Free memory for the schematic bounding box */ /*--------------------------------------------------------------*/ void invalidateschembbox(objinstptr thisinst) { if (thisinst->schembbox != NULL) { free(thisinst->schembbox); thisinst->schembbox = NULL; } } /*--------------------------------------------------------------*/ /* Calculate the bounding box for an object instance. Use the */ /* existing bbox and finish calculation on all the elements */ /* which have parameters not taking default values. */ /* This finishes the calculation partially done by */ /* calcbboxvalues(). */ /*--------------------------------------------------------------*/ void calcbboxinst(objinstptr thisinst) { objectptr thisobj; genericptr *gelem; short llx, lly, urx, ury; short pllx, plly, purx, pury; Boolean hasschembbox = FALSE; if (thisinst == NULL) return; thisobj = thisinst->thisobject; llx = thisobj->bbox.lowerleft.x; lly = thisobj->bbox.lowerleft.y; urx = llx + thisobj->bbox.width; ury = lly + thisobj->bbox.height; pllx = plly = 32767; purx = pury = -32768; for (gelem = thisobj->plist; gelem < thisobj->plist + thisobj->parts; gelem++) { /* pins which do not appear outside of the object */ /* contribute to the objects "schembbox". */ if (IS_LABEL(*gelem)) { labelptr btext = TOLABEL(gelem); if (btext->pin && !(btext->justify & PINVISIBLE)) { hasschembbox = TRUE; calcbboxsingle(gelem, thisinst, &pllx, &plly, &purx, &pury); continue; } } if (has_param(*gelem)) calcbboxsingle(gelem, thisinst, &llx, &lly, &urx, &ury); } thisinst->bbox.lowerleft.x = llx; thisinst->bbox.lowerleft.y = lly; thisinst->bbox.width = urx - llx; thisinst->bbox.height = ury - lly; if (hasschembbox) { if (thisinst->schembbox == NULL) thisinst->schembbox = (BBox *)malloc(sizeof(BBox)); thisinst->schembbox->lowerleft.x = pllx; thisinst->schembbox->lowerleft.y = plly; thisinst->schembbox->width = purx - pllx; thisinst->schembbox->height = pury - plly; } else invalidateschembbox(thisinst); } /*--------------------------------------------------------------*/ /* Update things based on a changed instance bounding box. */ /* If the parameter was a single-instance */ /* substitution, only the page should be updated. If the */ /* parameter was a default value, the library should be updated */ /* and any pages containing the object where the parameter */ /* takes the default value. */ /*--------------------------------------------------------------*/ void updateinstparam(objectptr bobj) { short i, j; objectptr pageobj; /* change bounds on pagelib and all pages */ /* containing this *object* if and only if the object */ /* instance takes the default value. Also update the */ /* library page. */ for (i = 0; i < xobjs.pages; i++) if (xobjs.pagelist[i]->pageinst != NULL) { pageobj = xobjs.pagelist[i]->pageinst->thisobject; if ((j = find_object(pageobj, topobject)) >= 0) { /* Really, we'd like to recalculate the bounding box only if the */ /* parameter value is the default value which was just changed. */ /* However, then any non-default values may contain the wrong */ /* substitutions. */ objinstptr cinst = TOOBJINST(pageobj->plist + j); if (cinst->thisobject->params == NULL) { calcbboxvalues(xobjs.pagelist[i]->pageinst, pageobj->plist + j); updatepagelib(PAGELIB, i); } } } for (i = 0; i < xobjs.numlibs; i++) if (object_in_library(i, topobject)) composelib(i + LIBRARY); } /*--------------------------------------------------------------*/ /* Calculate bbox on all elements of the given object */ /*--------------------------------------------------------------*/ void calcbbox(objinstptr binst) { calcbboxvalues(binst, (genericptr *)NULL); if (binst == areastruct.topinstance) { updatepagebounds(topobject); } } /*--------------------------------------------------------------*/ /* Calculate bbox on the given element of the specified object. */ /* This is a wrapper for calcbboxvalues() assuming that we're */ /* on the top-level, and that page bounds need to be updated. */ /*--------------------------------------------------------------*/ void singlebbox(genericptr *gelem) { calcbboxvalues(areastruct.topinstance, (genericptr *)gelem); updatepagebounds(topobject); } /*----------------------------------------------------------------------*/ /* Extend bounding box based on selected elements only */ /*----------------------------------------------------------------------*/ void calcbboxselect() { short *bsel; for (bsel = areastruct.selectlist; bsel < areastruct.selectlist + areastruct.selects; bsel++) calcbboxvalues(areastruct.topinstance, topobject->plist + *bsel); updatepagebounds(topobject); } /*--------------------------------------------------------------*/ /* Update Bounding box for an object. */ /* If newelement == NULL, calculate bounding box from scratch. */ /* Otherwise, expand bounding box to enclose newelement. */ /*--------------------------------------------------------------*/ void calcbboxvalues(objinstptr thisinst, genericptr *newelement) { genericptr *bboxgen; short llx, lly, urx, ury; objectptr thisobj = thisinst->thisobject; /* no action if there are no elements */ if (thisobj->parts == 0) return; /* If this object has parameters, then we will do a separate */ /* bounding box calculation on parameterized parts. This */ /* calculation ignores them, and the result is a base that the */ /* instance bounding-box computation can use as a starting point. */ /* set starting bounds as maximum bounds of screen */ llx = lly = 32767; urx = ury = -32768; for (bboxgen = thisobj->plist; bboxgen < thisobj->plist + thisobj->parts; bboxgen++) { /* override the "for" loop if we're doing a single element */ if (newelement != NULL) bboxgen = newelement; if ((thisobj->params == NULL) || (!has_param(*bboxgen))) { /* pins which do not appear outside of the object */ /* are ignored now---will be computed per instance. */ if (IS_LABEL(*bboxgen)) { labelptr btext = TOLABEL(bboxgen); if (btext->pin && !(btext->justify & PINVISIBLE)) { goto nextgen; } } calcbboxsingle(bboxgen, thisinst, &llx, &lly, &urx, &ury); } nextgen: if (newelement != NULL) break; } /* if this is a single-element calculation and its bounding box */ /* turned out to be smaller than the object's, then we need to */ /* recompute the entire object's bounding box in case it got */ /* smaller. This is not recursive, in spite of looks. */ if (newelement != NULL) { if (llx > thisobj->bbox.lowerleft.x && lly > thisobj->bbox.lowerleft.y && urx < (thisobj->bbox.lowerleft.x + thisobj->bbox.width) && ury < (thisobj->bbox.lowerleft.y + thisobj->bbox.height)) { calcbboxvalues(thisinst, NULL); return; } else { bboxcalc(thisobj->bbox.lowerleft.x, &llx, &urx); bboxcalc(thisobj->bbox.lowerleft.y, &lly, &ury); bboxcalc(thisobj->bbox.lowerleft.x + thisobj->bbox.width, &llx, &urx); bboxcalc(thisobj->bbox.lowerleft.y + thisobj->bbox.height, &lly, &ury); } } /* Set the new bounding box. In pathological cases, such as a page */ /* with only pin labels, the bounds may not have been changed from */ /* their initial values. If so, then don't touch the bounding box. */ if ((llx < urx) && (lly < ury)) { thisobj->bbox.lowerleft.x = llx; thisobj->bbox.lowerleft.y = lly; thisobj->bbox.width = urx - llx; thisobj->bbox.height = ury - lly; } /* calculate instance-specific values */ calcbboxinst(thisinst); } /*------------------------------------------------------*/ /* Center an object in the viewing window */ /*------------------------------------------------------*/ void centerview(objinstptr tinst) { XPoint origin, corner; Dimension width, height; float fitwidth, fitheight; objectptr tobj = tinst->thisobject; origin = tinst->bbox.lowerleft; corner.x = origin.x + tinst->bbox.width; corner.y = origin.y + tinst->bbox.height; extendschembbox(tinst, &origin, &corner); width = corner.x - origin.x; height = corner.y - origin.y; fitwidth = (float)areastruct.width / ((float)width + 2 * DEFAULTGRIDSPACE); fitheight = (float)areastruct.height / ((float)height + 2 * DEFAULTGRIDSPACE); tobj->viewscale = (fitwidth < fitheight) ? min(MINAUTOSCALE, fitwidth) : min(MINAUTOSCALE, fitheight); tobj->pcorner.x = origin.x - (areastruct.width / tobj->viewscale - width) / 2; tobj->pcorner.y = origin.y - (areastruct.height / tobj->viewscale - height) / 2; } /*-----------------------------------------------------------*/ /* Refresh the window and scrollbars and write the page name */ /*-----------------------------------------------------------*/ void refresh(xcWidget bw, caddr_t clientdata, caddr_t calldata) { drawarea(NULL, NULL, NULL); if (areastruct.scrollbarh) drawhbar(areastruct.scrollbarh, NULL, NULL); if (areastruct.scrollbarv) drawvbar(areastruct.scrollbarv, NULL, NULL); printname(topobject); } /*------------------------------------------------------*/ /* Center the current page in the viewing window */ /*------------------------------------------------------*/ void zoomview(xcWidget w, caddr_t clientdata, caddr_t calldata) { if (eventmode == NORMAL_MODE || eventmode == COPY_MODE || eventmode == MOVE_MODE || eventmode == CATALOG_MODE || eventmode == FONTCAT_MODE || eventmode == EFONTCAT_MODE) { #ifdef USE_WIN32_COM float scale = *(areastruct.vscale); #endif centerview(areastruct.topinstance); invalidate_graphics(topobject); areastruct.lastbackground = NULL; renderbackground(); refresh(NULL, NULL, NULL); #ifdef USE_WIN32_COM if (*(areastruct.vscale) != scale) win32_fire_event(ZoomChanged); #endif } } /*---------------------------------------------------------*/ /* Basic X Graphics Routines in the User coordinate system */ /*---------------------------------------------------------*/ void UDrawSimpleLine(XPoint *pt1, XPoint *pt2) { XPoint newpt1, newpt2; UTransformbyCTM(DCTM, pt1, &newpt1, 1); UTransformbyCTM(DCTM, pt2, &newpt2, 1); DrawLine(dpy, areastruct.areawin, areastruct.gc, newpt1.x, newpt1.y, newpt2.x, newpt2.y); } /*-------------------------------------------------------------------------*/ void UDrawLine(XPoint *pt1, XPoint *pt2) { float tmpwidth = UTopTransScale(xobjs.pagelist[areastruct.page]->wirewidth); SetLineAttributes(dpy, areastruct.gc, tmpwidth, LineSolid, CapRound, JoinBevel); UDrawSimpleLine(pt1, pt2); } /*----------------------------------------------------------------------*/ /* Add circle at given point to indicate that the point is a parameter. */ /* The circle is divided into quarters. For parameterized y-coordinate */ /* the top and bottom quarters are drawn. For parameterized x- */ /* coordinate, the left and right quarters are drawn. A full circle */ /* indicates either both x- and y-coordinates are parameterized, or */ /* else any other kind of parameterization (presently, not used). */ /* */ /* (note that the two angles in XDrawArc() are 1) the start angle, */ /* measured in absolute 64th degrees from 0 (3 o'clock), and 2) the */ /* path length, in relative 64th degrees (positive = counterclockwise, */ /* negative = clockwise)). */ /*----------------------------------------------------------------------*/ void UDrawCircle(XPoint *upt, u_char which) { XPoint wpt; user_to_window(*upt, &wpt); SetThinLineAttributes(dpy, areastruct.gc, 0, LineSolid, CapButt, JoinMiter); switch(which) { case P_POSITION_X: XDrawArc(dpy, areastruct.areawin, areastruct.gc, wpt.x - 4, wpt.y - 4, 8, 8, -(45 * 64), (90 * 64)); XDrawArc(dpy, areastruct.areawin, areastruct.gc, wpt.x - 4, wpt.y - 4, 8, 8, (135 * 64), (90 * 64)); break; case P_POSITION_Y: XDrawArc(dpy, areastruct.areawin, areastruct.gc, wpt.x - 4, wpt.y - 4, 8, 8, (45 * 64), (90 * 64)); XDrawArc(dpy, areastruct.areawin, areastruct.gc, wpt.x - 4, wpt.y - 4, 8, 8, (225 * 64), (90 * 64)); break; default: XDrawArc(dpy, areastruct.areawin, areastruct.gc, wpt.x - 4, wpt.y - 4, 8, 8, 0, (360 * 64)); break; } } /*----------------------------------------------------------------------*/ /* Add "X" at string origin */ /*----------------------------------------------------------------------*/ void UDrawXAt(XPoint *wpt) { SetThinLineAttributes(dpy, areastruct.gc, 0, LineSolid, CapButt, JoinMiter); DrawLine(dpy, areastruct.areawin, areastruct.gc, wpt->x - 3, wpt->y - 3, wpt->x + 3, wpt->y + 3); DrawLine(dpy, areastruct.areawin, areastruct.gc, wpt->x + 3, wpt->y - 3, wpt->x - 3, wpt->y + 3); } /*----------------------------------------------------------------------*/ /* Draw "X" on current level */ /*----------------------------------------------------------------------*/ void UDrawX(labelptr curlabel) { XPoint wpt; user_to_window(curlabel->position, &wpt); UDrawXAt(&wpt); } /*----------------------------------------------------------------------*/ /* Draw "X" on top level (only for LOCAL and GLOBAL pin labels) */ /*----------------------------------------------------------------------*/ void UDrawXDown(labelptr curlabel) { XPoint wpt; UTransformbyCTM(DCTM, &curlabel->position, &wpt, 1); UDrawXAt(&wpt); } /*----------------------------------------------------------------------*/ /* Find the "real" width, height, and origin of an object including pin */ /* labels and so forth that only show up on a schematic when it is the */ /* top-level object. */ /*----------------------------------------------------------------------*/ int toplevelwidth(objinstptr bbinst, short *rllx) { short llx, urx; short origin, corner; if (bbinst->schembbox == NULL) { if (rllx) *rllx = bbinst->bbox.lowerleft.x; return bbinst->bbox.width; } origin = bbinst->bbox.lowerleft.x; corner = origin + bbinst->bbox.width; llx = bbinst->schembbox->lowerleft.x; urx = llx + bbinst->schembbox->width; bboxcalc(llx, &origin, &corner); bboxcalc(urx, &origin, &corner); if (rllx) *rllx = origin; return(corner - origin); } /*----------------------------------------------------------------------*/ int toplevelheight(objinstptr bbinst, short *rlly) { short lly, ury; short origin, corner; if (bbinst->schembbox == NULL) { if (rlly) *rlly = bbinst->bbox.lowerleft.y; return bbinst->bbox.height; } origin = bbinst->bbox.lowerleft.y; corner = origin + bbinst->bbox.height; lly = bbinst->schembbox->lowerleft.y; ury = lly + bbinst->schembbox->height; bboxcalc(lly, &origin, &corner); bboxcalc(ury, &origin, &corner); if (rlly) *rlly = origin; return(corner - origin); } /*----------------------------------------------------------------------*/ /* Add dimensions of schematic pins to an object's bounding box */ /*----------------------------------------------------------------------*/ void extendschembbox(objinstptr bbinst, XPoint *origin, XPoint *corner) { short llx, lly, urx, ury; if ((bbinst == NULL) || (bbinst->schembbox == NULL)) return; llx = bbinst->schembbox->lowerleft.x; lly = bbinst->schembbox->lowerleft.y; urx = llx + bbinst->schembbox->width; ury = lly + bbinst->schembbox->height; bboxcalc(llx, &(origin->x), &(corner->x)); bboxcalc(lly, &(origin->y), &(corner->y)); bboxcalc(urx, &(origin->x), &(corner->x)); bboxcalc(ury, &(origin->y), &(corner->y)); } /*----------------------------------------------------------------------*/ /* Adjust a pinlabel position to account for pad spacing */ /*----------------------------------------------------------------------*/ void pinadjust (short justify, short *xpoint, short *ypoint, short dir) { int delx, dely; dely = (justify & NOTBOTTOM) ? ((justify & TOP) ? -PADSPACE : 0) : PADSPACE; delx = (justify & NOTLEFT) ? ((justify & RIGHT) ? -PADSPACE : 0) : PADSPACE; if (xpoint != NULL) *xpoint += (dir > 0) ? delx : -delx; if (ypoint != NULL) *ypoint += (dir > 0) ? dely : -dely; } /*----------------------------------------------------------------------*/ /* Draw line for editing text (position of cursor in string is given by */ /* tpos (2nd parameter) */ /*----------------------------------------------------------------------*/ void UDrawTextLine(labelptr curlabel, short tpos) { XPoint points[2]; /* top and bottom of text cursor line */ short xdist, xbase, tmpjust; TextExtents tmpext; /* correct for position, rotation, scale, and flip invariance of text */ UPushCTM(); UPreMultCTM(DCTM, curlabel->position, curlabel->scale, curlabel->rotation); tmpjust = flipadjust(curlabel->justify); SetFunction(dpy, areastruct.gc, GXxor); SetForeground(dpy, areastruct.gc, AUXCOLOR ^ BACKGROUND); tmpext = ULength(curlabel->string, areastruct.topinstance, 0.0, tpos, NULL); xdist = tmpext.width; xbase = tmpext.base; tmpext = ULength(curlabel->string, areastruct.topinstance, 0.0, 0, NULL); points[0].x = (tmpjust & NOTLEFT ? (tmpjust & RIGHT ? -tmpext.width : -tmpext.width >> 1) : 0) + xdist; points[0].y = (tmpjust & NOTBOTTOM ? (tmpjust & TOP ? -tmpext.ascent : -(tmpext.ascent + tmpext.base) / 2) : -tmpext.base) + xbase - 3; points[1].x = points[0].x; points[1].y = points[0].y + TEXTHEIGHT + 6; if (curlabel->pin) { pinadjust(tmpjust, &(points[0].x), &(points[0].y), 1); pinadjust(tmpjust, &(points[1].x), &(points[1].y), 1); } /* draw the line */ UDrawLine(&points[0], &points[1]); UPopCTM(); UDrawX(curlabel); } /*-----------------------------------------------------------------*/ /* Draw lines for editing text when multiple characters are chosen */ /*-----------------------------------------------------------------*/ void UDrawTLine(labelptr curlabel) { UDrawTextLine(curlabel, textpos); if ((textend > 0) && (textend < textpos)) { UDrawTextLine(curlabel, textend); } } /*----------------------*/ /* Draw an X */ /*----------------------*/ void UDrawXLine(XPoint opt, XPoint cpt) { XPoint upt, vpt; SetForeground(dpy, areastruct.gc, AUXCOLOR ^ BACKGROUND); SetFunction(dpy, areastruct.gc, GXxor); user_to_window(cpt, &upt); user_to_window(opt, &vpt); SetThinLineAttributes(dpy, areastruct.gc, 0, LineOnOffDash, CapButt, JoinMiter); DrawLine(dpy, areastruct.areawin, areastruct.gc, vpt.x, vpt.y, upt.x, upt.y); SetThinLineAttributes(dpy, areastruct.gc, 0, LineSolid, CapButt, JoinMiter); DrawLine(dpy, areastruct.areawin, areastruct.gc, upt.x - 3, upt.y - 3, upt.x + 3, upt.y + 3); DrawLine(dpy, areastruct.areawin, areastruct.gc, upt.x + 3, upt.y - 3, upt.x - 3, upt.y + 3); SetFunction(dpy, areastruct.gc, areastruct.gctype); SetForeground(dpy, areastruct.gc, areastruct.gccolor); } /*-------------------------------------------------------------------------*/ void UDrawBox(XPoint origin, XPoint corner) { XPoint worig, wcorn; user_to_window(origin, &worig); user_to_window(corner, &wcorn); SetFunction(dpy, areastruct.gc, GXxor); SetForeground(dpy, areastruct.gc, AUXCOLOR ^ BACKGROUND); SetThinLineAttributes(dpy, areastruct.gc, 0, LineSolid, CapRound, JoinBevel); DrawLine(dpy, areastruct.areawin, areastruct.gc, worig.x, worig.y, worig.x, wcorn.y); DrawLine(dpy, areastruct.areawin, areastruct.gc, worig.x, wcorn.y, wcorn.x, wcorn.y); DrawLine(dpy, areastruct.areawin, areastruct.gc, wcorn.x, wcorn.y, wcorn.x, worig.y); DrawLine(dpy, areastruct.areawin, areastruct.gc, wcorn.x, worig.y, worig.x, worig.y); } /*----------------------------------------------------------------------*/ /* Draw a box indicating the dimensions of the edit element that most */ /* closely reach the position "corner". */ /*----------------------------------------------------------------------*/ void UDrawRescaleBox(XPoint *corner) { XPoint origpoints[5], newpoints[5]; genericptr rgen; float savescale, newscale, lastscale, tempscale; long mindist, testdist, lastmindist; labelptr rlab; int i, j, idx; /* To be done---draw a box around each selected part, not */ /* just the first one. */ SetFunction(dpy, areastruct.gc, GXxor); SetForeground(dpy, areastruct.gc, AUXCOLOR ^ BACKGROUND); SetThinLineAttributes(dpy, areastruct.gc, 0, LineSolid, CapRound, JoinBevel); for (j = 0; j < areastruct.selects; j++) { rgen = SELTOGENERIC(areastruct.selectlist + j); switch(ELEMENTTYPE(rgen)) { case LABEL: rlab = (labelptr)rgen; savescale = rlab->scale; newscale = lastscale = rlab->scale * 2; mindist = LONG_MAX; while (mindist > 4) { rlab->scale = newscale; labelbbox(rlab, newpoints, areastruct.topinstance); newpoints[5] = newpoints[0]; lastmindist = mindist; mindist = LONG_MAX; for (i = 0; i < 4; i++) { testdist = finddist(&newpoints[i], &newpoints[i+1], corner); if (testdist < mindist) mindist = testdist; } if (mindist == lastmindist) break; else if (lastmindist == LONG_MAX) newscale /= 2; else { tempscale = (fabs)((newscale * lastmindist) - (lastscale * mindist)) / (abs)(lastmindist - mindist); lastscale = newscale; newscale = tempscale; } } rlab->scale = savescale; break; case GRAPHIC: break; case OBJINST: break; /* Note: should be able to handle other elements, as well */ } UTransformbyCTM(DCTM, newpoints, origpoints, 4); strokepath(origpoints, 4, 0, 1); } } /*-------------------------------------------------------------------------*/ void UDrawBBox() { XPoint origin; XPoint worig, wcorn, corner; objinstptr bbinst = areastruct.topinstance; if ((!areastruct.bboxon) || (checkforbbox(topobject) != NULL)) return; origin = bbinst->bbox.lowerleft; corner.x = origin.x + bbinst->bbox.width; corner.y = origin.y + bbinst->bbox.height; /* Include any schematic labels in the bounding box. */ extendschembbox(bbinst, &origin, &corner); user_to_window(origin, &worig); user_to_window(corner, &wcorn); SetForeground(dpy, areastruct.gc, BBOXCOLOR); DrawLine(dpy, areastruct.areawin, areastruct.gc, worig.x, worig.y, worig.x, wcorn.y); DrawLine(dpy, areastruct.areawin, areastruct.gc, worig.x, wcorn.y, wcorn.x, wcorn.y); DrawLine(dpy, areastruct.areawin, areastruct.gc, wcorn.x, wcorn.y, wcorn.x, worig.y); DrawLine(dpy, areastruct.areawin, areastruct.gc, wcorn.x, worig.y, worig.x, worig.y); } /*-------------------------------------------------------------------------*/ /* Fill and/or draw a border around the stroking path */ /*-------------------------------------------------------------------------*/ void strokepath(XPoint *pathlist, short number, short style, float width) { char solidpart; char dashstring[3]; float tmpwidth; short minwidth; tmpwidth = UTopTransScale(xobjs.pagelist[areastruct.page]->wirewidth * width); minwidth = max(1, (short)tmpwidth); if (style & FILLED || (!(style & FILLED) && style & OPAQUE)) { if ((style & FILLSOLID) == FILLSOLID) SetFillStyle(dpy, areastruct.gc, FillSolid); else if (!(style & FILLED)) { SetFillStyle(dpy, areastruct.gc, FillOpaqueStippled); SetStipple(dpy, areastruct.gc, 7); } else { if (style & OPAQUE) SetFillStyle(dpy, areastruct.gc, FillOpaqueStippled); else SetFillStyle(dpy, areastruct.gc, FillStippled); SetStipple(dpy, areastruct.gc, ((style & FILLSOLID) >> 5)); } FillPolygon(dpy, areastruct.areawin, areastruct.gc, pathlist, number, Nonconvex, CoordModeOrigin); /* return to original state */ SetFillStyle(dpy, areastruct.gc, FillSolid); } if (!(style & NOBORDER)) { /* set up dots or dashes */ if (style & DASHED) solidpart = (char)(4 * minwidth); else if (style & DOTTED) solidpart = (char)minwidth; sprintf(dashstring, "%c%c", solidpart, (char)(4 * minwidth)); if (style & (DASHED | DOTTED)) { SetDashes(dpy, areastruct.gc, 0, dashstring, 2); SetLineAttributes(dpy, areastruct.gc, tmpwidth, LineOnOffDash, CapButt, JoinMiter); } else SetLineAttributes(dpy, areastruct.gc, tmpwidth, LineSolid, (style & SQUARECAP) ? CapProjecting : CapRound, JoinMiter); /* draw the spline and close off if so specified */ DrawLines(dpy, areastruct.areawin, areastruct.gc, pathlist, number, CoordModeOrigin); if (!(style & UNCLOSED)) DrawLine(dpy, areastruct.areawin, areastruct.gc, pathlist[0].x, pathlist[0].y, pathlist[number - 1].x, pathlist[number - 1].y); } } /*-------------------------------------------------------------------------*/ void makesplinepath(splineptr thespline, XPoint *pathlist) { XPoint *tmpptr = pathlist; UTransformbyCTM(DCTM, &(thespline->ctrl[0]), tmpptr, 1); UfTransformbyCTM(DCTM, thespline->points, ++tmpptr, INTSEGS); UTransformbyCTM(DCTM, &(thespline->ctrl[3]), tmpptr + INTSEGS, 1); } /*-------------------------------------------------------------------------*/ void UDrawSpline(splineptr thespline) { XPoint tmppoints[SPLINESEGS]; makesplinepath(thespline, tmppoints); strokepath(tmppoints, SPLINESEGS, thespline->style, thespline->width); } /*-------------------------------------------------------------------------*/ void UDrawEditSpline(splineptr thespline) { UDrawSpline(thespline); UDrawXLine(thespline->ctrl[0], thespline->ctrl[1]); UDrawXLine(thespline->ctrl[3], thespline->ctrl[2]); } /*-------------------------------------------------------------------------*/ void UDrawPolygon(polyptr thepoly) { XPoint *tmppoints = (pointlist) malloc(thepoly->number * sizeof(XPoint)); UTransformbyCTM(DCTM, thepoly->points, tmppoints, thepoly->number); strokepath(tmppoints, thepoly->number, thepoly->style, thepoly->width); free(tmppoints); } /*-------------------------------------------------------------------------*/ void UDrawArc(arcptr thearc) { XPoint tmppoints[RSTEPS + 2]; UfTransformbyCTM(DCTM, thearc->points, tmppoints, thearc->number); strokepath(tmppoints, thearc->number, thearc->style, thearc->width); } /*-------------------------------------------------------------------------*/ void UDrawPath(pathptr thepath) { XPoint *tmppoints = (pointlist) malloc(sizeof(XPoint)); genericptr *genpath; polyptr thepoly; splineptr thespline; arcptr thearc; int pathsegs = 0, curseg = 0; for (genpath = thepath->plist; genpath < thepath->plist + thepath->parts; genpath++) { switch(ELEMENTTYPE(*genpath)) { case POLYGON: thepoly = TOPOLY(genpath); pathsegs += thepoly->number; tmppoints = (pointlist) realloc(tmppoints, pathsegs * sizeof(XPoint)); UTransformbyCTM(DCTM, thepoly->points, tmppoints + curseg, thepoly->number); curseg = pathsegs; break; case SPLINE: thespline = TOSPLINE(genpath); pathsegs += SPLINESEGS; tmppoints = (pointlist) realloc(tmppoints, pathsegs * sizeof(XPoint)); makesplinepath(thespline, tmppoints + curseg); curseg = pathsegs; break; case ARC: thearc = TOARC(genpath); pathsegs += thearc->number; tmppoints = (pointlist) realloc(tmppoints, pathsegs * sizeof(XPoint)); UfTransformbyCTM(DCTM, thearc->points, tmppoints + curseg, thearc->number); curseg = pathsegs; break; } } strokepath(tmppoints, pathsegs, thepath->style, thepath->width); free(tmppoints); } /*----------------------------------------------------------------------*/ /* Main recursive object instance drawing routine. */ /* context is the instance information passed down from above */ /* theinstance is the object instance to be drawn */ /* level is the level of recursion */ /* passcolor is the inherited color value passed to object */ /*----------------------------------------------------------------------*/ void UDrawObject(objinstptr theinstance, short level, int passcolor, pushlistptr *stack) { genericptr *areagen; float tmpwidth; int defaultcolor = passcolor; int curcolor = passcolor; int thispart; XPoint bboxin[2], bboxout[2]; u_char xm, ym; objectptr theobject = theinstance->thisobject; /* All parts are given in the coordinate system of the object, unless */ /* this is the top-level object, in which they will be interpreted as */ /* relative to the screen. */ UPushCTM(); if (stack) push_stack(stack, theinstance); if (level != 0) UPreMultCTM(DCTM, theinstance->position, theinstance->scale, theinstance->rotation); /* do a quick test for intersection with the display window */ bboxin[0].x = theobject->bbox.lowerleft.x; bboxin[0].y = theobject->bbox.lowerleft.y; bboxin[1].x = theobject->bbox.lowerleft.x + theobject->bbox.width; bboxin[1].y = theobject->bbox.lowerleft.y + theobject->bbox.height; if (level == 0) extendschembbox(theinstance, &(bboxin[0]), &(bboxin[1])); UTransformbyCTM(DCTM, bboxin, bboxout, 2); xm = (bboxout[0].x < bboxout[1].x) ? 0 : 1; ym = (bboxout[0].y < bboxout[1].y) ? 0 : 1; if (bboxout[xm].x < areastruct.width && bboxout[ym].y < areastruct.height && bboxout[1 - xm].x > 0 && bboxout[1 - ym].y > 0) { /* make parameter substitutions */ psubstitute(theinstance); /* draw all of the elements */ tmpwidth = UTopTransScale(xobjs.pagelist[areastruct.page]->wirewidth); SetLineAttributes(dpy, areastruct.gc, tmpwidth, LineSolid, CapRound, JoinBevel); /* guard against plist being regenerated during a redraw by the */ /* expression parameter mechanism (should that be prohibited?) */ for (thispart = 0; thispart < theobject->parts; thispart++) { areagen = theobject->plist + thispart; if ((*areagen)->type & DRAW_HIDE) continue; if (defaultcolor != DOFORALL) { if ((*areagen)->color != curcolor) { if ((*areagen)->color == DEFAULTCOLOR) curcolor = defaultcolor; else curcolor = (*areagen)->color; XTopSetForeground(curcolor); } } switch(ELEMENTTYPE(*areagen)) { case(POLYGON): if (level == 0 || !((TOPOLY(areagen))->style & BBOX)) UDrawPolygon(TOPOLY(areagen)); break; case(SPLINE): UDrawSpline(TOSPLINE(areagen)); break; case(ARC): UDrawArc(TOARC(areagen)); break; case(PATH): UDrawPath(TOPATH(areagen)); break; case(GRAPHIC): UDrawGraphic(TOGRAPHIC(areagen)); break; case(OBJINST): if (areastruct.editinplace && stack && (TOOBJINST(areagen) == areastruct.topinstance)) { /* If stack matches areastruct.stack, then don't draw */ /* because it would be redundant. */ pushlistptr alist = *stack, blist = areastruct.stack; while (alist && blist) { if (alist->thisinst != blist->thisinst) break; alist = alist->next; blist = blist->next; } if ((!alist) || (!blist)) break; } UDrawObject(TOOBJINST(areagen), level + 1, curcolor, stack); break; case(LABEL): if (level == 0 || TOLABEL(areagen)->pin == False || (TOLABEL(areagen)->justify & PINVISIBLE)) UDrawString(TOLABEL(areagen), curcolor, theinstance); else if (level == 1 && TOLABEL(areagen)->pin && TOLABEL(areagen)->pin != INFO && areastruct.pinpointon) UDrawXDown(TOLABEL(areagen)); break; } } /* restore the color passed to the object, if different from current color */ if ((defaultcolor != DOFORALL) && (passcolor != curcolor)) { XTopSetForeground(passcolor); } } UPopCTM(); if (stack) pop_stack(stack); } /*----------------------------------------------------------------------*/ /* Recursively run through the current page and find any labels which */ /* are declared to be style LATEX. If "checkonly" is present, we set */ /* it to TRUE or FALSE depending on whether or not LATEX labels have */ /* been encountered. If NULL, then we write LATEX output appropriately */ /* to a file named with the page filename + suffix ".tex". */ /*----------------------------------------------------------------------*/ void UDoLatex(objinstptr theinstance, short level, FILE *f, float scale, int tx, int ty, Boolean *checkonly) { XPoint lpos, xlpos; XfPoint xfpos; labelptr thislabel; genericptr *areagen; objectptr theobject = theinstance->thisobject; char *ltext; int lrjust, tbjust; UPushCTM(); if (level != 0) UPreMultCTM(DCTM, theinstance->position, theinstance->scale, theinstance->rotation); /* make parameter substitutions */ psubstitute(theinstance); /* find all of the elements */ for (areagen = theobject->plist; areagen < theobject->plist + theobject->parts; areagen++) { switch(ELEMENTTYPE(*areagen)) { case(OBJINST): UDoLatex(TOOBJINST(areagen), level + 1, f, scale, tx, ty, checkonly); break; case(LABEL): thislabel = TOLABEL(areagen); if (level == 0 || thislabel->pin == False || (thislabel->justify & PINVISIBLE)) if (thislabel->justify & LATEXLABEL) { if (checkonly) { *checkonly = TRUE; return; } else { lpos.x = thislabel->position.x; lpos.y = thislabel->position.y; UTransformbyCTM(DCTM, &lpos, &xlpos, 1); xlpos.x += tx; xlpos.y += ty; xfpos.x = (float)xlpos.x * scale; xfpos.y = (float)xlpos.y * scale; xfpos.x /= 72.0; xfpos.y /= 72.0; xfpos.x -= 1.0; xfpos.y -= 1.0; xfpos.x += 0.056; xfpos.y += 0.056; ltext = textprint(thislabel->string, theinstance); tbjust = thislabel->justify & (NOTBOTTOM | TOP); lrjust = thislabel->justify & (NOTLEFT | RIGHT); fprintf(f, "\\putbox{%3.2fin}{%3.2fin}{", xfpos.x, xfpos.y); if (lrjust == (NOTLEFT | RIGHT)) fprintf(f, "\\rightbox{"); else if (lrjust == NOTLEFT) fprintf(f, "\\centbox{"); if (tbjust == (NOTBOTTOM | TOP)) fprintf(f, "\\topbox{"); else if (tbjust == NOTBOTTOM) fprintf(f, "\\midbox{"); fprintf(f, "%s", ltext); if (lrjust != NORMAL) fprintf(f, "}"); if (tbjust != NORMAL) fprintf(f, "}"); fprintf(f, "}%%\n"); free(ltext); } } break; } } UPopCTM(); } /*----------------------------------------------------------------------*/ /* Top level routine for writing LATEX output. */ /*----------------------------------------------------------------------*/ void TopDoLatex() { FILE *f; float psscale; int tx, ty, width, height; XPoint origin; Boolean checklatex = FALSE; char filename[50]; UDoLatex(areastruct.topinstance, 0, NULL, 1.0, 0, 0, &checklatex); if (checklatex == FALSE) return; /* No LaTeX labels to write */ sprintf(filename, "%s.tex", xobjs.pagelist[areastruct.page]->filename); f = fopen(filename, "w"); fprintf(f, "%% XCircuit output \"%s\" for LaTeX input from %s.ps\n", filename, xobjs.pagelist[areastruct.page]->filename); fprintf(f, "\\def\\putbox#1#2#3{\\makebox[0in][l]{\\makebox[#1][l]{}" "\\raisebox{\\baselineskip}[0in][0in]" "{\\raisebox{#2}[0in][0in]{#3}}}}\n"); fprintf(f, "\\def\\rightbox#1{\\makebox[0in][r]{#1}}\n"); fprintf(f, "\\def\\centbox#1{\\makebox[0in]{#1}}\n"); fprintf(f, "\\def\\topbox#1{\\raisebox{-\\baselineskip}[0in][0in]{#1}}\n"); fprintf(f, "\\def\\midbox#1{\\raisebox{-0.5\\baselineskip}[0in][0in]{#1}}\n"); fprintf(f, "\\begin{flushleft}\n"); filename[strlen(filename) - 4] = '\0'; if (strchr(filename, '.') == NULL) sprintf(filename + strlen(filename), ".ps"); fprintf(f, "\\epsfig{file=%s}\\\\\n", filename); psscale = getpsscale(xobjs.pagelist[areastruct.page]->outscale, areastruct.page); width = toplevelwidth(areastruct.topinstance, &origin.x); height = toplevelheight(areastruct.topinstance, &origin.y); tx = (int)(72 / psscale) - origin.x, ty = (int)(72 / psscale) - origin.y; fprintf(f, "%% translate x=%d y=%d scale %3.2f\n", tx, ty, psscale); UPushCTM(); /* Save current state */ UResetCTM(DCTM); /* Set to identity matrix */ UDoLatex(areastruct.topinstance, 0, f, psscale, tx, ty, NULL); UPopCTM(); /* Restore state */ fprintf(f, "\\end{flushleft}\n"); fclose(f); }