Corrected a long-standing error in which ending text with a literal
[xcircuit.git] / functions.c
blobb193f50adddc122ccfbe61ba39d6adada83fd48b
1 /*-------------------------------------------------------------------------*/
2 /* functions.c */
3 /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */
4 /*-------------------------------------------------------------------------*/
6 /*-------------------------------------------------------------------------*/
7 /* written by Tim Edwards, 8/13/93 */
8 /*-------------------------------------------------------------------------*/
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <math.h>
14 #include <limits.h>
16 #ifndef _MSC_VER
17 #include <X11/Intrinsic.h>
18 #include <X11/StringDefs.h>
19 #endif
21 /*-------------------------------------------------------------------------*/
22 /* Local includes */
23 /*-------------------------------------------------------------------------*/
25 #ifdef TCL_WRAPPER
26 #include <tk.h>
27 #endif
29 #include "colordefs.h"
30 #include "xcircuit.h"
32 /*----------------------------------------------------------------------*/
33 /* Function prototype declarations */
34 /*----------------------------------------------------------------------*/
35 #include "prototypes.h"
37 /*-------------------------------------------------------------------------*/
38 /* External Variable definitions */
39 /*-------------------------------------------------------------------------*/
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;
48 /*------------------------------------------------------------------------*/
49 /* find the squared length of a wire (or distance between two points in */
50 /* user space). */
51 /*------------------------------------------------------------------------*/
53 long sqwirelen(XPoint *userpt1, XPoint *userpt2)
55 long xdist, ydist;
57 xdist = (long)userpt2->x - (long)userpt1->x;
58 ydist = (long)userpt2->y - (long)userpt1->y;
59 return (xdist * xdist + ydist * ydist);
62 /*------------------------------------------------------------------------*/
63 /* floating-point version of the above */
64 /*------------------------------------------------------------------------*/
66 float fsqwirelen(XfPoint *userpt1, XfPoint *userpt2)
68 float xdist, ydist;
70 xdist = userpt2->x - userpt1->x;
71 ydist = userpt2->y - userpt1->y;
72 return (xdist * xdist + ydist * ydist);
75 /*------------------------------------------------------------------------*/
76 /* Find absolute distance between two points in user space */
77 /*------------------------------------------------------------------------*/
79 int wirelength(XPoint *userpt1, XPoint *userpt2)
81 u_long xdist, ydist;
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));
88 /*------------------------------------------------------------------------*/
89 /* Find the closest (squared) distance from a point to a line */
90 /*------------------------------------------------------------------------*/
92 long finddist(XPoint *linept1, XPoint *linept2, XPoint *userpt)
94 long a, b, c, frac;
95 float protod;
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)));
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 /*----------------------------------------------------------------------*/
118 void decomposearc(pathptr thepath, XPoint *startpoint)
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;
131 pgen = thepath->plist + thepath->parts - 1;
132 if (ELEMENTTYPE(*pgen) != ARC) return;
133 thearc = TOARC(pgen);
135 if (thearc->radius < 0) {
136 reverse = TRUE;
137 thearc->radius = -thearc->radius;
140 fnc = (thearc->angle2 - thearc->angle1) / 90.0;
141 ncurves = (short)fnc;
142 if (fnc - (float)((int)fnc) > 0.01) ncurves++;
144 thepath->parts--; /* Forget the arc */
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;
153 if (i == ncurves - 1)
154 ang2 = thearc->angle1;
155 else
156 ang2 = ang1 - 90;
158 else { /* arc path is forward direction */
159 if (i == 0)
160 ang1 = thearc->angle1;
161 else
162 ang1 += 90;
164 if (i == ncurves - 1)
165 ang2 = thearc->angle2;
166 else
167 ang2 = ang1 + 90;
170 lambda1 = (double)ang1 * RADFAC;
171 lambda2 = (double)ang2 * RADFAC;
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);
190 P1.x = (int)(roundf(E1.x));
191 P1.y = (int)(roundf(E1.y));
193 tansq = tan((nu2 - nu1) / 2.0);
194 tansq *= tansq;
195 alpha = sin(nu2 - nu1) * 0.33333 * (sqrt(4 + (3 * tansq)) - 1);
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).
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;
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;
220 (*newspline)->ctrl[0].x = P1.x;
221 (*newspline)->ctrl[0].y = P1.y;
223 (*newspline)->ctrl[1].x = (int)(roundf(E1.x + alpha * Ep1.x));
224 (*newspline)->ctrl[1].y = (int)(roundf(E1.y + alpha * Ep1.y));
226 (*newspline)->ctrl[2].x = (int)(roundf(E2.x - alpha * Ep2.x));
227 (*newspline)->ctrl[2].y = (int)(roundf(E2.y - alpha * Ep2.y));
229 (*newspline)->ctrl[3].x = (int)(roundf(E2.x));
230 (*newspline)->ctrl[3].y = (int)(roundf(E2.y));
232 calcspline(*newspline);
235 /* Delete the arc */
236 free_single((genericptr)thearc);
239 /*----------------------------------------------------------------------*/
240 /* Calculate points for an arc */
241 /*----------------------------------------------------------------------*/
243 void calcarc(arcptr thearc)
245 short idx;
246 int sarc;
247 float theta, delta;
249 /* assume that angle2 > angle1 always: must be guaranteed by other routines */
251 sarc = (int)(thearc->angle2 - thearc->angle1) * RSTEPS;
252 thearc->number = (sarc / 360) + 1;
253 if (sarc % 360 != 0) thearc->number++;
255 delta = RADFAC * ((float)(thearc->angle2 - thearc->angle1) / (thearc->number - 1));
256 theta = thearc->angle1 * RADFAC;
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;
266 /* place last point exactly to avoid roundoff error */
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);
274 if (thearc->radius < 0) reversefpoints(thearc->points, thearc->number);
277 /*------------------------------------------------------------------------*/
278 /* Create a Bezier curve approximation from control points */
279 /* (using PostScript formula for Bezier cubic curve) */
280 /*------------------------------------------------------------------------*/
282 float par[INTSEGS];
283 float parsq[INTSEGS];
284 float parcb[INTSEGS];
286 void initsplines()
288 float t;
289 short idx;
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;
299 /*------------------------------------------------------------------------*/
300 /* Compute spline coefficients */
301 /*------------------------------------------------------------------------*/
303 void computecoeffs(splineptr thespline, float *ax, float *bx, float *cx,
304 float *ay, float *by, float *cy)
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;
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;
315 /*------------------------------------------------------------------------*/
317 void calcspline(splineptr thespline)
319 float ax, bx, cx, ay, by, cy;
320 short idx;
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;
331 /*------------------------------------------------------------------------*/
332 /* Find the (x,y) position and tangent rotation of a point on a spline */
333 /*------------------------------------------------------------------------*/
335 void findsplinepos(splineptr thespline, float t, XPoint *retpoint, float *retrot)
337 float ax, bx, cx, ay, by, cy;
338 float tsq = t * t;
339 float tcb = tsq * t;
340 double dxdt, dydt;
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);
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;
354 /*------------------------------------------------------------------------*/
355 /* floating-point version of the above */
356 /*------------------------------------------------------------------------*/
358 void ffindsplinepos(splineptr thespline, float t, XfPoint *retpoint)
360 float ax, bx, cx, ay, by, cy;
361 float tsq = t * t;
362 float tcb = tsq * t;
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;
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 /*------------------------------------------------------------------------*/
374 float findsplinemin(splineptr thespline, XPoint *upoint)
376 XfPoint *spt, flpt, newspt;
377 float minval = 1000000, tval, hval, ndist;
378 short j, ival;
380 flpt.x = (float)(upoint->x);
381 flpt.y = (float)(upoint->y);
383 /* get estimate from precalculated spline points */
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);
393 tval = (float)(ival + 1) / (INTSEGS + 1);
394 hval = 0.5 / (INTSEGS + 1);
396 /* short fixed iterative loop to converge on minimum t */
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;
410 hval /= 2;
413 if (tval < 0.1) {
414 if ((float)sqwirelen(&(thespline->ctrl[0]), upoint) < minval) tval = 0;
416 else if (tval > 0.9) {
417 if ((float)sqwirelen(&(thespline->ctrl[3]), upoint) < minval) tval = 1;
419 return tval;
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 /*----------------------------------------------------------------------*/
435 void converttocurve()
437 genericptr *ggen;
438 splineptr *newspline;
439 polyptr thispoly;
440 pathptr *newpath;
441 short *newselect;
442 XPoint firstpoint, lastpoint, initpoint;
443 int i, numpoints;
445 if (areawin->selects != 1) return;
447 thispoly = TOPOLY(topobject->plist + (*areawin->selectlist));
448 if (ELEMENTTYPE(thispoly) != POLYGON) return;
449 if (thispoly->number < 3) return; /* Will not convert */
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];
460 else {
461 numpoints = thispoly->number;
463 /* If the polygon is closed but the first and last points */
464 /* overlap, treat the last point as if it doesn't exist. */
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--;
472 NEW_PATH(newpath, topobject);
473 pathdefaults(*newpath, 0, 0);
474 (*newpath)->style = thispoly->style;
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;
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);
493 else
494 firstpoint = thispoly->points[0];
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;
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);
512 if (!(thispoly->style & UNCLOSED))
513 lastpoint = initpoint;
514 else
515 lastpoint = thispoly->points[i + 2];
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;
524 calcspline(*newspline);
525 calcbbox(areawin->topinstance);
526 setoptionmenu();
527 drawarea(NULL, NULL, NULL);
530 /*----------------------------------------------------------------------*/
531 /* Find closest point of a polygon to the cursor */
532 /*----------------------------------------------------------------------*/
534 short closepointdistance(polyptr curpoly, XPoint *cursloc, short *mindist)
536 short curdist;
537 XPoint *curpt, *savept;
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;
548 return (short)(savept - curpoly->points);
551 /*----------------------------------------------------------------------------*/
552 /* Find closest point of a polygon to the cursor */
553 /*----------------------------------------------------------------------------*/
555 short closepoint(polyptr curpoly, XPoint *cursloc)
557 short mindist;
558 return closepointdistance(curpoly, cursloc, &mindist);
561 /*----------------------------------------------------------------------------*/
562 /* Find the distance to the closest point of a polygon to the cursor */
563 /*----------------------------------------------------------------------------*/
565 short closedistance(polyptr curpoly, XPoint *cursloc)
567 short mindist;
568 closepointdistance(curpoly, cursloc, &mindist);
569 return mindist;
572 /*----------------------------------------------------------------------------*/
573 /* Coordinate system transformations */
574 /*----------------------------------------------------------------------------*/
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 /*------------------------------------------------------------------------------*/
583 short checkbounds()
585 long lval;
587 /* check window-to-user space */
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;
596 /* check user-to-window space */
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;
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;
612 return 0;
615 /*------------------------------------------------------------------------*/
616 /* Transform X-window coordinate to xcircuit coordinate system */
617 /*------------------------------------------------------------------------*/
619 void window_to_user(short xw, short yw, XPoint *upt)
621 float tmpx, tmpy;
623 tmpx = (float)xw / areawin->vscale + (float)areawin->pcorner.x;
624 tmpy = (float)(areawin->height - yw) / areawin->vscale +
625 (float)areawin->pcorner.y;
627 tmpx += (tmpx > 0) ? 0.5 : -0.5;
628 tmpy += (tmpy > 0) ? 0.5 : -0.5;
630 upt->x = (short)tmpx;
631 upt->y = (short)tmpy;
634 /*------------------------------------------------------------------------*/
635 /* Transform xcircuit coordinate back to X-window coordinate system */
636 /*------------------------------------------------------------------------*/
638 void user_to_window(XPoint upt, XPoint *wpt)
640 float tmpx, tmpy;
642 tmpx = (float)(upt.x - areawin->pcorner.x) * areawin->vscale;
643 tmpy = (float)areawin->height - (float)(upt.y - areawin->pcorner.y)
644 * areawin->vscale;
646 tmpx += (tmpx > 0) ? 0.5 : -0.5;
647 tmpy += (tmpy > 0) ? 0.5 : -0.5;
649 wpt->x = (short)tmpx;
650 wpt->y = (short)tmpy;
653 /*----------------------------------------------------------------------*/
654 /* Transformations in the object hierarchy */
655 /*----------------------------------------------------------------------*/
657 /*----------------------------------------------------------------------*/
658 /* Return rotation relative to a specific CTM */
659 /*----------------------------------------------------------------------*/
661 float UGetCTMRotation(Matrix *ctm)
663 float rads = (float)atan2((double)(ctm->d), (double)(ctm->a));
664 return rads / RADFAC;
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 /*----------------------------------------------------------------------*/
674 float UTopRotation()
676 return UGetCTMRotation(DCTM);
679 /*----------------------------------------------------------------------*/
680 /* Return scale relative to a specific CTM */
681 /*----------------------------------------------------------------------*/
683 float UGetCTMScale(Matrix *ctm)
685 return (float)(sqrt((double)(ctm->a * ctm->a + ctm->d * ctm->d)));
688 /*----------------------------------------------------------------------*/
689 /* Return scale relative to window */
690 /*----------------------------------------------------------------------*/
692 float UTopScale()
694 return UGetCTMScale(DCTM);
697 /*----------------------------------------------------------------------*/
698 /* Return scale multiplied by length */
699 /*----------------------------------------------------------------------*/
701 float UTopTransScale(float length)
703 return (float)(length * UTopScale());
706 /*----------------------------------------------------------------------*/
707 /* Return scale relative to the top-level schematic (not the window) */
708 /*----------------------------------------------------------------------*/
710 float UTopDrawingScale()
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);
721 /*----------------------------------------------------------------------*/
722 /* Return position offset relative to a specific CTM */
723 /*----------------------------------------------------------------------*/
725 void UGetCTMOffset(Matrix *ctm, int *offx, int *offy)
727 if (offx) *offx = (int)ctm->c;
728 if (offy) *offy = (int)ctm->f;
731 /*----------------------------------------------------------------------*/
732 /* Return position offset relative to top-level */
733 /*----------------------------------------------------------------------*/
735 void UTopOffset(int *offx, int *offy)
737 UGetCTMOffset(DCTM, offx, offy);
740 /*----------------------------------------------------------------------*/
741 /* Return postion relative to the top-level schematic (not the window) */
742 /*----------------------------------------------------------------------*/
744 void UTopDrawingOffset(int *offx, int *offy)
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);
755 /*----------------------------------------------------------------------*/
756 /* Get the cursor position */
757 /*----------------------------------------------------------------------*/
759 XPoint UGetCursor()
761 Window nullwin;
762 int nullint, xpos, ypos;
763 u_int nullui;
764 XPoint newpos;
766 /* Apparently this routine can get called before the display is valid */
767 if ((areawin->area == NULL) || (dpy == NULL)) {
768 newpos.x = newpos.y = 0;
769 return newpos;
772 #ifdef TCL_WRAPPER
773 /* Don't use areawin->window; if called from inside an object */
774 /* (e.g., "here" in a Tcl expression), areawin->window will be */
775 /* an off-screen pixmap, and cause a crash. */
777 if (Tk_WindowId(areawin->area) == (Window)NULL) {
778 newpos.x = newpos.y = 0;
779 return newpos;
782 #ifndef _MSC_VER
783 XQueryPointer(dpy, Tk_WindowId(areawin->area), &nullwin, &nullwin,
784 &nullint, &nullint, &xpos, &ypos, &nullui);
785 #else
786 XQueryPointer_TkW32(dpy, Tk_WindowId(areawin->area), &nullwin, &nullwin,
787 &nullint, &nullint, &xpos, &ypos, &nullui);
788 #endif
789 #else
790 XQueryPointer(dpy, areawin->window, &nullwin, &nullwin, &nullint,
791 &nullint, &xpos, &ypos, &nullui);
792 #endif
794 newpos.x = xpos;
795 newpos.y = ypos;
797 return newpos;
800 /*----------------------------------------------------------------------*/
801 /* Get the cursor position and translate to user coordinates */
802 /*----------------------------------------------------------------------*/
804 XPoint UGetCursorPos()
806 XPoint winpos, userpos;
808 if (areawin->area == NULL) {
809 winpos.x = winpos.y = 0;
811 else
812 winpos = UGetCursor();
814 window_to_user(winpos.x, winpos.y, &userpos);
816 return userpos;
819 /*----------------------------------------------------------------------*/
820 /* Translate a point to the nearest snap-to grid point */
821 /*----------------------------------------------------------------------*/
822 /* user coordinates to user coordinates version */
824 void u2u_snap(XPoint *uvalue)
826 float tmpx, tmpy;
827 float tmpix, tmpiy;
829 if (areawin->snapto) {
830 tmpx = (float)uvalue->x / xobjs.pagelist[areawin->page]->snapspace;
831 if (tmpx > 0)
832 tmpix = (float)((int)(tmpx + 0.5));
833 else
834 tmpix = (float)((int)(tmpx - 0.5));
836 tmpy = (float)uvalue->y / xobjs.pagelist[areawin->page]->snapspace;
837 if (tmpy > 0)
838 tmpiy = (float)((int)(tmpy + 0.5));
839 else
840 tmpiy = (float)((int)(tmpy - 0.5));
842 tmpix *= xobjs.pagelist[areawin->page]->snapspace;
843 tmpix += (tmpix > 0) ? 0.5 : -0.5;
844 tmpiy *= xobjs.pagelist[areawin->page]->snapspace;
845 tmpiy += (tmpiy > 0) ? 0.5 : -0.5;
847 uvalue->x = (int)tmpix;
848 uvalue->y = (int)tmpiy;
852 /*------------------------------------------------------------------------*/
853 /* window coordinates to user coordinates version */
854 /*------------------------------------------------------------------------*/
856 void snap(short valuex, short valuey, XPoint *returnpt)
858 window_to_user(valuex, valuey, returnpt);
859 u2u_snap(returnpt);
862 /*------------------------------------------------------------------------*/
863 /* Transform object coordinates through scale, translation, and rotation */
864 /* This routine attempts to match the PostScript definition of trans- */
865 /* formation matrices. */
866 /*------------------------------------------------------------------------*/
868 /*------------------------------------------------------------------------*/
869 /* Current transformation matrix manipulation routines */
870 /*------------------------------------------------------------------------*/
872 void UResetCTM(Matrix *ctm)
874 ctm->a = ctm->e = 1;
875 ctm->b = ctm->d = 0;
876 ctm->c = ctm->f = 0; /* 0.5 for nearest-int real->int conversion? */
878 #ifdef HAVE_CAIRO
879 if (ctm == DCTM && areawin->redraw_ongoing)
880 xc_cairo_set_matrix(ctm);
881 #endif /* HAVE_CAIRO */
884 /*------------------------------------------------------------------------*/
886 void InvertCTM(Matrix *ctm)
888 float det = ctm->a * ctm->e - ctm->b * ctm->d;
889 float tx = ctm->b * ctm->f - ctm->c * ctm->e;
890 float ty = ctm->d * ctm->c - ctm->a * ctm->f;
892 float tmpa = ctm->a;
894 ctm->b = -ctm->b / det;
895 ctm->d = -ctm->d / det;
897 ctm->a = ctm->e / det;
898 ctm->e = tmpa / det;
899 ctm->c = tx / det;
900 ctm->f = ty / det;
902 #ifdef HAVE_CAIRO
903 if (ctm == DCTM && areawin->redraw_ongoing)
904 xc_cairo_set_matrix(ctm);
905 #endif /* HAVE_CAIRO */
908 /*------------------------------------------------------------------------*/
910 void UCopyCTM(fctm, tctm)
911 Matrix *fctm, *tctm;
913 tctm->a = fctm->a;
914 tctm->b = fctm->b;
915 tctm->c = fctm->c;
916 tctm->d = fctm->d;
917 tctm->e = fctm->e;
918 tctm->f = fctm->f;
920 #ifdef HAVE_CAIRO
921 if (tctm == DCTM && areawin->redraw_ongoing)
922 xc_cairo_set_matrix(tctm);
923 #endif /* HAVE_CAIRO */
926 /*-------------------------------------------------------------------------*/
927 /* Multiply CTM by current screen position and scale to get transformation */
928 /* matrix from a user point to the X11 window */
929 /*-------------------------------------------------------------------------*/
931 void UMakeWCTM(Matrix *ctm)
933 ctm->a *= areawin->vscale;
934 ctm->b *= areawin->vscale;
935 ctm->c = (ctm->c - (float)areawin->pcorner.x) * areawin->vscale
936 + areawin->panx;
938 ctm->d *= -areawin->vscale;
939 ctm->e *= -areawin->vscale;
940 ctm->f = (float)areawin->height + ((float)areawin->pcorner.y - ctm->f) *
941 areawin->vscale + areawin->pany;
943 #ifdef HAVE_CAIRO
944 if (ctm == DCTM && areawin->redraw_ongoing)
945 xc_cairo_set_matrix(ctm);
946 #endif /* HAVE_CAIRO */
949 /*------------------------------------------------------------------------*/
951 void UMultCTM(Matrix *ctm, XPoint position, float scale, float rotate)
953 float tmpa, tmpb, tmpd, tmpe, yscale;
954 float mata, matb, matc;
955 double drot = (double)rotate * RADFAC;
957 yscale = abs(scale); /* -scale implies flip in x direction only */
959 tmpa = scale * cos(drot);
960 tmpb = yscale * sin(drot);
961 tmpd = -scale * sin(drot);
962 tmpe = yscale * cos(drot);
964 mata = ctm->a * tmpa + ctm->d * tmpb;
965 matb = ctm->b * tmpa + ctm->e * tmpb;
966 matc = ctm->c * tmpa + ctm->f * tmpb + position.x;
968 ctm->d = ctm->d * tmpe + ctm->a * tmpd;
969 ctm->e = ctm->e * tmpe + ctm->b * tmpd;
970 ctm->f = ctm->f * tmpe + ctm->c * tmpd + position.y;
972 ctm->a = mata;
973 ctm->b = matb;
974 ctm->c = matc;
976 #ifdef HAVE_CAIRO
977 if (ctm == DCTM && areawin->redraw_ongoing)
978 xc_cairo_set_matrix(ctm);
979 #endif /* HAVE_CAIRO */
982 /*----------------------------------------------------------------------*/
983 /* Slanting function x' = x + beta * y, y' = y */
984 /*----------------------------------------------------------------------*/
986 void USlantCTM(Matrix *ctm, float beta)
988 ctm->b += ctm->a * beta;
989 ctm->e += ctm->d * beta;
991 #ifdef HAVE_CAIRO
992 if (ctm == DCTM && areawin->redraw_ongoing)
993 xc_cairo_set_matrix(ctm);
994 #endif /* HAVE_CAIRO */
997 #define EPS 1e-9
998 /*----------------------------------------------------------------------*/
999 /* Transform text to make it right-side up within 90 degrees of page */
1000 /* NOTE: This is not yet resolved, as xcircuit does not agree with */
1001 /* PostScript in a few cases! */
1002 /*----------------------------------------------------------------------*/
1004 void UPreScaleCTM(Matrix *ctm)
1006 /* negative X scale (-1, +1) */
1007 if ((ctm->a < -EPS) || ((ctm->a < EPS) && (ctm->a > -EPS) &&
1008 ((ctm->d * ctm->b) < 0))) {
1009 ctm->a = -ctm->a;
1010 ctm->d = -ctm->d;
1013 /* negative Y scale (+1, -1) */
1014 if (ctm->e > EPS) {
1015 ctm->e = -ctm->e;
1016 ctm->b = -ctm->b;
1019 /* At 90, 270 degrees need special attention to avoid discrepencies */
1020 /* with the PostScript output due to roundoff error. This code */
1021 /* matches what PostScript produces. */
1023 #ifdef HAVE_CAIRO
1024 if (ctm == DCTM && areawin->redraw_ongoing)
1025 xc_cairo_set_matrix(ctm);
1026 #endif /* HAVE_CAIRO */
1029 /*----------------------------------------------------------------------*/
1030 /* Adjust anchoring and CTM as necessary for flip invariance */
1031 /*----------------------------------------------------------------------*/
1033 short flipadjust(short anchor)
1035 short tmpanchor = anchor & (~FLIPINV);
1037 if (anchor & FLIPINV) {
1038 if (((DCTM)->a < -EPS) || (((DCTM)->a < EPS) && ((DCTM)->a > -EPS) &&
1039 (((DCTM)->d * (DCTM)->b) < 0))) {
1040 if ((tmpanchor & (RIGHT | NOTLEFT)) != NOTLEFT)
1041 tmpanchor ^= (RIGHT | NOTLEFT);
1043 /* NOTE: Justification does not change under flip invariance. */
1045 if ((DCTM)->e > EPS) {
1046 if ((tmpanchor & (TOP | NOTBOTTOM)) != NOTBOTTOM)
1047 tmpanchor ^= (TOP | NOTBOTTOM);
1049 UPreScaleCTM(DCTM);
1051 return tmpanchor;
1054 /*------------------------------------------------------------------------*/
1056 void UPreMultCTM(Matrix *ctm, XPoint position, float scale, float rotate)
1058 float tmpa, tmpb, tmpd, tmpe, yscale;
1059 float mata, matd;
1060 double drot = (double)rotate * RADFAC;
1062 yscale = abs(scale); /* negative scale value implies flip in x only */
1064 tmpa = scale * cos(drot);
1065 tmpb = yscale * sin(drot);
1066 tmpd = -scale * sin(drot);
1067 tmpe = yscale * cos(drot);
1069 ctm->c += ctm->a * position.x + ctm->b * position.y;
1070 ctm->f += ctm->d * position.x + ctm->e * position.y;
1072 mata = ctm->a * tmpa + ctm->b * tmpd;
1073 ctm->b = ctm->a * tmpb + ctm->b * tmpe;
1075 matd = ctm->d * tmpa + ctm->e * tmpd;
1076 ctm->e = ctm->d * tmpb + ctm->e * tmpe;
1078 ctm->a = mata;
1079 ctm->d = matd;
1081 #ifdef HAVE_CAIRO
1082 if (ctm == DCTM && areawin->redraw_ongoing)
1083 xc_cairo_set_matrix(ctm);
1084 #endif /* HAVE_CAIRO */
1087 /*----------------------------------------------------------------------*/
1088 /* Direct Matrix-Matrix multiplication */
1089 /*----------------------------------------------------------------------*/
1091 void UPreMultCTMbyMat(Matrix *ctm, Matrix *pre)
1093 float mata, matd;
1095 mata = pre->a * ctm->a + pre->d * ctm->b;
1096 ctm->c += pre->c * ctm->a + pre->f * ctm->b;
1097 ctm->b = pre->b * ctm->a + pre->e * ctm->b;
1098 ctm->a = mata;
1100 matd = pre->a * ctm->d + pre->d * ctm->e;
1101 ctm->f += pre->c * ctm->d + pre->f * ctm->e;
1102 ctm->e = pre->b * ctm->d + pre->e * ctm->e;
1103 ctm->d = matd;
1105 #ifdef HAVE_CAIRO
1106 if (ctm == DCTM && areawin->redraw_ongoing)
1107 xc_cairo_set_matrix(ctm);
1108 #endif /* HAVE_CAIRO */
1111 /*------------------------------------------------------------------------*/
1113 void UTransformbyCTM(Matrix *ctm, XPoint *ipoints, XPoint *points, short number)
1115 pointlist current, ptptr = points;
1116 float fx, fy;
1117 /* short tmpx; (jdk) */
1119 for (current = ipoints; current < ipoints + number; current++, ptptr++) {
1120 fx = ctm->a * (float)current->x + ctm->b * (float)current->y + ctm->c;
1121 fy = ctm->d * (float)current->x + ctm->e * (float)current->y + ctm->f;
1123 ptptr->x = (fx >= 0) ? (short)(fx + 0.5) : (short)(fx - 0.5);
1124 ptptr->y = (fy >= 0) ? (short)(fy + 0.5) : (short)(fy - 0.5);
1128 /*------------------------------------------------------------------------*/
1129 /* (same as above routine but using type (float) for point values; this */
1130 /* is for calculation of Bezier curve internal points. */
1131 /*------------------------------------------------------------------------*/
1133 void UfTransformbyCTM(Matrix *ctm, XfPoint *fpoints, XPoint *points, short number)
1135 fpointlist current;
1136 pointlist new = points;
1137 float fx, fy;
1139 for (current = fpoints; current < fpoints + number; current++, new++) {
1140 fx = ctm->a * current->x + ctm->b * current->y + ctm->c;
1141 fy = ctm->d * current->x + ctm->e * current->y + ctm->f;
1142 new->x = (fx >= 0) ? (short)(fx + 0.5) : (short)(fx - 0.5);
1143 new->y = (fy >= 0) ? (short)(fy + 0.5) : (short)(fy - 0.5);
1147 /*------------------------------------------------------------------------*/
1149 void UPopCTM()
1151 Matrixptr lastmatrix;
1153 if (areawin->MatStack == NULL) {
1154 Wprintf("Matrix stack pop error");
1155 return;
1157 lastmatrix = areawin->MatStack->nextmatrix;
1158 free(areawin->MatStack);
1159 areawin->MatStack = lastmatrix;
1161 #ifdef HAVE_CAIRO
1162 if (areawin->area) {
1163 xc_cairo_set_matrix(lastmatrix);
1165 #endif /* HAVE_CAIRO */
1168 /*------------------------------------------------------------------------*/
1170 void UPushCTM()
1172 Matrixptr nmatrix;
1174 nmatrix = (Matrixptr)malloc(sizeof(Matrix));
1175 if (areawin->MatStack == NULL)
1176 UResetCTM(nmatrix);
1177 else
1178 UCopyCTM(areawin->MatStack, nmatrix);
1179 nmatrix->nextmatrix = areawin->MatStack;
1180 areawin->MatStack = nmatrix;
1183 /*------------------------------------------------------------------------*/
1185 void UTransformPoints(XPoint *points, XPoint *newpoints, short number,
1186 XPoint atpt, float scale, float rotate)
1188 Matrix LCTM;
1190 UResetCTM(&LCTM);
1191 UMultCTM(&LCTM, atpt, scale, rotate);
1192 UTransformbyCTM(&LCTM, points, newpoints, number);
1195 /*----------------------------------------------------*/
1196 /* Transform points inward to next hierarchical level */
1197 /*----------------------------------------------------*/
1199 void InvTransformPoints(XPoint *points, XPoint *newpoints, short number,
1200 XPoint atpt, float scale, float rotate)
1202 Matrix LCTM;
1204 UResetCTM(&LCTM);
1205 UPreMultCTM(&LCTM, atpt, scale, rotate);
1206 InvertCTM(&LCTM);
1207 UTransformbyCTM(&LCTM, points, newpoints, number);
1210 /*----------------------------------------------------------------------*/
1211 /* Adjust wire coords to force a wire to a horizontal or vertical */
1212 /* position. */
1213 /* "pospt" is the target position for the point of interest. */
1214 /* "cycle" is the point number in the polygon of the point of interest. */
1215 /* cycle == -1 is equivalent to the last point of the polygon. */
1216 /* If "strict" is TRUE then single-segment wires are forced manhattan */
1217 /* even if that means that the endpoint drifts from the target point. */
1218 /* If "strict" is FALSE then single-segment wires will become non- */
1219 /* manhattan so that the target point is reached. */
1220 /* NOTE: It might be preferable to add a segment to maintain a */
1221 /* manhattan layout, except that we want to avoid merging nets */
1222 /* together. . . */
1223 /*----------------------------------------------------------------------*/
1225 void manhattanize(XPoint *pospt, polyptr newpoly, short cycle, Boolean strict)
1227 XPoint *curpt, *bpt, *bbpt, *fpt, *ffpt;
1228 int deltax, deltay;
1230 if (newpoly->number == 1) return; /* sanity check */
1232 if (cycle == -1 || cycle == newpoly->number - 1) {
1233 curpt = newpoly->points + newpoly->number - 1;
1234 bpt = newpoly->points + newpoly->number - 2;
1235 fpt = NULL;
1236 ffpt = NULL;
1237 if (newpoly->number > 2)
1238 bbpt = newpoly->points + newpoly->number - 3;
1239 else
1240 bbpt = NULL;
1242 else if (cycle == 0) {
1243 curpt = newpoly->points;
1244 fpt = newpoly->points + 1;
1245 bpt = NULL;
1246 bbpt = NULL;
1247 if (newpoly->number > 2)
1248 ffpt = newpoly->points + 2;
1249 else
1250 ffpt = NULL;
1252 else {
1253 curpt = newpoly->points + cycle;
1254 fpt = newpoly->points + cycle + 1;
1255 bpt = newpoly->points + cycle - 1;
1256 if (cycle > 1)
1257 bbpt = newpoly->points + cycle - 2;
1258 else
1259 bbpt = NULL;
1261 if (cycle < newpoly->number - 2)
1262 ffpt = newpoly->points + cycle + 2;
1263 else
1264 ffpt = NULL;
1267 /* enforce constraints on point behind cycle position */
1269 if (bpt != NULL) {
1270 if (bbpt != NULL) {
1271 if (bpt->x == bbpt->x) bpt->y = pospt->y;
1272 if (bpt->y == bbpt->y) bpt->x = pospt->x;
1274 else if (strict) {
1275 deltax = abs(bpt->x - pospt->x);
1276 deltay = abs(bpt->y - pospt->y);
1278 /* Only one segment---just make sure it's horizontal or vertical */
1279 if (deltay > deltax) pospt->x = bpt->x;
1280 else pospt->y = bpt->y;
1284 /* enforce constraints on point forward of cycle position */
1286 if (fpt != NULL) {
1287 if (ffpt != NULL) {
1288 if (fpt->x == ffpt->x) fpt->y = pospt->y;
1289 if (fpt->y == ffpt->y) fpt->x = pospt->x;
1291 else if (strict) {
1292 deltax = abs(fpt->x - pospt->x);
1293 deltay = abs(fpt->y - pospt->y);
1295 /* Only one segment---just make sure it's horizontal or vertical */
1296 if (deltay > deltax) pospt->x = fpt->x;
1297 else pospt->y = fpt->y;
1302 /*----------------------------------------------------------------------*/
1303 /* Bounding box calculation routines */
1304 /*----------------------------------------------------------------------*/
1306 void bboxcalc(short testval, short *lowerval, short *upperval)
1308 if (testval < *lowerval) *lowerval = testval;
1309 if (testval > *upperval) *upperval = testval;
1312 /*----------------------------------------------------------------------*/
1313 /* Bounding box calculation for elements which can be part of a path */
1314 /*----------------------------------------------------------------------*/
1316 void calcextents(genericptr *bboxgen, short *llx, short *lly,
1317 short *urx, short *ury)
1319 switch (ELEMENTTYPE(*bboxgen)) {
1320 case(POLYGON): {
1321 pointlist bboxpts;
1322 for (bboxpts = TOPOLY(bboxgen)->points; bboxpts < TOPOLY(bboxgen)->points
1323 + TOPOLY(bboxgen)->number; bboxpts++) {
1324 bboxcalc(bboxpts->x, llx, urx);
1325 bboxcalc(bboxpts->y, lly, ury);
1327 } break;
1329 case(SPLINE): {
1330 fpointlist bboxpts;
1331 bboxcalc(TOSPLINE(bboxgen)->ctrl[0].x, llx, urx);
1332 bboxcalc(TOSPLINE(bboxgen)->ctrl[0].y, lly, ury);
1333 bboxcalc(TOSPLINE(bboxgen)->ctrl[3].x, llx, urx);
1334 bboxcalc(TOSPLINE(bboxgen)->ctrl[3].y, lly, ury);
1335 for (bboxpts = TOSPLINE(bboxgen)->points; bboxpts <
1336 TOSPLINE(bboxgen)->points + INTSEGS; bboxpts++) {
1337 bboxcalc((short)(bboxpts->x), llx, urx);
1338 bboxcalc((short)(bboxpts->y), lly, ury);
1340 } break;
1342 case (ARC): {
1343 fpointlist bboxpts;
1344 for (bboxpts = TOARC(bboxgen)->points; bboxpts < TOARC(bboxgen)->points +
1345 TOARC(bboxgen)->number; bboxpts++) {
1346 bboxcalc((short)(bboxpts->x), llx, urx);
1347 bboxcalc((short)(bboxpts->y), lly, ury);
1349 } break;
1353 /*----------------------------------------------------------------------*/
1354 /* Calculate the bounding box of an object instance */
1355 /*----------------------------------------------------------------------*/
1357 void objinstbbox(objinstptr obbox, XPoint *npoints, int extend)
1359 XPoint points[4];
1361 points[0].x = points[1].x = obbox->bbox.lowerleft.x - extend;
1362 points[1].y = points[2].y = obbox->bbox.lowerleft.y + obbox->bbox.height
1363 + extend;
1364 points[2].x = points[3].x = obbox->bbox.lowerleft.x + obbox->bbox.width
1365 + extend;
1366 points[0].y = points[3].y = obbox->bbox.lowerleft.y - extend;
1368 UTransformPoints(points, npoints, 4, obbox->position,
1369 obbox->scale, obbox->rotation);
1372 /*----------------------------------------------------------------------*/
1373 /* Calculate the bounding box of a label */
1374 /*----------------------------------------------------------------------*/
1376 void labelbbox(labelptr labox, XPoint *npoints, objinstptr callinst)
1378 XPoint points[4];
1379 TextExtents tmpext;
1380 short j;
1382 tmpext = ULength(labox, callinst, NULL);
1383 points[0].x = points[1].x = (labox->anchor & NOTLEFT ?
1384 (labox->anchor & RIGHT ? -tmpext.maxwidth :
1385 -tmpext.maxwidth / 2) : 0);
1386 points[2].x = points[3].x = points[0].x + tmpext.maxwidth;
1387 points[0].y = points[3].y = (labox->anchor & NOTBOTTOM ?
1388 (labox->anchor & TOP ? -tmpext.ascent :
1389 -(tmpext.ascent + tmpext.base) / 2) : -tmpext.base)
1390 + tmpext.descent;
1391 points[1].y = points[2].y = points[0].y + tmpext.ascent - tmpext.descent;
1393 /* separate bounding box for pinlabels and infolabels */
1395 if (labox->pin)
1396 for (j = 0; j < 4; j++)
1397 pinadjust(labox->anchor, &points[j].x, &points[j].y, 1);
1399 UTransformPoints(points, npoints, 4, labox->position,
1400 labox->scale, labox->rotation);
1403 /*----------------------------------------------------------------------*/
1404 /* Calculate the bounding box of a graphic image */
1405 /*----------------------------------------------------------------------*/
1407 void graphicbbox(graphicptr gp, XPoint *npoints)
1409 XPoint points[4];
1410 int hw = xcImageGetWidth(gp->source) >> 1;
1411 int hh = xcImageGetHeight(gp->source) >> 1;
1413 points[1].x = points[2].x = hw;
1414 points[0].x = points[3].x = -hw;
1416 points[0].y = points[1].y = -hh;
1417 points[2].y = points[3].y = hh;
1419 UTransformPoints(points, npoints, 4, gp->position,
1420 gp->scale, gp->rotation);
1423 /*--------------------------------------------------------------*/
1424 /* Wrapper for single call to calcbboxsingle() in the netlister */
1425 /*--------------------------------------------------------------*/
1427 void calcinstbbox(genericptr *bboxgen, short *llx, short *lly, short *urx,
1428 short *ury)
1430 *llx = *lly = 32767;
1431 *urx = *ury = -32768;
1433 calcbboxsingle(bboxgen, areawin->topinstance, llx, lly, urx, ury);
1436 /*----------------------------------------------------------------------*/
1437 /* Bounding box calculation for a single generic element */
1438 /*----------------------------------------------------------------------*/
1440 void calcbboxsingle(genericptr *bboxgen, objinstptr thisinst,
1441 short *llx, short *lly, short *urx, short *ury)
1443 XPoint npoints[4];
1444 short j;
1446 /* For each screen element, compute the extents and revise bounding */
1447 /* box points, if necessary. */
1449 switch(ELEMENTTYPE(*bboxgen)) {
1451 case(OBJINST):
1452 objinstbbox(TOOBJINST(bboxgen), npoints, 0);
1454 for (j = 0; j < 4; j++) {
1455 bboxcalc(npoints[j].x, llx, urx);
1456 bboxcalc(npoints[j].y, lly, ury);
1458 break;
1460 case(LABEL):
1461 /* because a pin is offset from its position point, include */
1462 /* that point in the bounding box. */
1464 if (TOLABEL(bboxgen)->pin) {
1465 bboxcalc(TOLABEL(bboxgen)->position.x, llx, urx);
1466 bboxcalc(TOLABEL(bboxgen)->position.y, lly, ury);
1468 labelbbox(TOLABEL(bboxgen), npoints, thisinst);
1470 for (j = 0; j < 4; j++) {
1471 bboxcalc(npoints[j].x, llx, urx);
1472 bboxcalc(npoints[j].y, lly, ury);
1474 break;
1476 case(GRAPHIC):
1477 graphicbbox(TOGRAPHIC(bboxgen), npoints);
1478 for (j = 0; j < 4; j++) {
1479 bboxcalc(npoints[j].x, llx, urx);
1480 bboxcalc(npoints[j].y, lly, ury);
1482 break;
1484 case(PATH): {
1485 genericptr *pathc;
1486 for (pathc = TOPATH(bboxgen)->plist; pathc < TOPATH(bboxgen)->plist
1487 + TOPATH(bboxgen)->parts; pathc++)
1488 calcextents(pathc, llx, lly, urx, ury);
1489 } break;
1491 default:
1492 calcextents(bboxgen, llx, lly, urx, ury);
1496 /*------------------------------------------------------*/
1497 /* Find if an object is in the specified library */
1498 /*------------------------------------------------------*/
1500 Boolean object_in_library(short libnum, objectptr thisobject)
1502 short i;
1504 for (i = 0; i < xobjs.userlibs[libnum].number; i++) {
1505 if (*(xobjs.userlibs[libnum].library + i) == thisobject)
1506 return True;
1508 return False;
1511 /*-----------------------------------------------------------*/
1512 /* Find if an object is in the hierarchy of the given object */
1513 /* Returns the number (position in plist) or -1 if not found */
1514 /*-----------------------------------------------------------*/
1516 short find_object(objectptr pageobj, objectptr thisobject)
1518 short i, j;
1519 genericptr *pelem;
1521 for (i = 0; i < pageobj->parts; i++) {
1522 pelem = pageobj->plist + i;
1523 if (IS_OBJINST(*pelem)) {
1524 if ((TOOBJINST(pelem))->thisobject == thisobject)
1525 return i;
1526 else if ((j = find_object((TOOBJINST(pelem))->thisobject, thisobject)) >= 0)
1527 return i; /* was j---is this the right fix? */
1530 return -1;
1533 /*------------------------------------------------------*/
1534 /* Find all pages and libraries containing this object */
1535 /* and update accordingly. If this object is a page, */
1536 /* just update the page directory. */
1537 /*------------------------------------------------------*/
1539 void updatepagebounds(objectptr thisobject)
1541 short i, j;
1542 objectptr pageobj;
1544 if ((i = is_page(thisobject)) >= 0) {
1545 if (xobjs.pagelist[i]->background.name != (char *)NULL)
1546 backgroundbbox(i);
1547 updatepagelib(PAGELIB, i);
1549 else {
1550 for (i = 0; i < xobjs.pages; i++) {
1551 if (xobjs.pagelist[i]->pageinst != NULL) {
1552 pageobj = xobjs.pagelist[i]->pageinst->thisobject;
1553 if ((j = find_object(pageobj, thisobject)) >= 0) {
1554 calcbboxvalues(xobjs.pagelist[i]->pageinst,
1555 (genericptr *)(pageobj->plist + j));
1556 updatepagelib(PAGELIB, i);
1560 for (i = 0; i < xobjs.numlibs; i++)
1561 if (object_in_library(i, thisobject))
1562 composelib(i + LIBRARY);
1566 /*--------------------------------------------------------------*/
1567 /* Free memory for the schematic bounding box */
1568 /*--------------------------------------------------------------*/
1570 void invalidateschembbox(objinstptr thisinst)
1572 if (thisinst->schembbox != NULL) {
1573 free(thisinst->schembbox);
1574 thisinst->schembbox = NULL;
1578 /*--------------------------------------------------------------*/
1579 /* Calculate the bounding box for an object instance. Use the */
1580 /* existing bbox and finish calculation on all the elements */
1581 /* which have parameters not taking default values. */
1582 /* This finishes the calculation partially done by */
1583 /* calcbboxvalues(). */
1584 /*--------------------------------------------------------------*/
1586 void calcbboxinst(objinstptr thisinst)
1588 objectptr thisobj;
1589 genericptr *gelem;
1590 short llx, lly, urx, ury;
1592 short pllx, plly, purx, pury;
1593 Boolean hasschembbox = FALSE;
1594 Boolean didparamsubs = FALSE;
1596 if (thisinst == NULL) return;
1598 thisobj = thisinst->thisobject;
1600 llx = thisobj->bbox.lowerleft.x;
1601 lly = thisobj->bbox.lowerleft.y;
1602 urx = llx + thisobj->bbox.width;
1603 ury = lly + thisobj->bbox.height;
1605 pllx = plly = 32767;
1606 purx = pury = -32768;
1608 for (gelem = thisobj->plist; gelem < thisobj->plist + thisobj->parts;
1609 gelem++) {
1610 /* pins which do not appear outside of the object */
1611 /* contribute to the objects "schembbox". */
1613 if (IS_LABEL(*gelem)) {
1614 labelptr btext = TOLABEL(gelem);
1615 if (btext->pin && !(btext->anchor & PINVISIBLE)) {
1616 hasschembbox = TRUE;
1617 calcbboxsingle(gelem, thisinst, &pllx, &plly, &purx, &pury);
1618 continue;
1622 if (has_param(*gelem)) {
1623 if (didparamsubs == FALSE) {
1624 psubstitute(thisinst);
1625 didparamsubs = TRUE;
1627 calcbboxsingle(gelem, thisinst, &llx, &lly, &urx, &ury);
1630 /* If we have a clipmask, the clipmask is used to calculate the */
1631 /* bounding box, not the element it is masking. */
1633 switch(ELEMENTTYPE(*gelem)) {
1634 case POLYGON: case SPLINE: case ARC: case PATH:
1635 if (TOPOLY(gelem)->style & CLIPMASK) gelem++;
1636 break;
1640 thisinst->bbox.lowerleft.x = llx;
1641 thisinst->bbox.lowerleft.y = lly;
1642 thisinst->bbox.width = urx - llx;
1643 thisinst->bbox.height = ury - lly;
1645 if (hasschembbox) {
1646 if (thisinst->schembbox == NULL)
1647 thisinst->schembbox = (BBox *)malloc(sizeof(BBox));
1649 thisinst->schembbox->lowerleft.x = pllx;
1650 thisinst->schembbox->lowerleft.y = plly;
1651 thisinst->schembbox->width = purx - pllx;
1652 thisinst->schembbox->height = pury - plly;
1654 else
1655 invalidateschembbox(thisinst);
1658 /*--------------------------------------------------------------*/
1659 /* Update things based on a changed instance bounding box. */
1660 /* If the parameter was a single-instance */
1661 /* substitution, only the page should be updated. If the */
1662 /* parameter was a default value, the library should be updated */
1663 /* and any pages containing the object where the parameter */
1664 /* takes the default value. */
1665 /*--------------------------------------------------------------*/
1667 void updateinstparam(objectptr bobj)
1669 short i, j;
1670 objectptr pageobj;
1672 /* change bounds on pagelib and all pages */
1673 /* containing this *object* if and only if the object */
1674 /* instance takes the default value. Also update the */
1675 /* library page. */
1677 for (i = 0; i < xobjs.pages; i++)
1678 if (xobjs.pagelist[i]->pageinst != NULL) {
1679 pageobj = xobjs.pagelist[i]->pageinst->thisobject;
1680 if ((j = find_object(pageobj, topobject)) >= 0) {
1682 /* Really, we'd like to recalculate the bounding box only if the */
1683 /* parameter value is the default value which was just changed. */
1684 /* However, then any non-default values may contain the wrong */
1685 /* substitutions. */
1687 objinstptr cinst = TOOBJINST(pageobj->plist + j);
1688 if (cinst->thisobject->params == NULL) {
1689 calcbboxvalues(xobjs.pagelist[i]->pageinst, pageobj->plist + j);
1690 updatepagelib(PAGELIB, i);
1695 for (i = 0; i < xobjs.numlibs; i++)
1696 if (object_in_library(i, topobject))
1697 composelib(i + LIBRARY);
1700 /*--------------------------------------------------------------*/
1701 /* Calculate bbox on all elements of the given object */
1702 /*--------------------------------------------------------------*/
1704 void calcbbox(objinstptr binst)
1706 calcbboxvalues(binst, (genericptr *)NULL);
1707 if (binst == areawin->topinstance) {
1708 updatepagebounds(topobject);
1712 /*--------------------------------------------------------------*/
1713 /* Calculate bbox on the given element of the specified object. */
1714 /* This is a wrapper for calcbboxvalues() assuming that we're */
1715 /* on the top-level, and that page bounds need to be updated. */
1716 /*--------------------------------------------------------------*/
1718 void singlebbox(genericptr *gelem)
1720 calcbboxvalues(areawin->topinstance, (genericptr *)gelem);
1721 updatepagebounds(topobject);
1724 /*----------------------------------------------------------------------*/
1725 /* Extend bounding box based on selected elements only */
1726 /*----------------------------------------------------------------------*/
1728 void calcbboxselect()
1730 short *bsel;
1731 for (bsel = areawin->selectlist; bsel < areawin->selectlist +
1732 areawin->selects; bsel++)
1733 calcbboxvalues(areawin->topinstance, topobject->plist + *bsel);
1735 updatepagebounds(topobject);
1738 /*--------------------------------------------------------------*/
1739 /* Update Bounding box for an object. */
1740 /* If newelement == NULL, calculate bounding box from scratch. */
1741 /* Otherwise, expand bounding box to enclose newelement. */
1742 /*--------------------------------------------------------------*/
1744 void calcbboxvalues(objinstptr thisinst, genericptr *newelement)
1746 genericptr *bboxgen;
1747 short llx, lly, urx, ury;
1748 objectptr thisobj = thisinst->thisobject;
1750 /* no action if there are no elements */
1751 if (thisobj->parts == 0) return;
1753 /* If this object has parameters, then we will do a separate */
1754 /* bounding box calculation on parameterized parts. This */
1755 /* calculation ignores them, and the result is a base that the */
1756 /* instance bounding-box computation can use as a starting point. */
1758 /* set starting bounds as maximum bounds of screen */
1759 llx = lly = 32767;
1760 urx = ury = -32768;
1762 for (bboxgen = thisobj->plist; bboxgen < thisobj->plist +
1763 thisobj->parts; bboxgen++) {
1765 /* override the "for" loop if we're doing a single element */
1766 if (newelement != NULL) bboxgen = newelement;
1768 if ((thisobj->params == NULL) || (!has_param(*bboxgen))) {
1769 /* pins which do not appear outside of the object */
1770 /* are ignored now---will be computed per instance. */
1772 if (IS_LABEL(*bboxgen)) {
1773 labelptr btext = TOLABEL(bboxgen);
1774 if (btext->pin && !(btext->anchor & PINVISIBLE)) {
1775 goto nextgen;
1778 calcbboxsingle(bboxgen, thisinst, &llx, &lly, &urx, &ury);
1780 if (newelement == NULL)
1781 switch(ELEMENTTYPE(*bboxgen)) {
1782 case POLYGON: case SPLINE: case ARC: case PATH:
1783 if (TOPOLY(bboxgen)->style & CLIPMASK)
1784 bboxgen++;
1785 break;
1788 nextgen:
1789 if (newelement != NULL) break;
1792 /* if this is a single-element calculation and its bounding box */
1793 /* turned out to be smaller than the object's, then we need to */
1794 /* recompute the entire object's bounding box in case it got */
1795 /* smaller. This is not recursive, in spite of looks. */
1797 if (newelement != NULL) {
1798 if (llx > thisobj->bbox.lowerleft.x &&
1799 lly > thisobj->bbox.lowerleft.y &&
1800 urx < (thisobj->bbox.lowerleft.x + thisobj->bbox.width) &&
1801 ury < (thisobj->bbox.lowerleft.y + thisobj->bbox.height)) {
1802 calcbboxvalues(thisinst, NULL);
1803 return;
1805 else {
1806 bboxcalc(thisobj->bbox.lowerleft.x, &llx, &urx);
1807 bboxcalc(thisobj->bbox.lowerleft.y, &lly, &ury);
1808 bboxcalc(thisobj->bbox.lowerleft.x + thisobj->bbox.width, &llx, &urx);
1809 bboxcalc(thisobj->bbox.lowerleft.y + thisobj->bbox.height, &lly, &ury);
1813 /* Set the new bounding box. In pathological cases, such as a page */
1814 /* with only pin labels, the bounds may not have been changed from */
1815 /* their initial values. If so, then don't touch the bounding box. */
1817 if ((llx <= urx) && (lly <= ury)) {
1818 thisobj->bbox.lowerleft.x = llx;
1819 thisobj->bbox.lowerleft.y = lly;
1820 thisobj->bbox.width = urx - llx;
1821 thisobj->bbox.height = ury - lly;
1824 /* calculate instance-specific values */
1825 calcbboxinst(thisinst);
1828 /*------------------------------------------------------*/
1829 /* Center an object in the viewing window */
1830 /*------------------------------------------------------*/
1832 void centerview(objinstptr tinst)
1834 XPoint origin, corner;
1835 Dimension width, height;
1836 float fitwidth, fitheight;
1837 objectptr tobj = tinst->thisobject;
1839 origin = tinst->bbox.lowerleft;
1840 corner.x = origin.x + tinst->bbox.width;
1841 corner.y = origin.y + tinst->bbox.height;
1843 extendschembbox(tinst, &origin, &corner);
1845 width = corner.x - origin.x;
1846 height = corner.y - origin.y;
1848 fitwidth = (float)areawin->width / ((float)width + 2 * DEFAULTGRIDSPACE);
1849 fitheight = (float)areawin->height / ((float)height + 2 * DEFAULTGRIDSPACE);
1851 tobj->viewscale = (fitwidth < fitheight) ?
1852 min(MINAUTOSCALE, fitwidth) : min(MINAUTOSCALE, fitheight);
1854 tobj->pcorner.x = origin.x - (areawin->width
1855 / tobj->viewscale - width) / 2;
1856 tobj->pcorner.y = origin.y - (areawin->height
1857 / tobj->viewscale - height) / 2;
1859 /* Copy new position values to the current window */
1861 if ((areawin->topinstance != NULL) && (tobj == topobject)) {
1862 areawin->pcorner = tobj->pcorner;
1863 areawin->vscale = tobj->viewscale;
1867 /*-----------------------------------------------------------*/
1868 /* Refresh the window and scrollbars and write the page name */
1869 /*-----------------------------------------------------------*/
1871 void refresh(xcWidget bw, caddr_t clientdata, caddr_t calldata)
1873 areawin->redraw_needed = True;
1874 drawarea(NULL, NULL, NULL);
1875 if (areawin->scrollbarh)
1876 drawhbar(areawin->scrollbarh, NULL, NULL);
1877 if (areawin->scrollbarv)
1878 drawvbar(areawin->scrollbarv, NULL, NULL);
1879 printname(topobject);
1882 /*------------------------------------------------------*/
1883 /* Center the current page in the viewing window */
1884 /*------------------------------------------------------*/
1886 void zoomview(xcWidget w, caddr_t clientdata, caddr_t calldata)
1888 if (eventmode == NORMAL_MODE || eventmode == COPY_MODE ||
1889 eventmode == MOVE_MODE || eventmode == CATALOG_MODE ||
1890 eventmode == FONTCAT_MODE || eventmode == EFONTCAT_MODE ||
1891 eventmode == CATMOVE_MODE) {
1893 if (areawin->topinstance)
1894 centerview(areawin->topinstance);
1895 areawin->lastbackground = NULL;
1896 renderbackground();
1897 refresh(NULL, NULL, NULL);
1901 /*---------------------------------------------------------*/
1902 /* Basic X Graphics Routines in the User coordinate system */
1903 /*---------------------------------------------------------*/
1905 #ifndef HAVE_CAIRO
1906 void UDrawSimpleLine(XPoint *pt1, XPoint *pt2)
1908 XPoint newpt1, newpt2;
1910 if (!areawin->redraw_ongoing) {
1911 areawin->redraw_needed = True;
1912 return;
1915 UTransformbyCTM(DCTM, pt1, &newpt1, 1);
1916 UTransformbyCTM(DCTM, pt2, &newpt2, 1);
1918 DrawLine(dpy, areawin->window, areawin->gc,
1919 newpt1.x, newpt1.y, newpt2.x, newpt2.y);
1921 #endif /* !HAVE_CAIRO */
1923 /*-------------------------------------------------------------------------*/
1925 #ifndef HAVE_CAIRO
1926 void UDrawLine(XPoint *pt1, XPoint *pt2)
1928 float tmpwidth = UTopTransScale(xobjs.pagelist[areawin->page]->wirewidth);
1930 if (!areawin->redraw_ongoing) {
1931 areawin->redraw_needed = True;
1932 return;
1935 SetLineAttributes(dpy, areawin->gc, tmpwidth, LineSolid, CapRound, JoinBevel);
1936 UDrawSimpleLine(pt1, pt2);
1938 #endif /* !HAVE_CAIRO */
1940 /*----------------------------------------------------------------------*/
1941 /* Add circle at given point to indicate that the point is a parameter. */
1942 /* The circle is divided into quarters. For parameterized y-coordinate */
1943 /* the top and bottom quarters are drawn. For parameterized x- */
1944 /* coordinate, the left and right quarters are drawn. A full circle */
1945 /* indicates either both x- and y-coordinates are parameterized, or */
1946 /* else any other kind of parameterization (presently, not used). */
1947 /* */
1948 /* (note that the two angles in XDrawArc() are 1) the start angle, */
1949 /* measured in absolute 64th degrees from 0 (3 o'clock), and 2) the */
1950 /* path length, in relative 64th degrees (positive = counterclockwise, */
1951 /* negative = clockwise)). */
1952 /*----------------------------------------------------------------------*/
1954 #ifndef HAVE_CAIRO
1955 void UDrawCircle(XPoint *upt, u_char which)
1957 XPoint wpt;
1959 if (!areawin->redraw_ongoing) {
1960 areawin->redraw_needed = True;
1961 return;
1964 user_to_window(*upt, &wpt);
1965 SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapButt, JoinMiter);
1967 switch(which) {
1968 case P_POSITION_X:
1969 XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1970 wpt.y - 4, 8, 8, -(45 * 64), (90 * 64));
1971 XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1972 wpt.y - 4, 8, 8, (135 * 64), (90 * 64));
1973 break;
1974 case P_POSITION_Y:
1975 XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1976 wpt.y - 4, 8, 8, (45 * 64), (90 * 64));
1977 XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1978 wpt.y - 4, 8, 8, (225 * 64), (90 * 64));
1979 break;
1980 default:
1981 XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1982 wpt.y - 4, 8, 8, 0, (360 * 64));
1983 break;
1986 #endif /* !HAVE_CAIRO */
1988 /*----------------------------------------------------------------------*/
1989 /* Add "X" at string origin */
1990 /*----------------------------------------------------------------------*/
1992 #ifndef HAVE_CAIRO
1993 void UDrawXAt(XPoint *wpt)
1995 if (!areawin->redraw_ongoing) {
1996 areawin->redraw_needed = True;
1997 return;
2000 SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapButt, JoinMiter);
2001 DrawLine(dpy, areawin->window, areawin->gc, wpt->x - 3,
2002 wpt->y - 3, wpt->x + 3, wpt->y + 3);
2003 DrawLine(dpy, areawin->window, areawin->gc, wpt->x + 3,
2004 wpt->y - 3, wpt->x - 3, wpt->y + 3);
2006 #endif /* !HAVE_CAIRO */
2008 /*----------------------------------------------------------------------*/
2009 /* Draw "X" on current level */
2010 /*----------------------------------------------------------------------*/
2012 void UDrawX(labelptr curlabel)
2014 XPoint wpt;
2016 user_to_window(curlabel->position, &wpt);
2017 UDrawXAt(&wpt);
2020 /*----------------------------------------------------------------------*/
2021 /* Draw "X" on top level (only for LOCAL and GLOBAL pin labels) */
2022 /*----------------------------------------------------------------------*/
2024 void UDrawXDown(labelptr curlabel)
2026 XPoint wpt;
2028 UTransformbyCTM(DCTM, &curlabel->position, &wpt, 1);
2029 UDrawXAt(&wpt);
2032 /*----------------------------------------------------------------------*/
2033 /* Find the "real" width, height, and origin of an object including pin */
2034 /* labels and so forth that only show up on a schematic when it is the */
2035 /* top-level object. */
2036 /*----------------------------------------------------------------------*/
2038 int toplevelwidth(objinstptr bbinst, short *rllx)
2040 short llx, urx;
2041 short origin, corner;
2043 if (bbinst->schembbox == NULL) {
2044 if (rllx) *rllx = bbinst->bbox.lowerleft.x;
2045 return bbinst->bbox.width;
2048 origin = bbinst->bbox.lowerleft.x;
2049 corner = origin + bbinst->bbox.width;
2051 llx = bbinst->schembbox->lowerleft.x;
2052 urx = llx + bbinst->schembbox->width;
2054 bboxcalc(llx, &origin, &corner);
2055 bboxcalc(urx, &origin, &corner);
2057 if (rllx) *rllx = origin;
2058 return(corner - origin);
2061 /*----------------------------------------------------------------------*/
2063 int toplevelheight(objinstptr bbinst, short *rlly)
2065 short lly, ury;
2066 short origin, corner;
2068 if (bbinst->schembbox == NULL) {
2069 if (rlly) *rlly = bbinst->bbox.lowerleft.y;
2070 return bbinst->bbox.height;
2073 origin = bbinst->bbox.lowerleft.y;
2074 corner = origin + bbinst->bbox.height;
2076 lly = bbinst->schembbox->lowerleft.y;
2077 ury = lly + bbinst->schembbox->height;
2079 bboxcalc(lly, &origin, &corner);
2080 bboxcalc(ury, &origin, &corner);
2082 if (rlly) *rlly = origin;
2083 return(corner - origin);
2086 /*----------------------------------------------------------------------*/
2087 /* Add dimensions of schematic pins to an object's bounding box */
2088 /*----------------------------------------------------------------------*/
2090 void extendschembbox(objinstptr bbinst, XPoint *origin, XPoint *corner)
2092 short llx, lly, urx, ury;
2094 if ((bbinst == NULL) || (bbinst->schembbox == NULL)) return;
2096 llx = bbinst->schembbox->lowerleft.x;
2097 lly = bbinst->schembbox->lowerleft.y;
2098 urx = llx + bbinst->schembbox->width;
2099 ury = lly + bbinst->schembbox->height;
2101 bboxcalc(llx, &(origin->x), &(corner->x));
2102 bboxcalc(lly, &(origin->y), &(corner->y));
2103 bboxcalc(urx, &(origin->x), &(corner->x));
2104 bboxcalc(ury, &(origin->y), &(corner->y));
2107 /*----------------------------------------------------------------------*/
2108 /* Adjust a pinlabel position to account for pad spacing */
2109 /*----------------------------------------------------------------------*/
2111 void pinadjust (short anchor, short *xpoint, short *ypoint, short dir)
2113 int delx, dely;
2115 dely = (anchor & NOTBOTTOM) ?
2116 ((anchor & TOP) ? -PADSPACE : 0) : PADSPACE;
2117 delx = (anchor & NOTLEFT) ?
2118 ((anchor & RIGHT) ? -PADSPACE : 0) : PADSPACE;
2120 if (xpoint != NULL) *xpoint += (dir > 0) ? delx : -delx;
2121 if (ypoint != NULL) *ypoint += (dir > 0) ? dely : -dely;
2124 /*----------------------------------------------------------------------*/
2125 /* Draw line for editing text (position of cursor in string is given by */
2126 /* tpos (2nd parameter) */
2127 /*----------------------------------------------------------------------*/
2129 void UDrawTextLine(labelptr curlabel, short tpos)
2131 XPoint points[2]; /* top and bottom of text cursor line */
2132 short tmpanchor, xbase;
2133 int maxwidth;
2134 TextExtents tmpext;
2135 TextLinesInfo tlinfo;
2137 if (!areawin->redraw_ongoing) {
2138 areawin->redraw_needed = True;
2139 return;
2142 /* correct for position, rotation, scale, and flip invariance of text */
2144 UPushCTM();
2145 UPreMultCTM(DCTM, curlabel->position, curlabel->scale, curlabel->rotation);
2146 tmpanchor = flipadjust(curlabel->anchor);
2148 SetForeground(dpy, areawin->gc, AUXCOLOR);
2150 tlinfo.dostop = 0;
2151 tlinfo.tbreak = NULL;
2152 tlinfo.padding = NULL;
2154 tmpext = ULength(curlabel, areawin->topinstance, &tlinfo);
2155 maxwidth = tmpext.maxwidth;
2156 xbase = tmpext.base;
2157 tlinfo.dostop = tpos;
2158 tmpext = ULength(curlabel, areawin->topinstance, &tlinfo);
2160 points[0].x = (tmpanchor & NOTLEFT ?
2161 (tmpanchor & RIGHT ? -maxwidth : -maxwidth >> 1) : 0) + tmpext.width;
2162 if ((tmpanchor & JUSTIFYRIGHT) && tlinfo.padding)
2163 points[0].x += tlinfo.padding[tlinfo.line];
2164 else if ((tmpanchor & TEXTCENTERED) && tlinfo.padding)
2165 points[0].x += 0.5 * tlinfo.padding[tlinfo.line];
2166 points[0].y = (tmpanchor & NOTBOTTOM ?
2167 (tmpanchor & TOP ? -tmpext.ascent : -(tmpext.ascent + xbase) / 2)
2168 : -xbase) + tmpext.base - 3;
2169 points[1].x = points[0].x;
2170 points[1].y = points[0].y + TEXTHEIGHT + 6;
2172 if (curlabel->pin) {
2173 pinadjust(tmpanchor, &(points[0].x), &(points[0].y), 1);
2174 pinadjust(tmpanchor, &(points[1].x), &(points[1].y), 1);
2176 if (tlinfo.padding != NULL) free(tlinfo.padding);
2178 /* draw the line */
2180 UDrawLine(&points[0], &points[1]);
2181 UPopCTM();
2183 UDrawX(curlabel);
2186 /*-----------------------------------------------------------------*/
2187 /* Draw lines for editing text when multiple characters are chosen */
2188 /*-----------------------------------------------------------------*/
2190 void UDrawTLine(labelptr curlabel)
2192 UDrawTextLine(curlabel, areawin->textpos);
2193 if ((areawin->textend > 0) && (areawin->textend < areawin->textpos)) {
2194 UDrawTextLine(curlabel, areawin->textend);
2198 /*----------------------*/
2199 /* Draw an X */
2200 /*----------------------*/
2202 #ifndef HAVE_CAIRO
2203 void UDrawXLine(XPoint opt, XPoint cpt)
2205 XPoint upt, vpt;
2207 if (!areawin->redraw_ongoing) {
2208 areawin->redraw_needed = True;
2209 return;
2212 SetForeground(dpy, areawin->gc, AUXCOLOR);
2214 user_to_window(cpt, &upt);
2215 user_to_window(opt, &vpt);
2217 SetThinLineAttributes(dpy, areawin->gc, 0, LineOnOffDash, CapButt, JoinMiter);
2218 DrawLine(dpy, areawin->window, areawin->gc, vpt.x, vpt.y, upt.x, upt.y);
2220 SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapButt, JoinMiter);
2221 DrawLine(dpy, areawin->window, areawin->gc, upt.x - 3, upt.y - 3,
2222 upt.x + 3, upt.y + 3);
2223 DrawLine(dpy, areawin->window, areawin->gc, upt.x + 3, upt.y - 3,
2224 upt.x - 3, upt.y + 3);
2226 SetForeground(dpy, areawin->gc, areawin->gccolor);
2228 #endif /* HAVE_CAIRO */
2230 /*-------------------------------------------------------------------------*/
2232 #ifndef HAVE_CAIRO
2233 void UDrawBox(XPoint origin, XPoint corner)
2235 XPoint worig, wcorn;
2237 if (!areawin->redraw_ongoing) {
2238 areawin->redraw_needed = True;
2239 return;
2242 user_to_window(origin, &worig);
2243 user_to_window(corner, &wcorn);
2245 SetForeground(dpy, areawin->gc, AUXCOLOR);
2246 SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapRound, JoinBevel);
2247 DrawLine(dpy, areawin->window, areawin->gc, worig.x, worig.y,
2248 worig.x, wcorn.y);
2249 DrawLine(dpy, areawin->window, areawin->gc, worig.x, wcorn.y,
2250 wcorn.x, wcorn.y);
2251 DrawLine(dpy, areawin->window, areawin->gc, wcorn.x, wcorn.y,
2252 wcorn.x, worig.y);
2253 DrawLine(dpy, areawin->window, areawin->gc, wcorn.x, worig.y,
2254 worig.x, worig.y);
2256 #endif /* HAVE_CAIRO */
2258 /*----------------------------------------------------------------------*/
2259 /* Get a box indicating the dimensions of the edit element that most */
2260 /* closely reach the position "corner". */
2261 /*----------------------------------------------------------------------*/
2263 float UGetRescaleBox(XPoint *corner, XPoint *newpoints)
2265 genericptr rgen;
2266 float savescale, newscale;
2267 long mindist, testdist, refdist;
2268 labelptr rlab;
2269 graphicptr rgraph;
2270 objinstptr rinst;
2271 int i;
2273 if (!areawin->redraw_ongoing) {
2274 areawin->redraw_needed = True;
2275 // return 0.0;
2278 if (areawin->selects == 0) return 0.0;
2280 /* Use only the 1st selection as a reference to set the scale */
2282 rgen = SELTOGENERIC(areawin->selectlist);
2284 switch(ELEMENTTYPE(rgen)) {
2285 case LABEL:
2286 rlab = (labelptr)rgen;
2287 labelbbox(rlab, newpoints, areawin->topinstance);
2288 newpoints[4] = newpoints[0];
2289 mindist = LONG_MAX;
2290 for (i = 0; i < 4; i++) {
2291 testdist = finddist(&newpoints[i], &newpoints[i+1], corner);
2292 if (testdist < mindist)
2293 mindist = testdist;
2295 refdist = wirelength(corner, &(rlab->position));
2296 mindist = (int)sqrt(abs((double)mindist));
2297 savescale = rlab->scale;
2298 if (!test_insideness((int)corner->x, (int)corner->y, newpoints))
2299 mindist = -mindist;
2300 if (refdist == mindist) refdist = 1 - mindist;
2301 if (rlab->scale < 0) rlab->scale = -rlab->scale;
2302 newscale = fabs(rlab->scale * (float)refdist / (float)(refdist + mindist));
2303 if (newscale > 10 * rlab->scale) newscale = 10 * rlab->scale;
2304 if (areawin->snapto) {
2305 float snapstep = 2 * (float)xobjs.pagelist[areawin->page]->gridspace
2306 / (float)xobjs.pagelist[areawin->page]->snapspace;
2307 newscale = (float)((int)(newscale * snapstep)) / snapstep;
2308 if (newscale < (1.0 / snapstep)) newscale = (1.0 / snapstep);
2310 else if (newscale < 0.1 * rlab->scale) newscale = 0.1 * rlab->scale;
2311 rlab->scale = (savescale < 0) ? -newscale : newscale;
2312 labelbbox(rlab, newpoints, areawin->topinstance);
2313 rlab->scale = savescale;
2314 if (savescale < 0) newscale = -newscale;
2315 break;
2317 case GRAPHIC:
2318 rgraph = (graphicptr)rgen;
2319 graphicbbox(rgraph, newpoints);
2320 newpoints[4] = newpoints[0];
2321 mindist = LONG_MAX;
2322 for (i = 0; i < 4; i++) {
2323 testdist = finddist(&newpoints[i], &newpoints[i+1], corner);
2324 if (testdist < mindist)
2325 mindist = testdist;
2327 refdist = wirelength(corner, &(rgraph->position));
2328 mindist = (int)sqrt(abs((double)mindist));
2329 savescale = rgraph->scale;
2330 if (!test_insideness((int)corner->x, (int)corner->y, newpoints))
2331 mindist = -mindist;
2332 if (refdist == mindist) refdist = 1 - mindist; /* avoid inf result */
2333 if (rgraph->scale < 0) rgraph->scale = -rgraph->scale;
2334 newscale = fabs(rgraph->scale * (float)refdist / (float)(refdist + mindist));
2335 if (newscale > 10 * rgraph->scale) newscale = 10 * rgraph->scale;
2336 if (areawin->snapto) {
2337 float snapstep = 2 * (float)xobjs.pagelist[areawin->page]->gridspace
2338 / (float)xobjs.pagelist[areawin->page]->snapspace;
2339 newscale = (float)((int)(newscale * snapstep)) / snapstep;
2340 if (newscale < (1.0 / snapstep)) newscale = (1.0 / snapstep);
2342 else if (newscale < 0.1 * rgraph->scale) newscale = 0.1 * rgraph->scale;
2343 rgraph->scale = (savescale < 0) ? -newscale : newscale;
2344 graphicbbox(rgraph, newpoints);
2345 rgraph->scale = savescale;
2346 if (savescale < 0) newscale = -newscale;
2347 break;
2349 case OBJINST:
2350 rinst = (objinstptr)rgen;
2351 objinstbbox(rinst, newpoints, 0);
2352 newpoints[4] = newpoints[0];
2353 mindist = LONG_MAX;
2354 for (i = 0; i < 4; i++) {
2355 testdist = finddist(&newpoints[i], &newpoints[i+1], corner);
2356 if (testdist < mindist)
2357 mindist = testdist;
2359 refdist = wirelength(corner, &(rinst->position));
2360 mindist = (int)sqrt(abs((double)mindist));
2361 savescale = rinst->scale;
2362 if (!test_insideness((int)corner->x, (int)corner->y, newpoints))
2363 mindist = -mindist;
2364 if (refdist == mindist) refdist = 1 - mindist; /* avoid inf result */
2365 if (rinst->scale < 0) rinst->scale = -rinst->scale;
2366 newscale = fabs(rinst->scale * (float)refdist / (float)(refdist + mindist));
2367 if (newscale > 10 * rinst->scale) newscale = 10 * rinst->scale;
2368 if (areawin->snapto) {
2369 float snapstep = 2 * (float)xobjs.pagelist[areawin->page]->gridspace
2370 / (float)xobjs.pagelist[areawin->page]->snapspace;
2371 newscale = (float)((int)(newscale * snapstep)) / snapstep;
2372 if (newscale < (1.0 / snapstep)) newscale = (1.0 / snapstep);
2374 else if (newscale < 0.1 * rinst->scale) newscale = 0.1 * rinst->scale;
2375 rinst->scale = (savescale < 0) ? -newscale : newscale;
2376 objinstbbox(rinst, newpoints, 0);
2377 rinst->scale = savescale;
2378 if (savescale < 0) newscale = -newscale;
2379 break;
2382 return newscale;
2385 /*----------------------------------------------------------------------*/
2386 /* Draw a box indicating the dimensions of the edit element that most */
2387 /* closely reach the position "corner". */
2388 /*----------------------------------------------------------------------*/
2390 #ifndef HAVE_CAIRO
2391 void UDrawRescaleBox(XPoint *corner)
2393 XPoint origpoints[5], newpoints[5];
2395 if (!areawin->redraw_ongoing) {
2396 areawin->redraw_needed = True;
2397 return;
2400 if (areawin->selects == 0)
2401 return;
2403 UGetRescaleBox(corner, newpoints);
2405 SetForeground(dpy, areawin->gc, AUXCOLOR);
2406 SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapRound, JoinBevel);
2408 UTransformbyCTM(DCTM, newpoints, origpoints, 4);
2409 strokepath(origpoints, 4, 0, 1);
2411 #endif /* HAVE_CAIRO */
2413 /*-------------------------------------------------------------------------*/
2415 #ifndef HAVE_CAIRO
2416 void UDrawBBox()
2418 XPoint origin;
2419 XPoint worig, wcorn, corner;
2420 objinstptr bbinst = areawin->topinstance;
2422 if (!areawin->redraw_ongoing) {
2423 areawin->redraw_needed = True;
2424 return;
2427 if ((!areawin->bboxon) || (checkforbbox(topobject) != NULL)) return;
2429 origin = bbinst->bbox.lowerleft;
2430 corner.x = origin.x + bbinst->bbox.width;
2431 corner.y = origin.y + bbinst->bbox.height;
2433 /* Include any schematic labels in the bounding box. */
2434 extendschembbox(bbinst, &origin, &corner);
2436 user_to_window(origin, &worig);
2437 user_to_window(corner, &wcorn);
2439 SetForeground(dpy, areawin->gc, BBOXCOLOR);
2440 DrawLine(dpy, areawin->window, areawin->gc, worig.x, worig.y,
2441 worig.x, wcorn.y);
2442 DrawLine(dpy, areawin->window, areawin->gc, worig.x, wcorn.y,
2443 wcorn.x, wcorn.y);
2444 DrawLine(dpy, areawin->window, areawin->gc, wcorn.x, wcorn.y,
2445 wcorn.x, worig.y);
2446 DrawLine(dpy, areawin->window, areawin->gc, wcorn.x, worig.y,
2447 worig.x, worig.y);
2449 #endif /* !HAVE_CAIRO */
2451 /*----------------------------------------------------------------------*/
2452 /* Fill and/or draw a border around the stroking path */
2453 /*----------------------------------------------------------------------*/
2455 #ifndef HAVE_CAIRO
2456 void strokepath(XPoint *pathlist, short number, short style, float width)
2458 float tmpwidth;
2460 tmpwidth = UTopTransScale(width);
2462 if (!(style & CLIPMASK) || (areawin->showclipmasks == TRUE) ||
2463 (areawin->clipped < 0)) {
2464 if (style & FILLED || (!(style & FILLED) && style & OPAQUE)) {
2465 if ((style & FILLSOLID) == FILLSOLID)
2466 SetFillStyle(dpy, areawin->gc, FillSolid);
2467 else if (!(style & FILLED)) {
2468 SetFillStyle(dpy, areawin->gc, FillOpaqueStippled);
2469 SetStipple(dpy, areawin->gc, 7);
2471 else {
2472 if (style & OPAQUE)
2473 SetFillStyle(dpy, areawin->gc, FillOpaqueStippled);
2474 else
2475 SetFillStyle(dpy, areawin->gc, FillStippled);
2476 SetStipple(dpy, areawin->gc, ((style & FILLSOLID) >> 5));
2478 FillPolygon(dpy, areawin->window, areawin->gc, pathlist, number, Nonconvex,
2479 CoordModeOrigin);
2480 /* return to original state */
2481 SetFillStyle(dpy, areawin->gc, FillSolid);
2483 if (!(style & NOBORDER)) {
2484 if (style & (DASHED | DOTTED)) {
2485 /* Set up dots or dashes */
2486 char dashstring[2];
2487 /* prevent values greater than 255 from folding back into */
2488 /* type char. Limit to 63 (=255/4) to keep at least the */
2489 /* dot/gap ratio to scale when 'gap' is at its maximum */
2490 /* value. */
2491 unsigned char dotsize = min(63, max(1, (short)tmpwidth));
2492 if (style & DASHED)
2493 dashstring[0] = 4 * dotsize;
2494 else if (style & DOTTED)
2495 dashstring[0] = dotsize;
2496 dashstring[1] = 4 * dotsize;
2497 SetDashes(dpy, areawin->gc, 0, dashstring, 2);
2498 SetLineAttributes(dpy, areawin->gc, tmpwidth, LineOnOffDash,
2499 CapButt, (style & SQUARECAP) ? JoinMiter : JoinBevel);
2501 else
2502 SetLineAttributes(dpy, areawin->gc, tmpwidth, LineSolid,
2503 (style & SQUARECAP) ? CapProjecting : CapRound,
2504 (style & SQUARECAP) ? JoinMiter : JoinBevel);
2506 /* draw the spline and close off if so specified */
2507 DrawLines(dpy, areawin->window, areawin->gc, pathlist,
2508 number, CoordModeOrigin);
2509 if (!(style & UNCLOSED))
2510 DrawLine(dpy, areawin->window, areawin->gc, pathlist[0].x,
2511 pathlist[0].y, pathlist[number - 1].x, pathlist[number - 1].y);
2515 if (style & CLIPMASK) {
2516 if (areawin->clipped == 0) {
2517 XSetForeground(dpy, areawin->cmgc, 0);
2518 XFillRectangle(dpy, areawin->clipmask, areawin->cmgc, 0, 0,
2519 areawin->width, areawin->height);
2520 XSetForeground(dpy, areawin->cmgc, 1);
2521 FillPolygon(dpy, areawin->clipmask, areawin->cmgc, pathlist,
2522 number, Nonconvex, CoordModeOrigin);
2523 XSetClipMask(dpy, areawin->gc, areawin->clipmask);
2524 // printf("level 0: Clip to clipmask\n"); // Diagnostic
2525 areawin->clipped++;
2527 else if ((areawin->clipped > 0) && (areawin->clipped & 1) == 0) {
2528 if (areawin->pbuf == (Pixmap)NULL) {
2529 areawin->pbuf = XCreatePixmap (dpy, areawin->window,
2530 areawin->width, areawin->height, 1);
2532 XCopyArea(dpy, areawin->clipmask, areawin->pbuf, areawin->cmgc,
2533 0, 0, areawin->width, areawin->height, 0, 0);
2534 XSetForeground(dpy, areawin->cmgc, 0);
2535 XFillRectangle(dpy, areawin->clipmask, areawin->cmgc, 0, 0,
2536 areawin->width, areawin->height);
2537 XSetForeground(dpy, areawin->cmgc, 1);
2538 FillPolygon(dpy, areawin->clipmask, areawin->cmgc, pathlist,
2539 number, Nonconvex, CoordModeOrigin);
2540 XSetFunction(dpy, areawin->cmgc, GXand);
2541 XCopyArea(dpy, areawin->pbuf, areawin->clipmask, areawin->cmgc,
2542 0, 0, areawin->width, areawin->height, 0, 0);
2543 XSetFunction(dpy, areawin->cmgc, GXcopy);
2544 XSetClipMask(dpy, areawin->gc, areawin->clipmask);
2545 // printf("level X: Clip to clipmask\n"); // Diagnostic
2546 areawin->clipped++;
2550 #endif /* !HAVE_CAIRO */
2552 /*-------------------------------------------------------------------------*/
2554 void makesplinepath(splineptr thespline, XPoint *pathlist)
2556 XPoint *tmpptr = pathlist;
2558 UTransformbyCTM(DCTM, &(thespline->ctrl[0]), tmpptr, 1);
2559 UfTransformbyCTM(DCTM, thespline->points, ++tmpptr, INTSEGS);
2560 UTransformbyCTM(DCTM, &(thespline->ctrl[3]), tmpptr + INTSEGS, 1);
2563 /*-------------------------------------------------------------------------*/
2565 #ifndef HAVE_CAIRO
2566 void UDrawSpline(splineptr thespline, float passwidth)
2568 XPoint tmppoints[SPLINESEGS];
2569 float scaledwidth;
2571 if (!areawin->redraw_ongoing) {
2572 areawin->redraw_needed = True;
2573 return;
2576 scaledwidth = thespline->width * passwidth;
2578 makesplinepath(thespline, tmppoints);
2579 strokepath(tmppoints, SPLINESEGS, thespline->style, scaledwidth);
2581 #endif /* HAVE_CAIRO */
2583 /*-------------------------------------------------------------------------*/
2585 #ifndef HAVE_CAIRO
2586 void UDrawPolygon(polyptr thepoly, float passwidth)
2588 XPoint *tmppoints = (pointlist) malloc(thepoly->number * sizeof(XPoint));
2589 float scaledwidth;
2591 if (!areawin->redraw_ongoing) {
2592 areawin->redraw_needed = True;
2593 return;
2596 scaledwidth = thepoly->width * passwidth;
2598 UTransformbyCTM(DCTM, thepoly->points, tmppoints, thepoly->number);
2599 strokepath(tmppoints, thepoly->number, thepoly->style, scaledwidth);
2600 free(tmppoints);
2602 #endif /* HAVE_CAIRO */
2604 /*-------------------------------------------------------------------------*/
2606 #ifndef HAVE_CAIRO
2607 void UDrawArc(arcptr thearc, float passwidth)
2609 XPoint tmppoints[RSTEPS + 2];
2610 float scaledwidth;
2612 if (!areawin->redraw_ongoing) {
2613 areawin->redraw_needed = True;
2614 return;
2617 scaledwidth = thearc->width * passwidth;
2619 UfTransformbyCTM(DCTM, thearc->points, tmppoints, thearc->number);
2620 strokepath(tmppoints, thearc->number, thearc->style, scaledwidth);
2622 #endif /* HAVE_CAIRO */
2624 /*-------------------------------------------------------------------------*/
2626 #ifndef HAVE_CAIRO
2627 void UDrawPath(pathptr thepath, float passwidth)
2629 XPoint *tmppoints = (pointlist) malloc(sizeof(XPoint));
2630 genericptr *genpath;
2631 polyptr thepoly;
2632 splineptr thespline;
2633 int pathsegs = 0, curseg = 0;
2634 float scaledwidth;
2636 if (!areawin->redraw_ongoing) {
2637 areawin->redraw_needed = True;
2638 return;
2641 for (genpath = thepath->plist; genpath < thepath->plist + thepath->parts;
2642 genpath++) {
2643 switch(ELEMENTTYPE(*genpath)) {
2644 case POLYGON:
2645 thepoly = TOPOLY(genpath);
2646 pathsegs += thepoly->number;
2647 tmppoints = (pointlist) realloc(tmppoints, pathsegs * sizeof(XPoint));
2648 UTransformbyCTM(DCTM, thepoly->points, tmppoints + curseg, thepoly->number);
2649 curseg = pathsegs;
2650 break;
2651 case SPLINE:
2652 thespline = TOSPLINE(genpath);
2653 pathsegs += SPLINESEGS;
2654 tmppoints = (pointlist) realloc(tmppoints, pathsegs * sizeof(XPoint));
2655 makesplinepath(thespline, tmppoints + curseg);
2656 curseg = pathsegs;
2657 break;
2660 scaledwidth = thepath->width * passwidth;
2662 strokepath(tmppoints, pathsegs, thepath->style, scaledwidth);
2663 free(tmppoints);
2665 #endif /* HAVE_CAIRO */
2667 /*----------------------------------------------------------------------*/
2668 /* Main recursive object instance drawing routine. */
2669 /* context is the instance information passed down from above */
2670 /* theinstance is the object instance to be drawn */
2671 /* level is the level of recursion */
2672 /* passcolor is the inherited color value passed to object */
2673 /* passwidth is the inherited linewidth value passed to the object */
2674 /* stack contains graphics context information */
2675 /*----------------------------------------------------------------------*/
2677 #ifndef HAVE_CAIRO
2678 void UDrawObject(objinstptr theinstance, short level, int passcolor,
2679 float passwidth, pushlistptr *stack)
2681 genericptr *areagen;
2682 float tmpwidth;
2683 int defaultcolor = passcolor;
2684 int curcolor = passcolor;
2685 int thispart;
2686 short savesel;
2687 XPoint bboxin[2], bboxout[2];
2688 u_char xm, ym;
2689 objectptr theobject = theinstance->thisobject;
2691 if (!areawin->redraw_ongoing) {
2692 areawin->redraw_needed = True;
2693 return;
2696 /* Save the number of selections and set it to zero while we do the */
2697 /* object drawing. */
2699 savesel = areawin->selects;
2700 areawin->selects = 0;
2702 /* All parts are given in the coordinate system of the object, unless */
2703 /* this is the top-level object, in which they will be interpreted as */
2704 /* relative to the screen. */
2706 UPushCTM();
2708 if (stack) {
2709 /* Save the current clipping mask and push it on the stack */
2710 if (areawin->clipped > 0) {
2711 push_stack((pushlistptr *)stack, theinstance, (char *)areawin->clipmask);
2712 areawin->clipmask = XCreatePixmap(dpy, areawin->window, areawin->width,
2713 areawin->height, 1);
2714 XCopyArea(dpy, (Pixmap)(*stack)->clientdata, areawin->clipmask, areawin->cmgc,
2715 0, 0, areawin->width, areawin->height, 0, 0);
2717 else
2718 push_stack((pushlistptr *)stack, theinstance, (char *)NULL);
2720 if (level != 0)
2721 UPreMultCTM(DCTM, theinstance->position, theinstance->scale,
2722 theinstance->rotation);
2724 if (theinstance->style & LINE_INVARIANT)
2725 passwidth /= fabs(theinstance->scale);
2727 /* do a quick test for intersection with the display window */
2729 bboxin[0].x = theobject->bbox.lowerleft.x;
2730 bboxin[0].y = theobject->bbox.lowerleft.y;
2731 bboxin[1].x = theobject->bbox.lowerleft.x + theobject->bbox.width;
2732 bboxin[1].y = theobject->bbox.lowerleft.y + theobject->bbox.height;
2733 if (level == 0)
2734 extendschembbox(theinstance, &(bboxin[0]), &(bboxin[1]));
2735 UTransformbyCTM(DCTM, bboxin, bboxout, 2);
2737 xm = (bboxout[0].x < bboxout[1].x) ? 0 : 1;
2738 ym = (bboxout[0].y < bboxout[1].y) ? 0 : 1;
2740 if (bboxout[xm].x < areawin->width && bboxout[ym].y < areawin->height &&
2741 bboxout[1 - xm].x > 0 && bboxout[1 - ym].y > 0) {
2743 /* make parameter substitutions */
2744 psubstitute(theinstance);
2746 /* draw all of the elements */
2748 tmpwidth = UTopTransScale(passwidth);
2749 SetLineAttributes(dpy, areawin->gc, tmpwidth, LineSolid, CapRound,
2750 JoinBevel);
2752 /* guard against plist being regenerated during a redraw by the */
2753 /* expression parameter mechanism (should that be prohibited?) */
2755 for (thispart = 0; thispart < theobject->parts; thispart++) {
2756 areagen = theobject->plist + thispart;
2757 if ((*areagen)->type & DRAW_HIDE) continue;
2759 if (defaultcolor != DOFORALL) {
2760 Boolean clipcolor = FALSE;
2761 switch(ELEMENTTYPE(*areagen)) {
2762 case(POLYGON): case(SPLINE): case(ARC): case(PATH):
2763 if (TOPOLY(areagen)->style & CLIPMASK)
2764 clipcolor = TRUE;
2765 break;
2767 if (((*areagen)->color != curcolor) || (clipcolor == TRUE)) {
2768 if (clipcolor)
2769 curcolor = CLIPMASKCOLOR;
2770 else if ((*areagen)->color == DEFAULTCOLOR)
2771 curcolor = defaultcolor;
2772 else
2773 curcolor = (*areagen)->color;
2775 XcTopSetForeground(curcolor);
2779 switch(ELEMENTTYPE(*areagen)) {
2780 case(POLYGON):
2781 if (level == 0 || !((TOPOLY(areagen))->style & BBOX))
2782 UDrawPolygon(TOPOLY(areagen), passwidth);
2783 break;
2785 case(SPLINE):
2786 UDrawSpline(TOSPLINE(areagen), passwidth);
2787 break;
2789 case(ARC):
2790 UDrawArc(TOARC(areagen), passwidth);
2791 break;
2793 case(PATH):
2794 UDrawPath(TOPATH(areagen), passwidth);
2795 break;
2797 case(GRAPHIC):
2798 UDrawGraphic(TOGRAPHIC(areagen));
2799 break;
2801 case(OBJINST):
2802 if (areawin->editinplace && stack && (TOOBJINST(areagen)
2803 == areawin->topinstance)) {
2804 /* If stack matches areawin->stack, then don't draw */
2805 /* because it would be redundant. */
2806 pushlistptr alist = *stack, blist = areawin->stack;
2807 while (alist && blist) {
2808 if (alist->thisinst != blist->thisinst) break;
2809 alist = alist->next;
2810 blist = blist->next;
2812 if ((!alist) || (!blist)) break;
2814 if (areawin->clipped > 0) areawin->clipped += 2;
2815 UDrawObject(TOOBJINST(areagen), level + 1, curcolor, passwidth, stack);
2816 if (areawin->clipped > 0) areawin->clipped -= 2;
2817 break;
2819 case(LABEL):
2820 if (level == 0 || TOLABEL(areagen)->pin == False)
2821 UDrawString(TOLABEL(areagen), curcolor, theinstance);
2822 else if ((TOLABEL(areagen)->anchor & PINVISIBLE) && areawin->pinpointon)
2823 UDrawString(TOLABEL(areagen), curcolor, theinstance);
2824 else if (TOLABEL(areagen)->anchor & PINVISIBLE)
2825 UDrawStringNoX(TOLABEL(areagen), curcolor, theinstance);
2826 else if (level == 1 && TOLABEL(areagen)->pin &&
2827 TOLABEL(areagen)->pin != INFO && areawin->pinpointon)
2828 UDrawXDown(TOLABEL(areagen));
2829 break;
2831 if (areawin->clipped > 0) {
2832 if ((areawin->clipped & 3) == 1) {
2833 areawin->clipped++;
2835 else if ((areawin->clipped & 3) == 2) {
2836 areawin->clipped -= 2;
2837 if ((!stack) || ((*stack)->clientdata == (char *)NULL)) {
2838 XSetClipMask(dpy, areawin->gc, None);
2839 // printf("1: Clear clipmask\n"); // Diagnostic
2841 else {
2842 XSetClipMask(dpy, areawin->gc, (Pixmap)((*stack)->clientdata));
2843 // printf("1: Set to pushed clipmask\n"); // Diagnostic
2849 /* restore the color passed to the object, if different from current color */
2851 if ((defaultcolor != DOFORALL) && (passcolor != curcolor)) {
2852 XTopSetForeground(passcolor);
2854 if (areawin->clipped > 0) {
2855 if ((areawin->clipped & 3) != 3) {
2856 if ((!stack) || ((*stack)->clientdata == (char *)NULL)) {
2857 XSetClipMask(dpy, areawin->gc, None);
2858 // printf("2: Clear clipmask\n"); // Diagnostic
2860 else {
2861 XSetClipMask(dpy, areawin->gc, (Pixmap)((*stack)->clientdata));
2862 // printf("2: Set to pushed clipmask\n"); // Diagnostic
2865 areawin->clipped &= ~3;
2869 /* restore the selection list (if any) */
2870 areawin->selects = savesel;
2871 UPopCTM();
2872 if (stack) {
2873 if ((*stack) != NULL) {
2874 if ((*stack)->clientdata != (char *)NULL) {
2875 XFreePixmap(dpy, areawin->clipmask);
2876 areawin->clipmask = (Pixmap)(*stack)->clientdata;
2877 // printf("3: Restore clipmask\n"); // Diagnostic
2880 pop_stack(stack);
2883 #endif /* HAVE_CAIRO */
2885 /*----------------------------------------------------------------------*/
2886 /* Recursively run through the current page and find any labels which */
2887 /* are declared to be style LATEX. If "checkonly" is present, we set */
2888 /* it to TRUE or FALSE depending on whether or not LATEX labels have */
2889 /* been encountered. If NULL, then we write LATEX output appropriately */
2890 /* to a file named with the page filename + suffix ".tex". */
2891 /*----------------------------------------------------------------------*/
2893 void UDoLatex(objinstptr theinstance, short level, FILE *f,
2894 float scale, float scale2, int tx, int ty, Boolean *checkonly)
2896 XPoint lpos, xlpos;
2897 XfPoint xfpos;
2898 labelptr thislabel;
2899 genericptr *areagen;
2900 objectptr theobject = theinstance->thisobject;
2901 char *ltext;
2902 int lranchor, tbanchor;
2904 UPushCTM();
2905 if (level != 0)
2906 UPreMultCTM(DCTM, theinstance->position, theinstance->scale,
2907 theinstance->rotation);
2909 /* make parameter substitutions */
2910 psubstitute(theinstance);
2912 /* find all of the elements */
2914 for (areagen = theobject->plist; areagen < theobject->plist +
2915 theobject->parts; areagen++) {
2917 switch(ELEMENTTYPE(*areagen)) {
2918 case(OBJINST):
2919 UDoLatex(TOOBJINST(areagen), level + 1, f, scale, scale2, tx, ty, checkonly);
2920 break;
2922 case(LABEL):
2923 thislabel = TOLABEL(areagen);
2924 if (level == 0 || thislabel->pin == False ||
2925 (thislabel->anchor & PINVISIBLE))
2926 if (thislabel->anchor & LATEXLABEL) {
2927 if (checkonly) {
2928 *checkonly = TRUE;
2929 return;
2931 else {
2932 lpos.x = thislabel->position.x;
2933 lpos.y = thislabel->position.y;
2934 UTransformbyCTM(DCTM, &lpos, &xlpos, 1);
2935 xlpos.x += tx;
2936 xlpos.y += ty;
2937 xfpos.x = (float)xlpos.x * scale;
2938 xfpos.y = (float)xlpos.y * scale;
2939 xfpos.x /= 72.0;
2940 xfpos.y /= 72.0;
2941 xfpos.x -= 1.0;
2942 xfpos.y -= 1.0;
2943 xfpos.x += 0.056;
2944 xfpos.y += 0.056;
2945 xfpos.x /= scale2;
2946 xfpos.y /= scale2;
2947 ltext = textprinttex(thislabel->string, theinstance);
2948 tbanchor = thislabel->anchor & (NOTBOTTOM | TOP);
2949 lranchor = thislabel->anchor & (NOTLEFT | RIGHT);
2951 /* The 1.2 factor accounts for the difference between */
2952 /* Xcircuit's label scale of "1" and LaTeX's "normalsize" */
2954 fprintf(f, " \\putbox{%3.2fin}{%3.2fin}{%3.2f}{",
2955 xfpos.x, xfpos.y, 1.2 * thislabel->scale);
2956 if (thislabel->rotation != 0)
2957 fprintf(f, "\\rotatebox{-%d}{", thislabel->rotation);
2958 if (lranchor == (NOTLEFT | RIGHT)) fprintf(f, "\\rightbox{");
2959 else if (lranchor == NOTLEFT) fprintf(f, "\\centbox{");
2960 if (tbanchor == (NOTBOTTOM | TOP)) fprintf(f, "\\topbox{");
2961 else if (tbanchor == NOTBOTTOM) fprintf(f, "\\midbox{");
2962 fprintf(f, "%s", ltext);
2963 if (lranchor != NORMAL) fprintf(f, "}");
2964 if (tbanchor != NORMAL) fprintf(f, "}");
2965 if (thislabel->rotation != 0) fprintf(f, "}");
2966 fprintf(f, "}%%\n");
2967 free(ltext);
2970 break;
2973 UPopCTM();
2976 /*----------------------------------------------------------------------*/
2977 /* Top level routine for writing LATEX output. */
2978 /*----------------------------------------------------------------------*/
2980 void TopDoLatex()
2982 FILE *f;
2983 float psscale, outscale;
2984 int tx, ty, width, height;
2985 polyptr framebox;
2986 XPoint origin;
2987 Boolean checklatex = FALSE;
2988 char filename[100], extension[10], *dotptr;
2990 UDoLatex(areawin->topinstance, 0, NULL, 1.0, 1.0, 0, 0, &checklatex);
2992 if (checklatex == FALSE) return; /* No LaTeX labels to write */
2994 /* Handle cases where the file might have a ".eps" extension. */
2995 /* Thanks to Graham Sheward for pointing this out. */
2997 /* Modified file path routines: */
2998 /* Solved problems with incomplete paths, NULL file pointers, */
2999 /* added tilde and variable expansion by Agustín Campeny, April 2020 */
3001 sprintf(filename, "%s", xobjs.pagelist[areawin->page]->filename);
3003 xc_tilde_expand(filename, 100);
3004 while(xc_variable_expand(filename, 100));
3006 dotptr = strrchr(filename, '.');
3007 sprintf(extension, "%s", dotptr);
3008 filename[dotptr - filename] = '\0';
3009 sprintf(filename, "%s.tex", filename);
3011 f = fopen(filename, "w");
3013 if (!f) {
3014 Wprintf("Couldn't save .tex file. Check file path");
3015 return;
3018 *dotptr = '\0';
3020 fprintf(f, "%% XCircuit output \"%s.tex\" for LaTeX input from %s%s\n",
3021 filename, filename, extension);
3022 fprintf(f, "\\def\\putbox#1#2#3#4{\\makebox[0in][l]{\\makebox[#1][l]{}"
3023 "\\raisebox{\\baselineskip}[0in][0in]"
3024 "{\\raisebox{#2}[0in][0in]{\\scalebox{#3}{#4}}}}}\n");
3025 fprintf(f, "\\def\\rightbox#1{\\makebox[0in][r]{#1}}\n");
3026 fprintf(f, "\\def\\centbox#1{\\makebox[0in]{#1}}\n");
3027 fprintf(f, "\\def\\topbox#1{\\raisebox{-0.60\\baselineskip}[0in][0in]{#1}}\n");
3028 fprintf(f, "\\def\\midbox#1{\\raisebox{-0.20\\baselineskip}[0in][0in]{#1}}\n");
3030 /* Modified to use \scalebox and \parbox by Alex Tercete, June 2008 */
3032 // fprintf(f, "\\begin{center}\n");
3034 outscale = xobjs.pagelist[areawin->page]->outscale;
3035 psscale = getpsscale(outscale, areawin->page);
3037 width = toplevelwidth(areawin->topinstance, &origin.x);
3038 height = toplevelheight(areawin->topinstance, &origin.y);
3040 /* Added 10/19/10: If there is a specified bounding box, let it */
3041 /* determine the figure origin; otherwise, the labels will be */
3042 /* mismatched to the bounding box. */
3044 if ((framebox = checkforbbox(topobject)) != NULL) {
3045 int i, maxx, maxy;
3047 origin.x = maxx = framebox->points[0].x;
3048 origin.y = maxy = framebox->points[0].y;
3049 for (i = 1; i < framebox->number; i++) {
3050 if (framebox->points[i].x < origin.x) origin.x = framebox->points[i].x;
3051 if (framebox->points[i].x > maxx) maxx = framebox->points[i].x;
3052 if (framebox->points[i].y < origin.y) origin.y = framebox->points[i].y;
3053 if (framebox->points[i].y > maxy) maxy = framebox->points[i].y;
3055 origin.x -= ((width - maxx + origin.x) / 2);
3056 origin.y -= ((height - maxy + origin.y) / 2);
3059 tx = (int)(72 / psscale) - origin.x,
3060 ty = (int)(72 / psscale) - origin.y;
3062 fprintf(f, " \\scalebox{%g}{\n", outscale);
3063 fprintf(f, " \\normalsize\n");
3064 fprintf(f, " \\parbox{%gin}{\n", (((float)width * psscale) / 72.0) / outscale);
3065 fprintf(f, " \\includegraphics[scale=%g]{%s%s}\\\\\n", 1.0 / outscale,
3066 filename, extension);
3067 fprintf(f, " %% translate x=%d y=%d scale %3.2f\n", tx, ty, psscale);
3069 UPushCTM(); /* Save current state */
3070 UResetCTM(DCTM); /* Set to identity matrix */
3071 UDoLatex(areawin->topinstance, 0, f, psscale, outscale, tx, ty, NULL);
3072 UPopCTM(); /* Restore state */
3074 fprintf(f, " } %% close \'parbox\'\n");
3075 fprintf(f, " } %% close \'scalebox\'\n");
3076 fprintf(f, " \\vspace{-\\baselineskip} %% this is not"
3077 " necessary, but looks better\n");
3078 // fprintf(f, "\\end{center}\n");
3079 fclose(f);
3081 Wprintf("Wrote auxiliary file %s.tex", filename);