Logo Search packages:      
Sourcecode: xcircuit version File versions

functions.c

/*-------------------------------------------------------------------------*/
/* functions.c                                                 */
/* Copyright (c) 2002  Tim Edwards, Johns Hopkins University               */
/*-------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------*/
/*      written by Tim Edwards, 8/13/93                              */
/*-------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>

/*-------------------------------------------------------------------------*/
/* Local includes                                        */
/*-------------------------------------------------------------------------*/

#ifdef TCL_WRAPPER 
#include <tk.h>
#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 closepoint(polyptr curpoly, XPoint *cursloc)
{
   short curdist, mindist;
   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);
}

/*----------------------------------------------------------------------------*/
/* 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)));
}

/*------------------------------------------------------------------------*/

short UTopTransScale(float length)
{
   /* 0.5 gives nearent int but unfortunately gives odd results. . .*/
  
   return (short)(length * UTopScale() + 0.45);
}

/*----------------------------------------------------------------------*/
/* Get the cursor position                                  */
/*----------------------------------------------------------------------*/

XPoint UGetCursor()
{
   Window nullwin;
   int    nullint, xpos, ypos;
   u_int   nullui;
   XPoint newpos;
 
   XQueryPointer(dpy, areastruct.areawin, &nullwin, &nullwin, &nullint,
      &nullint, &xpos, &ypos, &nullui);

   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;
   short tmpx;

   for (current = ipoints; current < ipoints + number; current++, ptptr++) {
      tmpx = (short)(ctm->a * (float)current->x + ctm->b * (float)current->y
             + ctm->c);
      ptptr->y = (short)(ctm->d * (float)current->x + ctm->e * (float)current->y
             + ctm->f);
      ptptr->x = tmpx;
   }
}
  
/*------------------------------------------------------------------------*/
/* (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;

   for (current = fpoints; current < fpoints + number; current++, new++) {
      new->x = (short)(ctm->a * current->x + ctm->b * current->y + ctm->c);
      new->y = (short)(ctm->d * current->x + ctm->e * current->y + ctm->f);
   }
}

/*------------------------------------------------------------------------*/

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 ((*bboxgen)->type) {
      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)
{
   XPoint points[4];

   points[0].x = points[1].x = obbox->bbox.lowerleft.x;
   points[1].y = points[2].y = obbox->bbox.lowerleft.y + obbox->bbox.height;
   points[2].x = points[3].x = obbox->bbox.lowerleft.x + obbox->bbox.width;
   points[0].y = points[3].y = obbox->bbox.lowerleft.y;

   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;

#ifdef SCHEMA
   /* 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);
#endif

   UTransformPoints(points, npoints, 4, labox->position,
            labox->scale, labox->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((*bboxgen)->type) {

      case(OBJECT):
       objinstbbox(TOOBJINST(bboxgen), npoints);

         for (j = 0; j < 4; j++) {
            bboxcalc(npoints[j].x, llx, urx);
            bboxcalc(npoints[j].y, lly, ury);
         }
       break;

      case(LABEL):
       {
#ifdef SCHEMA
          /* 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);
          }
#endif
          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(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 ((*pelem)->type == OBJECT) {
       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);
   }
}

#ifdef SCHEMA
/*--------------------------------------------------------------*/
/* Free memory for the schematic bounding box                   */
/*--------------------------------------------------------------*/

void invalidateschembbox(objinstptr thisinst)
{
   if (thisinst->schembbox != NULL) {
      free(thisinst->schembbox);
      thisinst->schembbox = NULL;
   }
}
#endif

/*--------------------------------------------------------------*/
/* 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;

#ifdef SCHEMA
   short pllx, plly, purx, pury;
   Boolean hasschembbox = FALSE;
#endif 

   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;

#ifdef SCHEMA
   pllx = plly = 32767;
   purx = pury = -32768;
#endif

   for (gelem = thisobj->plist; gelem < thisobj->plist + thisobj->parts;
            gelem++) {
#ifdef SCHEMA
      /* pins which do not appear outside of the object     */
      /* contribute to the objects "schembbox".       */

      if ((*gelem)->type == LABEL) {
       labelptr btext = TOLABEL(gelem);
       if (btext->pin && !(btext->justify & PINVISIBLE)) {
          hasschembbox = TRUE;
          calcbboxsingle(gelem, thisinst, &pllx, &plly, &purx, &pury);
          continue;
       }
      }
#endif

      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;

#ifdef SCHEMA
   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);
