Update at Fri Oct 12 11:00:31 EDT 2018 by tim
[xcircuit.git] / functions.c
blob38291a5f0df8a3feb6d6337eee9023c71c0dfd8b
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)
120 float fnc, ang1, ang2;
121 short ncurves, i;
122 arcptr thearc;
123 genericptr *pgen;
124 splineptr *newspline;
125 double nu1, nu2, lambda1, lambda2, alpha, tansq;
126 XfPoint E1, E2, Ep1, Ep2;
127 Boolean reverse = FALSE;
129 pgen = thepath->plist + thepath->parts - 1;
130 if (ELEMENTTYPE(*pgen) != ARC) return;
131 thearc = TOARC(pgen);
133 if (thearc->radius < 0) {
134 reverse = TRUE;
135 thearc->radius = -thearc->radius;
138 fnc = (thearc->angle2 - thearc->angle1) / 90.0;
139 ncurves = (short)fnc;
140 if (fnc - (float)((int)fnc) > 0.01) ncurves++;
142 thepath->parts--; /* Forget the arc */
144 for (i = 0; i < ncurves; i++) {
145 if (reverse) { /* arc path is reverse direction */
146 if (i == 0)
147 ang1 = thearc->angle2;
148 else
149 ang1 -= 90;
151 if (i == ncurves - 1)
152 ang2 = thearc->angle1;
153 else
154 ang2 = ang1 - 90;
156 else { /* arc path is forward direction */
157 if (i == 0)
158 ang1 = thearc->angle1;
159 else
160 ang1 += 90;
162 if (i == ncurves - 1)
163 ang2 = thearc->angle2;
164 else
165 ang2 = ang1 + 90;
168 lambda1 = (double)ang1 * RADFAC;
169 lambda2 = (double)ang2 * RADFAC;
171 nu1 = atan2(sin(lambda1) / (double)thearc->yaxis,
172 cos(lambda1) / (double)thearc->radius);
173 nu2 = atan2(sin(lambda2) / (double)thearc->yaxis,
174 cos(lambda2) / (double)thearc->radius);
175 E1.x = (float)thearc->position.x +
176 (float)thearc->radius * (float)cos(nu1);
177 E1.y = (float)thearc->position.y +
178 (float)thearc->yaxis * (float)sin(nu1);
179 E2.x = (float)thearc->position.x +
180 (float)thearc->radius * (float)cos(nu2);
181 E2.y = (float)thearc->position.y +
182 (float)thearc->yaxis * (float)sin(nu2);
183 Ep1.x = -(float)thearc->radius * (float)sin(nu1);
184 Ep1.y = (float)thearc->yaxis * (float)cos(nu1);
185 Ep2.x = -(float)thearc->radius * (float)sin(nu2);
186 Ep2.y = (float)thearc->yaxis * (float)cos(nu2);
188 tansq = tan((nu2 - nu1) / 2.0);
189 tansq *= tansq;
190 alpha = sin(nu2 - nu1) * 0.33333 * (sqrt(4 + (3 * tansq)) - 1);
192 NEW_SPLINE(newspline, thepath);
193 splinedefaults(*newspline, 0, 0);
194 (*newspline)->style = thearc->style;
195 (*newspline)->color = thearc->color;
196 (*newspline)->width = thearc->width;
198 (*newspline)->ctrl[0].x = E1.x;
199 (*newspline)->ctrl[0].y = E1.y;
201 (*newspline)->ctrl[1].x = E1.x + alpha * Ep1.x;
202 (*newspline)->ctrl[1].y = E1.y + alpha * Ep1.y;
204 (*newspline)->ctrl[2].x = E2.x - alpha * Ep2.x;
205 (*newspline)->ctrl[2].y = E2.y - alpha * Ep2.y;
207 (*newspline)->ctrl[3].x = E2.x;
208 (*newspline)->ctrl[3].y = E2.y;
210 calcspline(*newspline);
213 /* Delete the arc */
214 free_single((genericptr)thearc);
217 /*----------------------------------------------------------------------*/
218 /* Calculate points for an arc */
219 /*----------------------------------------------------------------------*/
221 void calcarc(arcptr thearc)
223 short idx;
224 int sarc;
225 float theta, delta;
227 /* assume that angle2 > angle1 always: must be guaranteed by other routines */
229 sarc = (int)(thearc->angle2 - thearc->angle1) * RSTEPS;
230 thearc->number = (sarc / 360) + 1;
231 if (sarc % 360 != 0) thearc->number++;
233 delta = RADFAC * ((float)(thearc->angle2 - thearc->angle1) / (thearc->number - 1));
234 theta = thearc->angle1 * RADFAC;
236 for (idx = 0; idx < thearc->number - 1; idx++) {
237 thearc->points[idx].x = (float)thearc->position.x +
238 fabs((float)thearc->radius) * cos(theta);
239 thearc->points[idx].y = (float)thearc->position.y +
240 (float)thearc->yaxis * sin(theta);
241 theta += delta;
244 /* place last point exactly to avoid roundoff error */
246 theta = thearc->angle2 * RADFAC;
247 thearc->points[thearc->number - 1].x = (float)thearc->position.x +
248 fabs((float)thearc->radius) * cos(theta);
249 thearc->points[thearc->number - 1].y = (float)thearc->position.y +
250 (float)thearc->yaxis * sin(theta);
252 if (thearc->radius < 0) reversefpoints(thearc->points, thearc->number);
255 /*------------------------------------------------------------------------*/
256 /* Create a Bezier curve approximation from control points */
257 /* (using PostScript formula for Bezier cubic curve) */
258 /*------------------------------------------------------------------------*/
260 float par[INTSEGS];
261 float parsq[INTSEGS];
262 float parcb[INTSEGS];
264 void initsplines()
266 float t;
267 short idx;
269 for (idx = 0; idx < INTSEGS; idx++) {
270 t = (float)(idx + 1) / (INTSEGS + 1);
271 par[idx] = t;
272 parsq[idx] = t * t;
273 parcb[idx] = parsq[idx] * t;
277 /*------------------------------------------------------------------------*/
278 /* Compute spline coefficients */
279 /*------------------------------------------------------------------------*/
281 void computecoeffs(splineptr thespline, float *ax, float *bx, float *cx,
282 float *ay, float *by, float *cy)
284 *cx = 3.0 * (float)(thespline->ctrl[1].x - thespline->ctrl[0].x);
285 *bx = 3.0 * (float)(thespline->ctrl[2].x - thespline->ctrl[1].x) - *cx;
286 *ax = (float)(thespline->ctrl[3].x - thespline->ctrl[0].x) - *cx - *bx;
288 *cy = 3.0 * (float)(thespline->ctrl[1].y - thespline->ctrl[0].y);
289 *by = 3.0 * (float)(thespline->ctrl[2].y - thespline->ctrl[1].y) - *cy;
290 *ay = (float)(thespline->ctrl[3].y - thespline->ctrl[0].y) - *cy - *by;
293 /*------------------------------------------------------------------------*/
295 void calcspline(splineptr thespline)
297 float ax, bx, cx, ay, by, cy;
298 short idx;
300 computecoeffs(thespline, &ax, &bx, &cx, &ay, &by, &cy);
301 for (idx = 0; idx < INTSEGS; idx++) {
302 thespline->points[idx].x = ax * parcb[idx] + bx * parsq[idx] +
303 cx * par[idx] + (float)thespline->ctrl[0].x;
304 thespline->points[idx].y = ay * parcb[idx] + by * parsq[idx] +
305 cy * par[idx] + (float)thespline->ctrl[0].y;
309 /*------------------------------------------------------------------------*/
310 /* Find the (x,y) position and tangent rotation of a point on a spline */
311 /*------------------------------------------------------------------------*/
313 void findsplinepos(splineptr thespline, float t, XPoint *retpoint, float *retrot)
315 float ax, bx, cx, ay, by, cy;
316 float tsq = t * t;
317 float tcb = tsq * t;
318 double dxdt, dydt;
320 computecoeffs(thespline, &ax, &bx, &cx, &ay, &by, &cy);
321 retpoint->x = (short)(ax * tcb + bx * tsq + cx * t + (float)thespline->ctrl[0].x);
322 retpoint->y = (short)(ay * tcb + by * tsq + cy * t + (float)thespline->ctrl[0].y);
324 if (retrot != NULL) {
325 dxdt = (double)(3 * ax * tsq + 2 * bx * t + cx);
326 dydt = (double)(3 * ay * tsq + 2 * by * t + cy);
327 *retrot = INVRFAC * atan2(dxdt, dydt); /* reversed y, x */
328 if (*retrot < 0) *retrot += 360;
332 /*------------------------------------------------------------------------*/
333 /* floating-point version of the above */
334 /*------------------------------------------------------------------------*/
336 void ffindsplinepos(splineptr thespline, float t, XfPoint *retpoint)
338 float ax, bx, cx, ay, by, cy;
339 float tsq = t * t;
340 float tcb = tsq * t;
342 computecoeffs(thespline, &ax, &bx, &cx, &ay, &by, &cy);
343 retpoint->x = ax * tcb + bx * tsq + cx * t + (float)thespline->ctrl[0].x;
344 retpoint->y = ay * tcb + by * tsq + cy * t + (float)thespline->ctrl[0].y;
347 /*------------------------------------------------------------------------*/
348 /* Find the closest distance between a point and a spline and return the */
349 /* fractional distance along the spline of this point. */
350 /*------------------------------------------------------------------------*/
352 float findsplinemin(splineptr thespline, XPoint *upoint)
354 XfPoint *spt, flpt, newspt;
355 float minval = 1000000, tval, hval, ndist;
356 short j, ival;
358 flpt.x = (float)(upoint->x);
359 flpt.y = (float)(upoint->y);
361 /* get estimate from precalculated spline points */
363 for (spt = thespline->points; spt < thespline->points + INTSEGS;
364 spt++) {
365 ndist = fsqwirelen(spt, &flpt);
366 if (ndist < minval) {
367 minval = ndist;
368 ival = (short)(spt - thespline->points);
371 tval = (float)(ival + 1) / (INTSEGS + 1);
372 hval = 0.5 / (INTSEGS + 1);
374 /* short fixed iterative loop to converge on minimum t */
376 for (j = 0; j < 5; j++) {
377 tval += hval;
378 ffindsplinepos(thespline, tval, &newspt);
379 ndist = fsqwirelen(&newspt, &flpt);
380 if (ndist < minval) minval = ndist;
381 else {
382 tval -= hval * 2;
383 ffindsplinepos(thespline, tval, &newspt);
384 ndist = fsqwirelen(&newspt, &flpt);
385 if (ndist < minval) minval = ndist;
386 else tval += hval;
388 hval /= 2;
391 if (tval < 0.1) {
392 if ((float)sqwirelen(&(thespline->ctrl[0]), upoint) < minval) tval = 0;
394 else if (tval > 0.9) {
395 if ((float)sqwirelen(&(thespline->ctrl[3]), upoint) < minval) tval = 1;
397 return tval;
400 /*----------------------------------------------------------------------*/
401 /* Convert a polygon to a Bezier curve path */
402 /* Curve must be selected and there must be only one selection. */
403 /* */
404 /* Note that this routine will draw inside the perimeter of a convex */
405 /* hull. A routine that places spline endpoints on the polygon */
406 /* vertices will draw outside the perimeter of a convex hull. An */
407 /* optimal algorithm presumably zeros the total area between the curve */
408 /* and the polygon (positive and negative), but I haven't worked out */
409 /* what that solution is. The algorithm below seems good enough for */
410 /* most purposes. */
411 /*----------------------------------------------------------------------*/
413 void converttocurve()
415 genericptr *ggen;
416 splineptr *newspline;
417 polyptr thispoly;
418 pathptr *newpath;
419 short *newselect;
420 XPoint firstpoint, lastpoint, initpoint;
421 int i, numpoints;
423 if (areawin->selects != 1) return;
425 thispoly = TOPOLY(topobject->plist + (*areawin->selectlist));
426 if (ELEMENTTYPE(thispoly) != POLYGON) return;
427 if (thispoly->number < 3) return; /* Will not convert */
429 standard_element_delete(ERASE);
430 if ((thispoly->style & UNCLOSED) && (thispoly->number == 3)) {
431 NEW_SPLINE(newspline, topobject);
432 splinedefaults(*newspline, 0, 0);
433 (*newspline)->ctrl[0] = thispoly->points[0];
434 (*newspline)->ctrl[1] = thispoly->points[1];
435 (*newspline)->ctrl[2] = thispoly->points[1];
436 (*newspline)->ctrl[3] = thispoly->points[2];
438 else {
439 numpoints = thispoly->number;
441 /* If the polygon is closed but the first and last points */
442 /* overlap, treat the last point as if it doesn't exist. */
444 if (!(thispoly->style & UNCLOSED))
445 if ((thispoly->points[0].x == thispoly->points[thispoly->number - 1].x)
446 && (thispoly->points[0].y ==
447 thispoly->points[thispoly->number - 1].y))
448 numpoints--;
450 NEW_PATH(newpath, topobject);
451 pathdefaults(*newpath, 0, 0);
452 (*newpath)->style = thispoly->style;
454 if (!(thispoly->style & UNCLOSED)) {
455 lastpoint = thispoly->points[numpoints - 1];
456 initpoint.x = (lastpoint.x + thispoly->points[0].x) / 2;
457 initpoint.y = (lastpoint.y + thispoly->points[0].y) / 2;
458 firstpoint.x = (thispoly->points[0].x
459 + thispoly->points[1].x) / 2;
460 firstpoint.y = (thispoly->points[0].y
461 + thispoly->points[1].y) / 2;
463 NEW_SPLINE(newspline, (*newpath));
464 splinedefaults(*newspline, 0, 0);
465 (*newspline)->ctrl[0] = initpoint;
466 (*newspline)->ctrl[1] = thispoly->points[0];
467 (*newspline)->ctrl[2] = thispoly->points[0];
468 (*newspline)->ctrl[3] = firstpoint;
469 calcspline(*newspline);
471 else
472 firstpoint = thispoly->points[0];
474 for (i = 0; i < numpoints - ((!(thispoly->style & UNCLOSED)) ?
475 2 : 3); i++) {
476 lastpoint.x = (thispoly->points[i + 1].x
477 + thispoly->points[i + 2].x) / 2;
478 lastpoint.y = (thispoly->points[i + 1].y
479 + thispoly->points[i + 2].y) / 2;
481 NEW_SPLINE(newspline, (*newpath));
482 splinedefaults(*newspline, 0, 0);
483 (*newspline)->ctrl[0] = firstpoint;
484 (*newspline)->ctrl[1] = thispoly->points[i + 1];
485 (*newspline)->ctrl[2] = thispoly->points[i + 1];
486 (*newspline)->ctrl[3] = lastpoint;
487 firstpoint = lastpoint;
488 calcspline(*newspline);
490 if (!(thispoly->style & UNCLOSED))
491 lastpoint = initpoint;
492 else
493 lastpoint = thispoly->points[i + 2];
495 NEW_SPLINE(newspline, (*newpath));
496 splinedefaults(*newspline, 0, 0);
497 (*newspline)->ctrl[0] = firstpoint;
498 (*newspline)->ctrl[1] = thispoly->points[i + 1];
499 (*newspline)->ctrl[2] = thispoly->points[i + 1];
500 (*newspline)->ctrl[3] = lastpoint;
502 calcspline(*newspline);
503 calcbbox(areawin->topinstance);
504 setoptionmenu();
505 drawarea(NULL, NULL, NULL);
508 /*----------------------------------------------------------------------*/
509 /* Find closest point of a polygon to the cursor */
510 /*----------------------------------------------------------------------*/
512 short closepointdistance(polyptr curpoly, XPoint *cursloc, short *mindist)
514 short curdist;
515 XPoint *curpt, *savept;
517 curpt = savept = curpoly->points;
518 *mindist = wirelength(curpt, cursloc);
519 while (++curpt < curpoly->points + curpoly->number) {
520 curdist = wirelength(curpt, cursloc);
521 if (curdist < *mindist) {
522 *mindist = curdist;
523 savept = curpt;
526 return (short)(savept - curpoly->points);
529 /*----------------------------------------------------------------------------*/
530 /* Find closest point of a polygon to the cursor */
531 /*----------------------------------------------------------------------------*/
533 short closepoint(polyptr curpoly, XPoint *cursloc)
535 short mindist;
536 return closepointdistance(curpoly, cursloc, &mindist);
539 /*----------------------------------------------------------------------------*/
540 /* Find the distance to the closest point of a polygon to the cursor */
541 /*----------------------------------------------------------------------------*/
543 short closedistance(polyptr curpoly, XPoint *cursloc)
545 short mindist;
546 closepointdistance(curpoly, cursloc, &mindist);
547 return mindist;
550 /*----------------------------------------------------------------------------*/
551 /* Coordinate system transformations */
552 /*----------------------------------------------------------------------------*/
554 /*------------------------------------------------------------------------------*/
555 /* Check screen bounds: minimum, maximum scale and translation is determined */
556 /* by values which fit in an X11 type XPoint (short int). If the window */
557 /* extremes exceed type short when mapped to user space, or if the page */
558 /* bounds exceed type short when mapped to X11 window space, return error. */
559 /*------------------------------------------------------------------------------*/
561 short checkbounds()
563 long lval;
565 /* check window-to-user space */
567 lval = 2 * (long)((float) (areawin->width) / areawin->vscale) +
568 (long)areawin->pcorner.x;
569 if (lval != (long)((short)lval)) return -1;
570 lval = 2 * (long)((float) (areawin->height) / areawin->vscale) +
571 (long)areawin->pcorner.y;
572 if (lval != (long)((short)lval)) return -1;
574 /* check user-to-window space */
576 lval = (long)((float)(topobject->bbox.lowerleft.x - areawin->pcorner.x) *
577 areawin->vscale);
578 if (lval != (long)((short)lval)) return -1;
579 lval = (long)areawin->height - (long)((float)(topobject->bbox.lowerleft.y -
580 areawin->pcorner.y) * areawin->vscale);
581 if (lval != (long)((short)lval)) return -1;
583 lval = (long)((float)(topobject->bbox.lowerleft.x + topobject->bbox.width -
584 areawin->pcorner.x) * areawin->vscale);
585 if (lval != (long)((short)lval)) return -1;
586 lval = (long)areawin->height - (long)((float)(topobject->bbox.lowerleft.y +
587 topobject->bbox.height - areawin->pcorner.y) * areawin->vscale);
588 if (lval != (long)((short)lval)) return -1;
590 return 0;
593 /*------------------------------------------------------------------------*/
594 /* Transform X-window coordinate to xcircuit coordinate system */
595 /*------------------------------------------------------------------------*/
597 void window_to_user(short xw, short yw, XPoint *upt)
599 float tmpx, tmpy;
601 tmpx = (float)xw / areawin->vscale + (float)areawin->pcorner.x;
602 tmpy = (float)(areawin->height - yw) / areawin->vscale +
603 (float)areawin->pcorner.y;
605 tmpx += (tmpx > 0) ? 0.5 : -0.5;
606 tmpy += (tmpy > 0) ? 0.5 : -0.5;
608 upt->x = (short)tmpx;
609 upt->y = (short)tmpy;
612 /*------------------------------------------------------------------------*/
613 /* Transform xcircuit coordinate back to X-window coordinate system */
614 /*------------------------------------------------------------------------*/
616 void user_to_window(XPoint upt, XPoint *wpt)
618 float tmpx, tmpy;
620 tmpx = (float)(upt.x - areawin->pcorner.x) * areawin->vscale;
621 tmpy = (float)areawin->height - (float)(upt.y - areawin->pcorner.y)
622 * areawin->vscale;
624 tmpx += (tmpx > 0) ? 0.5 : -0.5;
625 tmpy += (tmpy > 0) ? 0.5 : -0.5;
627 wpt->x = (short)tmpx;
628 wpt->y = (short)tmpy;
631 /*----------------------------------------------------------------------*/
632 /* Transformations in the object hierarchy */
633 /*----------------------------------------------------------------------*/
635 /*----------------------------------------------------------------------*/
636 /* Return rotation relative to a specific CTM */
637 /*----------------------------------------------------------------------*/
639 float UGetCTMRotation(Matrix *ctm)
641 float rads = (float)atan2((double)(ctm->d), (double)(ctm->a));
642 return rads / RADFAC;
645 /*----------------------------------------------------------------------*/
646 /* Return rotation relative to the top level */
647 /* Note that UTopRotation() is also the rotation relative to the window */
648 /* since the top-level drawing page is always upright relative to the */
649 /* window. Thus, there is no routine UTopDrawingRotation(). */
650 /*----------------------------------------------------------------------*/
652 float UTopRotation()
654 return UGetCTMRotation(DCTM);
657 /*----------------------------------------------------------------------*/
658 /* Return scale relative to a specific CTM */
659 /*----------------------------------------------------------------------*/
661 float UGetCTMScale(Matrix *ctm)
663 return (float)(sqrt((double)(ctm->a * ctm->a + ctm->d * ctm->d)));
666 /*----------------------------------------------------------------------*/
667 /* Return scale relative to window */
668 /*----------------------------------------------------------------------*/
670 float UTopScale()
672 return UGetCTMScale(DCTM);
675 /*----------------------------------------------------------------------*/
676 /* Return scale multiplied by length */
677 /*----------------------------------------------------------------------*/
679 float UTopTransScale(float length)
681 return (float)(length * UTopScale());
684 /*----------------------------------------------------------------------*/
685 /* Return scale relative to the top-level schematic (not the window) */
686 /*----------------------------------------------------------------------*/
688 float UTopDrawingScale()
690 Matrix lctm, wctm;
691 UCopyCTM(DCTM, &lctm);
692 UResetCTM(&wctm);
693 UMakeWCTM(&wctm);
694 InvertCTM(&wctm);
695 UPreMultCTMbyMat(&wctm, &lctm);
696 return UGetCTMScale(&wctm);
699 /*----------------------------------------------------------------------*/
700 /* Return position offset relative to a specific CTM */
701 /*----------------------------------------------------------------------*/
703 void UGetCTMOffset(Matrix *ctm, int *offx, int *offy)
705 if (offx) *offx = (int)ctm->c;
706 if (offy) *offy = (int)ctm->f;
709 /*----------------------------------------------------------------------*/
710 /* Return position offset relative to top-level */
711 /*----------------------------------------------------------------------*/
713 void UTopOffset(int *offx, int *offy)
715 UGetCTMOffset(DCTM, offx, offy);
718 /*----------------------------------------------------------------------*/
719 /* Return postion relative to the top-level schematic (not the window) */
720 /*----------------------------------------------------------------------*/
722 void UTopDrawingOffset(int *offx, int *offy)
724 Matrix lctm, wctm;
725 UCopyCTM(DCTM, &lctm);
726 UResetCTM(&wctm);
727 UMakeWCTM(&wctm);
728 InvertCTM(&wctm);
729 UPreMultCTMbyMat(&wctm, &lctm);
730 UGetCTMOffset(&wctm, offx, offy);
733 /*----------------------------------------------------------------------*/
734 /* Get the cursor position */
735 /*----------------------------------------------------------------------*/
737 XPoint UGetCursor()
739 Window nullwin;
740 int nullint, xpos, ypos;
741 u_int nullui;
742 XPoint newpos;
744 if (areawin->area == NULL) {
745 newpos.x = newpos.y = 0;
746 return newpos;
749 #ifdef TCL_WRAPPER
750 /* Don't use areawin->window; if called from inside an object */
751 /* (e.g., "here" in a Tcl expression), areawin->window will be */
752 /* an off-screen pixmap, and cause a crash. */
753 #ifndef _MSC_VER
754 XQueryPointer(dpy, Tk_WindowId(areawin->area), &nullwin, &nullwin,
755 &nullint, &nullint, &xpos, &ypos, &nullui);
756 #else
757 XQueryPointer_TkW32(dpy, Tk_WindowId(areawin->area), &nullwin, &nullwin,
758 &nullint, &nullint, &xpos, &ypos, &nullui);
759 #endif
760 #else
761 XQueryPointer(dpy, areawin->window, &nullwin, &nullwin, &nullint,
762 &nullint, &xpos, &ypos, &nullui);
763 #endif
765 newpos.x = xpos;
766 newpos.y = ypos;
768 return newpos;
771 /*----------------------------------------------------------------------*/
772 /* Get the cursor position and translate to user coordinates */
773 /*----------------------------------------------------------------------*/
775 XPoint UGetCursorPos()
777 XPoint winpos, userpos;
779 if (areawin->area == NULL) {
780 winpos.x = winpos.y = 0;
782 else
783 winpos = UGetCursor();
785 window_to_user(winpos.x, winpos.y, &userpos);
787 return userpos;
790 /*----------------------------------------------------------------------*/
791 /* Translate a point to the nearest snap-to grid point */
792 /*----------------------------------------------------------------------*/
793 /* user coordinates to user coordinates version */
795 void u2u_snap(XPoint *uvalue)
797 float tmpx, tmpy;
798 float tmpix, tmpiy;
800 if (areawin->snapto) {
801 tmpx = (float)uvalue->x / xobjs.pagelist[areawin->page]->snapspace;
802 if (tmpx > 0)
803 tmpix = (float)((int)(tmpx + 0.5));
804 else
805 tmpix = (float)((int)(tmpx - 0.5));
807 tmpy = (float)uvalue->y / xobjs.pagelist[areawin->page]->snapspace;
808 if (tmpy > 0)
809 tmpiy = (float)((int)(tmpy + 0.5));
810 else
811 tmpiy = (float)((int)(tmpy - 0.5));
813 tmpix *= xobjs.pagelist[areawin->page]->snapspace;
814 tmpix += (tmpix > 0) ? 0.5 : -0.5;
815 tmpiy *= xobjs.pagelist[areawin->page]->snapspace;
816 tmpiy += (tmpiy > 0) ? 0.5 : -0.5;
818 uvalue->x = (int)tmpix;
819 uvalue->y = (int)tmpiy;
823 /*------------------------------------------------------------------------*/
824 /* window coordinates to user coordinates version */
825 /*------------------------------------------------------------------------*/
827 void snap(short valuex, short valuey, XPoint *returnpt)
829 window_to_user(valuex, valuey, returnpt);
830 u2u_snap(returnpt);
833 /*------------------------------------------------------------------------*/
834 /* Transform object coordinates through scale, translation, and rotation */
835 /* This routine attempts to match the PostScript definition of trans- */
836 /* formation matrices. */
837 /*------------------------------------------------------------------------*/
839 /*------------------------------------------------------------------------*/
840 /* Current transformation matrix manipulation routines */
841 /*------------------------------------------------------------------------*/
843 void UResetCTM(Matrix *ctm)
845 ctm->a = ctm->e = 1;
846 ctm->b = ctm->d = 0;
847 ctm->c = ctm->f = 0; /* 0.5 for nearest-int real->int conversion? */
849 #ifdef HAVE_CAIRO
850 if (ctm == DCTM && areawin->redraw_ongoing)
851 xc_cairo_set_matrix(ctm);
852 #endif /* HAVE_CAIRO */
855 /*------------------------------------------------------------------------*/
857 void InvertCTM(Matrix *ctm)
859 float det = ctm->a * ctm->e - ctm->b * ctm->d;
860 float tx = ctm->b * ctm->f - ctm->c * ctm->e;
861 float ty = ctm->d * ctm->c - ctm->a * ctm->f;
863 float tmpa = ctm->a;
865 ctm->b = -ctm->b / det;
866 ctm->d = -ctm->d / det;
868 ctm->a = ctm->e / det;
869 ctm->e = tmpa / det;
870 ctm->c = tx / det;
871 ctm->f = ty / det;
873 #ifdef HAVE_CAIRO
874 if (ctm == DCTM && areawin->redraw_ongoing)
875 xc_cairo_set_matrix(ctm);
876 #endif /* HAVE_CAIRO */
879 /*------------------------------------------------------------------------*/
881 void UCopyCTM(fctm, tctm)
882 Matrix *fctm, *tctm;
884 tctm->a = fctm->a;
885 tctm->b = fctm->b;
886 tctm->c = fctm->c;
887 tctm->d = fctm->d;
888 tctm->e = fctm->e;
889 tctm->f = fctm->f;
891 #ifdef HAVE_CAIRO
892 if (tctm == DCTM && areawin->redraw_ongoing)
893 xc_cairo_set_matrix(tctm);
894 #endif /* HAVE_CAIRO */
897 /*-------------------------------------------------------------------------*/
898 /* Multiply CTM by current screen position and scale to get transformation */
899 /* matrix from a user point to the X11 window */
900 /*-------------------------------------------------------------------------*/
902 void UMakeWCTM(Matrix *ctm)
904 ctm->a *= areawin->vscale;
905 ctm->b *= areawin->vscale;
906 ctm->c = (ctm->c - (float)areawin->pcorner.x) * areawin->vscale
907 + areawin->panx;
909 ctm->d *= -areawin->vscale;
910 ctm->e *= -areawin->vscale;
911 ctm->f = (float)areawin->height + ((float)areawin->pcorner.y - ctm->f) *
912 areawin->vscale + areawin->pany;
914 #ifdef HAVE_CAIRO
915 if (ctm == DCTM && areawin->redraw_ongoing)
916 xc_cairo_set_matrix(ctm);
917 #endif /* HAVE_CAIRO */
920 /*------------------------------------------------------------------------*/
922 void UMultCTM(Matrix *ctm, XPoint position, float scale, float rotate)
924 float tmpa, tmpb, tmpd, tmpe, yscale;
925 float mata, matb, matc;
926 double drot = (double)rotate * RADFAC;
928 yscale = abs(scale); /* -scale implies flip in x direction only */
930 tmpa = scale * cos(drot);
931 tmpb = yscale * sin(drot);
932 tmpd = -scale * sin(drot);
933 tmpe = yscale * cos(drot);
935 mata = ctm->a * tmpa + ctm->d * tmpb;
936 matb = ctm->b * tmpa + ctm->e * tmpb;
937 matc = ctm->c * tmpa + ctm->f * tmpb + position.x;
939 ctm->d = ctm->d * tmpe + ctm->a * tmpd;
940 ctm->e = ctm->e * tmpe + ctm->b * tmpd;
941 ctm->f = ctm->f * tmpe + ctm->c * tmpd + position.y;
943 ctm->a = mata;
944 ctm->b = matb;
945 ctm->c = matc;
947 #ifdef HAVE_CAIRO
948 if (ctm == DCTM && areawin->redraw_ongoing)
949 xc_cairo_set_matrix(ctm);
950 #endif /* HAVE_CAIRO */
953 /*----------------------------------------------------------------------*/
954 /* Slanting function x' = x + beta * y, y' = y */
955 /*----------------------------------------------------------------------*/
957 void USlantCTM(Matrix *ctm, float beta)
959 ctm->b += ctm->a * beta;
960 ctm->e += ctm->d * beta;
962 #ifdef HAVE_CAIRO
963 if (ctm == DCTM && areawin->redraw_ongoing)
964 xc_cairo_set_matrix(ctm);
965 #endif /* HAVE_CAIRO */
968 #define EPS 1e-9
969 /*----------------------------------------------------------------------*/
970 /* Transform text to make it right-side up within 90 degrees of page */
971 /* NOTE: This is not yet resolved, as xcircuit does not agree with */
972 /* PostScript in a few cases! */
973 /*----------------------------------------------------------------------*/
975 void UPreScaleCTM(Matrix *ctm)
977 /* negative X scale (-1, +1) */
978 if ((ctm->a < -EPS) || ((ctm->a < EPS) && (ctm->a > -EPS) &&
979 ((ctm->d * ctm->b) < 0))) {
980 ctm->a = -ctm->a;
981 ctm->d = -ctm->d;
984 /* negative Y scale (+1, -1) */
985 if (ctm->e > EPS) {
986 ctm->e = -ctm->e;
987 ctm->b = -ctm->b;
990 /* At 90, 270 degrees need special attention to avoid discrepencies */
991 /* with the PostScript output due to roundoff error. This code */
992 /* matches what PostScript produces. */
994 #ifdef HAVE_CAIRO
995 if (ctm == DCTM && areawin->redraw_ongoing)
996 xc_cairo_set_matrix(ctm);
997 #endif /* HAVE_CAIRO */
1000 /*----------------------------------------------------------------------*/
1001 /* Adjust anchoring and CTM as necessary for flip invariance */
1002 /*----------------------------------------------------------------------*/
1004 short flipadjust(short anchor)
1006 short tmpanchor = anchor & (~FLIPINV);
1008 if (anchor & FLIPINV) {
1009 if (((DCTM)->a < -EPS) || (((DCTM)->a < EPS) && ((DCTM)->a > -EPS) &&
1010 (((DCTM)->d * (DCTM)->b) < 0))) {
1011 if ((tmpanchor & (RIGHT | NOTLEFT)) != NOTLEFT)
1012 tmpanchor ^= (RIGHT | NOTLEFT);
1014 /* NOTE: Justification does not change under flip invariance. */
1016 if ((DCTM)->e > EPS) {
1017 if ((tmpanchor & (TOP | NOTBOTTOM)) != NOTBOTTOM)
1018 tmpanchor ^= (TOP | NOTBOTTOM);
1020 UPreScaleCTM(DCTM);
1022 return tmpanchor;
1025 /*------------------------------------------------------------------------*/
1027 void UPreMultCTM(Matrix *ctm, XPoint position, float scale, float rotate)
1029 float tmpa, tmpb, tmpd, tmpe, yscale;
1030 float mata, matd;
1031 double drot = (double)rotate * RADFAC;
1033 yscale = abs(scale); /* negative scale value implies flip in x only */
1035 tmpa = scale * cos(drot);
1036 tmpb = yscale * sin(drot);
1037 tmpd = -scale * sin(drot);
1038 tmpe = yscale * cos(drot);
1040 ctm->c += ctm->a * position.x + ctm->b * position.y;
1041 ctm->f += ctm->d * position.x + ctm->e * position.y;
1043 mata = ctm->a * tmpa + ctm->b * tmpd;
1044 ctm->b = ctm->a * tmpb + ctm->b * tmpe;
1046 matd = ctm->d * tmpa + ctm->e * tmpd;
1047 ctm->e = ctm->d * tmpb + ctm->e * tmpe;
1049 ctm->a = mata;
1050 ctm->d = matd;
1052 #ifdef HAVE_CAIRO
1053 if (ctm == DCTM && areawin->redraw_ongoing)
1054 xc_cairo_set_matrix(ctm);
1055 #endif /* HAVE_CAIRO */
1058 /*----------------------------------------------------------------------*/
1059 /* Direct Matrix-Matrix multiplication */
1060 /*----------------------------------------------------------------------*/
1062 void UPreMultCTMbyMat(Matrix *ctm, Matrix *pre)
1064 float mata, matd;
1066 mata = pre->a * ctm->a + pre->d * ctm->b;
1067 ctm->c += pre->c * ctm->a + pre->f * ctm->b;
1068 ctm->b = pre->b * ctm->a + pre->e * ctm->b;
1069 ctm->a = mata;
1071 matd = pre->a * ctm->d + pre->d * ctm->e;
1072 ctm->f += pre->c * ctm->d + pre->f * ctm->e;
1073 ctm->e = pre->b * ctm->d + pre->e * ctm->e;
1074 ctm->d = matd;
1076 #ifdef HAVE_CAIRO
1077 if (ctm == DCTM && areawin->redraw_ongoing)
1078 xc_cairo_set_matrix(ctm);
1079 #endif /* HAVE_CAIRO */
1082 /*------------------------------------------------------------------------*/
1084 void UTransformbyCTM(Matrix *ctm, XPoint *ipoints, XPoint *points, short number)
1086 pointlist current, ptptr = points;
1087 float fx, fy;
1088 /* short tmpx; (jdk) */
1090 for (current = ipoints; current < ipoints + number; current++, ptptr++) {
1091 fx = ctm->a * (float)current->x + ctm->b * (float)current->y + ctm->c;
1092 fy = ctm->d * (float)current->x + ctm->e * (float)current->y + ctm->f;
1094 ptptr->x = (fx >= 0) ? (short)(fx + 0.5) : (short)(fx - 0.5);
1095 ptptr->y = (fy >= 0) ? (short)(fy + 0.5) : (short)(fy - 0.5);
1099 /*------------------------------------------------------------------------*/
1100 /* (same as above routine but using type (float) for point values; this */
1101 /* is for calculation of Bezier curve internal points. */
1102 /*------------------------------------------------------------------------*/
1104 void UfTransformbyCTM(Matrix *ctm, XfPoint *fpoints, XPoint *points, short number)
1106 fpointlist current;
1107 pointlist new = points;
1108 float fx, fy;
1110 for (current = fpoints; current < fpoints + number; current++, new++) {
1111 fx = ctm->a * current->x + ctm->b * current->y + ctm->c;
1112 fy = ctm->d * current->x + ctm->e * current->y + ctm->f;
1113 new->x = (fx >= 0) ? (short)(fx + 0.5) : (short)(fx - 0.5);
1114 new->y = (fy >= 0) ? (short)(fy + 0.5) : (short)(fy - 0.5);
1118 /*------------------------------------------------------------------------*/
1120 void UPopCTM()
1122 Matrixptr lastmatrix;
1124 if (areawin->MatStack == NULL) {
1125 Wprintf("Matrix stack pop error");
1126 return;
1128 lastmatrix = areawin->MatStack->nextmatrix;
1129 free(areawin->MatStack);
1130 areawin->MatStack = lastmatrix;
1132 #ifdef HAVE_CAIRO
1133 if (areawin->area) {
1134 xc_cairo_set_matrix(lastmatrix);
1136 #endif /* HAVE_CAIRO */
1139 /*------------------------------------------------------------------------*/
1141 void UPushCTM()
1143 Matrixptr nmatrix;
1145 nmatrix = (Matrixptr)malloc(sizeof(Matrix));
1146 if (areawin->MatStack == NULL)
1147 UResetCTM(nmatrix);
1148 else
1149 UCopyCTM(areawin->MatStack, nmatrix);
1150 nmatrix->nextmatrix = areawin->MatStack;
1151 areawin->MatStack = nmatrix;
1154 /*------------------------------------------------------------------------*/
1156 void UTransformPoints(XPoint *points, XPoint *newpoints, short number,
1157 XPoint atpt, float scale, float rotate)
1159 Matrix LCTM;
1161 UResetCTM(&LCTM);
1162 UMultCTM(&LCTM, atpt, scale, rotate);
1163 UTransformbyCTM(&LCTM, points, newpoints, number);
1166 /*----------------------------------------------------*/
1167 /* Transform points inward to next hierarchical level */
1168 /*----------------------------------------------------*/
1170 void InvTransformPoints(XPoint *points, XPoint *newpoints, short number,
1171 XPoint atpt, float scale, float rotate)
1173 Matrix LCTM;
1175 UResetCTM(&LCTM);
1176 UPreMultCTM(&LCTM, atpt, scale, rotate);
1177 InvertCTM(&LCTM);
1178 UTransformbyCTM(&LCTM, points, newpoints, number);
1181 /*----------------------------------------------------------------------*/
1182 /* Adjust wire coords to force a wire to a horizontal or vertical */
1183 /* position. */
1184 /* "pospt" is the target position for the point of interest. */
1185 /* "cycle" is the point number in the polygon of the point of interest. */
1186 /* cycle == -1 is equivalent to the last point of the polygon. */
1187 /* If "strict" is TRUE then single-segment wires are forced manhattan */
1188 /* even if that means that the endpoint drifts from the target point. */
1189 /* If "strict" is FALSE then single-segment wires will become non- */
1190 /* manhattan so that the target point is reached. */
1191 /* NOTE: It might be preferable to add a segment to maintain a */
1192 /* manhattan layout, except that we want to avoid merging nets */
1193 /* together. . . */
1194 /*----------------------------------------------------------------------*/
1196 void manhattanize(XPoint *pospt, polyptr newpoly, short cycle, Boolean strict)
1198 XPoint *curpt, *bpt, *bbpt, *fpt, *ffpt;
1199 int deltax, deltay;
1201 if (newpoly->number == 1) return; /* sanity check */
1203 if (cycle == -1 || cycle == newpoly->number - 1) {
1204 curpt = newpoly->points + newpoly->number - 1;
1205 bpt = newpoly->points + newpoly->number - 2;
1206 fpt = NULL;
1207 ffpt = NULL;
1208 if (newpoly->number > 2)
1209 bbpt = newpoly->points + newpoly->number - 3;
1210 else
1211 bbpt = NULL;
1213 else if (cycle == 0) {
1214 curpt = newpoly->points;
1215 fpt = newpoly->points + 1;
1216 bpt = NULL;
1217 bbpt = NULL;
1218 if (newpoly->number > 2)
1219 ffpt = newpoly->points + 2;
1220 else
1221 ffpt = NULL;
1223 else {
1224 curpt = newpoly->points + cycle;
1225 fpt = newpoly->points + cycle + 1;
1226 bpt = newpoly->points + cycle - 1;
1227 if (cycle > 1)
1228 bbpt = newpoly->points + cycle - 2;
1229 else
1230 bbpt = NULL;
1232 if (cycle < newpoly->number - 2)
1233 ffpt = newpoly->points + cycle + 2;
1234 else
1235 ffpt = NULL;
1238 /* enforce constraints on point behind cycle position */
1240 if (bpt != NULL) {
1241 if (bbpt != NULL) {
1242 if (bpt->x == bbpt->x) bpt->y = pospt->y;
1243 if (bpt->y == bbpt->y) bpt->x = pospt->x;
1245 else if (strict) {
1246 deltax = abs(bpt->x - pospt->x);
1247 deltay = abs(bpt->y - pospt->y);
1249 /* Only one segment---just make sure it's horizontal or vertical */
1250 if (deltay > deltax) pospt->x = bpt->x;
1251 else pospt->y = bpt->y;
1255 /* enforce constraints on point forward of cycle position */
1257 if (fpt != NULL) {
1258 if (ffpt != NULL) {
1259 if (fpt->x == ffpt->x) fpt->y = pospt->y;
1260 if (fpt->y == ffpt->y) fpt->x = pospt->x;
1262 else if (strict) {
1263 deltax = abs(fpt->x - pospt->x);
1264 deltay = abs(fpt->y - pospt->y);
1266 /* Only one segment---just make sure it's horizontal or vertical */
1267 if (deltay > deltax) pospt->x = fpt->x;
1268 else pospt->y = fpt->y;
1273 /*----------------------------------------------------------------------*/
1274 /* Bounding box calculation routines */
1275 /*----------------------------------------------------------------------*/
1277 void bboxcalc(short testval, short *lowerval, short *upperval)
1279 if (testval < *lowerval) *lowerval = testval;
1280 if (testval > *upperval) *upperval = testval;
1283 /*----------------------------------------------------------------------*/
1284 /* Bounding box calculation for elements which can be part of a path */
1285 /*----------------------------------------------------------------------*/
1287 void calcextents(genericptr *bboxgen, short *llx, short *lly,
1288 short *urx, short *ury)
1290 switch (ELEMENTTYPE(*bboxgen)) {
1291 case(POLYGON): {
1292 pointlist bboxpts;
1293 for (bboxpts = TOPOLY(bboxgen)->points; bboxpts < TOPOLY(bboxgen)->points
1294 + TOPOLY(bboxgen)->number; bboxpts++) {
1295 bboxcalc(bboxpts->x, llx, urx);
1296 bboxcalc(bboxpts->y, lly, ury);
1298 } break;
1300 case(SPLINE): {
1301 fpointlist bboxpts;
1302 bboxcalc(TOSPLINE(bboxgen)->ctrl[0].x, llx, urx);
1303 bboxcalc(TOSPLINE(bboxgen)->ctrl[0].y, lly, ury);
1304 bboxcalc(TOSPLINE(bboxgen)->ctrl[3].x, llx, urx);
1305 bboxcalc(TOSPLINE(bboxgen)->ctrl[3].y, lly, ury);
1306 for (bboxpts = TOSPLINE(bboxgen)->points; bboxpts <
1307 TOSPLINE(bboxgen)->points + INTSEGS; bboxpts++) {
1308 bboxcalc((short)(bboxpts->x), llx, urx);
1309 bboxcalc((short)(bboxpts->y), lly, ury);
1311 } break;
1313 case (ARC): {
1314 fpointlist bboxpts;
1315 for (bboxpts = TOARC(bboxgen)->points; bboxpts < TOARC(bboxgen)->points +
1316 TOARC(bboxgen)->number; bboxpts++) {
1317 bboxcalc((short)(bboxpts->x), llx, urx);
1318 bboxcalc((short)(bboxpts->y), lly, ury);
1320 } break;
1324 /*----------------------------------------------------------------------*/
1325 /* Calculate the bounding box of an object instance */
1326 /*----------------------------------------------------------------------*/
1328 void objinstbbox(objinstptr obbox, XPoint *npoints, int extend)
1330 XPoint points[4];
1332 points[0].x = points[1].x = obbox->bbox.lowerleft.x - extend;
1333 points[1].y = points[2].y = obbox->bbox.lowerleft.y + obbox->bbox.height
1334 + extend;
1335 points[2].x = points[3].x = obbox->bbox.lowerleft.x + obbox->bbox.width
1336 + extend;
1337 points[0].y = points[3].y = obbox->bbox.lowerleft.y - extend;
1339 UTransformPoints(points, npoints, 4, obbox->position,
1340 obbox->scale, obbox->rotation);
1343 /*----------------------------------------------------------------------*/
1344 /* Calculate the bounding box of a label */
1345 /*----------------------------------------------------------------------*/
1347 void labelbbox(labelptr labox, XPoint *npoints, objinstptr callinst)
1349 XPoint points[4];
1350 TextExtents tmpext;
1351 short j;
1353 tmpext = ULength(labox, callinst, NULL);
1354 points[0].x = points[1].x = (labox->anchor & NOTLEFT ?
1355 (labox->anchor & RIGHT ? -tmpext.maxwidth :
1356 -tmpext.maxwidth / 2) : 0);
1357 points[2].x = points[3].x = points[0].x + tmpext.maxwidth;
1358 points[0].y = points[3].y = (labox->anchor & NOTBOTTOM ?
1359 (labox->anchor & TOP ? -tmpext.ascent :
1360 -(tmpext.ascent + tmpext.base) / 2) : -tmpext.base)
1361 + tmpext.descent;
1362 points[1].y = points[2].y = points[0].y + tmpext.ascent - tmpext.descent;
1364 /* separate bounding box for pinlabels and infolabels */
1366 if (labox->pin)
1367 for (j = 0; j < 4; j++)
1368 pinadjust(labox->anchor, &points[j].x, &points[j].y, 1);
1370 UTransformPoints(points, npoints, 4, labox->position,
1371 labox->scale, labox->rotation);
1374 /*----------------------------------------------------------------------*/
1375 /* Calculate the bounding box of a graphic image */
1376 /*----------------------------------------------------------------------*/
1378 void graphicbbox(graphicptr gp, XPoint *npoints)
1380 XPoint points[4];
1381 int hw = xcImageGetWidth(gp->source) >> 1;
1382 int hh = xcImageGetHeight(gp->source) >> 1;
1384 points[1].x = points[2].x = hw;
1385 points[0].x = points[3].x = -hw;
1387 points[0].y = points[1].y = -hh;
1388 points[2].y = points[3].y = hh;
1390 UTransformPoints(points, npoints, 4, gp->position,
1391 gp->scale, gp->rotation);
1394 /*--------------------------------------------------------------*/
1395 /* Wrapper for single call to calcbboxsingle() in the netlister */
1396 /*--------------------------------------------------------------*/
1398 void calcinstbbox(genericptr *bboxgen, short *llx, short *lly, short *urx,
1399 short *ury)
1401 *llx = *lly = 32767;
1402 *urx = *ury = -32768;
1404 calcbboxsingle(bboxgen, areawin->topinstance, llx, lly, urx, ury);
1407 /*----------------------------------------------------------------------*/
1408 /* Bounding box calculation for a single generic element */
1409 /*----------------------------------------------------------------------*/
1411 void calcbboxsingle(genericptr *bboxgen, objinstptr thisinst,
1412 short *llx, short *lly, short *urx, short *ury)
1414 XPoint npoints[4];
1415 short j;
1417 /* For each screen element, compute the extents and revise bounding */
1418 /* box points, if necessary. */
1420 switch(ELEMENTTYPE(*bboxgen)) {
1422 case(OBJINST):
1423 objinstbbox(TOOBJINST(bboxgen), npoints, 0);
1425 for (j = 0; j < 4; j++) {
1426 bboxcalc(npoints[j].x, llx, urx);
1427 bboxcalc(npoints[j].y, lly, ury);
1429 break;
1431 case(LABEL):
1432 /* because a pin is offset from its position point, include */
1433 /* that point in the bounding box. */
1435 if (TOLABEL(bboxgen)->pin) {
1436 bboxcalc(TOLABEL(bboxgen)->position.x, llx, urx);
1437 bboxcalc(TOLABEL(bboxgen)->position.y, lly, ury);
1439 labelbbox(TOLABEL(bboxgen), npoints, thisinst);
1441 for (j = 0; j < 4; j++) {
1442 bboxcalc(npoints[j].x, llx, urx);
1443 bboxcalc(npoints[j].y, lly, ury);
1445 break;
1447 case(GRAPHIC):
1448 graphicbbox(TOGRAPHIC(bboxgen), npoints);
1449 for (j = 0; j < 4; j++) {
1450 bboxcalc(npoints[j].x, llx, urx);
1451 bboxcalc(npoints[j].y, lly, ury);
1453 break;
1455 case(PATH): {
1456 genericptr *pathc;
1457 for (pathc = TOPATH(bboxgen)->plist; pathc < TOPATH(bboxgen)->plist
1458 + TOPATH(bboxgen)->parts; pathc++)
1459 calcextents(pathc, llx, lly, urx, ury);
1460 } break;
1462 default:
1463 calcextents(bboxgen, llx, lly, urx, ury);
1467 /*------------------------------------------------------*/
1468 /* Find if an object is in the specified library */
1469 /*------------------------------------------------------*/
1471 Boolean object_in_library(short libnum, objectptr thisobject)
1473 short i;
1475 for (i = 0; i < xobjs.userlibs[libnum].number; i++) {
1476 if (*(xobjs.userlibs[libnum].library + i) == thisobject)
1477 return True;
1479 return False;
1482 /*-----------------------------------------------------------*/
1483 /* Find if an object is in the hierarchy of the given object */
1484 /* Returns the number (position in plist) or -1 if not found */
1485 /*-----------------------------------------------------------*/
1487 short find_object(objectptr pageobj, objectptr thisobject)
1489 short i, j;
1490 genericptr *pelem;
1492 for (i = 0; i < pageobj->parts; i++) {
1493 pelem = pageobj->plist + i;
1494 if (IS_OBJINST(*pelem)) {
1495 if ((TOOBJINST(pelem))->thisobject == thisobject)
1496 return i;
1497 else if ((j = find_object((TOOBJINST(pelem))->thisobject, thisobject)) >= 0)
1498 return i; /* was j---is this the right fix? */
1501 return -1;
1504 /*------------------------------------------------------*/
1505 /* Find all pages and libraries containing this object */
1506 /* and update accordingly. If this object is a page, */
1507 /* just update the page directory. */
1508 /*------------------------------------------------------*/
1510 void updatepagebounds(objectptr thisobject)
1512 short i, j;
1513 objectptr pageobj;
1515 if ((i = is_page(thisobject)) >= 0) {
1516 if (xobjs.pagelist[i]->background.name != (char *)NULL)
1517 backgroundbbox(i);
1518 updatepagelib(PAGELIB, i);
1520 else {
1521 for (i = 0; i < xobjs.pages; i++) {
1522 if (xobjs.pagelist[i]->pageinst != NULL) {
1523 pageobj = xobjs.pagelist[i]->pageinst->thisobject;
1524 if ((j = find_object(pageobj, thisobject)) >= 0) {
1525 calcbboxvalues(xobjs.pagelist[i]->pageinst,
1526 (genericptr *)(pageobj->plist + j));
1527 updatepagelib(PAGELIB, i);
1531 for (i = 0; i < xobjs.numlibs; i++)
1532 if (object_in_library(i, thisobject))
1533 composelib(i + LIBRARY);
1537 /*--------------------------------------------------------------*/
1538 /* Free memory for the schematic bounding box */
1539 /*--------------------------------------------------------------*/
1541 void invalidateschembbox(objinstptr thisinst)
1543 if (thisinst->schembbox != NULL) {
1544 free(thisinst->schembbox);
1545 thisinst->schembbox = NULL;
1549 /*--------------------------------------------------------------*/
1550 /* Calculate the bounding box for an object instance. Use the */
1551 /* existing bbox and finish calculation on all the elements */
1552 /* which have parameters not taking default values. */
1553 /* This finishes the calculation partially done by */
1554 /* calcbboxvalues(). */
1555 /*--------------------------------------------------------------*/
1557 void calcbboxinst(objinstptr thisinst)
1559 objectptr thisobj;
1560 genericptr *gelem;
1561 short llx, lly, urx, ury;
1563 short pllx, plly, purx, pury;
1564 Boolean hasschembbox = FALSE;
1565 Boolean didparamsubs = FALSE;
1567 if (thisinst == NULL) return;
1569 thisobj = thisinst->thisobject;
1571 llx = thisobj->bbox.lowerleft.x;
1572 lly = thisobj->bbox.lowerleft.y;
1573 urx = llx + thisobj->bbox.width;
1574 ury = lly + thisobj->bbox.height;
1576 pllx = plly = 32767;
1577 purx = pury = -32768;
1579 for (gelem = thisobj->plist; gelem < thisobj->plist + thisobj->parts;
1580 gelem++) {
1581 /* pins which do not appear outside of the object */
1582 /* contribute to the objects "schembbox". */
1584 if (IS_LABEL(*gelem)) {
1585 labelptr btext = TOLABEL(gelem);
1586 if (btext->pin && !(btext->anchor & PINVISIBLE)) {
1587 hasschembbox = TRUE;
1588 calcbboxsingle(gelem, thisinst, &pllx, &plly, &purx, &pury);
1589 continue;
1593 if (has_param(*gelem)) {
1594 if (didparamsubs == FALSE) {
1595 psubstitute(thisinst);
1596 didparamsubs = TRUE;
1598 calcbboxsingle(gelem, thisinst, &llx, &lly, &urx, &ury);
1601 /* If we have a clipmask, the clipmask is used to calculate the */
1602 /* bounding box, not the element it is masking. */
1604 switch(ELEMENTTYPE(*gelem)) {
1605 case POLYGON: case SPLINE: case ARC: case PATH:
1606 if (TOPOLY(gelem)->style & CLIPMASK) gelem++;
1607 break;
1611 thisinst->bbox.lowerleft.x = llx;
1612 thisinst->bbox.lowerleft.y = lly;
1613 thisinst->bbox.width = urx - llx;
1614 thisinst->bbox.height = ury - lly;
1616 if (hasschembbox) {
1617 if (thisinst->schembbox == NULL)
1618 thisinst->schembbox = (BBox *)malloc(sizeof(BBox));
1620 thisinst->schembbox->lowerleft.x = pllx;
1621 thisinst->schembbox->lowerleft.y = plly;
1622 thisinst->schembbox->width = purx - pllx;
1623 thisinst->schembbox->height = pury - plly;
1625 else
1626 invalidateschembbox(thisinst);
1629 /*--------------------------------------------------------------*/
1630 /* Update things based on a changed instance bounding box. */
1631 /* If the parameter was a single-instance */
1632 /* substitution, only the page should be updated. If the */
1633 /* parameter was a default value, the library should be updated */
1634 /* and any pages containing the object where the parameter */
1635 /* takes the default value. */
1636 /*--------------------------------------------------------------*/
1638 void updateinstparam(objectptr bobj)
1640 short i, j;
1641 objectptr pageobj;
1643 /* change bounds on pagelib and all pages */
1644 /* containing this *object* if and only if the object */
1645 /* instance takes the default value. Also update the */
1646 /* library page. */
1648 for (i = 0; i < xobjs.pages; i++)
1649 if (xobjs.pagelist[i]->pageinst != NULL) {
1650 pageobj = xobjs.pagelist[i]->pageinst->thisobject;
1651 if ((j = find_object(pageobj, topobject)) >= 0) {
1653 /* Really, we'd like to recalculate the bounding box only if the */
1654 /* parameter value is the default value which was just changed. */
1655 /* However, then any non-default values may contain the wrong */
1656 /* substitutions. */
1658 objinstptr cinst = TOOBJINST(pageobj->plist + j);
1659 if (cinst->thisobject->params == NULL) {
1660 calcbboxvalues(xobjs.pagelist[i]->pageinst, pageobj->plist + j);
1661 updatepagelib(PAGELIB, i);
1666 for (i = 0; i < xobjs.numlibs; i++)
1667 if (object_in_library(i, topobject))
1668 composelib(i + LIBRARY);
1671 /*--------------------------------------------------------------*/
1672 /* Calculate bbox on all elements of the given object */
1673 /*--------------------------------------------------------------*/
1675 void calcbbox(objinstptr binst)
1677 calcbboxvalues(binst, (genericptr *)NULL);
1678 if (binst == areawin->topinstance) {
1679 updatepagebounds(topobject);
1683 /*--------------------------------------------------------------*/
1684 /* Calculate bbox on the given element of the specified object. */
1685 /* This is a wrapper for calcbboxvalues() assuming that we're */
1686 /* on the top-level, and that page bounds need to be updated. */
1687 /*--------------------------------------------------------------*/
1689 void singlebbox(genericptr *gelem)
1691 calcbboxvalues(areawin->topinstance, (genericptr *)gelem);
1692 updatepagebounds(topobject);
1695 /*----------------------------------------------------------------------*/
1696 /* Extend bounding box based on selected elements only */
1697 /*----------------------------------------------------------------------*/
1699 void calcbboxselect()
1701 short *bsel;
1702 for (bsel = areawin->selectlist; bsel < areawin->selectlist +
1703 areawin->selects; bsel++)
1704 calcbboxvalues(areawin->topinstance, topobject->plist + *bsel);
1706 updatepagebounds(topobject);
1709 /*--------------------------------------------------------------*/
1710 /* Update Bounding box for an object. */
1711 /* If newelement == NULL, calculate bounding box from scratch. */
1712 /* Otherwise, expand bounding box to enclose newelement. */
1713 /*--------------------------------------------------------------*/
1715 void calcbboxvalues(objinstptr thisinst, genericptr *newelement)
1717 genericptr *bboxgen;
1718 short llx, lly, urx, ury;
1719 objectptr thisobj = thisinst->thisobject;
1721 /* no action if there are no elements */
1722 if (thisobj->parts == 0) return;
1724 /* If this object has parameters, then we will do a separate */
1725 /* bounding box calculation on parameterized parts. This */
1726 /* calculation ignores them, and the result is a base that the */
1727 /* instance bounding-box computation can use as a starting point. */
1729 /* set starting bounds as maximum bounds of screen */
1730 llx = lly = 32767;
1731 urx = ury = -32768;
1733 for (bboxgen = thisobj->plist; bboxgen < thisobj->plist +
1734 thisobj->parts; bboxgen++) {
1736 /* override the "for" loop if we're doing a single element */
1737 if (newelement != NULL) bboxgen = newelement;
1739 if ((thisobj->params == NULL) || (!has_param(*bboxgen))) {
1740 /* pins which do not appear outside of the object */
1741 /* are ignored now---will be computed per instance. */
1743 if (IS_LABEL(*bboxgen)) {
1744 labelptr btext = TOLABEL(bboxgen);
1745 if (btext->pin && !(btext->anchor & PINVISIBLE)) {
1746 goto nextgen;
1749 calcbboxsingle(bboxgen, thisinst, &llx, &lly, &urx, &ury);
1751 if (newelement == NULL)
1752 switch(ELEMENTTYPE(*bboxgen)) {
1753 case POLYGON: case SPLINE: case ARC: case PATH:
1754 if (TOPOLY(bboxgen)->style & CLIPMASK)
1755 bboxgen++;
1756 break;
1759 nextgen:
1760 if (newelement != NULL) break;
1763 /* if this is a single-element calculation and its bounding box */
1764 /* turned out to be smaller than the object's, then we need to */
1765 /* recompute the entire object's bounding box in case it got */
1766 /* smaller. This is not recursive, in spite of looks. */
1768 if (newelement != NULL) {
1769 if (llx > thisobj->bbox.lowerleft.x &&
1770 lly > thisobj->bbox.lowerleft.y &&
1771 urx < (thisobj->bbox.lowerleft.x + thisobj->bbox.width) &&
1772 ury < (thisobj->bbox.lowerleft.y + thisobj->bbox.height)) {
1773 calcbboxvalues(thisinst, NULL);
1774 return;
1776 else {
1777 bboxcalc(thisobj->bbox.lowerleft.x, &llx, &urx);
1778 bboxcalc(thisobj->bbox.lowerleft.y, &lly, &ury);
1779 bboxcalc(thisobj->bbox.lowerleft.x + thisobj->bbox.width, &llx, &urx);
1780 bboxcalc(thisobj->bbox.lowerleft.y + thisobj->bbox.height, &lly, &ury);
1784 /* Set the new bounding box. In pathological cases, such as a page */
1785 /* with only pin labels, the bounds may not have been changed from */
1786 /* their initial values. If so, then don't touch the bounding box. */
1788 if ((llx <= urx) && (lly <= ury)) {
1789 thisobj->bbox.lowerleft.x = llx;
1790 thisobj->bbox.lowerleft.y = lly;
1791 thisobj->bbox.width = urx - llx;
1792 thisobj->bbox.height = ury - lly;
1795 /* calculate instance-specific values */
1796 calcbboxinst(thisinst);
1799 /*------------------------------------------------------*/
1800 /* Center an object in the viewing window */
1801 /*------------------------------------------------------*/
1803 void centerview(objinstptr tinst)
1805 XPoint origin, corner;
1806 Dimension width, height;
1807 float fitwidth, fitheight;
1808 objectptr tobj = tinst->thisobject;
1810 origin = tinst->bbox.lowerleft;
1811 corner.x = origin.x + tinst->bbox.width;
1812 corner.y = origin.y + tinst->bbox.height;
1814 extendschembbox(tinst, &origin, &corner);
1816 width = corner.x - origin.x;
1817 height = corner.y - origin.y;
1819 fitwidth = (float)areawin->width / ((float)width + 2 * DEFAULTGRIDSPACE);
1820 fitheight = (float)areawin->height / ((float)height + 2 * DEFAULTGRIDSPACE);
1822 tobj->viewscale = (fitwidth < fitheight) ?
1823 min(MINAUTOSCALE, fitwidth) : min(MINAUTOSCALE, fitheight);
1825 tobj->pcorner.x = origin.x - (areawin->width
1826 / tobj->viewscale - width) / 2;
1827 tobj->pcorner.y = origin.y - (areawin->height
1828 / tobj->viewscale - height) / 2;
1830 /* Copy new position values to the current window */
1832 if ((areawin->topinstance != NULL) && (tobj == topobject)) {
1833 areawin->pcorner = tobj->pcorner;
1834 areawin->vscale = tobj->viewscale;
1838 /*-----------------------------------------------------------*/
1839 /* Refresh the window and scrollbars and write the page name */
1840 /*-----------------------------------------------------------*/
1842 void refresh(xcWidget bw, caddr_t clientdata, caddr_t calldata)
1844 areawin->redraw_needed = True;
1845 drawarea(NULL, NULL, NULL);
1846 if (areawin->scrollbarh)
1847 drawhbar(areawin->scrollbarh, NULL, NULL);
1848 if (areawin->scrollbarv)
1849 drawvbar(areawin->scrollbarv, NULL, NULL);
1850 printname(topobject);
1853 /*------------------------------------------------------*/
1854 /* Center the current page in the viewing window */
1855 /*------------------------------------------------------*/
1857 void zoomview(xcWidget w, caddr_t clientdata, caddr_t calldata)
1859 if (eventmode == NORMAL_MODE || eventmode == COPY_MODE ||
1860 eventmode == MOVE_MODE || eventmode == CATALOG_MODE ||
1861 eventmode == FONTCAT_MODE || eventmode == EFONTCAT_MODE ||
1862 eventmode == CATMOVE_MODE) {
1864 if (areawin->topinstance)
1865 centerview(areawin->topinstance);
1866 areawin->lastbackground = NULL;
1867 renderbackground();
1868 refresh(NULL, NULL, NULL);
1872 /*---------------------------------------------------------*/
1873 /* Basic X Graphics Routines in the User coordinate system */
1874 /*---------------------------------------------------------*/
1876 #ifndef HAVE_CAIRO
1877 void UDrawSimpleLine(XPoint *pt1, XPoint *pt2)
1879 XPoint newpt1, newpt2;
1881 if (!areawin->redraw_ongoing) {
1882 areawin->redraw_needed = True;
1883 return;
1886 UTransformbyCTM(DCTM, pt1, &newpt1, 1);
1887 UTransformbyCTM(DCTM, pt2, &newpt2, 1);
1889 DrawLine(dpy, areawin->window, areawin->gc,
1890 newpt1.x, newpt1.y, newpt2.x, newpt2.y);
1892 #endif /* !HAVE_CAIRO */
1894 /*-------------------------------------------------------------------------*/
1896 #ifndef HAVE_CAIRO
1897 void UDrawLine(XPoint *pt1, XPoint *pt2)
1899 float tmpwidth = UTopTransScale(xobjs.pagelist[areawin->page]->wirewidth);
1901 if (!areawin->redraw_ongoing) {
1902 areawin->redraw_needed = True;
1903 return;
1906 SetLineAttributes(dpy, areawin->gc, tmpwidth, LineSolid, CapRound, JoinBevel);
1907 UDrawSimpleLine(pt1, pt2);
1909 #endif /* !HAVE_CAIRO */
1911 /*----------------------------------------------------------------------*/
1912 /* Add circle at given point to indicate that the point is a parameter. */
1913 /* The circle is divided into quarters. For parameterized y-coordinate */
1914 /* the top and bottom quarters are drawn. For parameterized x- */
1915 /* coordinate, the left and right quarters are drawn. A full circle */
1916 /* indicates either both x- and y-coordinates are parameterized, or */
1917 /* else any other kind of parameterization (presently, not used). */
1918 /* */
1919 /* (note that the two angles in XDrawArc() are 1) the start angle, */
1920 /* measured in absolute 64th degrees from 0 (3 o'clock), and 2) the */
1921 /* path length, in relative 64th degrees (positive = counterclockwise, */
1922 /* negative = clockwise)). */
1923 /*----------------------------------------------------------------------*/
1925 #ifndef HAVE_CAIRO
1926 void UDrawCircle(XPoint *upt, u_char which)
1928 XPoint wpt;
1930 if (!areawin->redraw_ongoing) {
1931 areawin->redraw_needed = True;
1932 return;
1935 user_to_window(*upt, &wpt);
1936 SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapButt, JoinMiter);
1938 switch(which) {
1939 case P_POSITION_X:
1940 XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1941 wpt.y - 4, 8, 8, -(45 * 64), (90 * 64));
1942 XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1943 wpt.y - 4, 8, 8, (135 * 64), (90 * 64));
1944 break;
1945 case P_POSITION_Y:
1946 XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1947 wpt.y - 4, 8, 8, (45 * 64), (90 * 64));
1948 XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1949 wpt.y - 4, 8, 8, (225 * 64), (90 * 64));
1950 break;
1951 default:
1952 XDrawArc(dpy, areawin->window, areawin->gc, wpt.x - 4,
1953 wpt.y - 4, 8, 8, 0, (360 * 64));
1954 break;
1957 #endif /* !HAVE_CAIRO */
1959 /*----------------------------------------------------------------------*/
1960 /* Add "X" at string origin */
1961 /*----------------------------------------------------------------------*/
1963 #ifndef HAVE_CAIRO
1964 void UDrawXAt(XPoint *wpt)
1966 if (!areawin->redraw_ongoing) {
1967 areawin->redraw_needed = True;
1968 return;
1971 SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapButt, JoinMiter);
1972 DrawLine(dpy, areawin->window, areawin->gc, wpt->x - 3,
1973 wpt->y - 3, wpt->x + 3, wpt->y + 3);
1974 DrawLine(dpy, areawin->window, areawin->gc, wpt->x + 3,
1975 wpt->y - 3, wpt->x - 3, wpt->y + 3);
1977 #endif /* !HAVE_CAIRO */
1979 /*----------------------------------------------------------------------*/
1980 /* Draw "X" on current level */
1981 /*----------------------------------------------------------------------*/
1983 void UDrawX(labelptr curlabel)
1985 XPoint wpt;
1987 user_to_window(curlabel->position, &wpt);
1988 UDrawXAt(&wpt);
1991 /*----------------------------------------------------------------------*/
1992 /* Draw "X" on top level (only for LOCAL and GLOBAL pin labels) */
1993 /*----------------------------------------------------------------------*/
1995 void UDrawXDown(labelptr curlabel)
1997 XPoint wpt;
1999 UTransformbyCTM(DCTM, &curlabel->position, &wpt, 1);
2000 UDrawXAt(&wpt);
2003 /*----------------------------------------------------------------------*/
2004 /* Find the "real" width, height, and origin of an object including pin */
2005 /* labels and so forth that only show up on a schematic when it is the */
2006 /* top-level object. */
2007 /*----------------------------------------------------------------------*/
2009 int toplevelwidth(objinstptr bbinst, short *rllx)
2011 short llx, urx;
2012 short origin, corner;
2014 if (bbinst->schembbox == NULL) {
2015 if (rllx) *rllx = bbinst->bbox.lowerleft.x;
2016 return bbinst->bbox.width;
2019 origin = bbinst->bbox.lowerleft.x;
2020 corner = origin + bbinst->bbox.width;
2022 llx = bbinst->schembbox->lowerleft.x;
2023 urx = llx + bbinst->schembbox->width;
2025 bboxcalc(llx, &origin, &corner);
2026 bboxcalc(urx, &origin, &corner);
2028 if (rllx) *rllx = origin;
2029 return(corner - origin);
2032 /*----------------------------------------------------------------------*/
2034 int toplevelheight(objinstptr bbinst, short *rlly)
2036 short lly, ury;
2037 short origin, corner;
2039 if (bbinst->schembbox == NULL) {
2040 if (rlly) *rlly = bbinst->bbox.lowerleft.y;
2041 return bbinst->bbox.height;
2044 origin = bbinst->bbox.lowerleft.y;
2045 corner = origin + bbinst->bbox.height;
2047 lly = bbinst->schembbox->lowerleft.y;
2048 ury = lly + bbinst->schembbox->height;
2050 bboxcalc(lly, &origin, &corner);
2051 bboxcalc(ury, &origin, &corner);
2053 if (rlly) *rlly = origin;
2054 return(corner - origin);
2057 /*----------------------------------------------------------------------*/
2058 /* Add dimensions of schematic pins to an object's bounding box */
2059 /*----------------------------------------------------------------------*/
2061 void extendschembbox(objinstptr bbinst, XPoint *origin, XPoint *corner)
2063 short llx, lly, urx, ury;
2065 if ((bbinst == NULL) || (bbinst->schembbox == NULL)) return;
2067 llx = bbinst->schembbox->lowerleft.x;
2068 lly = bbinst->schembbox->lowerleft.y;
2069 urx = llx + bbinst->schembbox->width;
2070 ury = lly + bbinst->schembbox->height;
2072 bboxcalc(llx, &(origin->x), &(corner->x));
2073 bboxcalc(lly, &(origin->y), &(corner->y));
2074 bboxcalc(urx, &(origin->x), &(corner->x));
2075 bboxcalc(ury, &(origin->y), &(corner->y));
2078 /*----------------------------------------------------------------------*/
2079 /* Adjust a pinlabel position to account for pad spacing */
2080 /*----------------------------------------------------------------------*/
2082 void pinadjust (short anchor, short *xpoint, short *ypoint, short dir)
2084 int delx, dely;
2086 dely = (anchor & NOTBOTTOM) ?
2087 ((anchor & TOP) ? -PADSPACE : 0) : PADSPACE;
2088 delx = (anchor & NOTLEFT) ?
2089 ((anchor & RIGHT) ? -PADSPACE : 0) : PADSPACE;
2091 if (xpoint != NULL) *xpoint += (dir > 0) ? delx : -delx;
2092 if (ypoint != NULL) *ypoint += (dir > 0) ? dely : -dely;
2095 /*----------------------------------------------------------------------*/
2096 /* Draw line for editing text (position of cursor in string is given by */
2097 /* tpos (2nd parameter) */
2098 /*----------------------------------------------------------------------*/
2100 void UDrawTextLine(labelptr curlabel, short tpos)
2102 XPoint points[2]; /* top and bottom of text cursor line */
2103 short tmpanchor, xbase;
2104 int maxwidth;
2105 TextExtents tmpext;
2106 TextLinesInfo tlinfo;
2108 if (!areawin->redraw_ongoing) {
2109 areawin->redraw_needed = True;
2110 return;
2113 /* correct for position, rotation, scale, and flip invariance of text */
2115 UPushCTM();
2116 UPreMultCTM(DCTM, curlabel->position, curlabel->scale, curlabel->rotation);
2117 tmpanchor = flipadjust(curlabel->anchor);
2119 SetForeground(dpy, areawin->gc, AUXCOLOR);
2121 tlinfo.dostop = 0;
2122 tlinfo.tbreak = NULL;
2123 tlinfo.padding = NULL;
2125 tmpext = ULength(curlabel, areawin->topinstance, &tlinfo);
2126 maxwidth = tmpext.maxwidth;
2127 xbase = tmpext.base;
2128 tlinfo.dostop = tpos;
2129 tmpext = ULength(curlabel, areawin->topinstance, &tlinfo);
2131 points[0].x = (tmpanchor & NOTLEFT ?
2132 (tmpanchor & RIGHT ? -maxwidth : -maxwidth >> 1) : 0) + tmpext.width;
2133 if ((tmpanchor & JUSTIFYRIGHT) && tlinfo.padding)
2134 points[0].x += tlinfo.padding[tlinfo.line];
2135 else if ((tmpanchor & TEXTCENTERED) && tlinfo.padding)
2136 points[0].x += 0.5 * tlinfo.padding[tlinfo.line];
2137 points[0].y = (tmpanchor & NOTBOTTOM ?
2138 (tmpanchor & TOP ? -tmpext.ascent : -(tmpext.ascent + xbase) / 2)
2139 : -xbase) + tmpext.base - 3;
2140 points[1].x = points[0].x;
2141 points[1].y = points[0].y + TEXTHEIGHT + 6;
2143 if (curlabel->pin) {
2144 pinadjust(tmpanchor, &(points[0].x), &(points[0].y), 1);
2145 pinadjust(tmpanchor, &(points[1].x), &(points[1].y), 1);
2147 if (tlinfo.padding != NULL) free(tlinfo.padding);
2149 /* draw the line */
2151 UDrawLine(&points[0], &points[1]);
2152 UPopCTM();
2154 UDrawX(curlabel);
2157 /*-----------------------------------------------------------------*/
2158 /* Draw lines for editing text when multiple characters are chosen */
2159 /*-----------------------------------------------------------------*/
2161 void UDrawTLine(labelptr curlabel)
2163 UDrawTextLine(curlabel, areawin->textpos);
2164 if ((areawin->textend > 0) && (areawin->textend < areawin->textpos)) {
2165 UDrawTextLine(curlabel, areawin->textend);
2169 /*----------------------*/
2170 /* Draw an X */
2171 /*----------------------*/
2173 #ifndef HAVE_CAIRO
2174 void UDrawXLine(XPoint opt, XPoint cpt)
2176 XPoint upt, vpt;
2178 if (!areawin->redraw_ongoing) {
2179 areawin->redraw_needed = True;
2180 return;
2183 SetForeground(dpy, areawin->gc, AUXCOLOR);
2185 user_to_window(cpt, &upt);
2186 user_to_window(opt, &vpt);
2188 SetThinLineAttributes(dpy, areawin->gc, 0, LineOnOffDash, CapButt, JoinMiter);
2189 DrawLine(dpy, areawin->window, areawin->gc, vpt.x, vpt.y, upt.x, upt.y);
2191 SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapButt, JoinMiter);
2192 DrawLine(dpy, areawin->window, areawin->gc, upt.x - 3, upt.y - 3,
2193 upt.x + 3, upt.y + 3);
2194 DrawLine(dpy, areawin->window, areawin->gc, upt.x + 3, upt.y - 3,
2195 upt.x - 3, upt.y + 3);
2197 SetForeground(dpy, areawin->gc, areawin->gccolor);
2199 #endif /* HAVE_CAIRO */
2201 /*-------------------------------------------------------------------------*/
2203 #ifndef HAVE_CAIRO
2204 void UDrawBox(XPoint origin, XPoint corner)
2206 XPoint worig, wcorn;
2208 if (!areawin->redraw_ongoing) {
2209 areawin->redraw_needed = True;
2210 return;
2213 user_to_window(origin, &worig);
2214 user_to_window(corner, &wcorn);
2216 SetForeground(dpy, areawin->gc, AUXCOLOR);
2217 SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapRound, JoinBevel);
2218 DrawLine(dpy, areawin->window, areawin->gc, worig.x, worig.y,
2219 worig.x, wcorn.y);
2220 DrawLine(dpy, areawin->window, areawin->gc, worig.x, wcorn.y,
2221 wcorn.x, wcorn.y);
2222 DrawLine(dpy, areawin->window, areawin->gc, wcorn.x, wcorn.y,
2223 wcorn.x, worig.y);
2224 DrawLine(dpy, areawin->window, areawin->gc, wcorn.x, worig.y,
2225 worig.x, worig.y);
2227 #endif /* HAVE_CAIRO */
2229 /*----------------------------------------------------------------------*/
2230 /* Get a box indicating the dimensions of the edit element that most */
2231 /* closely reach the position "corner". */
2232 /*----------------------------------------------------------------------*/
2234 float UGetRescaleBox(XPoint *corner, XPoint *newpoints)
2236 genericptr rgen;
2237 float savescale, newscale;
2238 long mindist, testdist, refdist;
2239 labelptr rlab;
2240 graphicptr rgraph;
2241 objinstptr rinst;
2242 int i;
2244 if (!areawin->redraw_ongoing) {
2245 areawin->redraw_needed = True;
2246 // return 0.0;
2249 if (areawin->selects == 0) return 0.0;
2251 /* Use only the 1st selection as a reference to set the scale */
2253 rgen = SELTOGENERIC(areawin->selectlist);
2255 switch(ELEMENTTYPE(rgen)) {
2256 case LABEL:
2257 rlab = (labelptr)rgen;
2258 labelbbox(rlab, newpoints, areawin->topinstance);
2259 newpoints[4] = newpoints[0];
2260 mindist = LONG_MAX;
2261 for (i = 0; i < 4; i++) {
2262 testdist = finddist(&newpoints[i], &newpoints[i+1], corner);
2263 if (testdist < mindist)
2264 mindist = testdist;
2266 refdist = wirelength(corner, &(rlab->position));
2267 mindist = (int)sqrt(abs((double)mindist));
2268 savescale = rlab->scale;
2269 if (!test_insideness((int)corner->x, (int)corner->y, newpoints))
2270 mindist = -mindist;
2271 if (refdist == mindist) refdist = 1 - mindist;
2272 if (rlab->scale < 0) rlab->scale = -rlab->scale;
2273 newscale = fabs(rlab->scale * (float)refdist / (float)(refdist + mindist));
2274 if (newscale > 10 * rlab->scale) newscale = 10 * rlab->scale;
2275 if (areawin->snapto) {
2276 float snapstep = 2 * (float)xobjs.pagelist[areawin->page]->gridspace
2277 / (float)xobjs.pagelist[areawin->page]->snapspace;
2278 newscale = (float)((int)(newscale * snapstep)) / snapstep;
2279 if (newscale < (1.0 / snapstep)) newscale = (1.0 / snapstep);
2281 else if (newscale < 0.1 * rlab->scale) newscale = 0.1 * rlab->scale;
2282 rlab->scale = (savescale < 0) ? -newscale : newscale;
2283 labelbbox(rlab, newpoints, areawin->topinstance);
2284 rlab->scale = savescale;
2285 if (savescale < 0) newscale = -newscale;
2286 break;
2288 case GRAPHIC:
2289 rgraph = (graphicptr)rgen;
2290 graphicbbox(rgraph, newpoints);
2291 newpoints[4] = newpoints[0];
2292 mindist = LONG_MAX;
2293 for (i = 0; i < 4; i++) {
2294 testdist = finddist(&newpoints[i], &newpoints[i+1], corner);
2295 if (testdist < mindist)
2296 mindist = testdist;
2298 refdist = wirelength(corner, &(rgraph->position));
2299 mindist = (int)sqrt(abs((double)mindist));
2300 savescale = rgraph->scale;
2301 if (!test_insideness((int)corner->x, (int)corner->y, newpoints))
2302 mindist = -mindist;
2303 if (refdist == mindist) refdist = 1 - mindist; /* avoid inf result */
2304 if (rgraph->scale < 0) rgraph->scale = -rgraph->scale;
2305 newscale = fabs(rgraph->scale * (float)refdist / (float)(refdist + mindist));
2306 if (newscale > 10 * rgraph->scale) newscale = 10 * rgraph->scale;
2307 if (areawin->snapto) {
2308 float snapstep = 2 * (float)xobjs.pagelist[areawin->page]->gridspace
2309 / (float)xobjs.pagelist[areawin->page]->snapspace;
2310 newscale = (float)((int)(newscale * snapstep)) / snapstep;
2311 if (newscale < (1.0 / snapstep)) newscale = (1.0 / snapstep);
2313 else if (newscale < 0.1 * rgraph->scale) newscale = 0.1 * rgraph->scale;
2314 rgraph->scale = (savescale < 0) ? -newscale : newscale;
2315 graphicbbox(rgraph, newpoints);
2316 rgraph->scale = savescale;
2317 if (savescale < 0) newscale = -newscale;
2318 break;
2320 case OBJINST:
2321 rinst = (objinstptr)rgen;
2322 objinstbbox(rinst, newpoints, 0);
2323 newpoints[4] = newpoints[0];
2324 mindist = LONG_MAX;
2325 for (i = 0; i < 4; i++) {
2326 testdist = finddist(&newpoints[i], &newpoints[i+1], corner);
2327 if (testdist < mindist)
2328 mindist = testdist;
2330 refdist = wirelength(corner, &(rinst->position));
2331 mindist = (int)sqrt(abs((double)mindist));
2332 savescale = rinst->scale;
2333 if (!test_insideness((int)corner->x, (int)corner->y, newpoints))
2334 mindist = -mindist;
2335 if (refdist == mindist) refdist = 1 - mindist; /* avoid inf result */
2336 if (rinst->scale < 0) rinst->scale = -rinst->scale;
2337 newscale = fabs(rinst->scale * (float)refdist / (float)(refdist + mindist));
2338 if (newscale > 10 * rinst->scale) newscale = 10 * rinst->scale;
2339 if (areawin->snapto) {
2340 float snapstep = 2 * (float)xobjs.pagelist[areawin->page]->gridspace
2341 / (float)xobjs.pagelist[areawin->page]->snapspace;
2342 newscale = (float)((int)(newscale * snapstep)) / snapstep;
2343 if (newscale < (1.0 / snapstep)) newscale = (1.0 / snapstep);
2345 else if (newscale < 0.1 * rinst->scale) newscale = 0.1 * rinst->scale;
2346 rinst->scale = (savescale < 0) ? -newscale : newscale;
2347 objinstbbox(rinst, newpoints, 0);
2348 rinst->scale = savescale;
2349 if (savescale < 0) newscale = -newscale;
2350 break;
2353 return newscale;
2356 /*----------------------------------------------------------------------*/
2357 /* Draw a box indicating the dimensions of the edit element that most */
2358 /* closely reach the position "corner". */
2359 /*----------------------------------------------------------------------*/
2361 #ifndef HAVE_CAIRO
2362 void UDrawRescaleBox(XPoint *corner)
2364 XPoint origpoints[5], newpoints[5];
2366 if (!areawin->redraw_ongoing) {
2367 areawin->redraw_needed = True;
2368 return;
2371 if (areawin->selects == 0)
2372 return;
2374 UGetRescaleBox(corner, newpoints);
2376 SetForeground(dpy, areawin->gc, AUXCOLOR);
2377 SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapRound, JoinBevel);
2379 UTransformbyCTM(DCTM, newpoints, origpoints, 4);
2380 strokepath(origpoints, 4, 0, 1);
2382 #endif /* HAVE_CAIRO */
2384 /*-------------------------------------------------------------------------*/
2386 #ifndef HAVE_CAIRO
2387 void UDrawBBox()
2389 XPoint origin;
2390 XPoint worig, wcorn, corner;
2391 objinstptr bbinst = areawin->topinstance;
2393 if (!areawin->redraw_ongoing) {
2394 areawin->redraw_needed = True;
2395 return;
2398 if ((!areawin->bboxon) || (checkforbbox(topobject) != NULL)) return;
2400 origin = bbinst->bbox.lowerleft;
2401 corner.x = origin.x + bbinst->bbox.width;
2402 corner.y = origin.y + bbinst->bbox.height;
2404 /* Include any schematic labels in the bounding box. */
2405 extendschembbox(bbinst, &origin, &corner);
2407 user_to_window(origin, &worig);
2408 user_to_window(corner, &wcorn);
2410 SetForeground(dpy, areawin->gc, BBOXCOLOR);
2411 DrawLine(dpy, areawin->window, areawin->gc, worig.x, worig.y,
2412 worig.x, wcorn.y);
2413 DrawLine(dpy, areawin->window, areawin->gc, worig.x, wcorn.y,
2414 wcorn.x, wcorn.y);
2415 DrawLine(dpy, areawin->window, areawin->gc, wcorn.x, wcorn.y,
2416 wcorn.x, worig.y);
2417 DrawLine(dpy, areawin->window, areawin->gc, wcorn.x, worig.y,
2418 worig.x, worig.y);
2420 #endif /* !HAVE_CAIRO */
2422 /*----------------------------------------------------------------------*/
2423 /* Fill and/or draw a border around the stroking path */
2424 /*----------------------------------------------------------------------*/
2426 #ifndef HAVE_CAIRO
2427 void strokepath(XPoint *pathlist, short number, short style, float width)
2429 float tmpwidth;
2431 tmpwidth = UTopTransScale(width);
2433 if (!(style & CLIPMASK) || (areawin->showclipmasks == TRUE) ||
2434 (areawin->clipped < 0)) {
2435 if (style & FILLED || (!(style & FILLED) && style & OPAQUE)) {
2436 if ((style & FILLSOLID) == FILLSOLID)
2437 SetFillStyle(dpy, areawin->gc, FillSolid);
2438 else if (!(style & FILLED)) {
2439 SetFillStyle(dpy, areawin->gc, FillOpaqueStippled);
2440 SetStipple(dpy, areawin->gc, 7);
2442 else {
2443 if (style & OPAQUE)
2444 SetFillStyle(dpy, areawin->gc, FillOpaqueStippled);
2445 else
2446 SetFillStyle(dpy, areawin->gc, FillStippled);
2447 SetStipple(dpy, areawin->gc, ((style & FILLSOLID) >> 5));
2449 FillPolygon(dpy, areawin->window, areawin->gc, pathlist, number, Nonconvex,
2450 CoordModeOrigin);
2451 /* return to original state */
2452 SetFillStyle(dpy, areawin->gc, FillSolid);
2454 if (!(style & NOBORDER)) {
2455 if (style & (DASHED | DOTTED)) {
2456 /* Set up dots or dashes */
2457 char dashstring[2];
2458 /* prevent values greater than 255 from folding back into */
2459 /* type char. Limit to 63 (=255/4) to keep at least the */
2460 /* dot/gap ratio to scale when 'gap' is at its maximum */
2461 /* value. */
2462 unsigned char dotsize = min(63, max(1, (short)tmpwidth));
2463 if (style & DASHED)
2464 dashstring[0] = 4 * dotsize;
2465 else if (style & DOTTED)
2466 dashstring[0] = dotsize;
2467 dashstring[1] = 4 * dotsize;
2468 SetDashes(dpy, areawin->gc, 0, dashstring, 2);
2469 SetLineAttributes(dpy, areawin->gc, tmpwidth, LineOnOffDash,
2470 CapButt, (style & SQUARECAP) ? JoinMiter : JoinBevel);
2472 else
2473 SetLineAttributes(dpy, areawin->gc, tmpwidth, LineSolid,
2474 (style & SQUARECAP) ? CapProjecting : CapRound,
2475 (style & SQUARECAP) ? JoinMiter : JoinBevel);
2477 /* draw the spline and close off if so specified */
2478 DrawLines(dpy, areawin->window, areawin->gc, pathlist,
2479 number, CoordModeOrigin);
2480 if (!(style & UNCLOSED))
2481 DrawLine(dpy, areawin->window, areawin->gc, pathlist[0].x,
2482 pathlist[0].y, pathlist[number - 1].x, pathlist[number - 1].y);
2486 if (style & CLIPMASK) {
2487 if (areawin->clipped == 0) {
2488 XSetForeground(dpy, areawin->cmgc, 0);
2489 XFillRectangle(dpy, areawin->clipmask, areawin->cmgc, 0, 0,
2490 areawin->width, areawin->height);
2491 XSetForeground(dpy, areawin->cmgc, 1);
2492 FillPolygon(dpy, areawin->clipmask, areawin->cmgc, pathlist,
2493 number, Nonconvex, CoordModeOrigin);
2494 XSetClipMask(dpy, areawin->gc, areawin->clipmask);
2495 // printf("level 0: Clip to clipmask\n"); // Diagnostic
2496 areawin->clipped++;
2498 else if ((areawin->clipped > 0) && (areawin->clipped & 1) == 0) {
2499 if (areawin->pbuf == (Pixmap)NULL) {
2500 areawin->pbuf = XCreatePixmap (dpy, areawin->window,
2501 areawin->width, areawin->height, 1);
2503 XCopyArea(dpy, areawin->clipmask, areawin->pbuf, areawin->cmgc,
2504 0, 0, areawin->width, areawin->height, 0, 0);
2505 XSetForeground(dpy, areawin->cmgc, 0);
2506 XFillRectangle(dpy, areawin->clipmask, areawin->cmgc, 0, 0,
2507 areawin->width, areawin->height);
2508 XSetForeground(dpy, areawin->cmgc, 1);
2509 FillPolygon(dpy, areawin->clipmask, areawin->cmgc, pathlist,
2510 number, Nonconvex, CoordModeOrigin);
2511 XSetFunction(dpy, areawin->cmgc, GXand);
2512 XCopyArea(dpy, areawin->pbuf, areawin->clipmask, areawin->cmgc,
2513 0, 0, areawin->width, areawin->height, 0, 0);
2514 XSetFunction(dpy, areawin->cmgc, GXcopy);
2515 XSetClipMask(dpy, areawin->gc, areawin->clipmask);
2516 // printf("level X: Clip to clipmask\n"); // Diagnostic
2517 areawin->clipped++;
2521 #endif /* !HAVE_CAIRO */
2523 /*-------------------------------------------------------------------------*/
2525 void makesplinepath(splineptr thespline, XPoint *pathlist)
2527 XPoint *tmpptr = pathlist;
2529 UTransformbyCTM(DCTM, &(thespline->ctrl[0]), tmpptr, 1);
2530 UfTransformbyCTM(DCTM, thespline->points, ++tmpptr, INTSEGS);
2531 UTransformbyCTM(DCTM, &(thespline->ctrl[3]), tmpptr + INTSEGS, 1);
2534 /*-------------------------------------------------------------------------*/
2536 #ifndef HAVE_CAIRO
2537 void UDrawSpline(splineptr thespline, float passwidth)
2539 XPoint tmppoints[SPLINESEGS];
2540 float scaledwidth;
2542 if (!areawin->redraw_ongoing) {
2543 areawin->redraw_needed = True;
2544 return;
2547 scaledwidth = thespline->width * passwidth;
2549 makesplinepath(thespline, tmppoints);
2550 strokepath(tmppoints, SPLINESEGS, thespline->style, scaledwidth);
2552 #endif /* HAVE_CAIRO */
2554 /*-------------------------------------------------------------------------*/
2556 #ifndef HAVE_CAIRO
2557 void UDrawPolygon(polyptr thepoly, float passwidth)
2559 XPoint *tmppoints = (pointlist) malloc(thepoly->number * sizeof(XPoint));
2560 float scaledwidth;
2562 if (!areawin->redraw_ongoing) {
2563 areawin->redraw_needed = True;
2564 return;
2567 scaledwidth = thepoly->width * passwidth;
2569 UTransformbyCTM(DCTM, thepoly->points, tmppoints, thepoly->number);
2570 strokepath(tmppoints, thepoly->number, thepoly->style, scaledwidth);
2571 free(tmppoints);
2573 #endif /* HAVE_CAIRO */
2575 /*-------------------------------------------------------------------------*/
2577 #ifndef HAVE_CAIRO
2578 void UDrawArc(arcptr thearc, float passwidth)
2580 XPoint tmppoints[RSTEPS + 2];
2581 float scaledwidth;
2583 if (!areawin->redraw_ongoing) {
2584 areawin->redraw_needed = True;
2585 return;
2588 scaledwidth = thearc->width * passwidth;
2590 UfTransformbyCTM(DCTM, thearc->points, tmppoints, thearc->number);
2591 strokepath(tmppoints, thearc->number, thearc->style, scaledwidth);
2593 #endif /* HAVE_CAIRO */
2595 /*-------------------------------------------------------------------------*/
2597 #ifndef HAVE_CAIRO
2598 void UDrawPath(pathptr thepath, float passwidth)
2600 XPoint *tmppoints = (pointlist) malloc(sizeof(XPoint));
2601 genericptr *genpath;
2602 polyptr thepoly;
2603 splineptr thespline;
2604 int pathsegs = 0, curseg = 0;
2605 float scaledwidth;
2607 if (!areawin->redraw_ongoing) {
2608 areawin->redraw_needed = True;
2609 return;
2612 for (genpath = thepath->plist; genpath < thepath->plist + thepath->parts;
2613 genpath++) {
2614 switch(ELEMENTTYPE(*genpath)) {
2615 case POLYGON:
2616 thepoly = TOPOLY(genpath);
2617 pathsegs += thepoly->number;
2618 tmppoints = (pointlist) realloc(tmppoints, pathsegs * sizeof(XPoint));
2619 UTransformbyCTM(DCTM, thepoly->points, tmppoints + curseg, thepoly->number);
2620 curseg = pathsegs;
2621 break;
2622 case SPLINE:
2623 thespline = TOSPLINE(genpath);
2624 pathsegs += SPLINESEGS;
2625 tmppoints = (pointlist) realloc(tmppoints, pathsegs * sizeof(XPoint));
2626 makesplinepath(thespline, tmppoints + curseg);
2627 curseg = pathsegs;
2628 break;
2631 scaledwidth = thepath->width * passwidth;
2633 strokepath(tmppoints, pathsegs, thepath->style, scaledwidth);
2634 free(tmppoints);
2636 #endif /* HAVE_CAIRO */
2638 /*----------------------------------------------------------------------*/
2639 /* Main recursive object instance drawing routine. */
2640 /* context is the instance information passed down from above */
2641 /* theinstance is the object instance to be drawn */
2642 /* level is the level of recursion */
2643 /* passcolor is the inherited color value passed to object */
2644 /* passwidth is the inherited linewidth value passed to the object */
2645 /* stack contains graphics context information */
2646 /*----------------------------------------------------------------------*/
2648 #ifndef HAVE_CAIRO
2649 void UDrawObject(objinstptr theinstance, short level, int passcolor,
2650 float passwidth, pushlistptr *stack)
2652 genericptr *areagen;
2653 float tmpwidth;
2654 int defaultcolor = passcolor;
2655 int curcolor = passcolor;
2656 int thispart;
2657 short savesel;
2658 XPoint bboxin[2], bboxout[2];
2659 u_char xm, ym;
2660 objectptr theobject = theinstance->thisobject;
2662 if (!areawin->redraw_ongoing) {
2663 areawin->redraw_needed = True;
2664 return;
2667 /* Save the number of selections and set it to zero while we do the */
2668 /* object drawing. */
2670 savesel = areawin->selects;
2671 areawin->selects = 0;
2673 /* All parts are given in the coordinate system of the object, unless */
2674 /* this is the top-level object, in which they will be interpreted as */
2675 /* relative to the screen. */
2677 UPushCTM();
2679 if (stack) {
2680 /* Save the current clipping mask and push it on the stack */
2681 if (areawin->clipped > 0) {
2682 push_stack((pushlistptr *)stack, theinstance, (char *)areawin->clipmask);
2683 areawin->clipmask = XCreatePixmap(dpy, areawin->window, areawin->width,
2684 areawin->height, 1);
2685 XCopyArea(dpy, (Pixmap)(*stack)->clientdata, areawin->clipmask, areawin->cmgc,
2686 0, 0, areawin->width, areawin->height, 0, 0);
2688 else
2689 push_stack((pushlistptr *)stack, theinstance, (char *)NULL);
2691 if (level != 0)
2692 UPreMultCTM(DCTM, theinstance->position, theinstance->scale,
2693 theinstance->rotation);
2695 if (theinstance->style & LINE_INVARIANT)
2696 passwidth /= fabs(theinstance->scale);
2698 /* do a quick test for intersection with the display window */
2700 bboxin[0].x = theobject->bbox.lowerleft.x;
2701 bboxin[0].y = theobject->bbox.lowerleft.y;
2702 bboxin[1].x = theobject->bbox.lowerleft.x + theobject->bbox.width;
2703 bboxin[1].y = theobject->bbox.lowerleft.y + theobject->bbox.height;
2704 if (level == 0)
2705 extendschembbox(theinstance, &(bboxin[0]), &(bboxin[1]));
2706 UTransformbyCTM(DCTM, bboxin, bboxout, 2);
2708 xm = (bboxout[0].x < bboxout[1].x) ? 0 : 1;
2709 ym = (bboxout[0].y < bboxout[1].y) ? 0 : 1;
2711 if (bboxout[xm].x < areawin->width && bboxout[ym].y < areawin->height &&
2712 bboxout[1 - xm].x > 0 && bboxout[1 - ym].y > 0) {
2714 /* make parameter substitutions */
2715 psubstitute(theinstance);
2717 /* draw all of the elements */
2719 tmpwidth = UTopTransScale(passwidth);
2720 SetLineAttributes(dpy, areawin->gc, tmpwidth, LineSolid, CapRound,
2721 JoinBevel);
2723 /* guard against plist being regenerated during a redraw by the */
2724 /* expression parameter mechanism (should that be prohibited?) */
2726 for (thispart = 0; thispart < theobject->parts; thispart++) {
2727 areagen = theobject->plist + thispart;
2728 if ((*areagen)->type & DRAW_HIDE) continue;
2730 if (defaultcolor != DOFORALL) {
2731 Boolean clipcolor = FALSE;
2732 switch(ELEMENTTYPE(*areagen)) {
2733 case(POLYGON): case(SPLINE): case(ARC): case(PATH):
2734 if (TOPOLY(areagen)->style & CLIPMASK)
2735 clipcolor = TRUE;
2736 break;
2738 if (((*areagen)->color != curcolor) || (clipcolor == TRUE)) {
2739 if (clipcolor)
2740 curcolor = CLIPMASKCOLOR;
2741 else if ((*areagen)->color == DEFAULTCOLOR)
2742 curcolor = defaultcolor;
2743 else
2744 curcolor = (*areagen)->color;
2746 XcTopSetForeground(curcolor);
2750 switch(ELEMENTTYPE(*areagen)) {
2751 case(POLYGON):
2752 if (level == 0 || !((TOPOLY(areagen))->style & BBOX))
2753 UDrawPolygon(TOPOLY(areagen), passwidth);
2754 break;
2756 case(SPLINE):
2757 UDrawSpline(TOSPLINE(areagen), passwidth);
2758 break;
2760 case(ARC):
2761 UDrawArc(TOARC(areagen), passwidth);
2762 break;
2764 case(PATH):
2765 UDrawPath(TOPATH(areagen), passwidth);
2766 break;
2768 case(GRAPHIC):
2769 UDrawGraphic(TOGRAPHIC(areagen));
2770 break;
2772 case(OBJINST):
2773 if (areawin->editinplace && stack && (TOOBJINST(areagen)
2774 == areawin->topinstance)) {
2775 /* If stack matches areawin->stack, then don't draw */
2776 /* because it would be redundant. */
2777 pushlistptr alist = *stack, blist = areawin->stack;
2778 while (alist && blist) {
2779 if (alist->thisinst != blist->thisinst) break;
2780 alist = alist->next;
2781 blist = blist->next;
2783 if ((!alist) || (!blist)) break;
2785 if (areawin->clipped > 0) areawin->clipped += 2;
2786 UDrawObject(TOOBJINST(areagen), level + 1, curcolor, passwidth, stack);
2787 if (areawin->clipped > 0) areawin->clipped -= 2;
2788 break;
2790 case(LABEL):
2791 if (level == 0 || TOLABEL(areagen)->pin == False)
2792 UDrawString(TOLABEL(areagen), curcolor, theinstance);
2793 else if ((TOLABEL(areagen)->anchor & PINVISIBLE) && areawin->pinpointon)
2794 UDrawString(TOLABEL(areagen), curcolor, theinstance);
2795 else if (TOLABEL(areagen)->anchor & PINVISIBLE)
2796 UDrawStringNoX(TOLABEL(areagen), curcolor, theinstance);
2797 else if (level == 1 && TOLABEL(areagen)->pin &&
2798 TOLABEL(areagen)->pin != INFO && areawin->pinpointon)
2799 UDrawXDown(TOLABEL(areagen));
2800 break;
2802 if (areawin->clipped > 0) {
2803 if ((areawin->clipped & 3) == 1) {
2804 areawin->clipped++;
2806 else if ((areawin->clipped & 3) == 2) {
2807 areawin->clipped -= 2;
2808 if ((!stack) || ((*stack)->clientdata == (char *)NULL)) {
2809 XSetClipMask(dpy, areawin->gc, None);
2810 // printf("1: Clear clipmask\n"); // Diagnostic
2812 else {
2813 XSetClipMask(dpy, areawin->gc, (Pixmap)((*stack)->clientdata));
2814 // printf("1: Set to pushed clipmask\n"); // Diagnostic
2820 /* restore the color passed to the object, if different from current color */
2822 if ((defaultcolor != DOFORALL) && (passcolor != curcolor)) {
2823 XTopSetForeground(passcolor);
2825 if (areawin->clipped > 0) {
2826 if ((areawin->clipped & 3) != 3) {
2827 if ((!stack) || ((*stack)->clientdata == (char *)NULL)) {
2828 XSetClipMask(dpy, areawin->gc, None);
2829 // printf("2: Clear clipmask\n"); // Diagnostic
2831 else {
2832 XSetClipMask(dpy, areawin->gc, (Pixmap)((*stack)->clientdata));
2833 // printf("2: Set to pushed clipmask\n"); // Diagnostic
2836 areawin->clipped &= ~3;
2840 /* restore the selection list (if any) */
2841 areawin->selects = savesel;
2842 UPopCTM();
2843 if (stack) {
2844 if ((*stack) != NULL) {
2845 if ((*stack)->clientdata != (char *)NULL) {
2846 XFreePixmap(dpy, areawin->clipmask);
2847 areawin->clipmask = (Pixmap)(*stack)->clientdata;
2848 // printf("3: Restore clipmask\n"); // Diagnostic
2851 pop_stack(stack);
2854 #endif /* HAVE_CAIRO */
2856 /*----------------------------------------------------------------------*/
2857 /* Recursively run through the current page and find any labels which */
2858 /* are declared to be style LATEX. If "checkonly" is present, we set */
2859 /* it to TRUE or FALSE depending on whether or not LATEX labels have */
2860 /* been encountered. If NULL, then we write LATEX output appropriately */
2861 /* to a file named with the page filename + suffix ".tex". */
2862 /*----------------------------------------------------------------------*/
2864 void UDoLatex(objinstptr theinstance, short level, FILE *f,
2865 float scale, float scale2, int tx, int ty, Boolean *checkonly)
2867 XPoint lpos, xlpos;
2868 XfPoint xfpos;
2869 labelptr thislabel;
2870 genericptr *areagen;
2871 objectptr theobject = theinstance->thisobject;
2872 char *ltext;
2873 int lranchor, tbanchor;
2875 UPushCTM();
2876 if (level != 0)
2877 UPreMultCTM(DCTM, theinstance->position, theinstance->scale,
2878 theinstance->rotation);
2880 /* make parameter substitutions */
2881 psubstitute(theinstance);
2883 /* find all of the elements */
2885 for (areagen = theobject->plist; areagen < theobject->plist +
2886 theobject->parts; areagen++) {
2888 switch(ELEMENTTYPE(*areagen)) {
2889 case(OBJINST):
2890 UDoLatex(TOOBJINST(areagen), level + 1, f, scale, scale2, tx, ty, checkonly);
2891 break;
2893 case(LABEL):
2894 thislabel = TOLABEL(areagen);
2895 if (level == 0 || thislabel->pin == False ||
2896 (thislabel->anchor & PINVISIBLE))
2897 if (thislabel->anchor & LATEXLABEL) {
2898 if (checkonly) {
2899 *checkonly = TRUE;
2900 return;
2902 else {
2903 lpos.x = thislabel->position.x;
2904 lpos.y = thislabel->position.y;
2905 UTransformbyCTM(DCTM, &lpos, &xlpos, 1);
2906 xlpos.x += tx;
2907 xlpos.y += ty;
2908 xfpos.x = (float)xlpos.x * scale;
2909 xfpos.y = (float)xlpos.y * scale;
2910 xfpos.x /= 72.0;
2911 xfpos.y /= 72.0;
2912 xfpos.x -= 1.0;
2913 xfpos.y -= 1.0;
2914 xfpos.x += 0.056;
2915 xfpos.y += 0.056;
2916 xfpos.x /= scale2;
2917 xfpos.y /= scale2;
2918 ltext = textprinttex(thislabel->string, theinstance);
2919 tbanchor = thislabel->anchor & (NOTBOTTOM | TOP);
2920 lranchor = thislabel->anchor & (NOTLEFT | RIGHT);
2922 /* The 1.2 factor accounts for the difference between */
2923 /* Xcircuit's label scale of "1" and LaTeX's "normalsize" */
2925 fprintf(f, " \\putbox{%3.2fin}{%3.2fin}{%3.2f}{",
2926 xfpos.x, xfpos.y, 1.2 * thislabel->scale);
2927 if (thislabel->rotation != 0)
2928 fprintf(f, "\\rotatebox{-%d}{", thislabel->rotation);
2929 if (lranchor == (NOTLEFT | RIGHT)) fprintf(f, "\\rightbox{");
2930 else if (lranchor == NOTLEFT) fprintf(f, "\\centbox{");
2931 if (tbanchor == (NOTBOTTOM | TOP)) fprintf(f, "\\topbox{");
2932 else if (tbanchor == NOTBOTTOM) fprintf(f, "\\midbox{");
2933 fprintf(f, "%s", ltext);
2934 if (lranchor != NORMAL) fprintf(f, "}");
2935 if (tbanchor != NORMAL) fprintf(f, "}");
2936 if (thislabel->rotation != 0) fprintf(f, "}");
2937 fprintf(f, "}%%\n");
2938 free(ltext);
2941 break;
2944 UPopCTM();
2947 /*----------------------------------------------------------------------*/
2948 /* Top level routine for writing LATEX output. */
2949 /*----------------------------------------------------------------------*/
2951 void TopDoLatex()
2953 FILE *f;
2954 float psscale, outscale;
2955 int tx, ty, width, height;
2956 polyptr framebox;
2957 XPoint origin;
2958 Boolean checklatex = FALSE;
2959 char filename[100], extend[10], *dotptr;
2961 UDoLatex(areawin->topinstance, 0, NULL, 1.0, 1.0, 0, 0, &checklatex);
2963 if (checklatex == FALSE) return; /* No LaTeX labels to write */
2965 /* Handle cases where the file might have a ".eps" extension. */
2966 /* Thanks to Graham Sheward for pointing this out. */
2968 if (xobjs.pagelist[areawin->page]->filename)
2969 sprintf(filename, "%s", xobjs.pagelist[areawin->page]->filename);
2970 else
2971 sprintf(filename, "%s",
2972 xobjs.pagelist[areawin->page]->pageinst->thisobject->name);
2974 if ((dotptr = strchr(filename + strlen(filename) - 4, '.')) == NULL) {
2975 dotptr = filename + strlen(filename);
2976 sprintf(dotptr, ".ps");
2978 strcpy(extend, dotptr);
2979 strcpy(dotptr, ".tex");
2981 f = fopen(filename, "w");
2983 *dotptr = '\0';
2985 fprintf(f, "%% XCircuit output \"%s.tex\" for LaTeX input from %s%s\n",
2986 filename, filename, extend);
2987 fprintf(f, "\\def\\putbox#1#2#3#4{\\makebox[0in][l]{\\makebox[#1][l]{}"
2988 "\\raisebox{\\baselineskip}[0in][0in]"
2989 "{\\raisebox{#2}[0in][0in]{\\scalebox{#3}{#4}}}}}\n");
2990 fprintf(f, "\\def\\rightbox#1{\\makebox[0in][r]{#1}}\n");
2991 fprintf(f, "\\def\\centbox#1{\\makebox[0in]{#1}}\n");
2992 fprintf(f, "\\def\\topbox#1{\\raisebox{-0.60\\baselineskip}[0in][0in]{#1}}\n");
2993 fprintf(f, "\\def\\midbox#1{\\raisebox{-0.20\\baselineskip}[0in][0in]{#1}}\n");
2995 /* Modified to use \scalebox and \parbox by Alex Tercete, June 2008 */
2997 // fprintf(f, "\\begin{center}\n");
2999 outscale = xobjs.pagelist[areawin->page]->outscale;
3000 psscale = getpsscale(outscale, areawin->page);
3002 width = toplevelwidth(areawin->topinstance, &origin.x);
3003 height = toplevelheight(areawin->topinstance, &origin.y);
3005 /* Added 10/19/10: If there is a specified bounding box, let it */
3006 /* determine the figure origin; otherwise, the labels will be */
3007 /* mismatched to the bounding box. */
3009 if ((framebox = checkforbbox(topobject)) != NULL) {
3010 int i, maxx, maxy;
3012 origin.x = maxx = framebox->points[0].x;
3013 origin.y = maxy = framebox->points[0].y;
3014 for (i = 1; i < framebox->number; i++) {
3015 if (framebox->points[i].x < origin.x) origin.x = framebox->points[i].x;
3016 if (framebox->points[i].x > maxx) maxx = framebox->points[i].x;
3017 if (framebox->points[i].y < origin.y) origin.y = framebox->points[i].y;
3018 if (framebox->points[i].y > maxy) maxy = framebox->points[i].y;
3020 origin.x -= ((width - maxx + origin.x) / 2);
3021 origin.y -= ((height - maxy + origin.y) / 2);
3024 tx = (int)(72 / psscale) - origin.x,
3025 ty = (int)(72 / psscale) - origin.y;
3027 fprintf(f, " \\scalebox{%g}{\n", outscale);
3028 fprintf(f, " \\normalsize\n");
3029 fprintf(f, " \\parbox{%gin}{\n", (((float)width * psscale) / 72.0) / outscale);
3030 fprintf(f, " \\includegraphics[scale=%g]{%s}\\\\\n", 1.0 / outscale,
3031 filename);
3032 fprintf(f, " %% translate x=%d y=%d scale %3.2f\n", tx, ty, psscale);
3034 UPushCTM(); /* Save current state */
3035 UResetCTM(DCTM); /* Set to identity matrix */
3036 UDoLatex(areawin->topinstance, 0, f, psscale, outscale, tx, ty, NULL);
3037 UPopCTM(); /* Restore state */
3039 fprintf(f, " } %% close \'parbox\'\n");
3040 fprintf(f, " } %% close \'scalebox\'\n");
3041 fprintf(f, " \\vspace{-\\baselineskip} %% this is not"
3042 " necessary, but looks better\n");
3043 // fprintf(f, "\\end{center}\n");
3044 fclose(f);
3046 Wprintf("Wrote auxiliary file %s.tex", filename);