1 /*-------------------------------------------------------------------------*/
2 /* functions.c 								   */
3 /* Copyright (c) 2002  Tim Edwards, Johns Hopkins University        	   */
4 /*-------------------------------------------------------------------------*/
5 
6 /*-------------------------------------------------------------------------*/
7 /*      written by Tim Edwards, 8/13/93    				   */
8 /*-------------------------------------------------------------------------*/
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <math.h>
14 #include <limits.h>
15 
16 #ifndef _MSC_VER
17 #include <X11/Intrinsic.h>
18 #include <X11/StringDefs.h>
19 #endif
20 
21 /*-------------------------------------------------------------------------*/
22 /* Local includes							   */
23 /*-------------------------------------------------------------------------*/
24 
25 #ifdef TCL_WRAPPER
26 #include <tk.h>
27 #endif
28 
29 #include "colordefs.h"
30 #include "xcircuit.h"
31 
32 /*----------------------------------------------------------------------*/
33 /* Function prototype declarations                                      */
34 /*----------------------------------------------------------------------*/
35 #include "prototypes.h"
36 
37 /*-------------------------------------------------------------------------*/
38 /* External Variable definitions					   */
39 /*-------------------------------------------------------------------------*/
40 
41 extern Display *dpy;
42 extern Pixmap STIPPLE[8];
43 extern XCWindowData *areawin;
44 extern Globaldata xobjs;
45 extern int number_colors;
46 extern colorindex *colorlist;
47 
48 /*------------------------------------------------------------------------*/
49 /* find the squared length of a wire (or distance between two points in   */
50 /* user space).								  */
51 /*------------------------------------------------------------------------*/
52 
sqwirelen(XPoint * userpt1,XPoint * userpt2)53 long sqwirelen(XPoint *userpt1, XPoint *userpt2)
54 {
55   long xdist, ydist;
56 
57   xdist = (long)userpt2->x - (long)userpt1->x;
58   ydist = (long)userpt2->y - (long)userpt1->y;
59   return (xdist * xdist + ydist * ydist);
60 }
61 
62 /*------------------------------------------------------------------------*/
63 /* floating-point version of the above					  */
64 /*------------------------------------------------------------------------*/
65 
fsqwirelen(XfPoint * userpt1,XfPoint * userpt2)66 float fsqwirelen(XfPoint *userpt1, XfPoint *userpt2)
67 {
68   float xdist, ydist;
69 
70   xdist = userpt2->x - userpt1->x;
71   ydist = userpt2->y - userpt1->y;
72   return (xdist * xdist + ydist * ydist);
73 }
74 
75 /*------------------------------------------------------------------------*/
76 /* Find absolute distance between two points in user space		  */
77 /*------------------------------------------------------------------------*/
78 
wirelength(XPoint * userpt1,XPoint * userpt2)79 int wirelength(XPoint *userpt1, XPoint *userpt2)
80 {
81   u_long xdist, ydist;
82 
83   xdist = (long)(userpt2->x) - (long)(userpt1->x);
84   ydist = (long)(userpt2->y) - (long)(userpt1->y);
85   return (int)sqrt((double)(xdist * xdist + ydist * ydist));
86 }
87 
88 /*------------------------------------------------------------------------*/
89 /* Find the closest (squared) distance from a point to a line		  */
90 /*------------------------------------------------------------------------*/
91 
finddist(XPoint * linept1,XPoint * linept2,XPoint * userpt)92 long finddist(XPoint *linept1, XPoint *linept2, XPoint *userpt)
93 {
94    long a, b, c, frac;
95    float protod;
96 
97    c = sqwirelen(linept1, linept2);
98    a = sqwirelen(linept1, userpt);
99    b = sqwirelen(linept2, userpt);
100    frac = a - b;
101    if (frac >= c) return b;	  /* "=" is important if c = 0 ! */
102    else if (-frac >= c) return a;
103    else {
104       protod = (float)(c + a - b);
105       return (a - (long)((protod * protod) / (float)(c << 2)));
106    }
107 }
108 
109 /*----------------------------------------------------------------------*/
110 /* Decompose an arc segment into one to four bezier curves according	*/
111 /* the approximation algorithm lifted from the paper by  L. Maisonobe	*/
112 /* (spaceroots.org).  This decomposition is done when an arc in a path	*/
113 /* is read from an (older) xcircuit file, or when an arc is a selected	*/
114 /* item when a path is created.	 Because arcs are decomposed when	*/
115 /* encountered, we assume that the arc is the last element of the path.	*/
116 /*----------------------------------------------------------------------*/
117 
decomposearc(pathptr thepath,XPoint * startpoint)118 void decomposearc(pathptr thepath, XPoint *startpoint)
119 {
120    float fnc, ang1, ang2;
121    short ncurves, i;
122    arcptr thearc;
123    genericptr *pgen;
124    splineptr *newspline;
125    polyptr *newpoly;
126    double nu1, nu2, lambda1, lambda2, alpha, tansq;
127    XfPoint E1, E2, Ep1, Ep2;
128    XPoint P1;
129    Boolean reverse = FALSE;
130 
131    pgen = thepath->plist + thepath->parts - 1;
132    if (ELEMENTTYPE(*pgen) != ARC) return;
133    thearc = TOARC(pgen);
134 
135    if (thearc->radius < 0) {
136       reverse = TRUE;
137       thearc->radius = -thearc->radius;
138    }
139 
140    fnc = (thearc->angle2 - thearc->angle1) / 90.0;
141    ncurves = (short)fnc;
142    if (fnc - (float)((int)fnc) > 0.01) ncurves++;
143 
144    thepath->parts--;  /* Forget the arc */
145 
146    for (i = 0; i < ncurves; i++) {
147       if (reverse) {			/* arc path is reverse direction */
148          if (i == 0)
149 	    ang1 = thearc->angle2;
150          else
151 	    ang1 -= 90;
152 
153          if (i == ncurves - 1)
154 	    ang2 = thearc->angle1;
155          else
156 	    ang2 = ang1 - 90;
157       }
158       else {				/* arc path is forward direction */
159          if (i == 0)
160 	    ang1 = thearc->angle1;
161          else
162 	    ang1 += 90;
163 
164          if (i == ncurves - 1)
165 	    ang2 = thearc->angle2;
166          else
167 	    ang2 = ang1 + 90;
168       }
169 
170       lambda1 = (double)ang1 * RADFAC;
171       lambda2 = (double)ang2 * RADFAC;
172 
173       nu1 = atan2(sin(lambda1) / (double)thearc->yaxis,
174 		cos(lambda1) / (double)thearc->radius);
175       nu2 = atan2(sin(lambda2) / (double)thearc->yaxis,
176 		cos(lambda2) / (double)thearc->radius);
177       E1.x = (float)thearc->position.x +
178 		(float)thearc->radius * (float)cos(nu1);
179       E1.y = (float)thearc->position.y +
180 		(float)thearc->yaxis * (float)sin(nu1);
181       E2.x = (float)thearc->position.x +
182 		(float)thearc->radius * (float)cos(nu2);
183       E2.y = (float)thearc->position.y +
184 		(float)thearc->yaxis * (float)sin(nu2);
185       Ep1.x = -(float)thearc->radius * (float)sin(nu1);
186       Ep1.y = (float)thearc->yaxis * (float)cos(nu1);
187       Ep2.x = -(float)thearc->radius * (float)sin(nu2);
188       Ep2.y = (float)thearc->yaxis * (float)cos(nu2);
189 
190       P1.x = (int)(roundf(E1.x));
191       P1.y = (int)(roundf(E1.y));
192 
193       tansq = tan((nu2 - nu1) / 2.0);
194       tansq *= tansq;
195       alpha = sin(nu2 - nu1) * 0.33333 * (sqrt(4 + (3 * tansq)) - 1);
196 
197       /* If the arc 1st point is not the same as the previous path point,
198        * then add a straight line to the 1st arc point (mimics PostScript
199        * behavior).
200        */
201 
202       if (startpoint && (i == 0)) {
203          if ((startpoint->x != P1.x) || (startpoint->y != P1.y)) {
204 	    NEW_POLY(newpoly, thepath);
205 	    polydefaults(*newpoly, 2, startpoint->x, startpoint->y);
206 	    (*newpoly)->style = thearc->style;
207 	    (*newpoly)->color = thearc->color;
208 	    (*newpoly)->width = thearc->width;
209             (*newpoly)->points[1].x = P1.x;
210             (*newpoly)->points[1].y = P1.y;
211 	 }
212       }
213 
214       NEW_SPLINE(newspline, thepath);
215       splinedefaults(*newspline, 0, 0);
216       (*newspline)->style = thearc->style;
217       (*newspline)->color = thearc->color;
218       (*newspline)->width = thearc->width;
219 
220       (*newspline)->ctrl[0].x = P1.x;
221       (*newspline)->ctrl[0].y = P1.y;
222 
223       (*newspline)->ctrl[1].x = (int)(roundf(E1.x + alpha * Ep1.x));
224       (*newspline)->ctrl[1].y = (int)(roundf(E1.y + alpha * Ep1.y));
225 
226       (*newspline)->ctrl[2].x = (int)(roundf(E2.x - alpha * Ep2.x));
227       (*newspline)->ctrl[2].y = (int)(roundf(E2.y - alpha * Ep2.y));
228 
229       (*newspline)->ctrl[3].x = (int)(roundf(E2.x));
230       (*newspline)->ctrl[3].y = (int)(roundf(E2.y));
231 
232       calcspline(*newspline);
233    }
234 
235    /* Delete the arc */
236    free_single((genericptr)thearc);
237 }
238 
239 /*----------------------------------------------------------------------*/
240 /* Calculate points for an arc						*/
241 /*----------------------------------------------------------------------*/
242 
calcarc(arcptr thearc)243 void calcarc(arcptr thearc)
244 {
245    short idx;
246    int sarc;
247    float theta, delta;
248 
249    /* assume that angle2 > angle1 always: must be guaranteed by other routines */
250 
251    sarc = (int)(thearc->angle2 - thearc->angle1) * RSTEPS;
252    thearc->number = (sarc / 360) + 1;
253    if (sarc % 360 != 0) thearc->number++;
254 
255    delta = RADFAC * ((float)(thearc->angle2 - thearc->angle1) / (thearc->number - 1));
256    theta = thearc->angle1 * RADFAC;
257 
258    for (idx = 0; idx < thearc->number - 1; idx++) {
259       thearc->points[idx].x = (float)thearc->position.x +
260 	   fabs((float)thearc->radius) * cos(theta);
261       thearc->points[idx].y = (float)thearc->position.y +
262 	   (float)thearc->yaxis * sin(theta);
263       theta += delta;
264    }
265 
266    /* place last point exactly to avoid roundoff error */
267 
268    theta = thearc->angle2 * RADFAC;
269    thearc->points[thearc->number - 1].x = (float)thearc->position.x +
270 	   fabs((float)thearc->radius) * cos(theta);
271    thearc->points[thearc->number - 1].y = (float)thearc->position.y +
272 	   (float)thearc->yaxis * sin(theta);
273 
274    if (thearc->radius < 0) reversefpoints(thearc->points, thearc->number);
275 }
276 
277 /*------------------------------------------------------------------------*/
278 /* Create a Bezier curve approximation from control points		  */
279 /* (using PostScript formula for Bezier cubic curve)			  */
280 /*------------------------------------------------------------------------*/
281 
282 float par[INTSEGS];
283 float parsq[INTSEGS];
284 float parcb[INTSEGS];
285 
initsplines()286 void initsplines()
287 {
288    float t;
289    short idx;
290 
291    for (idx = 0; idx < INTSEGS; idx++) {
292       t = (float)(idx + 1) / (INTSEGS + 1);
293       par[idx] = t;
294       parsq[idx] = t * t;
295       parcb[idx] = parsq[idx] * t;
296    }
297 }
298 
299 /*------------------------------------------------------------------------*/
300 /* Compute spline coefficients						  */
301 /*------------------------------------------------------------------------*/
302 
computecoeffs(splineptr thespline,float * ax,float * bx,float * cx,float * ay,float * by,float * cy)303 void computecoeffs(splineptr thespline, float *ax, float *bx, float *cx,
304 	float *ay, float *by, float *cy)
305 {
306    *cx = 3.0 * (float)(thespline->ctrl[1].x - thespline->ctrl[0].x);
307    *bx = 3.0 * (float)(thespline->ctrl[2].x - thespline->ctrl[1].x) - *cx;
308    *ax = (float)(thespline->ctrl[3].x - thespline->ctrl[0].x) - *cx - *bx;
309 
310    *cy = 3.0 * (float)(thespline->ctrl[1].y - thespline->ctrl[0].y);
311    *by = 3.0 * (float)(thespline->ctrl[2].y - thespline->ctrl[1].y) - *cy;
312    *ay = (float)(thespline->ctrl[3].y - thespline->ctrl[0].y) - *cy - *by;
313 }
314 
315 /*------------------------------------------------------------------------*/
316 
calcspline(splineptr thespline)317 void calcspline(splineptr thespline)
318 {
319    float ax, bx, cx, ay, by, cy;
320    short idx;
321 
322    computecoeffs(thespline, &ax, &bx, &cx, &ay, &by, &cy);
323    for (idx = 0; idx < INTSEGS; idx++) {
324       thespline->points[idx].x = ax * parcb[idx] + bx * parsq[idx] +
325 	 cx * par[idx] + (float)thespline->ctrl[0].x;
326       thespline->points[idx].y = ay * parcb[idx] + by * parsq[idx] +
327 	 cy * par[idx] + (float)thespline->ctrl[0].y;
328    }
329 }
330 
331 /*------------------------------------------------------------------------*/
332 /* Find the (x,y) position and tangent rotation of a point on a spline    */
333 /*------------------------------------------------------------------------*/
334 
findsplinepos(splineptr thespline,float t,XPoint * retpoint,float * retrot)335 void findsplinepos(splineptr thespline, float t, XPoint *retpoint, float *retrot)
336 {
337    float ax, bx, cx, ay, by, cy;
338    float tsq = t * t;
339    float tcb = tsq * t;
340    double dxdt, dydt;
341 
342    computecoeffs(thespline, &ax, &bx, &cx, &ay, &by, &cy);
343    retpoint->x = (short)(ax * tcb + bx * tsq + cx * t + (float)thespline->ctrl[0].x);
344    retpoint->y = (short)(ay * tcb + by * tsq + cy * t + (float)thespline->ctrl[0].y);
345 
346    if (retrot != NULL) {
347       dxdt = (double)(3 * ax * tsq + 2 * bx * t + cx);
348       dydt = (double)(3 * ay * tsq + 2 * by * t + cy);
349       *retrot = INVRFAC * atan2(dxdt, dydt);  /* reversed y, x */
350       if (*retrot < 0) *retrot += 360;
351    }
352 }
353 
354 /*------------------------------------------------------------------------*/
355 /* floating-point version of the above					  */
356 /*------------------------------------------------------------------------*/
357 
ffindsplinepos(splineptr thespline,float t,XfPoint * retpoint)358 void ffindsplinepos(splineptr thespline, float t, XfPoint *retpoint)
359 {
360    float ax, bx, cx, ay, by, cy;
361    float tsq = t * t;
362    float tcb = tsq * t;
363 
364    computecoeffs(thespline, &ax, &bx, &cx, &ay, &by, &cy);
365    retpoint->x = ax * tcb + bx * tsq + cx * t + (float)thespline->ctrl[0].x;
366    retpoint->y = ay * tcb + by * tsq + cy * t + (float)thespline->ctrl[0].y;
367 }
368 
369 /*------------------------------------------------------------------------*/
370 /* Find the closest distance between a point and a spline and return the  */
371 /* fractional distance along the spline of this point.			  */
372 /*------------------------------------------------------------------------*/
373 
findsplinemin(splineptr thespline,XPoint * upoint)374 float findsplinemin(splineptr thespline, XPoint *upoint)
375 {
376    XfPoint 	*spt, flpt, newspt;
377    float	minval = 1000000, tval, hval, ndist;
378    short	j, ival;
379 
380    flpt.x = (float)(upoint->x);
381    flpt.y = (float)(upoint->y);
382 
383    /* get estimate from precalculated spline points */
384 
385    for (spt = thespline->points; spt < thespline->points + INTSEGS;
386 	spt++) {
387       ndist = fsqwirelen(spt, &flpt);
388       if (ndist < minval) {
389 	 minval = ndist;
390 	 ival = (short)(spt - thespline->points);
391       }
392    }
393    tval = (float)(ival + 1) / (INTSEGS + 1);
394    hval = 0.5 / (INTSEGS + 1);
395 
396    /* short fixed iterative loop to converge on minimum t */
397 
398    for (j = 0; j < 5; j++) {
399       tval += hval;
400       ffindsplinepos(thespline, tval, &newspt);
401       ndist = fsqwirelen(&newspt, &flpt);
402       if (ndist < minval) minval = ndist;
403       else {
404          tval -= hval * 2;
405          ffindsplinepos(thespline, tval, &newspt);
406          ndist = fsqwirelen(&newspt, &flpt);
407          if (ndist < minval) minval = ndist;
408 	 else tval += hval;
409       }
410       hval /= 2;
411    }
412 
413    if (tval < 0.1) {
414       if ((float)sqwirelen(&(thespline->ctrl[0]), upoint) < minval) tval = 0;
415    }
416    else if (tval > 0.9) {
417       if ((float)sqwirelen(&(thespline->ctrl[3]), upoint) < minval) tval = 1;
418    }
419    return tval;
420 }
421 
422 /*----------------------------------------------------------------------*/
423 /* Convert a polygon to a Bezier curve path				*/
424 /* Curve must be selected and there must be only one selection.		*/
425 /*									*/
426 /* Note that this routine will draw inside the perimeter of a convex	*/
427 /* hull.  A routine that places spline endpoints on the polygon		*/
428 /* vertices will draw outside the perimeter of a convex hull.  An	*/
429 /* optimal algorithm presumably zeros the total area between the curve	*/
430 /* and the polygon (positive and negative), but I haven't worked out	*/
431 /* what that solution is.  The algorithm below seems good enough for	*/
432 /* most purposes.							*/
433 /*----------------------------------------------------------------------*/
434 
converttocurve()435 void converttocurve()
436 {
437    genericptr *ggen;
438    splineptr *newspline;
439    polyptr thispoly;
440    pathptr *newpath;
441    short *newselect;
442    XPoint firstpoint, lastpoint, initpoint;
443    int i, numpoints;
444 
445    if (areawin->selects != 1) return;
446 
447    thispoly = TOPOLY(topobject->plist + (*areawin->selectlist));
448    if (ELEMENTTYPE(thispoly) != POLYGON) return;
449    if (thispoly->number < 3) return;	/* Will not convert */
450 
451    standard_element_delete(ERASE);
452    if ((thispoly->style & UNCLOSED) && (thispoly->number == 3)) {
453       NEW_SPLINE(newspline, topobject);
454       splinedefaults(*newspline, 0, 0);
455       (*newspline)->ctrl[0] = thispoly->points[0];
456       (*newspline)->ctrl[1] = thispoly->points[1];
457       (*newspline)->ctrl[2] = thispoly->points[1];
458       (*newspline)->ctrl[3] = thispoly->points[2];
459    }
460    else {
461       numpoints = thispoly->number;
462 
463       /* If the polygon is closed but the first and last points	*/
464       /* overlap, treat the last point as if it doesn't exist.	*/
465 
466       if (!(thispoly->style & UNCLOSED))
467          if ((thispoly->points[0].x == thispoly->points[thispoly->number - 1].x)
468 		&& (thispoly->points[0].y ==
469 		thispoly->points[thispoly->number - 1].y))
470 	    numpoints--;
471 
472       NEW_PATH(newpath, topobject);
473       pathdefaults(*newpath, 0, 0);
474       (*newpath)->style = thispoly->style;
475 
476       if (!(thispoly->style & UNCLOSED)) {
477 	 lastpoint = thispoly->points[numpoints - 1];
478 	 initpoint.x = (lastpoint.x + thispoly->points[0].x) / 2;
479 	 initpoint.y = (lastpoint.y + thispoly->points[0].y) / 2;
480 	 firstpoint.x = (thispoly->points[0].x
481 		+ thispoly->points[1].x) / 2;
482 	 firstpoint.y = (thispoly->points[0].y
483 			+ thispoly->points[1].y) / 2;
484 
485          NEW_SPLINE(newspline, (*newpath));
486 	 splinedefaults(*newspline, 0, 0);
487 	 (*newspline)->ctrl[0] = initpoint;
488 	 (*newspline)->ctrl[1] = thispoly->points[0];
489 	 (*newspline)->ctrl[2] = thispoly->points[0];
490 	 (*newspline)->ctrl[3] = firstpoint;
491          calcspline(*newspline);
492       }
493       else
494          firstpoint = thispoly->points[0];
495 
496       for (i = 0; i < numpoints - ((!(thispoly->style & UNCLOSED)) ?
497 		2 : 3); i++) {
498 	 lastpoint.x = (thispoly->points[i + 1].x
499 		+ thispoly->points[i + 2].x) / 2;
500 	 lastpoint.y = (thispoly->points[i + 1].y
501 			+ thispoly->points[i + 2].y) / 2;
502 
503          NEW_SPLINE(newspline, (*newpath));
504 	 splinedefaults(*newspline, 0, 0);
505 	 (*newspline)->ctrl[0] = firstpoint;
506 	 (*newspline)->ctrl[1] = thispoly->points[i + 1];
507 	 (*newspline)->ctrl[2] = thispoly->points[i + 1];
508 	 (*newspline)->ctrl[3] = lastpoint;
509 	 firstpoint = lastpoint;
510          calcspline(*newspline);
511       }
512       if (!(thispoly->style & UNCLOSED))
513 	 lastpoint = initpoint;
514       else
515 	 lastpoint = thispoly->points[i + 2];
516 
517       NEW_SPLINE(newspline, (*newpath));
518       splinedefaults(*newspline, 0, 0);
519       (*newspline)->ctrl[0] = firstpoint;
520       (*newspline)->ctrl[1] = thispoly->points[i + 1];
521       (*newspline)->ctrl[2] = thispoly->points[i + 1];
522       (*newspline)->ctrl[3] = lastpoint;
523    }
524    calcspline(*newspline);
525    calcbbox(areawin->topinstance);
526    setoptionmenu();
527    drawarea(NULL, NULL, NULL);
528 }
529 
530 /*----------------------------------------------------------------------*/
531 /* Find closest point of a polygon to the cursor			*/
532 /*----------------------------------------------------------------------*/
533 
closepointdistance(polyptr curpoly,XPoint * cursloc,short * mindist)534 short closepointdistance(polyptr curpoly, XPoint *cursloc, short *mindist)
535 {
536    short curdist;
537    XPoint *curpt, *savept;
538 
539    curpt = savept = curpoly->points;
540    *mindist = wirelength(curpt, cursloc);
541    while (++curpt < curpoly->points + curpoly->number) {
542       curdist = wirelength(curpt, cursloc);
543       if (curdist < *mindist) {
544          *mindist = curdist;
545          savept = curpt;
546       }
547    }
548    return (short)(savept - curpoly->points);
549 }
550 
551 /*----------------------------------------------------------------------------*/
552 /* Find closest point of a polygon to the cursor			      */
553 /*----------------------------------------------------------------------------*/
554 
closepoint(polyptr curpoly,XPoint * cursloc)555 short closepoint(polyptr curpoly, XPoint *cursloc)
556 {
557    short mindist;
558    return closepointdistance(curpoly, cursloc, &mindist);
559 }
560 
561 /*----------------------------------------------------------------------------*/
562 /* Find the distance to the closest point of a polygon to the cursor	      */
563 /*----------------------------------------------------------------------------*/
564 
closedistance(polyptr curpoly,XPoint * cursloc)565 short closedistance(polyptr curpoly, XPoint *cursloc)
566 {
567    short mindist;
568    closepointdistance(curpoly, cursloc, &mindist);
569    return mindist;
570 }
571 
572 /*----------------------------------------------------------------------------*/
573 /* Coordinate system transformations 					      */
574 /*----------------------------------------------------------------------------*/
575 
576 /*------------------------------------------------------------------------------*/
577 /*  Check screen bounds:  minimum, maximum scale and translation is determined	*/
578 /*  by values which fit in an X11 type XPoint (short int).  If the window	*/
579 /*  extremes exceed type short when mapped to user space, or if the page 	*/
580 /*  bounds exceed type short when mapped to X11 window space, return error.	*/
581 /*------------------------------------------------------------------------------*/
582 
checkbounds()583 short checkbounds()
584 {
585    long lval;
586 
587    /* check window-to-user space */
588 
589    lval = 2 * (long)((float) (areawin->width) / areawin->vscale) +
590 	(long)areawin->pcorner.x;
591    if (lval != (long)((short)lval)) return -1;
592    lval = 2 * (long)((float) (areawin->height) / areawin->vscale) +
593 	(long)areawin->pcorner.y;
594    if (lval != (long)((short)lval)) return -1;
595 
596    /* check user-to-window space */
597 
598    lval = (long)((float)(topobject->bbox.lowerleft.x - areawin->pcorner.x) *
599 	areawin->vscale);
600    if (lval != (long)((short)lval)) return -1;
601    lval = (long)areawin->height - (long)((float)(topobject->bbox.lowerleft.y -
602 	areawin->pcorner.y) * areawin->vscale);
603    if (lval != (long)((short)lval)) return -1;
604 
605    lval = (long)((float)(topobject->bbox.lowerleft.x + topobject->bbox.width -
606 	areawin->pcorner.x) * areawin->vscale);
607    if (lval != (long)((short)lval)) return -1;
608    lval = (long)areawin->height - (long)((float)(topobject->bbox.lowerleft.y +
609 	topobject->bbox.height - areawin->pcorner.y) * areawin->vscale);
610    if (lval != (long)((short)lval)) return -1;
611 
612    return 0;
613 }
614 
615 /*------------------------------------------------------------------------*/
616 /* Transform X-window coordinate to xcircuit coordinate system		  */
617 /*------------------------------------------------------------------------*/
618 
window_to_user(short xw,short yw,XPoint * upt)619 void window_to_user(short xw, short yw, XPoint *upt)
620 {
621   float tmpx, tmpy;
622 
623   tmpx = (float)xw / areawin->vscale + (float)areawin->pcorner.x;
624   tmpy = (float)(areawin->height - yw) / areawin->vscale +
625 	(float)areawin->pcorner.y;
626 
627   tmpx += (tmpx > 0) ? 0.5 : -0.5;
628   tmpy += (tmpy > 0) ? 0.5 : -0.5;
629 
630   upt->x = (short)tmpx;
631   upt->y = (short)tmpy;
632 }
633 
634 /*------------------------------------------------------------------------*/
635 /* Transform xcircuit coordinate back to X-window coordinate system       */
636 /*------------------------------------------------------------------------*/
637 
user_to_window(XPoint upt,XPoint * wpt)638 void user_to_window(XPoint upt, XPoint *wpt)
639 {
640   float tmpx, tmpy;
641 
642   tmpx = (float)(upt.x - areawin->pcorner.x) * areawin->vscale;
643   tmpy = (float)areawin->height - (float)(upt.y - areawin->pcorner.y)
644 	* areawin->vscale;
645 
646   tmpx += (tmpx > 0) ? 0.5 : -0.5;
647   tmpy += (tmpy > 0) ? 0.5 : -0.5;
648 
649   wpt->x = (short)tmpx;
650   wpt->y = (short)tmpy;
651 }
652 
653 /*----------------------------------------------------------------------*/
654 /* Transformations in the object hierarchy				*/
655 /*----------------------------------------------------------------------*/
656 
657 /*----------------------------------------------------------------------*/
658 /* Return rotation relative to a specific CTM				*/
659 /*----------------------------------------------------------------------*/
660 
UGetCTMRotation(Matrix * ctm)661 float UGetCTMRotation(Matrix *ctm)
662 {
663    float rads = (float)atan2((double)(ctm->d), (double)(ctm->a));
664    return rads / RADFAC;
665 }
666 
667 /*----------------------------------------------------------------------*/
668 /* Return rotation relative to the top level				*/
669 /* Note that UTopRotation() is also the rotation relative to the window	*/
670 /* since the top-level drawing page is always upright relative to the	*/
671 /* window.  Thus, there is no routine UTopDrawingRotation().		*/
672 /*----------------------------------------------------------------------*/
673 
UTopRotation()674 float UTopRotation()
675 {
676    return UGetCTMRotation(DCTM);
677 }
678 
679 /*----------------------------------------------------------------------*/
680 /* Return scale relative to a specific CTM				*/
681 /*----------------------------------------------------------------------*/
682 
UGetCTMScale(Matrix * ctm)683 float UGetCTMScale(Matrix *ctm)
684 {
685    return (float)(sqrt((double)(ctm->a * ctm->a + ctm->d * ctm->d)));
686 }
687 
688 /*----------------------------------------------------------------------*/
689 /* Return scale relative to window					*/
690 /*----------------------------------------------------------------------*/
691 
UTopScale()692 float UTopScale()
693 {
694    return UGetCTMScale(DCTM);
695 }
696 
697 /*----------------------------------------------------------------------*/
698 /* Return scale multiplied by length					*/
699 /*----------------------------------------------------------------------*/
700 
UTopTransScale(float length)701 float UTopTransScale(float length)
702 {
703    return (float)(length * UTopScale());
704 }
705 
706 /*----------------------------------------------------------------------*/
707 /* Return scale relative to the top-level schematic (not the window)	*/
708 /*----------------------------------------------------------------------*/
709 
UTopDrawingScale()710 float UTopDrawingScale()
711 {
712    Matrix lctm, wctm;
713    UCopyCTM(DCTM, &lctm);
714    UResetCTM(&wctm);
715    UMakeWCTM(&wctm);
716    InvertCTM(&wctm);
717    UPreMultCTMbyMat(&wctm, &lctm);
718    return UGetCTMScale(&wctm);
719 }
720 
721 /*----------------------------------------------------------------------*/
722 /* Return position offset relative to a specific CTM			*/
723 /*----------------------------------------------------------------------*/
724 
UGetCTMOffset(Matrix * ctm,int * offx,int * offy)725 void UGetCTMOffset(Matrix *ctm, int *offx, int *offy)
726 {
727    if (offx) *offx = (int)ctm->c;
728    if (offy) *offy = (int)ctm->f;
729 }
730 
731 /*----------------------------------------------------------------------*/
732 /* Return position offset relative to top-level				*/
733 /*----------------------------------------------------------------------*/
734 
UTopOffset(int * offx,int * offy)735 void UTopOffset(int *offx, int *offy)
736 {
737    UGetCTMOffset(DCTM, offx, offy);
738 }
739 
740 /*----------------------------------------------------------------------*/
741 /* Return postion relative to the top-level schematic (not the window)	*/
742 /*----------------------------------------------------------------------*/
743 
UTopDrawingOffset(int * offx,int * offy)744 void UTopDrawingOffset(int *offx, int *offy)
745 {
746    Matrix lctm, wctm;
747    UCopyCTM(DCTM, &lctm);
748    UResetCTM(&wctm);
749    UMakeWCTM(&wctm);
750    InvertCTM(&wctm);
751    UPreMultCTMbyMat(&wctm, &lctm);
752    UGetCTMOffset(&wctm, offx, offy);
753 }
754 
755 /*----------------------------------------------------------------------*/
756 /* Get the cursor position						*/
757 /*----------------------------------------------------------------------*/
758 
UGetCursor()759 XPoint UGetCursor()
760 {
761    Window nullwin;
762    int    nullint, xpos, ypos;
763    u_int   nullui;
764    XPoint newpos;
765 
766    if (areawin->area == NULL) {
767       newpos.x = newpos.y = 0;
768       return newpos;
769    }
770 
771 #ifdef TCL_WRAPPER
772    /* Don't use areawin->window;  if called from inside an object	*/
773    /* (e.g., "here" in a Tcl expression), areawin->window will be	*/
774    /* an off-screen pixmap, and cause a crash.				*/
775 #ifndef _MSC_VER
776    XQueryPointer(dpy, Tk_WindowId(areawin->area), &nullwin, &nullwin,
777 	&nullint, &nullint, &xpos, &ypos, &nullui);
778 #else
779    XQueryPointer_TkW32(dpy, Tk_WindowId(areawin->area), &nullwin, &nullwin,
780 	&nullint, &nullint, &xpos, &ypos, &nullui);
781 #endif
782 #else
783    XQueryPointer(dpy, areawin->window, &nullwin, &nullwin, &nullint,
784 	&nullint, &xpos, &ypos, &nullui);
785 #endif
786 
787    newpos.x = xpos;
788    newpos.y = ypos;
789 
790    return newpos;
791 }
792 
793 /*----------------------------------------------------------------------*/
794 /* Get the cursor position and translate to user coordinates		*/
795 /*----------------------------------------------------------------------*/
796 
UGetCursorPos()797 XPoint UGetCursorPos()
798 {
799    XPoint winpos, userpos;
800 
801    if (areawin->area == NULL) {
802       winpos.x = winpos.y = 0;
803    }
804    else
805       winpos = UGetCursor();
806 
807    window_to_user(winpos.x, winpos.y, &userpos);
808 
809    return userpos;
810 }
811 
812 /*----------------------------------------------------------------------*/
813 /* Translate a point to the nearest snap-to grid point			*/
814 /*----------------------------------------------------------------------*/
815 /* user coordinates to user coordinates version 			*/
816 
u2u_snap(XPoint * uvalue)817 void u2u_snap(XPoint *uvalue)
818 {
819    float tmpx, tmpy;
820    float tmpix, tmpiy;
821 
822    if (areawin->snapto) {
823       tmpx = (float)uvalue->x / xobjs.pagelist[areawin->page]->snapspace;
824       if (tmpx > 0)
825 	 tmpix = (float)((int)(tmpx + 0.5));
826       else
827          tmpix = (float)((int)(tmpx - 0.5));
828 
829       tmpy = (float)uvalue->y / xobjs.pagelist[areawin->page]->snapspace;
830       if (tmpy > 0)
831          tmpiy = (float)((int)(tmpy + 0.5));
832       else
833          tmpiy = (float)((int)(tmpy - 0.5));
834 
835       tmpix *= xobjs.pagelist[areawin->page]->snapspace;
836       tmpix += (tmpix > 0) ? 0.5 : -0.5;
837       tmpiy *= xobjs.pagelist[areawin->page]->snapspace;
838       tmpiy += (tmpiy > 0) ? 0.5 : -0.5;
839 
840       uvalue->x = (int)tmpix;
841       uvalue->y = (int)tmpiy;
842    }
843 }
844 
845 /*------------------------------------------------------------------------*/
846 /* window coordinates to user coordinates version 			  */
847 /*------------------------------------------------------------------------*/
848 
snap(short valuex,short valuey,XPoint * returnpt)849 void snap(short valuex, short valuey, XPoint *returnpt)
850 {
851    window_to_user(valuex, valuey, returnpt);
852    u2u_snap(returnpt);
853 }
854 
855 /*------------------------------------------------------------------------*/
856 /* Transform object coordinates through scale, translation, and rotation  */
857 /* This routine attempts to match the PostScript definition of trans-     */
858 /*    formation matrices.						  */
859 /*------------------------------------------------------------------------*/
860 
861 /*------------------------------------------------------------------------*/
862 /* Current transformation matrix manipulation routines			  */
863 /*------------------------------------------------------------------------*/
864 
UResetCTM(Matrix * ctm)865 void UResetCTM(Matrix *ctm)
866 {
867    ctm->a = ctm->e = 1;
868    ctm->b = ctm->d = 0;
869    ctm->c = ctm->f = 0;  /* 0.5 for nearest-int real->int conversion? */
870 
871 #ifdef HAVE_CAIRO
872    if (ctm == DCTM && areawin->redraw_ongoing)
873       xc_cairo_set_matrix(ctm);
874 #endif /* HAVE_CAIRO */
875 }
876 
877 /*------------------------------------------------------------------------*/
878 
InvertCTM(Matrix * ctm)879 void InvertCTM(Matrix *ctm)
880 {
881    float det = ctm->a * ctm->e - ctm->b * ctm->d;
882    float tx = ctm->b * ctm->f - ctm->c * ctm->e;
883    float ty = ctm->d * ctm->c - ctm->a * ctm->f;
884 
885    float tmpa = ctm->a;
886 
887    ctm->b = -ctm->b / det;
888    ctm->d = -ctm->d / det;
889 
890    ctm->a = ctm->e / det;
891    ctm->e = tmpa / det;
892    ctm->c = tx / det;
893    ctm->f = ty / det;
894 
895 #ifdef HAVE_CAIRO
896    if (ctm == DCTM && areawin->redraw_ongoing)
897       xc_cairo_set_matrix(ctm);
898 #endif /* HAVE_CAIRO */
899 }
900 
901 /*------------------------------------------------------------------------*/
902 
UCopyCTM(fctm,tctm)903 void UCopyCTM(fctm, tctm)
904    Matrix *fctm, *tctm;
905 {
906    tctm->a = fctm->a;
907    tctm->b = fctm->b;
908    tctm->c = fctm->c;
909    tctm->d = fctm->d;
910    tctm->e = fctm->e;
911    tctm->f = fctm->f;
912 
913 #ifdef HAVE_CAIRO
914    if (tctm == DCTM && areawin->redraw_ongoing)
915       xc_cairo_set_matrix(tctm);
916 #endif /* HAVE_CAIRO */
917 }
918 
919 /*-------------------------------------------------------------------------*/
920 /* Multiply CTM by current screen position and scale to get transformation */
921 /* matrix from a user point to the X11 window				   */
922 /*-------------------------------------------------------------------------*/
923 
UMakeWCTM(Matrix * ctm)924 void UMakeWCTM(Matrix *ctm)
925 {
926    ctm->a *= areawin->vscale;
927    ctm->b *= areawin->vscale;
928    ctm->c = (ctm->c - (float)areawin->pcorner.x) * areawin->vscale
929 	 + areawin->panx;
930 
931    ctm->d *= -areawin->vscale;
932    ctm->e *= -areawin->vscale;
933    ctm->f = (float)areawin->height + ((float)areawin->pcorner.y - ctm->f) *
934 	 areawin->vscale + areawin->pany;
935 
936 #ifdef HAVE_CAIRO
937    if (ctm == DCTM && areawin->redraw_ongoing)
938       xc_cairo_set_matrix(ctm);
939 #endif /* HAVE_CAIRO */
940 }
941 
942 /*------------------------------------------------------------------------*/
943 
UMultCTM(Matrix * ctm,XPoint position,float scale,float rotate)944 void UMultCTM(Matrix *ctm, XPoint position, float scale, float rotate)
945 {
946    float tmpa, tmpb, tmpd, tmpe, yscale;
947    float mata, matb, matc;
948    double drot = (double)rotate * RADFAC;
949 
950    yscale = abs(scale);  /* -scale implies flip in x direction only */
951 
952    tmpa =  scale * cos(drot);
953    tmpb = yscale * sin(drot);
954    tmpd = -scale * sin(drot);
955    tmpe = yscale * cos(drot);
956 
957    mata = ctm->a * tmpa + ctm->d * tmpb;
958    matb = ctm->b * tmpa + ctm->e * tmpb;
959    matc = ctm->c * tmpa + ctm->f * tmpb + position.x;
960 
961    ctm->d = ctm->d * tmpe + ctm->a * tmpd;
962    ctm->e = ctm->e * tmpe + ctm->b * tmpd;
963    ctm->f = ctm->f * tmpe + ctm->c * tmpd + position.y;
964 
965    ctm->a = mata;
966    ctm->b = matb;
967    ctm->c = matc;
968 
969 #ifdef HAVE_CAIRO
970    if (ctm == DCTM && areawin->redraw_ongoing)
971       xc_cairo_set_matrix(ctm);
972 #endif /* HAVE_CAIRO */
973 }
974 
975 /*----------------------------------------------------------------------*/
976 /* Slanting function x' = x + beta * y, y' = y				*/
977 /*----------------------------------------------------------------------*/
978 
USlantCTM(Matrix * ctm,float beta)979 void USlantCTM(Matrix *ctm, float beta)
980 {
981    ctm->b += ctm->a * beta;
982    ctm->e += ctm->d * beta;
983 
984 #ifdef HAVE_CAIRO
985    if (ctm == DCTM && areawin->redraw_ongoing)
986       xc_cairo_set_matrix(ctm);
987 #endif /* HAVE_CAIRO */
988 }
989 
990 #define EPS 1e-9
991 /*----------------------------------------------------------------------*/
992 /* Transform text to make it right-side up within 90 degrees of page	*/
993 /* NOTE:  This is not yet resolved, as xcircuit does not agree with	*/
994 /* PostScript in a few cases!						*/
995 /*----------------------------------------------------------------------*/
996 
UPreScaleCTM(Matrix * ctm)997 void UPreScaleCTM(Matrix *ctm)
998 {
999    /* negative X scale (-1, +1) */
1000    if ((ctm->a < -EPS) || ((ctm->a < EPS) && (ctm->a > -EPS) &&
1001 		((ctm->d * ctm->b) < 0))) {
1002       ctm->a = -ctm->a;
1003       ctm->d = -ctm->d;
1004    }
1005 
1006    /* negative Y scale (+1, -1) */
1007    if (ctm->e > EPS) {
1008       ctm->e = -ctm->e;
1009       ctm->b = -ctm->b;
1010    }
1011 
1012    /* At 90, 270 degrees need special attention to avoid discrepencies	*/
1013    /* with the PostScript output due to roundoff error.  This code	*/
1014    /* matches what PostScript produces.					*/
1015 
1016 #ifdef HAVE_CAIRO
1017    if (ctm == DCTM && areawin->redraw_ongoing)
1018       xc_cairo_set_matrix(ctm);
1019 #endif /* HAVE_CAIRO */
1020 }
1021 
1022 /*----------------------------------------------------------------------*/
1023 /* Adjust anchoring and CTM as necessary for flip invariance		*/
1024 /*----------------------------------------------------------------------*/
1025 
flipadjust(short anchor)1026 short flipadjust(short anchor)
1027 {
1028    short tmpanchor = anchor & (~FLIPINV);
1029 
1030    if (anchor & FLIPINV) {
1031       if (((DCTM)->a < -EPS) || (((DCTM)->a < EPS) && ((DCTM)->a > -EPS) &&
1032 		(((DCTM)->d * (DCTM)->b) < 0))) {
1033          if ((tmpanchor & (RIGHT | NOTLEFT)) != NOTLEFT)
1034             tmpanchor ^= (RIGHT | NOTLEFT);
1035 
1036 	 /* NOTE:  Justification does not change under flip invariance.	*/
1037       }
1038       if ((DCTM)->e > EPS) {
1039          if ((tmpanchor & (TOP | NOTBOTTOM)) != NOTBOTTOM)
1040             tmpanchor ^= (TOP | NOTBOTTOM);
1041       }
1042       UPreScaleCTM(DCTM);
1043    }
1044    return tmpanchor;
1045 }
1046 
1047 /*------------------------------------------------------------------------*/
1048 
UPreMultCTM(Matrix * ctm,XPoint position,float scale,float rotate)1049 void UPreMultCTM(Matrix *ctm, XPoint position, float scale, float rotate)
1050 {
1051    float tmpa, tmpb, tmpd, tmpe, yscale;
1052    float mata, matd;
1053    double drot = (double)rotate * RADFAC;
1054 
1055    yscale = abs(scale);		/* negative scale value implies flip in x only */
1056 
1057    tmpa =  scale * cos(drot);
1058    tmpb = yscale * sin(drot);
1059    tmpd = -scale * sin(drot);
1060    tmpe = yscale * cos(drot);
1061 
1062    ctm->c += ctm->a * position.x + ctm->b * position.y;
1063    ctm->f += ctm->d * position.x + ctm->e * position.y;
1064 
1065    mata = ctm->a * tmpa + ctm->b * tmpd;
1066    ctm->b = ctm->a * tmpb + ctm->b * tmpe;
1067 
1068    matd = ctm->d * tmpa + ctm->e * tmpd;
1069    ctm->e = ctm->d * tmpb + ctm->e * tmpe;
1070 
1071    ctm->a = mata;
1072    ctm->d = matd;
1073 
1074 #ifdef HAVE_CAIRO
1075    if (ctm == DCTM && areawin->redraw_ongoing)
1076       xc_cairo_set_matrix(ctm);
1077 #endif /* HAVE_CAIRO */
1078 }
1079 
1080 /*----------------------------------------------------------------------*/
1081 /* Direct Matrix-Matrix multiplication					*/
1082 /*----------------------------------------------------------------------*/
1083 
UPreMultCTMbyMat(Matrix * ctm,Matrix * pre)1084 void UPreMultCTMbyMat(Matrix *ctm, Matrix *pre)
1085 {
1086    float mata, matd;
1087 
1088    mata = pre->a * ctm->a + pre->d * ctm->b;
1089    ctm->c += pre->c * ctm->a + pre->f * ctm->b;
1090    ctm->b = pre->b * ctm->a + pre->e * ctm->b;
1091    ctm->a = mata;
1092 
1093    matd = pre->a * ctm->d + pre->d * ctm->e;
1094    ctm->f += pre->c * ctm->d + pre->f * ctm->e;
1095    ctm->e = pre->b * ctm->d + pre->e * ctm->e;
1096    ctm->d = matd;
1097 
1098 #ifdef HAVE_CAIRO
1099    if (ctm == DCTM && areawin->redraw_ongoing)
1100       xc_cairo_set_matrix(ctm);
1101 #endif /* HAVE_CAIRO */
1102 }
1103 
1104 /*------------------------------------------------------------------------*/
1105 
UTransformbyCTM(Matrix * ctm,XPoint * ipoints,XPoint * points,short number)1106 void UTransformbyCTM(Matrix *ctm, XPoint *ipoints, XPoint *points, short number)
1107 {
1108    pointlist current, ptptr = points;
1109    float fx, fy;
1110    /* short tmpx; (jdk) */
1111 
1112    for (current = ipoints; current < ipoints + number; current++, ptptr++) {
1113       fx = ctm->a * (float)current->x + ctm->b * (float)current->y + ctm->c;
1114       fy = ctm->d * (float)current->x + ctm->e * (float)current->y + ctm->f;
1115 
1116       ptptr->x = (fx >= 0) ? (short)(fx + 0.5) : (short)(fx - 0.5);
1117       ptptr->y = (fy >= 0) ? (short)(fy + 0.5) : (short)(fy - 0.5);
1118    }
1119 }
1120 
1121 /*------------------------------------------------------------------------*/
1122 /* (same as above routine but using type (float) for point values;  this  */
1123 /* is for calculation of Bezier curve internal points.			  */
1124 /*------------------------------------------------------------------------*/
1125 
UfTransformbyCTM(Matrix * ctm,XfPoint * fpoints,XPoint * points,short number)1126 void UfTransformbyCTM(Matrix *ctm, XfPoint *fpoints, XPoint *points, short number)
1127 {
1128    fpointlist current;
1129    pointlist new = points;
1130    float fx, fy;
1131 
1132    for (current = fpoints; current < fpoints + number; current++, new++) {
1133       fx = ctm->a * current->x + ctm->b * current->y + ctm->c;
1134       fy = ctm->d * current->x + ctm->e * current->y + ctm->f;
1135       new->x = (fx >= 0) ? (short)(fx + 0.5) : (short)(fx - 0.5);
1136       new->y = (fy >= 0) ? (short)(fy + 0.5) : (short)(fy - 0.5);
1137    }
1138 }
1139 
1140 /*------------------------------------------------------------------------*/
1141 
UPopCTM()1142 void UPopCTM()
1143 {
1144    Matrixptr lastmatrix;
1145 
1146    if (areawin->MatStack == NULL) {
1147       Wprintf("Matrix stack pop error");
1148       return;
1149    }
1150    lastmatrix = areawin->MatStack->nextmatrix;
1151    free(areawin->MatStack);
1152    areawin->MatStack = lastmatrix;
1153 
1154 #ifdef HAVE_CAIRO
1155    if (areawin->area) {
1156       xc_cairo_set_matrix(lastmatrix);
1157    }
1158 #endif /* HAVE_CAIRO */
1159 }
1160 
1161 /*------------------------------------------------------------------------*/
1162 
UPushCTM()1163 void UPushCTM()
1164 {
1165    Matrixptr nmatrix;
1166 
1167    nmatrix = (Matrixptr)malloc(sizeof(Matrix));
1168    if (areawin->MatStack == NULL)
1169       UResetCTM(nmatrix);
1170    else
1171       UCopyCTM(areawin->MatStack, nmatrix);
1172    nmatrix->nextmatrix = areawin->MatStack;
1173    areawin->MatStack = nmatrix;
1174 }
1175 
1176 /*------------------------------------------------------------------------*/
1177 
UTransformPoints(XPoint * points,XPoint * newpoints,short number,XPoint atpt,float scale,float rotate)1178 void UTransformPoints(XPoint *points, XPoint *newpoints, short number,
1179 	XPoint atpt, float scale, float rotate)
1180 {
1181    Matrix LCTM;
1182 
1183    UResetCTM(&LCTM);
1184    UMultCTM(&LCTM, atpt, scale, rotate);
1185    UTransformbyCTM(&LCTM, points, newpoints, number);
1186 }
1187 
1188 /*----------------------------------------------------*/
1189 /* Transform points inward to next hierarchical level */
1190 /*----------------------------------------------------*/
1191 
InvTransformPoints(XPoint * points,XPoint * newpoints,short number,XPoint atpt,float scale,float rotate)1192 void InvTransformPoints(XPoint *points, XPoint *newpoints, short number,
1193 	XPoint atpt, float scale, float rotate)
1194 {
1195    Matrix LCTM;
1196 
1197    UResetCTM(&LCTM);
1198    UPreMultCTM(&LCTM, atpt, scale, rotate);
1199    InvertCTM(&LCTM);
1200    UTransformbyCTM(&LCTM, points, newpoints, number);
1201 }
1202 
1203 /*----------------------------------------------------------------------*/
1204 /* Adjust wire coords to force a wire to a horizontal or vertical	*/
1205 /* position.								*/
1206 /* "pospt" is the target position for the point of interest.		*/
1207 /* "cycle" is the point number in the polygon of the point of interest.	*/
1208 /* cycle == -1 is equivalent to the last point of the polygon.		*/
1209 /* If "strict" is TRUE then single-segment wires are forced manhattan	*/
1210 /* even if that means that the endpoint drifts from the target point.	*/
1211 /* If "strict" is FALSE then single-segment wires will become non-	*/
1212 /* manhattan so that the target point is reached.			*/
1213 /* NOTE:  It might be preferable to add a segment to maintain a		*/
1214 /* manhattan layout, except that we want to avoid merging nets		*/
1215 /* together. . .							*/
1216 /*----------------------------------------------------------------------*/
1217 
manhattanize(XPoint * pospt,polyptr newpoly,short cycle,Boolean strict)1218 void manhattanize(XPoint *pospt, polyptr newpoly, short cycle, Boolean strict)
1219 {
1220    XPoint *curpt, *bpt, *bbpt, *fpt, *ffpt;
1221    int deltax, deltay;
1222 
1223    if (newpoly->number == 1) return;	/* sanity check */
1224 
1225    if (cycle == -1 || cycle == newpoly->number - 1) {
1226       curpt = newpoly->points + newpoly->number - 1;
1227       bpt = newpoly->points + newpoly->number - 2;
1228       fpt = NULL;
1229       ffpt = NULL;
1230       if (newpoly->number > 2)
1231 	 bbpt = newpoly->points + newpoly->number - 3;
1232       else
1233 	 bbpt = NULL;
1234    }
1235    else if (cycle == 0) {
1236       curpt = newpoly->points;
1237       fpt = newpoly->points + 1;
1238       bpt = NULL;
1239       bbpt = NULL;
1240       if (newpoly->number > 2)
1241 	 ffpt = newpoly->points + 2;
1242       else
1243 	 ffpt = NULL;
1244    }
1245    else {
1246       curpt = newpoly->points + cycle;
1247       fpt = newpoly->points + cycle + 1;
1248       bpt = newpoly->points + cycle - 1;
1249       if (cycle > 1)
1250 	 bbpt = newpoly->points + cycle - 2;
1251       else
1252 	 bbpt = NULL;
1253 
1254       if (cycle < newpoly->number - 2)
1255 	 ffpt = newpoly->points + cycle + 2;
1256       else
1257 	 ffpt = NULL;
1258    }
1259 
1260    /* enforce constraints on point behind cycle position */
1261 
1262    if (bpt != NULL) {
1263       if (bbpt != NULL) {
1264          if (bpt->x == bbpt->x) bpt->y = pospt->y;
1265          if (bpt->y == bbpt->y) bpt->x = pospt->x;
1266       }
1267       else if (strict) {
1268          deltax = abs(bpt->x - pospt->x);
1269          deltay = abs(bpt->y - pospt->y);
1270 
1271          /* Only one segment---just make sure it's horizontal or vertical */
1272          if (deltay > deltax) pospt->x = bpt->x;
1273          else pospt->y = bpt->y;
1274       }
1275    }
1276 
1277    /* enforce constraints on point forward of cycle position */
1278 
1279    if (fpt != NULL) {
1280       if (ffpt != NULL) {
1281          if (fpt->x == ffpt->x) fpt->y = pospt->y;
1282          if (fpt->y == ffpt->y) fpt->x = pospt->x;
1283       }
1284       else if (strict) {
1285          deltax = abs(fpt->x - pospt->x);
1286          deltay = abs(fpt->y - pospt->y);
1287 
1288          /* Only one segment---just make sure it's horizontal or vertical */
1289          if (deltay > deltax) pospt->x = fpt->x;
1290          else pospt->y = fpt->y;
1291       }
1292    }
1293 }
1294 
1295 /*----------------------------------------------------------------------*/
1296 /* Bounding box calculation routines					*/
1297 /*----------------------------------------------------------------------*/
1298 
bboxcalc(short testval,short * lowerval,short * upperval)1299 void bboxcalc(short testval, short *lowerval, short *upperval)
1300 {
1301    if (testval < *lowerval) *lowerval = testval;
1302    if (testval > *upperval) *upperval = testval;
1303 }
1304 
1305 /*----------------------------------------------------------------------*/
1306 /* Bounding box calculation for elements which can be part of a path	*/
1307 /*----------------------------------------------------------------------*/
1308 
calcextents(genericptr * bboxgen,short * llx,short * lly,short * urx,short * ury)1309 void calcextents(genericptr *bboxgen, short *llx, short *lly,
1310 	short *urx, short *ury)
1311 {
1312    switch (ELEMENTTYPE(*bboxgen)) {
1313       case(POLYGON): {
1314          pointlist bboxpts;
1315          for (bboxpts = TOPOLY(bboxgen)->points; bboxpts < TOPOLY(bboxgen)->points
1316 		 + TOPOLY(bboxgen)->number; bboxpts++) {
1317 	    bboxcalc(bboxpts->x, llx, urx);
1318 	    bboxcalc(bboxpts->y, lly, ury);
1319          }
1320          } break;
1321 
1322       case(SPLINE): {
1323          fpointlist bboxpts;
1324          bboxcalc(TOSPLINE(bboxgen)->ctrl[0].x, llx, urx);
1325          bboxcalc(TOSPLINE(bboxgen)->ctrl[0].y, lly, ury);
1326          bboxcalc(TOSPLINE(bboxgen)->ctrl[3].x, llx, urx);
1327          bboxcalc(TOSPLINE(bboxgen)->ctrl[3].y, lly, ury);
1328          for (bboxpts = TOSPLINE(bboxgen)->points; bboxpts <
1329 		 TOSPLINE(bboxgen)->points + INTSEGS; bboxpts++) {
1330 	    bboxcalc((short)(bboxpts->x), llx, urx);
1331 	    bboxcalc((short)(bboxpts->y), lly, ury);
1332          }
1333          } break;
1334 
1335       case (ARC): {
1336          fpointlist bboxpts;
1337          for (bboxpts = TOARC(bboxgen)->points; bboxpts < TOARC(bboxgen)->points +
1338 	         TOARC(bboxgen)->number; bboxpts++) {
1339             bboxcalc((short)(bboxpts->x), llx, urx);
1340 	    bboxcalc((short)(bboxpts->y), lly, ury);
1341          }
1342          } break;
1343    }
1344 }
1345 
1346 /*----------------------------------------------------------------------*/
1347 /* Calculate the bounding box of an object instance			*/
1348 /*----------------------------------------------------------------------*/
1349 
objinstbbox(objinstptr obbox,XPoint * npoints,int extend)1350 void objinstbbox(objinstptr obbox, XPoint *npoints, int extend)
1351 {
1352    XPoint points[4];
1353 
1354    points[0].x = points[1].x = obbox->bbox.lowerleft.x - extend;
1355    points[1].y = points[2].y = obbox->bbox.lowerleft.y + obbox->bbox.height
1356 		+ extend;
1357    points[2].x = points[3].x = obbox->bbox.lowerleft.x + obbox->bbox.width
1358 		+ extend;
1359    points[0].y = points[3].y = obbox->bbox.lowerleft.y - extend;
1360 
1361    UTransformPoints(points, npoints, 4, obbox->position,
1362 		 obbox->scale, obbox->rotation);
1363 }
1364 
1365 /*----------------------------------------------------------------------*/
1366 /* Calculate the bounding box of a label				*/
1367 /*----------------------------------------------------------------------*/
1368 
labelbbox(labelptr labox,XPoint * npoints,objinstptr callinst)1369 void labelbbox(labelptr labox, XPoint *npoints, objinstptr callinst)
1370 {
1371    XPoint points[4];
1372    TextExtents tmpext;
1373    short j;
1374 
1375    tmpext = ULength(labox, callinst, NULL);
1376    points[0].x = points[1].x = (labox->anchor & NOTLEFT ?
1377 	       (labox->anchor & RIGHT ? -tmpext.maxwidth :
1378 		-tmpext.maxwidth / 2) : 0);
1379    points[2].x = points[3].x = points[0].x + tmpext.maxwidth;
1380    points[0].y = points[3].y = (labox->anchor & NOTBOTTOM ?
1381 	       (labox->anchor & TOP ? -tmpext.ascent :
1382 		-(tmpext.ascent + tmpext.base) / 2) : -tmpext.base)
1383 		+ tmpext.descent;
1384    points[1].y = points[2].y = points[0].y + tmpext.ascent - tmpext.descent;
1385 
1386    /* separate bounding box for pinlabels and infolabels */
1387 
1388    if (labox->pin)
1389       for (j = 0; j < 4; j++)
1390 	 pinadjust(labox->anchor, &points[j].x, &points[j].y, 1);
1391 
1392    UTransformPoints(points, npoints, 4, labox->position,
1393 		labox->scale, labox->rotation);
1394 }
1395 
1396 /*----------------------------------------------------------------------*/
1397 /* Calculate the bounding box of a graphic image			*/
1398 /*----------------------------------------------------------------------*/
1399 
graphicbbox(graphicptr gp,XPoint * npoints)1400 void graphicbbox(graphicptr gp, XPoint *npoints)
1401 {
1402    XPoint points[4];
1403    int hw = xcImageGetWidth(gp->source) >> 1;
1404    int hh = xcImageGetHeight(gp->source) >> 1;
1405 
1406    points[1].x = points[2].x = hw;
1407    points[0].x = points[3].x = -hw;
1408 
1409    points[0].y = points[1].y = -hh;
1410    points[2].y = points[3].y = hh;
1411 
1412    UTransformPoints(points, npoints, 4, gp->position,
1413 		gp->scale, gp->rotation);
1414 }
1415 
1416 /*--------------------------------------------------------------*/
1417 /* Wrapper for single call to calcbboxsingle() in the netlister */
1418 /*--------------------------------------------------------------*/
1419 
calcinstbbox(genericptr * bboxgen,short * llx,short * lly,short * urx,short * ury)1420 void calcinstbbox(genericptr *bboxgen, short *llx, short *lly, short *urx,
1421                 short *ury)
1422 {
1423    *llx = *lly = 32767;
1424    *urx = *ury = -32768;
1425 
1426    calcbboxsingle(bboxgen, areawin->topinstance, llx, lly, urx, ury);
1427 }
1428 
1429 /*----------------------------------------------------------------------*/
1430 /* Bounding box calculation for a single generic element		*/
1431 /*----------------------------------------------------------------------*/
1432 
calcbboxsingle(genericptr * bboxgen,objinstptr thisinst,short * llx,short * lly,short * urx,short * ury)1433 void calcbboxsingle(genericptr *bboxgen, objinstptr thisinst,
1434 		short *llx, short *lly, short *urx, short *ury)
1435 {
1436    XPoint npoints[4];
1437    short j;
1438 
1439    /* For each screen element, compute the extents and revise bounding	*/
1440    /* box points, if necessary. 					*/
1441 
1442    switch(ELEMENTTYPE(*bboxgen)) {
1443 
1444       case(OBJINST):
1445 	 objinstbbox(TOOBJINST(bboxgen), npoints, 0);
1446 
1447          for (j = 0; j < 4; j++) {
1448             bboxcalc(npoints[j].x, llx, urx);
1449             bboxcalc(npoints[j].y, lly, ury);
1450          }
1451 	 break;
1452 
1453       case(LABEL):
1454 	 /* because a pin is offset from its position point, include */
1455 	 /* that point in the bounding box.				*/
1456 
1457 	 if (TOLABEL(bboxgen)->pin) {
1458             bboxcalc(TOLABEL(bboxgen)->position.x, llx, urx);
1459             bboxcalc(TOLABEL(bboxgen)->position.y, lly, ury);
1460 	 }
1461 	 labelbbox(TOLABEL(bboxgen), npoints, thisinst);
1462 
1463          for (j = 0; j < 4; j++) {
1464             bboxcalc(npoints[j].x, llx, urx);
1465             bboxcalc(npoints[j].y, lly, ury);
1466          }
1467          break;
1468 
1469       case(GRAPHIC):
1470 	 graphicbbox(TOGRAPHIC(bboxgen), npoints);
1471          for (j = 0; j < 4; j++) {
1472             bboxcalc(npoints[j].x, llx, urx);
1473             bboxcalc(npoints[j].y, lly, ury);
1474          }
1475 	 break;
1476 
1477       case(PATH): {
1478 	 genericptr *pathc;
1479 	 for (pathc = TOPATH(bboxgen)->plist; pathc < TOPATH(bboxgen)->plist
1480 		  + TOPATH(bboxgen)->parts; pathc++)
1481 	       calcextents(pathc, llx, lly, urx, ury);
1482 	 } break;
1483 
1484       default:
1485 	 calcextents(bboxgen, llx, lly, urx, ury);
1486    }
1487 }
1488 
1489 /*------------------------------------------------------*/
1490 /* Find if an object is in the specified library	*/
1491 /*------------------------------------------------------*/
1492 
object_in_library(short libnum,objectptr thisobject)1493 Boolean object_in_library(short libnum, objectptr thisobject)
1494 {
1495    short i;
1496 
1497    for (i = 0; i < xobjs.userlibs[libnum].number; i++) {
1498       if (*(xobjs.userlibs[libnum].library + i) == thisobject)
1499 	 return True;
1500    }
1501    return False;
1502 }
1503 
1504 /*-----------------------------------------------------------*/
1505 /* Find if an object is in the hierarchy of the given object */
1506 /* Returns the number (position in plist) or -1 if not found */
1507 /*-----------------------------------------------------------*/
1508 
find_object(objectptr pageobj,objectptr thisobject)1509 short find_object(objectptr pageobj, objectptr thisobject)
1510 {
1511    short i, j;
1512    genericptr *pelem;
1513 
1514    for (i = 0; i < pageobj->parts; i++) {
1515       pelem = pageobj->plist + i;
1516       if (IS_OBJINST(*pelem)) {
1517 	 if ((TOOBJINST(pelem))->thisobject == thisobject)
1518 	    return i;
1519 	 else if ((j = find_object((TOOBJINST(pelem))->thisobject, thisobject)) >= 0)
1520 	    return i;  /* was j---is this the right fix? */
1521       }
1522    }
1523    return -1;
1524 }
1525 
1526 /*------------------------------------------------------*/
1527 /* Find all pages and libraries containing this object	*/
1528 /* and update accordingly.  If this object is a page,	*/
1529 /* just update the page directory.			*/
1530 /*------------------------------------------------------*/
1531 
updatepagebounds(objectptr thisobject)1532 void updatepagebounds(objectptr thisobject)
1533 {
1534    short i, j;
1535    objectptr pageobj;
1536 
1537    if ((i = is_page(thisobject)) >= 0) {
1538       if (xobjs.pagelist[i]->background.name != (char *)NULL)
1539          backgroundbbox(i);
1540       updatepagelib(PAGELIB, i);
1541    }
1542    else {
1543       for (i = 0; i < xobjs.pages; i++) {
1544          if (xobjs.pagelist[i]->pageinst != NULL) {
1545 	    pageobj = xobjs.pagelist[i]->pageinst->thisobject;
1546             if ((j = find_object(pageobj, thisobject)) >= 0) {
1547 	       calcbboxvalues(xobjs.pagelist[i]->pageinst,
1548 			(genericptr *)(pageobj->plist + j));
1549 	       updatepagelib(PAGELIB, i);
1550 	    }
1551          }
1552       }
1553       for (i = 0; i < xobjs.numlibs; i++)
1554          if (object_in_library(i, thisobject))
1555 	    composelib(i + LIBRARY);
1556    }
1557 }
1558 
1559 /*--------------------------------------------------------------*/
1560 /* Free memory for the schematic bounding box                   */
1561 /*--------------------------------------------------------------*/
1562 
invalidateschembbox(objinstptr thisinst)1563 void invalidateschembbox(objinstptr thisinst)
1564 {
1565    if (thisinst->schembbox != NULL) {
1566       free(thisinst->schembbox);
1567       thisinst->schembbox = NULL;
1568    }
1569 }
1570 
1571 /*--------------------------------------------------------------*/
1572 /* Calculate the bounding box for an object instance.  Use the	*/
1573 /* existing bbox and finish calculation on all the elements	*/
1574 /* which have parameters not taking default values.		*/
1575 /* This finishes the calculation partially done by		*/
1576 /* calcbboxvalues().						*/
1577 /*--------------------------------------------------------------*/
1578 
calcbboxinst(objinstptr thisinst)1579 void calcbboxinst(objinstptr thisinst)
1580 {
1581    objectptr thisobj;
1582    genericptr *gelem;
1583    short llx, lly, urx, ury;
1584 
1585    short pllx, plly, purx, pury;
1586    Boolean hasschembbox = FALSE;
1587    Boolean didparamsubs = FALSE;
1588 
1589    if (thisinst == NULL) return;
1590 
1591    thisobj = thisinst->thisobject;
1592 
1593    llx = thisobj->bbox.lowerleft.x;
1594    lly = thisobj->bbox.lowerleft.y;
1595    urx = llx + thisobj->bbox.width;
1596    ury = lly + thisobj->bbox.height;
1597 
1598    pllx = plly = 32767;
1599    purx = pury = -32768;
1600 
1601    for (gelem = thisobj->plist; gelem < thisobj->plist + thisobj->parts;
1602 		gelem++) {
1603       /* pins which do not appear outside of the object	*/
1604       /* contribute to the objects "schembbox".		*/
1605 
1606       if (IS_LABEL(*gelem)) {
1607 	 labelptr btext = TOLABEL(gelem);
1608 	 if (btext->pin && !(btext->anchor & PINVISIBLE)) {
1609 	    hasschembbox = TRUE;
1610 	    calcbboxsingle(gelem, thisinst, &pllx, &plly, &purx, &pury);
1611 	    continue;
1612 	 }
1613       }
1614 
1615       if (has_param(*gelem)) {
1616 	 if (didparamsubs == FALSE) {
1617 	    psubstitute(thisinst);
1618 	    didparamsubs = TRUE;
1619 	 }
1620 	 calcbboxsingle(gelem, thisinst, &llx, &lly, &urx, &ury);
1621       }
1622 
1623       /* If we have a clipmask, the clipmask is used to calculate the	*/
1624       /* bounding box, not the element it is masking.			*/
1625 
1626       switch(ELEMENTTYPE(*gelem)) {
1627 	 case POLYGON: case SPLINE: case ARC: case PATH:
1628 	    if (TOPOLY(gelem)->style & CLIPMASK) gelem++;
1629 	    break;
1630       }
1631    }
1632 
1633    thisinst->bbox.lowerleft.x = llx;
1634    thisinst->bbox.lowerleft.y = lly;
1635    thisinst->bbox.width = urx - llx;
1636    thisinst->bbox.height = ury - lly;
1637 
1638    if (hasschembbox) {
1639       if (thisinst->schembbox == NULL)
1640          thisinst->schembbox = (BBox *)malloc(sizeof(BBox));
1641 
1642       thisinst->schembbox->lowerleft.x = pllx;
1643       thisinst->schembbox->lowerleft.y = plly;
1644       thisinst->schembbox->width = purx - pllx;
1645       thisinst->schembbox->height = pury - plly;
1646    }
1647    else
1648       invalidateschembbox(thisinst);
1649 }
1650 
1651 /*--------------------------------------------------------------*/
1652 /* Update things based on a changed instance bounding box.	*/
1653 /* If the parameter was a single-instance			*/
1654 /* substitution, only the page should be updated.  If the	*/
1655 /* parameter was a default value, the library should be updated */
1656 /* and any pages containing the object where the parameter	*/
1657 /* takes the default value.					*/
1658 /*--------------------------------------------------------------*/
1659 
updateinstparam(objectptr bobj)1660 void updateinstparam(objectptr bobj)
1661 {
1662    short i, j;
1663    objectptr pageobj;
1664 
1665    /* change bounds on pagelib and all pages			*/
1666    /* containing this *object* if and only if the object	*/
1667    /* instance takes the default value.  Also update the	*/
1668    /* library page.						*/
1669 
1670    for (i = 0; i < xobjs.pages; i++)
1671       if (xobjs.pagelist[i]->pageinst != NULL) {
1672 	 pageobj = xobjs.pagelist[i]->pageinst->thisobject;
1673          if ((j = find_object(pageobj, topobject)) >= 0) {
1674 
1675 	    /* Really, we'd like to recalculate the bounding box only if the */
1676 	    /* parameter value is the default value which was just changed.	*/
1677 	    /* However, then any non-default values may contain the wrong	*/
1678 	    /* substitutions.						*/
1679 
1680 	    objinstptr cinst = TOOBJINST(pageobj->plist + j);
1681 	    if (cinst->thisobject->params == NULL) {
1682 	       calcbboxvalues(xobjs.pagelist[i]->pageinst, pageobj->plist + j);
1683 	       updatepagelib(PAGELIB, i);
1684 	    }
1685 	 }
1686       }
1687 
1688    for (i = 0; i < xobjs.numlibs; i++)
1689       if (object_in_library(i, topobject))
1690 	 composelib(i + LIBRARY);
1691 }
1692 
1693 /*--------------------------------------------------------------*/
1694 /* Calculate bbox on all elements of the given object		*/
1695 /*--------------------------------------------------------------*/
1696 
calcbbox(objinstptr binst)1697 void calcbbox(objinstptr binst)
1698 {
1699    calcbboxvalues(binst, (genericptr *)NULL);
1700    if (binst == areawin->topinstance) {
1701       updatepagebounds(topobject);
1702    }
1703 }
1704 
1705 /*--------------------------------------------------------------*/
1706 /* Calculate bbox on the given element of the specified object.	*/
1707 /* This is a wrapper for calcbboxvalues() assuming that we're	*/
1708 /* on the top-level, and that page bounds need to be updated.	*/
1709 /*--------------------------------------------------------------*/
1710 
singlebbox(genericptr * gelem)1711 void singlebbox(genericptr *gelem)
1712 {
1713    calcbboxvalues(areawin->topinstance, (genericptr *)gelem);
1714    updatepagebounds(topobject);
1715 }
1716 
1717 /*----------------------------------------------------------------------*/
1718 /* Extend bounding box based on selected elements only			*/
1719 /*----------------------------------------------------------------------*/
1720 
calcbboxselect()1721 void calcbboxselect()
1722 {
1723    short *bsel;
1724    for (bsel = areawin->selectlist; bsel < areawin->selectlist +
1725 		areawin->selects; bsel++)
1726       calcbboxvalues(areawin->topinstance, topobject->plist + *bsel);
1727 
1728    updatepagebounds(topobject);
1729 }
1730 
1731 /*--------------------------------------------------------------*/
1732 /* Update Bounding box for an object.				*/
1733 /* If newelement == NULL, calculate bounding box from scratch.	*/
1734 /* Otherwise, expand bounding box to enclose newelement.	*/
1735 /*--------------------------------------------------------------*/
1736 
calcbboxvalues(objinstptr thisinst,genericptr * newelement)1737 void calcbboxvalues(objinstptr thisinst, genericptr *newelement)
1738 {
1739    genericptr *bboxgen;
1740    short llx, lly, urx, ury;
1741    objectptr thisobj = thisinst->thisobject;
1742 
1743    /* no action if there are no elements */
1744    if (thisobj->parts == 0) return;
1745 
1746    /* If this object has parameters, then we will do a separate		*/
1747    /* bounding box calculation on parameterized parts.  This		*/
1748    /* calculation ignores them, and the result is a base that the	*/
1749    /* instance bounding-box computation can use as a starting point.	*/
1750 
1751    /* set starting bounds as maximum bounds of screen */
1752    llx = lly = 32767;
1753    urx = ury = -32768;
1754 
1755    for (bboxgen = thisobj->plist; bboxgen < thisobj->plist +
1756 		thisobj->parts; bboxgen++) {
1757 
1758       /* override the "for" loop if we're doing a single element */
1759       if (newelement != NULL) bboxgen = newelement;
1760 
1761       if ((thisobj->params == NULL) || (!has_param(*bboxgen))) {
1762 	 /* pins which do not appear outside of the object 	*/
1763 	 /* are ignored now---will be computed per instance.	*/
1764 
1765 	 if (IS_LABEL(*bboxgen)) {
1766 	    labelptr btext = TOLABEL(bboxgen);
1767 	    if (btext->pin && !(btext->anchor & PINVISIBLE)) {
1768 	       goto nextgen;
1769 	    }
1770 	 }
1771 	 calcbboxsingle(bboxgen, thisinst, &llx, &lly, &urx, &ury);
1772 
1773 	 if (newelement == NULL)
1774 	    switch(ELEMENTTYPE(*bboxgen)) {
1775 	       case POLYGON: case SPLINE: case ARC: case PATH:
1776 	          if (TOPOLY(bboxgen)->style & CLIPMASK)
1777 		     bboxgen++;
1778 		  break;
1779 	    }
1780       }
1781 nextgen:
1782       if (newelement != NULL) break;
1783    }
1784 
1785    /* if this is a single-element calculation and its bounding box	*/
1786    /* turned out to be smaller than the object's, then we need to	*/
1787    /* recompute the entire object's bounding box in case it got		*/
1788    /* smaller.  This is not recursive, in spite of looks.		*/
1789 
1790    if (newelement != NULL) {
1791       if (llx > thisobj->bbox.lowerleft.x &&
1792 		lly > thisobj->bbox.lowerleft.y &&
1793 		urx < (thisobj->bbox.lowerleft.x + thisobj->bbox.width) &&
1794 		ury < (thisobj->bbox.lowerleft.y + thisobj->bbox.height)) {
1795 	 calcbboxvalues(thisinst, NULL);
1796 	 return;
1797       }
1798       else {
1799 	 bboxcalc(thisobj->bbox.lowerleft.x, &llx, &urx);
1800 	 bboxcalc(thisobj->bbox.lowerleft.y, &lly, &ury);
1801 	 bboxcalc(thisobj->bbox.lowerleft.x + thisobj->bbox.width, &llx, &urx);
1802 	 bboxcalc(thisobj->bbox.lowerleft.y + thisobj->bbox.height, &lly, &ury);
1803       }
1804    }
1805 
1806    /* Set the new bounding box.  In pathological cases, such as a page	*/
1807    /* with only pin labels, the bounds may not have been changed from	*/
1808    /* their initial values.  If so, then don't touch the bounding box.	*/
1809 
1810    if ((llx <= urx) && (lly <= ury)) {
1811       thisobj->bbox.lowerleft.x = llx;
1812       thisobj->bbox.lowerleft.y = lly;
1813       thisobj->bbox.width = urx - llx;
1814       thisobj->bbox.height = ury - lly;
1815    }
1816 
1817    /* calculate instance-specific values */
1818    calcbboxinst(thisinst);
1819 }
1820 
1821 /*------------------------------------------------------*/
1822 /* Center an object in the viewing window		*/
1823 /*------------------------------------------------------*/
1824 
centerview(objinstptr tinst)1825 void centerview(objinstptr tinst)
1826 {
1827    XPoint origin, corner;
1828    Dimension width, height;
1829    float fitwidth, fitheight;
1830    objectptr tobj = tinst->thisobject;
1831 
1832    origin = tinst->bbox.lowerleft;
1833    corner.x = origin.x + tinst->bbox.width;
1834    corner.y = origin.y + tinst->bbox.height;
1835 
1836    extendschembbox(tinst, &origin, &corner);
1837 
1838    width = corner.x - origin.x;
1839    height = corner.y - origin.y;
1840 
1841    fitwidth = (float)areawin->width / ((float)width + 2 * DEFAULTGRIDSPACE);
1842    fitheight = (float)areawin->height / ((float)height + 2 * DEFAULTGRIDSPACE);
1843 
1844    tobj->viewscale = (fitwidth < fitheight) ?
1845 		 min(MINAUTOSCALE, fitwidth) : min(MINAUTOSCALE, fitheight);
1846 
1847    tobj->pcorner.x = origin.x - (areawin->width
1848 	       / tobj->viewscale - width) / 2;
1849    tobj->pcorner.y = origin.y - (areawin->height
1850 	       / tobj->viewscale - height) / 2;
1851 
1852    /* Copy new position values to the current window */
1853 
1854    if ((areawin->topinstance != NULL) && (tobj == topobject)) {
1855       areawin->pcorner = tobj->pcorner;
1856       areawin->vscale = tobj->viewscale;
1857    }
1858 }
1859 
1860 /*-----------------------------------------------------------*/
1861 /* Refresh the window and scrollbars and write the page name */
1862 /*-----------------------------------------------------------*/
1863 
refresh(xcWidget bw,caddr_t clientdata,caddr_t calldata)1864 void refresh(xcWidget bw, caddr_t clientdata, caddr_t calldata)
1865 {
1866    areawin->redraw_needed = True;
1867    drawarea(NULL, NULL, NULL);
1868    if (areawin->scrollbarh)
1869       drawhbar(areawin->scrollbarh, NULL, NULL);
1870    if (areawin->scrollbarv)
1871       drawvbar(areawin->scrollbarv, NULL, NULL);
1872    printname(topobject);
1873 }
1874 
1875 /*------------------------------------------------------*/
1876 /* Center the current page in the viewing window	*/
1877 /*------------------------------------------------------*/
1878 
zoomview(xcWidget w,caddr_t clientdata,caddr_t calldata)1879 void zoomview(xcWidget w, caddr_t clientdata, caddr_t calldata)
1880 {
1881    if (eventmode == NORMAL_MODE || eventmode == COPY_MODE ||
1882 	 eventmode == MOVE_MODE || eventmode == CATALOG_MODE ||
1883 	eventmode == FONTCAT_MODE || eventmode == EFONTCAT_MODE ||
1884 	eventmode == CATMOVE_MODE) {
1885 
1886       if (areawin->topinstance)
1887 	 centerview(areawin->topinstance);
1888       areawin->lastbackground = NULL;
1889       renderbackground();
1890       refresh(NULL, NULL, NULL);
1891    }
1892 }
1893 
1894 /*---------------------------------------------------------*/
1895 /* Basic X Graphics Routines in the User coordinate system */
1896 /*---------------------------------------------------------*/
1897 
1898 #ifndef HAVE_CAIRO
UDrawSimpleLine(XPoint * pt1,XPoint * pt2)1899 void UDrawSimpleLine(XPoint *pt1, XPoint *pt2)
1900 {
1901    XPoint newpt1, newpt2;
1902 
1903    if (!areawin->redraw_ongoing) {
1904       areawin->redraw_needed = True;
1905       return;
1906    }
1907 
1908    UTransformbyCTM(DCTM, pt1, &newpt1, 1);
1909    UTransformbyCTM(DCTM, pt2, &newpt2, 1);
1910 
1911    DrawLine(dpy, areawin->window, areawin->gc,
1912 		newpt1.x, newpt1.y, newpt2.x, newpt2.y);
1913 }
1914 #endif /* !HAVE_CAIRO */
1915 
1916 /*-------------------------------------------------------------------------*/
1917 
1918 #ifndef HAVE_CAIRO
UDrawLine(XPoint * pt1,XPoint * pt2)1919 void UDrawLine(XPoint *pt1, XPoint *pt2)
1920 {
1921    float tmpwidth = UTopTransScale(xobjs.pagelist[areawin->page]->wirewidth);
1922 
1923    if (!areawin->redraw_ongoing) {
1924       areawin->redraw_needed = True;
1925       return;
1926    }
1927 
1928    SetLineAttributes(dpy, areawin->gc, tmpwidth, LineSolid, CapRound, JoinBevel);
1929    UDrawSimpleLine(pt1, pt2);
1930 }
1931 #endif /* !HAVE_CAIRO */
1932 
1933 /*----------------------------------------------------------------------*/
1934 /* Add circle at given point to indicate that the point is a parameter.	*/
1935 /* The circle is divided into quarters.  For parameterized y-coordinate	*/
1936 /* the top and bottom quarters are drawn.  For parameterized x-		*/
1937 /* coordinate, the left and right quarters are drawn.  A full circle	*/
1938 /* indicates either both x- and y-coordinates are parameterized, or	*/
1939 /* else any other kind of parameterization (presently, not used).	*/
1940 /*									*/
1941 /* (note that the two angles in XDrawArc() are 1) the start angle,	*/
1942 /* measured in absolute 64th degrees from 0 (3 o'clock), and 2) the	*/
1943 /* path length, in relative 64th degrees (positive = counterclockwise,	*/
1944 /* negative = clockwise)).						*/
1945 /*----------------------------------------------------------------------*/
1946 
1947 #ifndef HAVE_CAIRO
UDrawCircle(XPoint * upt,u_char which)1948 void UDrawCircle(XPoint *upt, u_char which)
1949 {
1950    XPoint wpt;
1951 
1952    if (!areawin->redraw_ongoing) {
1953       areawin->redraw_needed = True;
1954       return;
1955    }
1956 
1957    user_to_window(*upt, &wpt);
1958    SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapButt, JoinMiter);
1959 
1960    switch(which) {
1961       case P_POSITION_X:
1962          XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1963 		wpt.y - 4, 8, 8, -(45 * 64), (90 * 64));
1964          XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1965 		wpt.y - 4, 8, 8, (135 * 64), (90 * 64));
1966 	 break;
1967       case P_POSITION_Y:
1968          XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1969 		wpt.y - 4, 8, 8, (45 * 64), (90 * 64));
1970          XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1971 		wpt.y - 4, 8, 8, (225 * 64), (90 * 64));
1972 	 break;
1973       default:
1974          XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1975 		wpt.y - 4, 8, 8, 0, (360 * 64));
1976 	 break;
1977    }
1978 }
1979 #endif /* !HAVE_CAIRO */
1980 
1981 /*----------------------------------------------------------------------*/
1982 /* Add "X" at string origin						*/
1983 /*----------------------------------------------------------------------*/
1984 
1985 #ifndef HAVE_CAIRO
UDrawXAt(XPoint * wpt)1986 void UDrawXAt(XPoint *wpt)
1987 {
1988    if (!areawin->redraw_ongoing) {
1989       areawin->redraw_needed = True;
1990       return;
1991    }
1992 
1993    SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapButt, JoinMiter);
1994    DrawLine(dpy, areawin->window, areawin->gc, wpt->x - 3,
1995 		wpt->y - 3, wpt->x + 3, wpt->y + 3);
1996    DrawLine(dpy, areawin->window, areawin->gc, wpt->x + 3,
1997 		wpt->y - 3, wpt->x - 3, wpt->y + 3);
1998 }
1999 #endif /* !HAVE_CAIRO */
2000 
2001 /*----------------------------------------------------------------------*/
2002 /* Draw "X" on current level						*/
2003 /*----------------------------------------------------------------------*/
2004 
UDrawX(labelptr curlabel)2005 void UDrawX(labelptr curlabel)
2006 {
2007    XPoint wpt;
2008 
2009    user_to_window(curlabel->position, &wpt);
2010    UDrawXAt(&wpt);
2011 }
2012 
2013 /*----------------------------------------------------------------------*/
2014 /* Draw "X" on top level (only for LOCAL and GLOBAL pin labels)		*/
2015 /*----------------------------------------------------------------------*/
2016 
UDrawXDown(labelptr curlabel)2017 void UDrawXDown(labelptr curlabel)
2018 {
2019    XPoint wpt;
2020 
2021    UTransformbyCTM(DCTM, &curlabel->position, &wpt, 1);
2022    UDrawXAt(&wpt);
2023 }
2024 
2025 /*----------------------------------------------------------------------*/
2026 /* Find the "real" width, height, and origin of an object including pin	*/
2027 /* labels and so forth that only show up on a schematic when it is the	*/
2028 /* top-level object.							*/
2029 /*----------------------------------------------------------------------*/
2030 
toplevelwidth(objinstptr bbinst,short * rllx)2031 int toplevelwidth(objinstptr bbinst, short *rllx)
2032 {
2033    short llx, urx;
2034    short origin, corner;
2035 
2036    if (bbinst->schembbox == NULL) {
2037       if (rllx) *rllx = bbinst->bbox.lowerleft.x;
2038       return bbinst->bbox.width;
2039    }
2040 
2041    origin = bbinst->bbox.lowerleft.x;
2042    corner = origin + bbinst->bbox.width;
2043 
2044    llx = bbinst->schembbox->lowerleft.x;
2045    urx = llx + bbinst->schembbox->width;
2046 
2047    bboxcalc(llx, &origin, &corner);
2048    bboxcalc(urx, &origin, &corner);
2049 
2050    if (rllx) *rllx = origin;
2051    return(corner - origin);
2052 }
2053 
2054 /*----------------------------------------------------------------------*/
2055 
toplevelheight(objinstptr bbinst,short * rlly)2056 int toplevelheight(objinstptr bbinst, short *rlly)
2057 {
2058    short lly, ury;
2059    short origin, corner;
2060 
2061    if (bbinst->schembbox == NULL) {
2062       if (rlly) *rlly = bbinst->bbox.lowerleft.y;
2063       return bbinst->bbox.height;
2064    }
2065 
2066    origin = bbinst->bbox.lowerleft.y;
2067    corner = origin + bbinst->bbox.height;
2068 
2069    lly = bbinst->schembbox->lowerleft.y;
2070    ury = lly + bbinst->schembbox->height;
2071 
2072    bboxcalc(lly, &origin, &corner);
2073    bboxcalc(ury, &origin, &corner);
2074 
2075    if (rlly) *rlly = origin;
2076    return(corner - origin);
2077 }
2078 
2079 /*----------------------------------------------------------------------*/
2080 /* Add dimensions of schematic pins to an object's bounding box		*/
2081 /*----------------------------------------------------------------------*/
2082 
extendschembbox(objinstptr bbinst,XPoint * origin,XPoint * corner)2083 void extendschembbox(objinstptr bbinst, XPoint *origin, XPoint *corner)
2084 {
2085    short llx, lly, urx, ury;
2086 
2087    if ((bbinst == NULL) || (bbinst->schembbox == NULL)) return;
2088 
2089    llx = bbinst->schembbox->lowerleft.x;
2090    lly = bbinst->schembbox->lowerleft.y;
2091    urx = llx + bbinst->schembbox->width;
2092    ury = lly + bbinst->schembbox->height;
2093 
2094    bboxcalc(llx, &(origin->x), &(corner->x));
2095    bboxcalc(lly, &(origin->y), &(corner->y));
2096    bboxcalc(urx, &(origin->x), &(corner->x));
2097    bboxcalc(ury, &(origin->y), &(corner->y));
2098 }
2099 
2100 /*----------------------------------------------------------------------*/
2101 /* Adjust a pinlabel position to account for pad spacing		*/
2102 /*----------------------------------------------------------------------*/
2103 
pinadjust(short anchor,short * xpoint,short * ypoint,short dir)2104 void pinadjust (short anchor, short *xpoint, short *ypoint, short dir)
2105 {
2106    int delx, dely;
2107 
2108    dely = (anchor & NOTBOTTOM) ?
2109             ((anchor & TOP) ? -PADSPACE : 0) : PADSPACE;
2110    delx = (anchor & NOTLEFT) ?
2111             ((anchor & RIGHT) ? -PADSPACE : 0) : PADSPACE;
2112 
2113    if (xpoint != NULL) *xpoint += (dir > 0) ? delx : -delx;
2114    if (ypoint != NULL) *ypoint += (dir > 0) ? dely : -dely;
2115 }
2116 
2117 /*----------------------------------------------------------------------*/
2118 /* Draw line for editing text (position of cursor in string is given by */
2119 /*   tpos (2nd parameter)						*/
2120 /*----------------------------------------------------------------------*/
2121 
UDrawTextLine(labelptr curlabel,short tpos)2122 void UDrawTextLine(labelptr curlabel, short tpos)
2123 {
2124    XPoint  points[2]; /* top and bottom of text cursor line */
2125    short   tmpanchor, xbase;
2126    int     maxwidth;
2127    TextExtents tmpext;
2128    TextLinesInfo tlinfo;
2129 
2130    if (!areawin->redraw_ongoing) {
2131       areawin->redraw_needed = True;
2132       return;
2133    }
2134 
2135    /* correct for position, rotation, scale, and flip invariance of text */
2136 
2137    UPushCTM();
2138    UPreMultCTM(DCTM, curlabel->position, curlabel->scale, curlabel->rotation);
2139    tmpanchor = flipadjust(curlabel->anchor);
2140 
2141    SetForeground(dpy, areawin->gc, AUXCOLOR);
2142 
2143    tlinfo.dostop = 0;
2144    tlinfo.tbreak = NULL;
2145    tlinfo.padding = NULL;
2146 
2147    tmpext = ULength(curlabel, areawin->topinstance, &tlinfo);
2148    maxwidth = tmpext.maxwidth;
2149    xbase = tmpext.base;
2150    tlinfo.dostop = tpos;
2151    tmpext = ULength(curlabel, areawin->topinstance, &tlinfo);
2152 
2153    points[0].x = (tmpanchor & NOTLEFT ?
2154         (tmpanchor & RIGHT ? -maxwidth : -maxwidth >> 1) : 0) + tmpext.width;
2155    if ((tmpanchor & JUSTIFYRIGHT) && tlinfo.padding)
2156       points[0].x += tlinfo.padding[tlinfo.line];
2157    else if ((tmpanchor & TEXTCENTERED) && tlinfo.padding)
2158       points[0].x += 0.5 * tlinfo.padding[tlinfo.line];
2159    points[0].y = (tmpanchor & NOTBOTTOM ?
2160         (tmpanchor & TOP ? -tmpext.ascent : -(tmpext.ascent + xbase) / 2)
2161 	: -xbase) + tmpext.base - 3;
2162    points[1].x = points[0].x;
2163    points[1].y = points[0].y + TEXTHEIGHT + 6;
2164 
2165    if (curlabel->pin) {
2166       pinadjust(tmpanchor, &(points[0].x), &(points[0].y), 1);
2167       pinadjust(tmpanchor, &(points[1].x), &(points[1].y), 1);
2168    }
2169    if (tlinfo.padding != NULL) free(tlinfo.padding);
2170 
2171    /* draw the line */
2172 
2173    UDrawLine(&points[0], &points[1]);
2174    UPopCTM();
2175 
2176    UDrawX(curlabel);
2177 }
2178 
2179 /*-----------------------------------------------------------------*/
2180 /* Draw lines for editing text when multiple characters are chosen */
2181 /*-----------------------------------------------------------------*/
2182 
UDrawTLine(labelptr curlabel)2183 void UDrawTLine(labelptr curlabel)
2184 {
2185    UDrawTextLine(curlabel, areawin->textpos);
2186    if ((areawin->textend > 0) && (areawin->textend < areawin->textpos)) {
2187       UDrawTextLine(curlabel, areawin->textend);
2188    }
2189 }
2190 
2191 /*----------------------*/
2192 /* Draw an X		*/
2193 /*----------------------*/
2194 
2195 #ifndef HAVE_CAIRO
UDrawXLine(XPoint opt,XPoint cpt)2196 void UDrawXLine(XPoint opt, XPoint cpt)
2197 {
2198    XPoint upt, vpt;
2199 
2200    if (!areawin->redraw_ongoing) {
2201       areawin->redraw_needed = True;
2202       return;
2203    }
2204 
2205    SetForeground(dpy, areawin->gc, AUXCOLOR);
2206 
2207    user_to_window(cpt, &upt);
2208    user_to_window(opt, &vpt);
2209 
2210    SetThinLineAttributes(dpy, areawin->gc, 0, LineOnOffDash, CapButt, JoinMiter);
2211    DrawLine(dpy, areawin->window, areawin->gc, vpt.x, vpt.y, upt.x, upt.y);
2212 
2213    SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapButt, JoinMiter);
2214    DrawLine(dpy, areawin->window, areawin->gc, upt.x - 3, upt.y - 3,
2215 		upt.x + 3, upt.y + 3);
2216    DrawLine(dpy, areawin->window, areawin->gc, upt.x + 3, upt.y - 3,
2217 		upt.x - 3, upt.y + 3);
2218 
2219    SetForeground(dpy, areawin->gc, areawin->gccolor);
2220 }
2221 #endif /* HAVE_CAIRO */
2222 
2223 /*-------------------------------------------------------------------------*/
2224 
2225 #ifndef HAVE_CAIRO
UDrawBox(XPoint origin,XPoint corner)2226 void UDrawBox(XPoint origin, XPoint corner)
2227 {
2228    XPoint	worig, wcorn;
2229 
2230    if (!areawin->redraw_ongoing) {
2231       areawin->redraw_needed = True;
2232       return;
2233    }
2234 
2235    user_to_window(origin, &worig);
2236    user_to_window(corner, &wcorn);
2237 
2238    SetForeground(dpy, areawin->gc, AUXCOLOR);
2239    SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapRound, JoinBevel);
2240    DrawLine(dpy, areawin->window, areawin->gc, worig.x, worig.y,
2241 		worig.x, wcorn.y);
2242    DrawLine(dpy, areawin->window, areawin->gc, worig.x, wcorn.y,
2243 		wcorn.x, wcorn.y);
2244    DrawLine(dpy, areawin->window, areawin->gc, wcorn.x, wcorn.y,
2245 		wcorn.x, worig.y);
2246    DrawLine(dpy, areawin->window, areawin->gc, wcorn.x, worig.y,
2247 		worig.x, worig.y);
2248 }
2249 #endif /* HAVE_CAIRO */
2250 
2251 /*----------------------------------------------------------------------*/
2252 /* Get a box indicating the dimensions of the edit element that most	*/
2253 /* closely reach the position "corner".					*/
2254 /*----------------------------------------------------------------------*/
2255 
UGetRescaleBox(XPoint * corner,XPoint * newpoints)2256 float UGetRescaleBox(XPoint *corner, XPoint *newpoints)
2257 {
2258    genericptr	rgen;
2259    float	savescale, newscale;
2260    long		mindist, testdist, refdist;
2261    labelptr	rlab;
2262    graphicptr   rgraph;
2263    objinstptr	rinst;
2264    int		i;
2265 
2266    if (!areawin->redraw_ongoing) {
2267       areawin->redraw_needed = True;
2268       // return 0.0;
2269    }
2270 
2271    if (areawin->selects == 0) return 0.0;
2272 
2273    /* Use only the 1st selection as a reference to set the scale */
2274 
2275    rgen = SELTOGENERIC(areawin->selectlist);
2276 
2277    switch(ELEMENTTYPE(rgen)) {
2278       case LABEL:
2279 	 rlab = (labelptr)rgen;
2280 	 labelbbox(rlab, newpoints, areawin->topinstance);
2281 	 newpoints[4] = newpoints[0];
2282 	 mindist = LONG_MAX;
2283 	 for (i = 0; i < 4; i++) {
2284 	    testdist = finddist(&newpoints[i], &newpoints[i+1], corner);
2285 	    if (testdist < mindist)
2286 	       mindist = testdist;
2287 	 }
2288 	 refdist = wirelength(corner, &(rlab->position));
2289 	 mindist = (int)sqrt(abs((double)mindist));
2290 	 savescale = rlab->scale;
2291 	 if (!test_insideness((int)corner->x, (int)corner->y, newpoints))
2292 	    mindist = -mindist;
2293 	 if (refdist == mindist) refdist = 1 - mindist;
2294 	 if (rlab->scale < 0) rlab->scale = -rlab->scale;
2295 	 newscale = fabs(rlab->scale * (float)refdist / (float)(refdist + mindist));
2296 	 if (newscale > 10 * rlab->scale) newscale = 10 * rlab->scale;
2297 	 if (areawin->snapto) {
2298 	    float snapstep = 2 * (float)xobjs.pagelist[areawin->page]->gridspace
2299 			/ (float)xobjs.pagelist[areawin->page]->snapspace;
2300 	    newscale = (float)((int)(newscale * snapstep)) / snapstep;
2301 	    if (newscale < (1.0 / snapstep)) newscale = (1.0 / snapstep);
2302 	 }
2303 	 else if (newscale < 0.1 * rlab->scale) newscale = 0.1 * rlab->scale;
2304 	 rlab->scale = (savescale < 0) ? -newscale : newscale;
2305 	 labelbbox(rlab, newpoints, areawin->topinstance);
2306 	 rlab->scale = savescale;
2307 	 if (savescale < 0) newscale = -newscale;
2308          break;
2309 
2310       case GRAPHIC:
2311 	 rgraph = (graphicptr)rgen;
2312 	 graphicbbox(rgraph, newpoints);
2313 	 newpoints[4] = newpoints[0];
2314 	 mindist = LONG_MAX;
2315 	 for (i = 0; i < 4; i++) {
2316 	    testdist = finddist(&newpoints[i], &newpoints[i+1], corner);
2317 	    if (testdist < mindist)
2318 	       mindist = testdist;
2319 	 }
2320 	 refdist = wirelength(corner, &(rgraph->position));
2321 	 mindist = (int)sqrt(abs((double)mindist));
2322 	 savescale = rgraph->scale;
2323 	 if (!test_insideness((int)corner->x, (int)corner->y, newpoints))
2324 	    mindist = -mindist;
2325 	 if (refdist == mindist) refdist = 1 - mindist;  /* avoid inf result */
2326 	 if (rgraph->scale < 0) rgraph->scale = -rgraph->scale;
2327 	 newscale = fabs(rgraph->scale * (float)refdist / (float)(refdist + mindist));
2328 	 if (newscale > 10 * rgraph->scale) newscale = 10 * rgraph->scale;
2329 	 if (areawin->snapto) {
2330 	    float snapstep = 2 * (float)xobjs.pagelist[areawin->page]->gridspace
2331 			/ (float)xobjs.pagelist[areawin->page]->snapspace;
2332 	    newscale = (float)((int)(newscale * snapstep)) / snapstep;
2333 	    if (newscale < (1.0 / snapstep)) newscale = (1.0 / snapstep);
2334 	 }
2335 	 else if (newscale < 0.1 * rgraph->scale) newscale = 0.1 * rgraph->scale;
2336 	 rgraph->scale = (savescale < 0) ? -newscale : newscale;
2337 	 graphicbbox(rgraph, newpoints);
2338 	 rgraph->scale = savescale;
2339 	 if (savescale < 0) newscale = -newscale;
2340          break;
2341 
2342       case OBJINST:
2343 	 rinst = (objinstptr)rgen;
2344 	 objinstbbox(rinst, newpoints, 0);
2345 	 newpoints[4] = newpoints[0];
2346 	 mindist = LONG_MAX;
2347 	 for (i = 0; i < 4; i++) {
2348 	    testdist = finddist(&newpoints[i], &newpoints[i+1], corner);
2349 	    if (testdist < mindist)
2350 	       mindist = testdist;
2351 	 }
2352 	 refdist = wirelength(corner, &(rinst->position));
2353 	 mindist = (int)sqrt(abs((double)mindist));
2354 	 savescale = rinst->scale;
2355 	 if (!test_insideness((int)corner->x, (int)corner->y, newpoints))
2356 	    mindist = -mindist;
2357 	 if (refdist == mindist) refdist = 1 - mindist;  /* avoid inf result */
2358 	 if (rinst->scale < 0) rinst->scale = -rinst->scale;
2359 	 newscale = fabs(rinst->scale * (float)refdist / (float)(refdist + mindist));
2360 	 if (newscale > 10 * rinst->scale) newscale = 10 * rinst->scale;
2361 	 if (areawin->snapto) {
2362 	    float snapstep = 2 * (float)xobjs.pagelist[areawin->page]->gridspace
2363 			/ (float)xobjs.pagelist[areawin->page]->snapspace;
2364 	    newscale = (float)((int)(newscale * snapstep)) / snapstep;
2365 	    if (newscale < (1.0 / snapstep)) newscale = (1.0 / snapstep);
2366 	 }
2367 	 else if (newscale < 0.1 * rinst->scale) newscale = 0.1 * rinst->scale;
2368 	 rinst->scale = (savescale < 0) ? -newscale : newscale;
2369 	 objinstbbox(rinst, newpoints, 0);
2370 	 rinst->scale = savescale;
2371 	 if (savescale < 0) newscale = -newscale;
2372          break;
2373    }
2374 
2375    return newscale;
2376 }
2377 
2378 /*----------------------------------------------------------------------*/
2379 /* Draw a box indicating the dimensions of the edit element that most	*/
2380 /* closely reach the position "corner".					*/
2381 /*----------------------------------------------------------------------*/
2382 
2383 #ifndef HAVE_CAIRO
UDrawRescaleBox(XPoint * corner)2384 void UDrawRescaleBox(XPoint *corner)
2385 {
2386    XPoint origpoints[5], newpoints[5];
2387 
2388    if (!areawin->redraw_ongoing) {
2389       areawin->redraw_needed = True;
2390       return;
2391    }
2392 
2393    if (areawin->selects == 0)
2394       return;
2395 
2396    UGetRescaleBox(corner, newpoints);
2397 
2398    SetForeground(dpy, areawin->gc, AUXCOLOR);
2399    SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapRound, JoinBevel);
2400 
2401    UTransformbyCTM(DCTM, newpoints, origpoints, 4);
2402    strokepath(origpoints, 4, 0, 1);
2403 }
2404 #endif /* HAVE_CAIRO */
2405 
2406 /*-------------------------------------------------------------------------*/
2407 
2408 #ifndef HAVE_CAIRO
UDrawBBox()2409 void UDrawBBox()
2410 {
2411    XPoint	origin;
2412    XPoint	worig, wcorn, corner;
2413    objinstptr	bbinst = areawin->topinstance;
2414 
2415    if (!areawin->redraw_ongoing) {
2416       areawin->redraw_needed = True;
2417       return;
2418    }
2419 
2420    if ((!areawin->bboxon) || (checkforbbox(topobject) != NULL)) return;
2421 
2422    origin = bbinst->bbox.lowerleft;
2423    corner.x = origin.x + bbinst->bbox.width;
2424    corner.y = origin.y + bbinst->bbox.height;
2425 
2426    /* Include any schematic labels in the bounding box.	*/
2427    extendschembbox(bbinst, &origin, &corner);
2428 
2429    user_to_window(origin, &worig);
2430    user_to_window(corner, &wcorn);
2431 
2432    SetForeground(dpy, areawin->gc, BBOXCOLOR);
2433    DrawLine(dpy, areawin->window, areawin->gc, worig.x, worig.y,
2434 		worig.x, wcorn.y);
2435    DrawLine(dpy, areawin->window, areawin->gc, worig.x, wcorn.y,
2436 		wcorn.x, wcorn.y);
2437    DrawLine(dpy, areawin->window, areawin->gc, wcorn.x, wcorn.y,
2438 		wcorn.x, worig.y);
2439    DrawLine(dpy, areawin->window, areawin->gc, wcorn.x, worig.y,
2440 		worig.x, worig.y);
2441 }
2442 #endif /* !HAVE_CAIRO */
2443 
2444 /*----------------------------------------------------------------------*/
2445 /* Fill and/or draw a border around the stroking path			*/
2446 /*----------------------------------------------------------------------*/
2447 
2448 #ifndef HAVE_CAIRO
strokepath(XPoint * pathlist,short number,short style,float width)2449 void strokepath(XPoint *pathlist, short number, short style, float width)
2450 {
2451    float        tmpwidth;
2452 
2453    tmpwidth = UTopTransScale(width);
2454 
2455    if (!(style & CLIPMASK) || (areawin->showclipmasks == TRUE) ||
2456 		(areawin->clipped < 0)) {
2457       if (style & FILLED || (!(style & FILLED) && style & OPAQUE)) {
2458          if ((style & FILLSOLID) == FILLSOLID)
2459             SetFillStyle(dpy, areawin->gc, FillSolid);
2460          else if (!(style & FILLED)) {
2461             SetFillStyle(dpy, areawin->gc, FillOpaqueStippled);
2462 	    SetStipple(dpy, areawin->gc, 7);
2463          }
2464          else {
2465 	    if (style & OPAQUE)
2466                SetFillStyle(dpy, areawin->gc, FillOpaqueStippled);
2467 	    else
2468                SetFillStyle(dpy, areawin->gc, FillStippled);
2469             SetStipple(dpy, areawin->gc, ((style & FILLSOLID) >> 5));
2470          }
2471          FillPolygon(dpy, areawin->window, areawin->gc, pathlist, number, Nonconvex,
2472 		CoordModeOrigin);
2473          /* return to original state */
2474          SetFillStyle(dpy, areawin->gc, FillSolid);
2475       }
2476       if (!(style & NOBORDER)) {
2477          if (style & (DASHED | DOTTED)) {
2478 	    /* Set up dots or dashes */
2479 	    char dashstring[2];
2480 	    /* prevent values greater than 255 from folding back into	*/
2481 	    /* type char.  Limit to 63 (=255/4) to keep at least the	*/
2482 	    /* dot/gap ratio to scale when 'gap' is at its maximum	*/
2483 	    /* value.							*/
2484 	    unsigned char dotsize = min(63, max(1, (short)tmpwidth));
2485 	    if (style & DASHED)
2486 	       dashstring[0] = 4 * dotsize;
2487 	    else if (style & DOTTED)
2488 	       dashstring[0] = dotsize;
2489 	    dashstring[1] = 4 * dotsize;
2490             SetDashes(dpy, areawin->gc, 0, dashstring, 2);
2491             SetLineAttributes(dpy, areawin->gc, tmpwidth, LineOnOffDash,
2492 		CapButt, (style & SQUARECAP) ? JoinMiter : JoinBevel);
2493          }
2494          else
2495             SetLineAttributes(dpy, areawin->gc, tmpwidth, LineSolid,
2496 		(style & SQUARECAP) ? CapProjecting : CapRound,
2497 		(style & SQUARECAP) ? JoinMiter : JoinBevel);
2498 
2499          /* draw the spline and close off if so specified */
2500          DrawLines(dpy, areawin->window, areawin->gc, pathlist,
2501 		number, CoordModeOrigin);
2502          if (!(style & UNCLOSED))
2503             DrawLine(dpy, areawin->window, areawin->gc, pathlist[0].x,
2504 		pathlist[0].y, pathlist[number - 1].x, pathlist[number - 1].y);
2505       }
2506    }
2507 
2508    if (style & CLIPMASK) {
2509       if (areawin->clipped == 0) {
2510          XSetForeground(dpy, areawin->cmgc, 0);
2511          XFillRectangle(dpy, areawin->clipmask, areawin->cmgc, 0, 0,
2512 		areawin->width, areawin->height);
2513          XSetForeground(dpy, areawin->cmgc, 1);
2514          FillPolygon(dpy, areawin->clipmask, areawin->cmgc, pathlist,
2515 		number, Nonconvex, CoordModeOrigin);
2516          XSetClipMask(dpy, areawin->gc, areawin->clipmask);
2517 	 // printf("level 0: Clip to clipmask\n");	// Diagnostic
2518          areawin->clipped++;
2519       }
2520       else if ((areawin->clipped > 0) && (areawin->clipped & 1) == 0) {
2521 	 if (areawin->pbuf == (Pixmap)NULL) {
2522 	    areawin->pbuf = XCreatePixmap (dpy, areawin->window,
2523 			areawin->width, areawin->height, 1);
2524 	 }
2525 	 XCopyArea(dpy, areawin->clipmask, areawin->pbuf, areawin->cmgc,
2526 		0, 0, areawin->width, areawin->height, 0, 0);
2527          XSetForeground(dpy, areawin->cmgc, 0);
2528          XFillRectangle(dpy, areawin->clipmask, areawin->cmgc, 0, 0,
2529 		areawin->width, areawin->height);
2530          XSetForeground(dpy, areawin->cmgc, 1);
2531          FillPolygon(dpy, areawin->clipmask, areawin->cmgc, pathlist,
2532 		number, Nonconvex, CoordModeOrigin);
2533          XSetFunction(dpy, areawin->cmgc, GXand);
2534 	 XCopyArea(dpy, areawin->pbuf, areawin->clipmask, areawin->cmgc,
2535 		0, 0, areawin->width, areawin->height, 0, 0);
2536          XSetFunction(dpy, areawin->cmgc, GXcopy);
2537          XSetClipMask(dpy, areawin->gc, areawin->clipmask);
2538 	 // printf("level X: Clip to clipmask\n");	// Diagnostic
2539          areawin->clipped++;
2540       }
2541    }
2542 }
2543 #endif /* !HAVE_CAIRO */
2544 
2545 /*-------------------------------------------------------------------------*/
2546 
makesplinepath(splineptr thespline,XPoint * pathlist)2547 void makesplinepath(splineptr thespline, XPoint *pathlist)
2548 {
2549    XPoint *tmpptr = pathlist;
2550 
2551    UTransformbyCTM(DCTM, &(thespline->ctrl[0]), tmpptr, 1);
2552    UfTransformbyCTM(DCTM, thespline->points, ++tmpptr, INTSEGS);
2553    UTransformbyCTM(DCTM, &(thespline->ctrl[3]), tmpptr + INTSEGS, 1);
2554 }
2555 
2556 /*-------------------------------------------------------------------------*/
2557 
2558 #ifndef HAVE_CAIRO
UDrawSpline(splineptr thespline,float passwidth)2559 void UDrawSpline(splineptr thespline, float passwidth)
2560 {
2561    XPoint       tmppoints[SPLINESEGS];
2562    float	scaledwidth;
2563 
2564    if (!areawin->redraw_ongoing) {
2565       areawin->redraw_needed = True;
2566       return;
2567    }
2568 
2569    scaledwidth = thespline->width * passwidth;
2570 
2571    makesplinepath(thespline, tmppoints);
2572    strokepath(tmppoints, SPLINESEGS, thespline->style, scaledwidth);
2573 }
2574 #endif /* HAVE_CAIRO */
2575 
2576 /*-------------------------------------------------------------------------*/
2577 
2578 #ifndef HAVE_CAIRO
UDrawPolygon(polyptr thepoly,float passwidth)2579 void UDrawPolygon(polyptr thepoly, float passwidth)
2580 {
2581    XPoint *tmppoints = (pointlist) malloc(thepoly->number * sizeof(XPoint));
2582    float scaledwidth;
2583 
2584    if (!areawin->redraw_ongoing) {
2585       areawin->redraw_needed = True;
2586       return;
2587    }
2588 
2589    scaledwidth = thepoly->width * passwidth;
2590 
2591    UTransformbyCTM(DCTM, thepoly->points, tmppoints, thepoly->number);
2592    strokepath(tmppoints, thepoly->number, thepoly->style, scaledwidth);
2593    free(tmppoints);
2594 }
2595 #endif /* HAVE_CAIRO */
2596 
2597 /*-------------------------------------------------------------------------*/
2598 
2599 #ifndef HAVE_CAIRO
UDrawArc(arcptr thearc,float passwidth)2600 void UDrawArc(arcptr thearc, float passwidth)
2601 {
2602    XPoint  tmppoints[RSTEPS + 2];
2603    float scaledwidth;
2604 
2605    if (!areawin->redraw_ongoing) {
2606       areawin->redraw_needed = True;
2607       return;
2608    }
2609 
2610    scaledwidth = thearc->width * passwidth;
2611 
2612    UfTransformbyCTM(DCTM, thearc->points, tmppoints, thearc->number);
2613    strokepath(tmppoints, thearc->number, thearc->style, scaledwidth);
2614 }
2615 #endif /* HAVE_CAIRO */
2616 
2617 /*-------------------------------------------------------------------------*/
2618 
2619 #ifndef HAVE_CAIRO
UDrawPath(pathptr thepath,float passwidth)2620 void UDrawPath(pathptr thepath, float passwidth)
2621 {
2622    XPoint	*tmppoints = (pointlist) malloc(sizeof(XPoint));
2623    genericptr	*genpath;
2624    polyptr	thepoly;
2625    splineptr	thespline;
2626    int		pathsegs = 0, curseg = 0;
2627    float	scaledwidth;
2628 
2629    if (!areawin->redraw_ongoing) {
2630       areawin->redraw_needed = True;
2631       return;
2632    }
2633 
2634    for (genpath = thepath->plist; genpath < thepath->plist + thepath->parts;
2635 	  genpath++) {
2636       switch(ELEMENTTYPE(*genpath)) {
2637 	 case POLYGON:
2638 	    thepoly = TOPOLY(genpath);
2639 	    pathsegs += thepoly->number;
2640 	    tmppoints = (pointlist) realloc(tmppoints, pathsegs * sizeof(XPoint));
2641    	    UTransformbyCTM(DCTM, thepoly->points, tmppoints + curseg, thepoly->number);
2642 	    curseg = pathsegs;
2643 	    break;
2644 	 case SPLINE:
2645 	    thespline = TOSPLINE(genpath);
2646 	    pathsegs += SPLINESEGS;
2647 	    tmppoints = (pointlist) realloc(tmppoints, pathsegs * sizeof(XPoint));
2648    	    makesplinepath(thespline, tmppoints + curseg);
2649 	    curseg = pathsegs;
2650 	    break;
2651       }
2652    }
2653    scaledwidth = thepath->width * passwidth;
2654 
2655    strokepath(tmppoints, pathsegs, thepath->style, scaledwidth);
2656    free(tmppoints);
2657 }
2658 #endif /* HAVE_CAIRO */
2659 
2660 /*----------------------------------------------------------------------*/
2661 /* Main recursive object instance drawing routine.			*/
2662 /*    context is the instance information passed down from above	*/
2663 /*    theinstance is the object instance to be drawn			*/
2664 /*    level is the level of recursion 					*/
2665 /*    passcolor is the inherited color value passed to object		*/
2666 /*    passwidth is the inherited linewidth value passed to the object	*/
2667 /*    stack contains graphics context information			*/
2668 /*----------------------------------------------------------------------*/
2669 
2670 #ifndef HAVE_CAIRO
UDrawObject(objinstptr theinstance,short level,int passcolor,float passwidth,pushlistptr * stack)2671 void UDrawObject(objinstptr theinstance, short level, int passcolor,
2672 		float passwidth, pushlistptr *stack)
2673 {
2674    genericptr	*areagen;
2675    float	tmpwidth;
2676    int		defaultcolor = passcolor;
2677    int		curcolor = passcolor;
2678    int		thispart;
2679    short	savesel;
2680    XPoint 	bboxin[2], bboxout[2];
2681    u_char	xm, ym;
2682    objectptr	theobject = theinstance->thisobject;
2683 
2684    if (!areawin->redraw_ongoing) {
2685       areawin->redraw_needed = True;
2686       return;
2687    }
2688 
2689    /* Save the number of selections and set it to zero while we do the	*/
2690    /* object drawing.							*/
2691 
2692    savesel = areawin->selects;
2693    areawin->selects = 0;
2694 
2695    /* All parts are given in the coordinate system of the object, unless */
2696    /* this is the top-level object, in which they will be interpreted as */
2697    /* relative to the screen.						 */
2698 
2699    UPushCTM();
2700 
2701    if (stack) {
2702       /* Save the current clipping mask and push it on the stack */
2703       if (areawin->clipped > 0) {
2704          push_stack((pushlistptr *)stack, theinstance, (char *)areawin->clipmask);
2705 	 areawin->clipmask = XCreatePixmap(dpy, areawin->window, areawin->width,
2706 		areawin->height, 1);
2707 	 XCopyArea(dpy, (Pixmap)(*stack)->clientdata, areawin->clipmask, areawin->cmgc,
2708 		0, 0, areawin->width, areawin->height, 0, 0);
2709       }
2710       else
2711          push_stack((pushlistptr *)stack, theinstance, (char *)NULL);
2712    }
2713    if (level != 0)
2714        UPreMultCTM(DCTM, theinstance->position, theinstance->scale,
2715 			theinstance->rotation);
2716 
2717    if (theinstance->style & LINE_INVARIANT)
2718       passwidth /= fabs(theinstance->scale);
2719 
2720    /* do a quick test for intersection with the display window */
2721 
2722    bboxin[0].x = theobject->bbox.lowerleft.x;
2723    bboxin[0].y = theobject->bbox.lowerleft.y;
2724    bboxin[1].x = theobject->bbox.lowerleft.x + theobject->bbox.width;
2725    bboxin[1].y = theobject->bbox.lowerleft.y + theobject->bbox.height;
2726    if (level == 0)
2727       extendschembbox(theinstance, &(bboxin[0]), &(bboxin[1]));
2728    UTransformbyCTM(DCTM, bboxin, bboxout, 2);
2729 
2730    xm = (bboxout[0].x < bboxout[1].x) ? 0 : 1;
2731    ym = (bboxout[0].y < bboxout[1].y) ? 0 : 1;
2732 
2733    if (bboxout[xm].x < areawin->width && bboxout[ym].y < areawin->height &&
2734        bboxout[1 - xm].x > 0 && bboxout[1 - ym].y > 0) {
2735 
2736      /* make parameter substitutions */
2737      psubstitute(theinstance);
2738 
2739      /* draw all of the elements */
2740 
2741      tmpwidth = UTopTransScale(passwidth);
2742      SetLineAttributes(dpy, areawin->gc, tmpwidth, LineSolid, CapRound,
2743 		JoinBevel);
2744 
2745      /* guard against plist being regenerated during a redraw by the	*/
2746      /* expression parameter mechanism (should that be prohibited?)	*/
2747 
2748      for (thispart = 0; thispart < theobject->parts; thispart++) {
2749        areagen = theobject->plist + thispart;
2750        if ((*areagen)->type & DRAW_HIDE) continue;
2751 
2752        if (defaultcolor != DOFORALL) {
2753 	  Boolean clipcolor = FALSE;
2754 	  switch(ELEMENTTYPE(*areagen)) {
2755  	     case(POLYGON): case(SPLINE): case(ARC): case(PATH):
2756 	        if (TOPOLY(areagen)->style & CLIPMASK)
2757 		   clipcolor = TRUE;
2758 		break;
2759 	  }
2760 	  if (((*areagen)->color != curcolor) || (clipcolor == TRUE)) {
2761 	     if (clipcolor)
2762 		curcolor = CLIPMASKCOLOR;
2763 	     else if ((*areagen)->color == DEFAULTCOLOR)
2764 		curcolor = defaultcolor;
2765 	     else
2766 		curcolor = (*areagen)->color;
2767 
2768 	     XcTopSetForeground(curcolor);
2769 	  }
2770        }
2771 
2772        switch(ELEMENTTYPE(*areagen)) {
2773 	  case(POLYGON):
2774 	     if (level == 0 || !((TOPOLY(areagen))->style & BBOX))
2775                 UDrawPolygon(TOPOLY(areagen), passwidth);
2776 	     break;
2777 
2778 	  case(SPLINE):
2779              UDrawSpline(TOSPLINE(areagen), passwidth);
2780 	     break;
2781 
2782 	  case(ARC):
2783              UDrawArc(TOARC(areagen), passwidth);
2784 	     break;
2785 
2786 	  case(PATH):
2787 	     UDrawPath(TOPATH(areagen), passwidth);
2788 	     break;
2789 
2790 	  case(GRAPHIC):
2791 	     UDrawGraphic(TOGRAPHIC(areagen));
2792 	     break;
2793 
2794           case(OBJINST):
2795 	     if (areawin->editinplace && stack && (TOOBJINST(areagen)
2796 			== areawin->topinstance)) {
2797 		/* If stack matches areawin->stack, then don't draw */
2798 		/* because it would be redundant.		 */
2799 		pushlistptr alist = *stack, blist = areawin->stack;
2800 		while (alist && blist) {
2801 		   if (alist->thisinst != blist->thisinst) break;
2802 		   alist = alist->next;
2803 		   blist = blist->next;
2804 		}
2805 		if ((!alist) || (!blist)) break;
2806 	     }
2807 	     if (areawin->clipped > 0) areawin->clipped += 2;
2808              UDrawObject(TOOBJINST(areagen), level + 1, curcolor, passwidth, stack);
2809 	     if (areawin->clipped > 0) areawin->clipped -= 2;
2810 	     break;
2811 
2812   	  case(LABEL):
2813 	     if (level == 0 || TOLABEL(areagen)->pin == False)
2814                 UDrawString(TOLABEL(areagen), curcolor, theinstance);
2815 	     else if ((TOLABEL(areagen)->anchor & PINVISIBLE) && areawin->pinpointon)
2816                 UDrawString(TOLABEL(areagen), curcolor, theinstance);
2817 	     else if (TOLABEL(areagen)->anchor & PINVISIBLE)
2818                 UDrawStringNoX(TOLABEL(areagen), curcolor, theinstance);
2819 	     else if (level == 1 && TOLABEL(areagen)->pin &&
2820 			TOLABEL(areagen)->pin != INFO && areawin->pinpointon)
2821 		UDrawXDown(TOLABEL(areagen));
2822 	     break;
2823        }
2824        if (areawin->clipped > 0) {
2825 	  if ((areawin->clipped & 3) == 1) {
2826 	     areawin->clipped++;
2827 	  }
2828 	  else if ((areawin->clipped & 3) == 2) {
2829 	     areawin->clipped -= 2;
2830 	     if ((!stack) || ((*stack)->clientdata == (char *)NULL)) {
2831 	        XSetClipMask(dpy, areawin->gc, None);
2832 	 	// printf("1: Clear clipmask\n");	// Diagnostic
2833 	     }
2834 	     else {
2835 	        XSetClipMask(dpy, areawin->gc, (Pixmap)((*stack)->clientdata));
2836 	 	// printf("1: Set to pushed clipmask\n");	// Diagnostic
2837 	     }
2838 	  }
2839        }
2840      }
2841 
2842      /* restore the color passed to the object, if different from current color */
2843 
2844      if ((defaultcolor != DOFORALL) && (passcolor != curcolor)) {
2845 	XTopSetForeground(passcolor);
2846      }
2847      if (areawin->clipped > 0) {
2848 	if ((areawin->clipped & 3) != 3) {
2849 	   if ((!stack) || ((*stack)->clientdata == (char *)NULL)) {
2850 	      XSetClipMask(dpy, areawin->gc, None);
2851 	      // printf("2: Clear clipmask\n");	// Diagnostic
2852 	   }
2853 	   else {
2854 	      XSetClipMask(dpy, areawin->gc, (Pixmap)((*stack)->clientdata));
2855 	      // printf("2: Set to pushed clipmask\n");	// Diagnostic
2856 	   }
2857 	}
2858 	areawin->clipped &= ~3;
2859      }
2860    }
2861 
2862    /* restore the selection list (if any) */
2863    areawin->selects = savesel;
2864    UPopCTM();
2865    if (stack) {
2866       if ((*stack) != NULL) {
2867 	 if ((*stack)->clientdata != (char *)NULL) {
2868 	    XFreePixmap(dpy, areawin->clipmask);
2869             areawin->clipmask = (Pixmap)(*stack)->clientdata;
2870 	    // printf("3: Restore clipmask\n");	// Diagnostic
2871 	 }
2872       }
2873       pop_stack(stack);
2874    }
2875 }
2876 #endif /* HAVE_CAIRO */
2877 
2878 /*----------------------------------------------------------------------*/
2879 /* Recursively run through the current page and find any labels which	*/
2880 /* are declared to be style LATEX.  If "checkonly" is present, we set	*/
2881 /* it to TRUE or FALSE depending on whether or not LATEX labels have	*/
2882 /* been encountered.  If NULL, then we write LATEX output appropriately	*/
2883 /* to a file named with the page filename + suffix ".tex".		*/
2884 /*----------------------------------------------------------------------*/
2885 
UDoLatex(objinstptr theinstance,short level,FILE * f,float scale,float scale2,int tx,int ty,Boolean * checkonly)2886 void UDoLatex(objinstptr theinstance, short level, FILE *f,
2887 	float scale, float scale2, int tx, int ty, Boolean *checkonly)
2888 {
2889    XPoint	lpos, xlpos;
2890    XfPoint	xfpos;
2891    labelptr	thislabel;
2892    genericptr	*areagen;
2893    objectptr	theobject = theinstance->thisobject;
2894    char		*ltext;
2895    int		lranchor, tbanchor;
2896 
2897    UPushCTM();
2898    if (level != 0)
2899        UPreMultCTM(DCTM, theinstance->position, theinstance->scale,
2900 			theinstance->rotation);
2901 
2902    /* make parameter substitutions */
2903    psubstitute(theinstance);
2904 
2905    /* find all of the elements */
2906 
2907    for (areagen = theobject->plist; areagen < theobject->plist +
2908            theobject->parts; areagen++) {
2909 
2910       switch(ELEMENTTYPE(*areagen)) {
2911          case(OBJINST):
2912             UDoLatex(TOOBJINST(areagen), level + 1, f, scale, scale2, tx, ty, checkonly);
2913 	    break;
2914 
2915 	 case(LABEL):
2916 	    thislabel = TOLABEL(areagen);
2917 	    if (level == 0 || thislabel->pin == False ||
2918 			(thislabel->anchor & PINVISIBLE))
2919 		if (thislabel->anchor & LATEXLABEL) {
2920 		    if (checkonly) {
2921 		       *checkonly = TRUE;
2922 		       return;
2923 		    }
2924 		    else {
2925 		       lpos.x = thislabel->position.x;
2926 		       lpos.y = thislabel->position.y;
2927 		       UTransformbyCTM(DCTM, &lpos, &xlpos, 1);
2928 		       xlpos.x += tx;
2929 		       xlpos.y += ty;
2930 		       xfpos.x = (float)xlpos.x * scale;
2931 		       xfpos.y = (float)xlpos.y * scale;
2932 		       xfpos.x /= 72.0;
2933 		       xfpos.y /= 72.0;
2934 		       xfpos.x -= 1.0;
2935 		       xfpos.y -= 1.0;
2936 		       xfpos.x += 0.056;
2937 		       xfpos.y += 0.056;
2938 		       xfpos.x /= scale2;
2939 		       xfpos.y /= scale2;
2940 		       ltext = textprinttex(thislabel->string, theinstance);
2941 		       tbanchor = thislabel->anchor & (NOTBOTTOM | TOP);
2942 		       lranchor = thislabel->anchor & (NOTLEFT | RIGHT);
2943 
2944 		       /* The 1.2 factor accounts for the difference between	 */
2945 		       /* Xcircuit's label scale of "1" and LaTeX's "normalsize" */
2946 
2947 		       fprintf(f, "   \\putbox{%3.2fin}{%3.2fin}{%3.2f}{",
2948 				xfpos.x, xfpos.y, 1.2 * thislabel->scale);
2949 		       if (thislabel->rotation != 0)
2950 			  fprintf(f, "\\rotatebox{-%d}{", thislabel->rotation);
2951 		       if (lranchor == (NOTLEFT | RIGHT)) fprintf(f, "\\rightbox{");
2952 		       else if (lranchor == NOTLEFT) fprintf(f, "\\centbox{");
2953 		       if (tbanchor == (NOTBOTTOM | TOP)) fprintf(f, "\\topbox{");
2954 		       else if (tbanchor == NOTBOTTOM) fprintf(f, "\\midbox{");
2955 		       fprintf(f, "%s", ltext);
2956 		       if (lranchor != NORMAL) fprintf(f, "}");
2957 		       if (tbanchor != NORMAL) fprintf(f, "}");
2958 		       if (thislabel->rotation != 0) fprintf(f, "}");
2959 		       fprintf(f, "}%%\n");
2960 		       free(ltext);
2961 		    }
2962 		}
2963 	    break;
2964        }
2965    }
2966    UPopCTM();
2967 }
2968 
2969 /*----------------------------------------------------------------------*/
2970 /*  Top level routine for writing LATEX output.				*/
2971 /*----------------------------------------------------------------------*/
2972 
TopDoLatex()2973 void TopDoLatex()
2974 {
2975    FILE *f;
2976    float psscale, outscale;
2977    int tx, ty, width, height;
2978    polyptr framebox;
2979    XPoint origin;
2980    Boolean checklatex = FALSE;
2981    char filename[100], extension[10], *dotptr;
2982 
2983    UDoLatex(areawin->topinstance, 0, NULL, 1.0, 1.0, 0, 0, &checklatex);
2984 
2985    if (checklatex == FALSE) return;	/* No LaTeX labels to write */
2986 
2987    /* Handle cases where the file might have a ".eps" extension.	*/
2988    /* Thanks to Graham Sheward for pointing this out.			*/
2989 
2990    /* Modified file path routines:                                      */
2991    /* Solved problems with incomplete paths, NULL file pointers,        */
2992    /* added tilde and variable expansion by Agustín Campeny, April 2020 */
2993 
2994    sprintf(filename, "%s", xobjs.pagelist[areawin->page]->filename);
2995 
2996    xc_tilde_expand(filename, 100);
2997    while(xc_variable_expand(filename, 100));
2998 
2999    dotptr = strrchr(filename, '.');
3000    sprintf(extension, "%s", dotptr);
3001    filename[dotptr - filename] = '\0';
3002    sprintf(filename, "%s.tex", filename);
3003 
3004    f = fopen(filename, "w");
3005 
3006    if (!f) {
3007      Wprintf("Couldn't save .tex file. Check file path");
3008      return;
3009    }
3010 
3011    *dotptr = '\0';
3012 
3013    fprintf(f, "%% XCircuit output \"%s.tex\" for LaTeX input from %s%s\n",
3014 		filename, filename, extension);
3015    fprintf(f, "\\def\\putbox#1#2#3#4{\\makebox[0in][l]{\\makebox[#1][l]{}"
3016 		"\\raisebox{\\baselineskip}[0in][0in]"
3017 		"{\\raisebox{#2}[0in][0in]{\\scalebox{#3}{#4}}}}}\n");
3018    fprintf(f, "\\def\\rightbox#1{\\makebox[0in][r]{#1}}\n");
3019    fprintf(f, "\\def\\centbox#1{\\makebox[0in]{#1}}\n");
3020    fprintf(f, "\\def\\topbox#1{\\raisebox{-0.60\\baselineskip}[0in][0in]{#1}}\n");
3021    fprintf(f, "\\def\\midbox#1{\\raisebox{-0.20\\baselineskip}[0in][0in]{#1}}\n");
3022 
3023    /* Modified to use \scalebox and \parbox by Alex Tercete, June 2008 */
3024 
3025    // fprintf(f, "\\begin{center}\n");
3026 
3027    outscale = xobjs.pagelist[areawin->page]->outscale;
3028    psscale = getpsscale(outscale, areawin->page);
3029 
3030    width = toplevelwidth(areawin->topinstance, &origin.x);
3031    height = toplevelheight(areawin->topinstance, &origin.y);
3032 
3033    /* Added 10/19/10:  If there is a specified bounding box, let it	*/
3034    /* determine the figure origin;  otherwise, the labels will be	*/
3035    /* mismatched to the bounding box.					*/
3036 
3037    if ((framebox = checkforbbox(topobject)) != NULL) {
3038       int i, maxx, maxy;
3039 
3040       origin.x = maxx = framebox->points[0].x;
3041       origin.y = maxy = framebox->points[0].y;
3042       for (i = 1; i < framebox->number; i++) {
3043 	 if (framebox->points[i].x < origin.x) origin.x = framebox->points[i].x;
3044 	 if (framebox->points[i].x > maxx) maxx = framebox->points[i].x;
3045 	 if (framebox->points[i].y < origin.y) origin.y = framebox->points[i].y;
3046 	 if (framebox->points[i].y > maxy) maxy = framebox->points[i].y;
3047       }
3048       origin.x -= ((width - maxx + origin.x) / 2);
3049       origin.y -= ((height - maxy + origin.y) / 2);
3050    }
3051 
3052    tx = (int)(72 / psscale) - origin.x,
3053    ty = (int)(72 / psscale) - origin.y;
3054 
3055    fprintf(f, "   \\scalebox{%g}{\n", outscale);
3056    fprintf(f, "   \\normalsize\n");
3057    fprintf(f, "   \\parbox{%gin}{\n", (((float)width * psscale) / 72.0) / outscale);
3058    fprintf(f, "   \\includegraphics[scale=%g]{%s%s}\\\\\n", 1.0 / outscale,
3059     			filename, extension);
3060    fprintf(f, "   %% translate x=%d y=%d scale %3.2f\n", tx, ty, psscale);
3061 
3062    UPushCTM();		/* Save current state */
3063    UResetCTM(DCTM);	/* Set to identity matrix */
3064    UDoLatex(areawin->topinstance, 0, f, psscale, outscale, tx, ty, NULL);
3065    UPopCTM();		/* Restore state */
3066 
3067    fprintf(f, "   } %% close \'parbox\'\n");
3068    fprintf(f, "   } %% close \'scalebox\'\n");
3069    fprintf(f, "   \\vspace{-\\baselineskip} %% this is not"
3070 		" necessary, but looks better\n");
3071    // fprintf(f, "\\end{center}\n");
3072    fclose(f);
3073 
3074    Wprintf("Wrote auxiliary file %s.tex", filename);
3075 }
3076 
3077