#endif

}

/*--------------------------------------------------------------*/
/* 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, int mode)
{
   short i, j;
   objectptr pageobj;

   /* this is a modified version of updatepagebounds()      */
   /* mode == 0:  change bounds on pagelib and the page     */
   /* containing this object *instance*               */

   if (mode == 0) {
      /* pick up calling object and instance from the edit stack */
      objinstptr pinst = areastruct.stack->thisinst;

      pageobj = pinst->thisobject;

      for (j = 0; j < pageobj->parts; j++)
       if (*(pageobj->plist + j) == (genericptr)pinst) break;
      if (j == pageobj->parts) {
       Fprintf(stderr, "Error: Calling page does not contain expected objinst!\n");
       return;
      }
      calcbboxvalues(pinst, (genericptr *)(pageobj->plist + j));
      for (i = 0; i < xobjs.pages; i++) {
       if (xobjs.pagelist[i]->pageinst != NULL) {
          if (xobjs.pagelist[i]->pageinst->thisobject == pageobj) {
               updatepagelib(PAGELIB, i);
             break;
          }
       }
      }
      if (i == xobjs.pages)
       Fprintf(stderr, "Error: Calling page not found!\n");
   }

   /* mode == 1:  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.                                 */

   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, 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->num_params == 0) {
                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))) {
#ifdef SCHEMA
       /* pins which do not appear outside of the object    */
       /* are ignored now---will be computed per instance.  */

       if ((*bboxgen)->type == LABEL) {
          labelptr btext = TOLABEL(bboxgen);
          if (btext->pin && !(btext->justify & PINVISIBLE)) {
             goto nextgen;
          }
       }
#endif
       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 */

   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;

#ifdef SCHEMA
   extendschembbox(tinst, &origin, &corner);
#endif

   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);
   drawhbar(areastruct.scrollbarh, NULL, NULL);
   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 == COPY2_MODE ||
       eventmode == PUSH_MODE || eventmode == PRESS_MODE ||
       eventmode == CATALOG_MODE || eventmode == FONTCAT_MODE ||
       eventmode == FONTCAT2_MODE) {

      centerview(areastruct.topinstance);
      areastruct.lastbackground = NULL;
      renderbackground();
      refresh(NULL, NULL, NULL);
   }
}

/*---------------------------------------------------------*/
/* 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);

   XDrawLine(dpy, areastruct.areawin, areastruct.gc,
            newpt1.x, newpt1.y, newpt2.x, newpt2.y); 
} 

/*-------------------------------------------------------------------------*/

void UDrawLine(XPoint *pt1, XPoint *pt2)
{
   short tmpwidth = UTopTransScale(xobjs.pagelist[areastruct.page]->wirewidth);

   XSetLineAttributes(dpy, areastruct.gc, tmpwidth >= 2.0 ? (int)tmpwidth : 
      0, 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);
   XSetLineAttributes(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)
{
   XSetLineAttributes(dpy, areastruct.gc, 0, LineSolid, CapButt, JoinMiter);
   XDrawLine(dpy, areastruct.areawin, areastruct.gc, wpt->x - 3,
            wpt->y - 3, wpt->x + 3, wpt->y + 3);
   XDrawLine(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);
}

#ifdef SCHEMA

/*----------------------------------------------------------------------*/
/* 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 and height 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 llx, urx;
   short origin, corner;

   if (bbinst->schembbox == NULL) 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);

   return(corner - origin);
}

/*----------------------------------------------------------------------*/

int toplevelheight(objinstptr bbinst)
{
   short lly, ury;
   short origin, corner;

   if (bbinst->schembbox == NULL) 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);

   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;
}

#endif

/*----------------------------------------------------------------------*/
/* 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);

   XSetFunction(dpy, areastruct.gc, GXxor);
   XSetForeground(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;

#ifdef SCHEMA
   if (curlabel->pin) {
      pinadjust(tmpjust, &(points[0].x), &(points[0].y), 1);
      pinadjust(tmpjust, &(points[1].x), &(points[1].y), 1);
   }
#endif

   /* 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;

   XSetForeground(dpy, areastruct.gc, AUXCOLOR ^ BACKGROUND);
   XSetFunction(dpy, areastruct.gc, GXxor);

   user_to_window(cpt, &upt);
   user_to_window(opt, &vpt);

   XSetLineAttributes(dpy, areastruct.gc, 0, LineOnOffDash, CapButt, JoinMiter);
   XDrawLine(dpy, areastruct.areawin, areastruct.gc, vpt.x, vpt.y, upt.x, upt.y);

   XSetLineAttributes(dpy, areastruct.gc, 0, LineSolid, CapButt, JoinMiter);
   XDrawLine(dpy, areastruct.areawin, areastruct.gc, upt.x - 3, upt.y - 3,
            upt.x + 3, upt.y + 3);
   XDrawLine(dpy, areastruct.areawin, areastruct.gc, upt.x + 3, upt.y - 3,
            upt.x - 3, upt.y + 3);

   XSetFunction(dpy, areastruct.gc, areastruct.gctype);
   XSetForeground(dpy, areastruct.gc, areastruct.gccolor);
}

/*-------------------------------------------------------------------------*/

void UDrawBox(XPoint origin, XPoint corner)
{
   XPoint   worig, wcorn;

   user_to_window(origin, &worig);
   user_to_window(corner, &wcorn);

   XSetFunction(dpy, areastruct.gc, GXxor);
   XSetForeground(dpy, areastruct.gc, AUXCOLOR ^ BACKGROUND);
   XSetLineAttributes(dpy, areastruct.gc, 0, LineSolid, CapRound, JoinBevel);

   XDrawLine(dpy, areastruct.areawin, areastruct.gc, worig.x, worig.y,
            worig.x, wcorn.y);
   XDrawLine(dpy, areastruct.areawin, areastruct.gc, worig.x, wcorn.y,
            wcorn.x, wcorn.y);
   XDrawLine(dpy, areastruct.areawin, areastruct.gc, wcorn.x, wcorn.y,
            wcorn.x, worig.y);
   XDrawLine(dpy, areastruct.areawin, areastruct.gc, wcorn.x, worig.y,
            worig.x, worig.y);
}

/*-------------------------------------------------------------------------*/
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;

#ifdef SCHEMA
   /* Include any schematic labels in the bounding box.     */
   extendschembbox(bbinst, &origin, &corner);
#endif

   user_to_window(origin, &worig);
   user_to_window(corner, &wcorn);

   XSetForeground(dpy, areastruct.gc, BBOXCOLOR);
   XDrawLine(dpy, areastruct.areawin, areastruct.gc, worig.x, worig.y,
            worig.x, wcorn.y);
   XDrawLine(dpy, areastruct.areawin, areastruct.gc, worig.x, wcorn.y,
            wcorn.x, wcorn.y);
   XDrawLine(dpy, areastruct.areawin, areastruct.gc, wcorn.x, wcorn.y,
            wcorn.x, worig.y);
   XDrawLine(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];
   short        tmpwidth;

   tmpwidth = max(1, UTopTransScale(xobjs.pagelist[areastruct.page]->wirewidth *
      width));

   if (style & FILLED || (!(style & FILLED) && style & OPAQUE)) {
      if ((style & FILLSOLID) == FILLSOLID)
         XSetFillStyle(dpy, areastruct.gc, FillSolid);
      else if (!(style & FILLED)) {
         XSetFillStyle(dpy, areastruct.gc, FillOpaqueStippled); 
       XSetStipple(dpy, areastruct.gc, STIPPLE[7]);
      }
      else {
       if (style & OPAQUE)
            XSetFillStyle(dpy, areastruct.gc, FillOpaqueStippled);
       else
            XSetFillStyle(dpy, areastruct.gc, FillStippled);
         XSetStipple(dpy, areastruct.gc, STIPPLE[ (style &
                FILLSOLID) >> 5] );
      }
      XFillPolygon(dpy, areastruct.areawin, areastruct.gc, pathlist, number, Nonconvex,
            CoordModeOrigin);
      /* return to original state */
      XSetFillStyle(dpy, areastruct.gc, FillSolid);
   }
   if (!(style & NOBORDER)) {
      /* set up dots or dashes */
      if (style & DASHED) solidpart = (char)(4 * tmpwidth);
      else if (style & DOTTED) solidpart = (char)tmpwidth;
      sprintf(dashstring, "%c%c", solidpart, (char)(4 * tmpwidth));
      if (style & (DASHED | DOTTED)) {
         XSetDashes(dpy, areastruct.gc, 0, dashstring, 2);
         XSetLineAttributes(dpy, areastruct.gc, tmpwidth >= 2.0 ?
            (int)tmpwidth : 0, LineOnOffDash, CapButt, JoinBevel);
      }
      else
         XSetLineAttributes(dpy, areastruct.gc, tmpwidth >= 2.0 ?
            (int)tmpwidth : 0, LineSolid, CapRound, JoinBevel);

      /* draw the spline and close off if so specified */
      XDrawLines(dpy, areastruct.areawin, areastruct.gc, pathlist,
            number, CoordModeOrigin);
      if (!(style & UNCLOSED))
         XDrawLine(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((*genpath)->type) {
       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;
   short    tmpwidth;
   int            defaultcolor = passcolor;
   int            curcolor = passcolor;
   XPoint   bboxin, 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 */

   UTransformbyCTM(DCTM, &(theobject->bbox.lowerleft), &(bboxout[0]), 1);
   bboxin.x = theobject->bbox.lowerleft.x + theobject->bbox.width;
   bboxin.y = theobject->bbox.lowerleft.y + theobject->bbox.height; 
   UTransformbyCTM(DCTM, &bboxin, &(bboxout[1]), 1);

   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);
     XSetLineAttributes(dpy, areastruct.gc, tmpwidth >= 2.0 ? (int)tmpwidth :
            0, LineSolid, CapRound, JoinBevel);

     for (areagen = theobject->plist; areagen < theobject->plist +
           theobject->parts; areagen++) {

       if (defaultcolor != DOFORALL) {
        if ((*areagen)->color != curcolor) {
           if ((*areagen)->color == DEFAULTCOLOR)
            curcolor = defaultcolor;
           else
            curcolor = (*areagen)->color;
           XTopSetForeground(curcolor);
        }
       }

       switch((*areagen)->type) {
        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(OBJECT):
           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): 
#ifdef SCHEMA
           if (level == 0 || TOLABEL(areagen)->pin == False ||
                  (TOLABEL(areagen)->justify & PINVISIBLE))
#endif
             UDrawString(TOLABEL(areagen), curcolor, theinstance);
#ifdef SCHEMA
           else if (level == 1 && TOLABEL(areagen)->pin &&
                  TOLABEL(areagen)->pin != INFO && areastruct.pinpointon)
            UDrawXDown(TOLABEL(areagen));
#endif
           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);
}

/*-------------------------------------------------------------------------*/

Generated by  Doxygen 1.6.0   Back to index