1 /*-------------------------------------------------------------------------*/
3 /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */
4 /*-------------------------------------------------------------------------*/
6 /*-------------------------------------------------------------------------*/
7 /* written by Tim Edwards, 8/13/93 */
8 /*-------------------------------------------------------------------------*/
17 #include <X11/Intrinsic.h>
18 #include <X11/StringDefs.h>
21 /*-------------------------------------------------------------------------*/
23 /*-------------------------------------------------------------------------*/
29 #include "colordefs.h"
32 /*----------------------------------------------------------------------*/
33 /* Function prototype declarations */
34 /*----------------------------------------------------------------------*/
35 #include "prototypes.h"
37 /*-------------------------------------------------------------------------*/
38 /* External Variable definitions */
39 /*-------------------------------------------------------------------------*/
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 */
51 /*------------------------------------------------------------------------*/
53 long sqwirelen(XPoint
*userpt1
, XPoint
*userpt2
)
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
)
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
)
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
)
97 c
= sqwirelen(linept1
, linept2
);
98 a
= sqwirelen(linept1
, userpt
);
99 b
= sqwirelen(linept2
, userpt
);
101 if (frac
>= c
) return b
; /* "=" is important if c = 0 ! */
102 else if (-frac
>= c
) return a
;
104 protod
= (float)(c
+ a
- b
);
105 return (a
- (long)((protod
* protod
) / (float)(c
<< 2)));
109 /*----------------------------------------------------------------------*/
110 /* Decompose an arc segment into one to four bezier curves according */
111 /* the approximation algorithm lifted from the paper by L. Maisonobe */
112 /* (spaceroots.org). This decomposition is done when an arc in a path */
113 /* is read from an (older) xcircuit file, or when an arc is a selected */
114 /* item when a path is created. Because arcs are decomposed when */
115 /* encountered, we assume that the arc is the last element of the path. */
116 /*----------------------------------------------------------------------*/
118 void decomposearc(pathptr thepath
, XPoint
*startpoint
)
120 float fnc
, ang1
, ang2
;
124 splineptr
*newspline
;
126 double nu1
, nu2
, lambda1
, lambda2
, alpha
, tansq
;
127 XfPoint E1
, E2
, Ep1
, Ep2
;
129 Boolean reverse
= FALSE
;
131 pgen
= thepath
->plist
+ thepath
->parts
- 1;
132 if (ELEMENTTYPE(*pgen
) != ARC
) return;
133 thearc
= TOARC(pgen
);
135 if (thearc
->radius
< 0) {
137 thearc
->radius
= -thearc
->radius
;
140 fnc
= (thearc
->angle2
- thearc
->angle1
) / 90.0;
141 ncurves
= (short)fnc
;
142 if (fnc
- (float)((int)fnc
) > 0.01) ncurves
++;
144 thepath
->parts
--; /* Forget the arc */
146 for (i
= 0; i
< ncurves
; i
++) {
147 if (reverse
) { /* arc path is reverse direction */
149 ang1
= thearc
->angle2
;
153 if (i
== ncurves
- 1)
154 ang2
= thearc
->angle1
;
158 else { /* arc path is forward direction */
160 ang1
= thearc
->angle1
;
164 if (i
== ncurves
- 1)
165 ang2
= thearc
->angle2
;
170 lambda1
= (double)ang1
* RADFAC
;
171 lambda2
= (double)ang2
* RADFAC
;
173 nu1
= atan2(sin(lambda1
) / (double)thearc
->yaxis
,
174 cos(lambda1
) / (double)thearc
->radius
);
175 nu2
= atan2(sin(lambda2
) / (double)thearc
->yaxis
,
176 cos(lambda2
) / (double)thearc
->radius
);
177 E1
.x
= (float)thearc
->position
.x
+
178 (float)thearc
->radius
* (float)cos(nu1
);
179 E1
.y
= (float)thearc
->position
.y
+
180 (float)thearc
->yaxis
* (float)sin(nu1
);
181 E2
.x
= (float)thearc
->position
.x
+
182 (float)thearc
->radius
* (float)cos(nu2
);
183 E2
.y
= (float)thearc
->position
.y
+
184 (float)thearc
->yaxis
* (float)sin(nu2
);
185 Ep1
.x
= -(float)thearc
->radius
* (float)sin(nu1
);
186 Ep1
.y
= (float)thearc
->yaxis
* (float)cos(nu1
);
187 Ep2
.x
= -(float)thearc
->radius
* (float)sin(nu2
);
188 Ep2
.y
= (float)thearc
->yaxis
* (float)cos(nu2
);
190 P1
.x
= (int)(roundf(E1
.x
));
191 P1
.y
= (int)(roundf(E1
.y
));
193 tansq
= tan((nu2
- nu1
) / 2.0);
195 alpha
= sin(nu2
- nu1
) * 0.33333 * (sqrt(4 + (3 * tansq
)) - 1);
197 /* If the arc 1st point is not the same as the previous path point,
198 * then add a straight line to the 1st arc point (mimics PostScript
202 if (startpoint
&& (i
== 0)) {
203 if ((startpoint
->x
!= P1
.x
) || (startpoint
->y
!= P1
.y
)) {
204 NEW_POLY(newpoly
, thepath
);
205 polydefaults(*newpoly
, 2, startpoint
->x
, startpoint
->y
);
206 (*newpoly
)->style
= thearc
->style
;
207 (*newpoly
)->color
= thearc
->color
;
208 (*newpoly
)->width
= thearc
->width
;
209 (*newpoly
)->points
[1].x
= P1
.x
;
210 (*newpoly
)->points
[1].y
= P1
.y
;
214 NEW_SPLINE(newspline
, thepath
);
215 splinedefaults(*newspline
, 0, 0);
216 (*newspline
)->style
= thearc
->style
;
217 (*newspline
)->color
= thearc
->color
;
218 (*newspline
)->width
= thearc
->width
;
220 (*newspline
)->ctrl
[0].x
= P1
.x
;
221 (*newspline
)->ctrl
[0].y
= P1
.y
;
223 (*newspline
)->ctrl
[1].x
= (int)(roundf(E1
.x
+ alpha
* Ep1
.x
));
224 (*newspline
)->ctrl
[1].y
= (int)(roundf(E1
.y
+ alpha
* Ep1
.y
));
226 (*newspline
)->ctrl
[2].x
= (int)(roundf(E2
.x
- alpha
* Ep2
.x
));
227 (*newspline
)->ctrl
[2].y
= (int)(roundf(E2
.y
- alpha
* Ep2
.y
));
229 (*newspline
)->ctrl
[3].x
= (int)(roundf(E2
.x
));
230 (*newspline
)->ctrl
[3].y
= (int)(roundf(E2
.y
));
232 calcspline(*newspline
);
236 free_single((genericptr
)thearc
);
239 /*----------------------------------------------------------------------*/
240 /* Calculate points for an arc */
241 /*----------------------------------------------------------------------*/
243 void calcarc(arcptr thearc
)
249 /* assume that angle2 > angle1 always: must be guaranteed by other routines */
251 sarc
= (int)(thearc
->angle2
- thearc
->angle1
) * RSTEPS
;
252 thearc
->number
= (sarc
/ 360) + 1;
253 if (sarc
% 360 != 0) thearc
->number
++;
255 delta
= RADFAC
* ((float)(thearc
->angle2
- thearc
->angle1
) / (thearc
->number
- 1));
256 theta
= thearc
->angle1
* RADFAC
;
258 for (idx
= 0; idx
< thearc
->number
- 1; idx
++) {
259 thearc
->points
[idx
].x
= (float)thearc
->position
.x
+
260 fabs((float)thearc
->radius
) * cos(theta
);
261 thearc
->points
[idx
].y
= (float)thearc
->position
.y
+
262 (float)thearc
->yaxis
* sin(theta
);
266 /* place last point exactly to avoid roundoff error */
268 theta
= thearc
->angle2
* RADFAC
;
269 thearc
->points
[thearc
->number
- 1].x
= (float)thearc
->position
.x
+
270 fabs((float)thearc
->radius
) * cos(theta
);
271 thearc
->points
[thearc
->number
- 1].y
= (float)thearc
->position
.y
+
272 (float)thearc
->yaxis
* sin(theta
);
274 if (thearc
->radius
< 0) reversefpoints(thearc
->points
, thearc
->number
);
277 /*------------------------------------------------------------------------*/
278 /* Create a Bezier curve approximation from control points */
279 /* (using PostScript formula for Bezier cubic curve) */
280 /*------------------------------------------------------------------------*/
283 float parsq
[INTSEGS
];
284 float parcb
[INTSEGS
];
291 for (idx
= 0; idx
< INTSEGS
; idx
++) {
292 t
= (float)(idx
+ 1) / (INTSEGS
+ 1);
295 parcb
[idx
] = parsq
[idx
] * t
;
299 /*------------------------------------------------------------------------*/
300 /* Compute spline coefficients */
301 /*------------------------------------------------------------------------*/
303 void computecoeffs(splineptr thespline
, float *ax
, float *bx
, float *cx
,
304 float *ay
, float *by
, float *cy
)
306 *cx
= 3.0 * (float)(thespline
->ctrl
[1].x
- thespline
->ctrl
[0].x
);
307 *bx
= 3.0 * (float)(thespline
->ctrl
[2].x
- thespline
->ctrl
[1].x
) - *cx
;
308 *ax
= (float)(thespline
->ctrl
[3].x
- thespline
->ctrl
[0].x
) - *cx
- *bx
;
310 *cy
= 3.0 * (float)(thespline
->ctrl
[1].y
- thespline
->ctrl
[0].y
);
311 *by
= 3.0 * (float)(thespline
->ctrl
[2].y
- thespline
->ctrl
[1].y
) - *cy
;
312 *ay
= (float)(thespline
->ctrl
[3].y
- thespline
->ctrl
[0].y
) - *cy
- *by
;
315 /*------------------------------------------------------------------------*/
317 void calcspline(splineptr thespline
)
319 float ax
, bx
, cx
, ay
, by
, cy
;
322 computecoeffs(thespline
, &ax
, &bx
, &cx
, &ay
, &by
, &cy
);
323 for (idx
= 0; idx
< INTSEGS
; idx
++) {
324 thespline
->points
[idx
].x
= ax
* parcb
[idx
] + bx
* parsq
[idx
] +
325 cx
* par
[idx
] + (float)thespline
->ctrl
[0].x
;
326 thespline
->points
[idx
].y
= ay
* parcb
[idx
] + by
* parsq
[idx
] +
327 cy
* par
[idx
] + (float)thespline
->ctrl
[0].y
;
331 /*------------------------------------------------------------------------*/
332 /* Find the (x,y) position and tangent rotation of a point on a spline */
333 /*------------------------------------------------------------------------*/
335 void findsplinepos(splineptr thespline
, float t
, XPoint
*retpoint
, float *retrot
)
337 float ax
, bx
, cx
, ay
, by
, cy
;
342 computecoeffs(thespline
, &ax
, &bx
, &cx
, &ay
, &by
, &cy
);
343 retpoint
->x
= (short)(ax
* tcb
+ bx
* tsq
+ cx
* t
+ (float)thespline
->ctrl
[0].x
);
344 retpoint
->y
= (short)(ay
* tcb
+ by
* tsq
+ cy
* t
+ (float)thespline
->ctrl
[0].y
);
346 if (retrot
!= NULL
) {
347 dxdt
= (double)(3 * ax
* tsq
+ 2 * bx
* t
+ cx
);
348 dydt
= (double)(3 * ay
* tsq
+ 2 * by
* t
+ cy
);
349 *retrot
= INVRFAC
* atan2(dxdt
, dydt
); /* reversed y, x */
350 if (*retrot
< 0) *retrot
+= 360;
354 /*------------------------------------------------------------------------*/
355 /* floating-point version of the above */
356 /*------------------------------------------------------------------------*/
358 void ffindsplinepos(splineptr thespline
, float t
, XfPoint
*retpoint
)
360 float ax
, bx
, cx
, ay
, by
, cy
;
364 computecoeffs(thespline
, &ax
, &bx
, &cx
, &ay
, &by
, &cy
);
365 retpoint
->x
= ax
* tcb
+ bx
* tsq
+ cx
* t
+ (float)thespline
->ctrl
[0].x
;
366 retpoint
->y
= ay
* tcb
+ by
* tsq
+ cy
* t
+ (float)thespline
->ctrl
[0].y
;
369 /*------------------------------------------------------------------------*/
370 /* Find the closest distance between a point and a spline and return the */
371 /* fractional distance along the spline of this point. */
372 /*------------------------------------------------------------------------*/
374 float findsplinemin(splineptr thespline
, XPoint
*upoint
)
376 XfPoint
*spt
, flpt
, newspt
;
377 float minval
= 1000000, tval
, hval
, ndist
;
380 flpt
.x
= (float)(upoint
->x
);
381 flpt
.y
= (float)(upoint
->y
);
383 /* get estimate from precalculated spline points */
385 for (spt
= thespline
->points
; spt
< thespline
->points
+ INTSEGS
;
387 ndist
= fsqwirelen(spt
, &flpt
);
388 if (ndist
< minval
) {
390 ival
= (short)(spt
- thespline
->points
);
393 tval
= (float)(ival
+ 1) / (INTSEGS
+ 1);
394 hval
= 0.5 / (INTSEGS
+ 1);
396 /* short fixed iterative loop to converge on minimum t */
398 for (j
= 0; j
< 5; j
++) {
400 ffindsplinepos(thespline
, tval
, &newspt
);
401 ndist
= fsqwirelen(&newspt
, &flpt
);
402 if (ndist
< minval
) minval
= ndist
;
405 ffindsplinepos(thespline
, tval
, &newspt
);
406 ndist
= fsqwirelen(&newspt
, &flpt
);
407 if (ndist
< minval
) minval
= ndist
;
414 if ((float)sqwirelen(&(thespline
->ctrl
[0]), upoint
) < minval
) tval
= 0;
416 else if (tval
> 0.9) {
417 if ((float)sqwirelen(&(thespline
->ctrl
[3]), upoint
) < minval
) tval
= 1;
422 /*----------------------------------------------------------------------*/
423 /* Convert a polygon to a Bezier curve path */
424 /* Curve must be selected and there must be only one selection. */
426 /* Note that this routine will draw inside the perimeter of a convex */
427 /* hull. A routine that places spline endpoints on the polygon */
428 /* vertices will draw outside the perimeter of a convex hull. An */
429 /* optimal algorithm presumably zeros the total area between the curve */
430 /* and the polygon (positive and negative), but I haven't worked out */
431 /* what that solution is. The algorithm below seems good enough for */
433 /*----------------------------------------------------------------------*/
435 void converttocurve()
438 splineptr
*newspline
;
442 XPoint firstpoint
, lastpoint
, initpoint
;
445 if (areawin
->selects
!= 1) return;
447 thispoly
= TOPOLY(topobject
->plist
+ (*areawin
->selectlist
));
448 if (ELEMENTTYPE(thispoly
) != POLYGON
) return;
449 if (thispoly
->number
< 3) return; /* Will not convert */
451 standard_element_delete(ERASE
);
452 if ((thispoly
->style
& UNCLOSED
) && (thispoly
->number
== 3)) {
453 NEW_SPLINE(newspline
, topobject
);
454 splinedefaults(*newspline
, 0, 0);
455 (*newspline
)->ctrl
[0] = thispoly
->points
[0];
456 (*newspline
)->ctrl
[1] = thispoly
->points
[1];
457 (*newspline
)->ctrl
[2] = thispoly
->points
[1];
458 (*newspline
)->ctrl
[3] = thispoly
->points
[2];
461 numpoints
= thispoly
->number
;
463 /* If the polygon is closed but the first and last points */
464 /* overlap, treat the last point as if it doesn't exist. */
466 if (!(thispoly
->style
& UNCLOSED
))
467 if ((thispoly
->points
[0].x
== thispoly
->points
[thispoly
->number
- 1].x
)
468 && (thispoly
->points
[0].y
==
469 thispoly
->points
[thispoly
->number
- 1].y
))
472 NEW_PATH(newpath
, topobject
);
473 pathdefaults(*newpath
, 0, 0);
474 (*newpath
)->style
= thispoly
->style
;
476 if (!(thispoly
->style
& UNCLOSED
)) {
477 lastpoint
= thispoly
->points
[numpoints
- 1];
478 initpoint
.x
= (lastpoint
.x
+ thispoly
->points
[0].x
) / 2;
479 initpoint
.y
= (lastpoint
.y
+ thispoly
->points
[0].y
) / 2;
480 firstpoint
.x
= (thispoly
->points
[0].x
481 + thispoly
->points
[1].x
) / 2;
482 firstpoint
.y
= (thispoly
->points
[0].y
483 + thispoly
->points
[1].y
) / 2;
485 NEW_SPLINE(newspline
, (*newpath
));
486 splinedefaults(*newspline
, 0, 0);
487 (*newspline
)->ctrl
[0] = initpoint
;
488 (*newspline
)->ctrl
[1] = thispoly
->points
[0];
489 (*newspline
)->ctrl
[2] = thispoly
->points
[0];
490 (*newspline
)->ctrl
[3] = firstpoint
;
491 calcspline(*newspline
);
494 firstpoint
= thispoly
->points
[0];
496 for (i
= 0; i
< numpoints
- ((!(thispoly
->style
& UNCLOSED
)) ?
498 lastpoint
.x
= (thispoly
->points
[i
+ 1].x
499 + thispoly
->points
[i
+ 2].x
) / 2;
500 lastpoint
.y
= (thispoly
->points
[i
+ 1].y
501 + thispoly
->points
[i
+ 2].y
) / 2;
503 NEW_SPLINE(newspline
, (*newpath
));
504 splinedefaults(*newspline
, 0, 0);
505 (*newspline
)->ctrl
[0] = firstpoint
;
506 (*newspline
)->ctrl
[1] = thispoly
->points
[i
+ 1];
507 (*newspline
)->ctrl
[2] = thispoly
->points
[i
+ 1];
508 (*newspline
)->ctrl
[3] = lastpoint
;
509 firstpoint
= lastpoint
;
510 calcspline(*newspline
);
512 if (!(thispoly
->style
& UNCLOSED
))
513 lastpoint
= initpoint
;
515 lastpoint
= thispoly
->points
[i
+ 2];
517 NEW_SPLINE(newspline
, (*newpath
));
518 splinedefaults(*newspline
, 0, 0);
519 (*newspline
)->ctrl
[0] = firstpoint
;
520 (*newspline
)->ctrl
[1] = thispoly
->points
[i
+ 1];
521 (*newspline
)->ctrl
[2] = thispoly
->points
[i
+ 1];
522 (*newspline
)->ctrl
[3] = lastpoint
;
524 calcspline(*newspline
);
525 calcbbox(areawin
->topinstance
);
527 drawarea(NULL
, NULL
, NULL
);
530 /*----------------------------------------------------------------------*/
531 /* Find closest point of a polygon to the cursor */
532 /*----------------------------------------------------------------------*/
534 short closepointdistance(polyptr curpoly
, XPoint
*cursloc
, short *mindist
)
537 XPoint
*curpt
, *savept
;
539 curpt
= savept
= curpoly
->points
;
540 *mindist
= wirelength(curpt
, cursloc
);
541 while (++curpt
< curpoly
->points
+ curpoly
->number
) {
542 curdist
= wirelength(curpt
, cursloc
);
543 if (curdist
< *mindist
) {
548 return (short)(savept
- curpoly
->points
);
551 /*----------------------------------------------------------------------------*/
552 /* Find closest point of a polygon to the cursor */
553 /*----------------------------------------------------------------------------*/
555 short closepoint(polyptr curpoly
, XPoint
*cursloc
)
558 return closepointdistance(curpoly
, cursloc
, &mindist
);
561 /*----------------------------------------------------------------------------*/
562 /* Find the distance to the closest point of a polygon to the cursor */
563 /*----------------------------------------------------------------------------*/
565 short closedistance(polyptr curpoly
, XPoint
*cursloc
)
568 closepointdistance(curpoly
, cursloc
, &mindist
);
572 /*----------------------------------------------------------------------------*/
573 /* Coordinate system transformations */
574 /*----------------------------------------------------------------------------*/
576 /*------------------------------------------------------------------------------*/
577 /* Check screen bounds: minimum, maximum scale and translation is determined */
578 /* by values which fit in an X11 type XPoint (short int). If the window */
579 /* extremes exceed type short when mapped to user space, or if the page */
580 /* bounds exceed type short when mapped to X11 window space, return error. */
581 /*------------------------------------------------------------------------------*/
587 /* check window-to-user space */
589 lval
= 2 * (long)((float) (areawin
->width
) / areawin
->vscale
) +
590 (long)areawin
->pcorner
.x
;
591 if (lval
!= (long)((short)lval
)) return -1;
592 lval
= 2 * (long)((float) (areawin
->height
) / areawin
->vscale
) +
593 (long)areawin
->pcorner
.y
;
594 if (lval
!= (long)((short)lval
)) return -1;
596 /* check user-to-window space */
598 lval
= (long)((float)(topobject
->bbox
.lowerleft
.x
- areawin
->pcorner
.x
) *
600 if (lval
!= (long)((short)lval
)) return -1;
601 lval
= (long)areawin
->height
- (long)((float)(topobject
->bbox
.lowerleft
.y
-
602 areawin
->pcorner
.y
) * areawin
->vscale
);
603 if (lval
!= (long)((short)lval
)) return -1;
605 lval
= (long)((float)(topobject
->bbox
.lowerleft
.x
+ topobject
->bbox
.width
-
606 areawin
->pcorner
.x
) * areawin
->vscale
);
607 if (lval
!= (long)((short)lval
)) return -1;
608 lval
= (long)areawin
->height
- (long)((float)(topobject
->bbox
.lowerleft
.y
+
609 topobject
->bbox
.height
- areawin
->pcorner
.y
) * areawin
->vscale
);
610 if (lval
!= (long)((short)lval
)) return -1;
615 /*------------------------------------------------------------------------*/
616 /* Transform X-window coordinate to xcircuit coordinate system */
617 /*------------------------------------------------------------------------*/
619 void window_to_user(short xw
, short yw
, XPoint
*upt
)
623 tmpx
= (float)xw
/ areawin
->vscale
+ (float)areawin
->pcorner
.x
;
624 tmpy
= (float)(areawin
->height
- yw
) / areawin
->vscale
+
625 (float)areawin
->pcorner
.y
;
627 tmpx
+= (tmpx
> 0) ? 0.5 : -0.5;
628 tmpy
+= (tmpy
> 0) ? 0.5 : -0.5;
630 upt
->x
= (short)tmpx
;
631 upt
->y
= (short)tmpy
;
634 /*------------------------------------------------------------------------*/
635 /* Transform xcircuit coordinate back to X-window coordinate system */
636 /*------------------------------------------------------------------------*/
638 void user_to_window(XPoint upt
, XPoint
*wpt
)
642 tmpx
= (float)(upt
.x
- areawin
->pcorner
.x
) * areawin
->vscale
;
643 tmpy
= (float)areawin
->height
- (float)(upt
.y
- areawin
->pcorner
.y
)
646 tmpx
+= (tmpx
> 0) ? 0.5 : -0.5;
647 tmpy
+= (tmpy
> 0) ? 0.5 : -0.5;
649 wpt
->x
= (short)tmpx
;
650 wpt
->y
= (short)tmpy
;
653 /*----------------------------------------------------------------------*/
654 /* Transformations in the object hierarchy */
655 /*----------------------------------------------------------------------*/
657 /*----------------------------------------------------------------------*/
658 /* Return rotation relative to a specific CTM */
659 /*----------------------------------------------------------------------*/
661 float UGetCTMRotation(Matrix
*ctm
)
663 float rads
= (float)atan2((double)(ctm
->d
), (double)(ctm
->a
));
664 return rads
/ RADFAC
;
667 /*----------------------------------------------------------------------*/
668 /* Return rotation relative to the top level */
669 /* Note that UTopRotation() is also the rotation relative to the window */
670 /* since the top-level drawing page is always upright relative to the */
671 /* window. Thus, there is no routine UTopDrawingRotation(). */
672 /*----------------------------------------------------------------------*/
676 return UGetCTMRotation(DCTM
);
679 /*----------------------------------------------------------------------*/
680 /* Return scale relative to a specific CTM */
681 /*----------------------------------------------------------------------*/
683 float UGetCTMScale(Matrix
*ctm
)
685 return (float)(sqrt((double)(ctm
->a
* ctm
->a
+ ctm
->d
* ctm
->d
)));
688 /*----------------------------------------------------------------------*/
689 /* Return scale relative to window */
690 /*----------------------------------------------------------------------*/
694 return UGetCTMScale(DCTM
);
697 /*----------------------------------------------------------------------*/
698 /* Return scale multiplied by length */
699 /*----------------------------------------------------------------------*/
701 float UTopTransScale(float length
)
703 return (float)(length
* UTopScale());
706 /*----------------------------------------------------------------------*/
707 /* Return scale relative to the top-level schematic (not the window) */
708 /*----------------------------------------------------------------------*/
710 float UTopDrawingScale()
713 UCopyCTM(DCTM
, &lctm
);
717 UPreMultCTMbyMat(&wctm
, &lctm
);
718 return UGetCTMScale(&wctm
);
721 /*----------------------------------------------------------------------*/
722 /* Return position offset relative to a specific CTM */
723 /*----------------------------------------------------------------------*/
725 void UGetCTMOffset(Matrix
*ctm
, int *offx
, int *offy
)
727 if (offx
) *offx
= (int)ctm
->c
;
728 if (offy
) *offy
= (int)ctm
->f
;
731 /*----------------------------------------------------------------------*/
732 /* Return position offset relative to top-level */
733 /*----------------------------------------------------------------------*/
735 void UTopOffset(int *offx
, int *offy
)
737 UGetCTMOffset(DCTM
, offx
, offy
);
740 /*----------------------------------------------------------------------*/
741 /* Return postion relative to the top-level schematic (not the window) */
742 /*----------------------------------------------------------------------*/
744 void UTopDrawingOffset(int *offx
, int *offy
)
747 UCopyCTM(DCTM
, &lctm
);
751 UPreMultCTMbyMat(&wctm
, &lctm
);
752 UGetCTMOffset(&wctm
, offx
, offy
);
755 /*----------------------------------------------------------------------*/
756 /* Get the cursor position */
757 /*----------------------------------------------------------------------*/
762 int nullint
, xpos
, ypos
;
766 /* Apparently this routine can get called before the display is valid */
767 if ((areawin
->area
== NULL
) || (dpy
== NULL
)) {
768 newpos
.x
= newpos
.y
= 0;
773 /* Don't use areawin->window; if called from inside an object */
774 /* (e.g., "here" in a Tcl expression), areawin->window will be */
775 /* an off-screen pixmap, and cause a crash. */
777 if (Tk_WindowId(areawin
->area
) == (Window
)NULL
) {
778 newpos
.x
= newpos
.y
= 0;
783 XQueryPointer(dpy
, Tk_WindowId(areawin
->area
), &nullwin
, &nullwin
,
784 &nullint
, &nullint
, &xpos
, &ypos
, &nullui
);
786 XQueryPointer_TkW32(dpy
, Tk_WindowId(areawin
->area
), &nullwin
, &nullwin
,
787 &nullint
, &nullint
, &xpos
, &ypos
, &nullui
);
790 XQueryPointer(dpy
, areawin
->window
, &nullwin
, &nullwin
, &nullint
,
791 &nullint
, &xpos
, &ypos
, &nullui
);
800 /*----------------------------------------------------------------------*/
801 /* Get the cursor position and translate to user coordinates */
802 /*----------------------------------------------------------------------*/
804 XPoint
UGetCursorPos()
806 XPoint winpos
, userpos
;
808 if (areawin
->area
== NULL
) {
809 winpos
.x
= winpos
.y
= 0;
812 winpos
= UGetCursor();
814 window_to_user(winpos
.x
, winpos
.y
, &userpos
);
819 /*----------------------------------------------------------------------*/
820 /* Translate a point to the nearest snap-to grid point */
821 /*----------------------------------------------------------------------*/
822 /* user coordinates to user coordinates version */
824 void u2u_snap(XPoint
*uvalue
)
829 if (areawin
->snapto
) {
830 tmpx
= (float)uvalue
->x
/ xobjs
.pagelist
[areawin
->page
]->snapspace
;
832 tmpix
= (float)((int)(tmpx
+ 0.5));
834 tmpix
= (float)((int)(tmpx
- 0.5));
836 tmpy
= (float)uvalue
->y
/ xobjs
.pagelist
[areawin
->page
]->snapspace
;
838 tmpiy
= (float)((int)(tmpy
+ 0.5));
840 tmpiy
= (float)((int)(tmpy
- 0.5));
842 tmpix
*= xobjs
.pagelist
[areawin
->page
]->snapspace
;
843 tmpix
+= (tmpix
> 0) ? 0.5 : -0.5;
844 tmpiy
*= xobjs
.pagelist
[areawin
->page
]->snapspace
;
845 tmpiy
+= (tmpiy
> 0) ? 0.5 : -0.5;
847 uvalue
->x
= (int)tmpix
;
848 uvalue
->y
= (int)tmpiy
;
852 /*------------------------------------------------------------------------*/
853 /* window coordinates to user coordinates version */
854 /*------------------------------------------------------------------------*/
856 void snap(short valuex
, short valuey
, XPoint
*returnpt
)
858 window_to_user(valuex
, valuey
, returnpt
);
862 /*------------------------------------------------------------------------*/
863 /* Transform object coordinates through scale, translation, and rotation */
864 /* This routine attempts to match the PostScript definition of trans- */
865 /* formation matrices. */
866 /*------------------------------------------------------------------------*/
868 /*------------------------------------------------------------------------*/
869 /* Current transformation matrix manipulation routines */
870 /*------------------------------------------------------------------------*/
872 void UResetCTM(Matrix
*ctm
)
876 ctm
->c
= ctm
->f
= 0; /* 0.5 for nearest-int real->int conversion? */
879 if (ctm
== DCTM
&& areawin
->redraw_ongoing
)
880 xc_cairo_set_matrix(ctm
);
881 #endif /* HAVE_CAIRO */
884 /*------------------------------------------------------------------------*/
886 void InvertCTM(Matrix
*ctm
)
888 float det
= ctm
->a
* ctm
->e
- ctm
->b
* ctm
->d
;
889 float tx
= ctm
->b
* ctm
->f
- ctm
->c
* ctm
->e
;
890 float ty
= ctm
->d
* ctm
->c
- ctm
->a
* ctm
->f
;
894 ctm
->b
= -ctm
->b
/ det
;
895 ctm
->d
= -ctm
->d
/ det
;
897 ctm
->a
= ctm
->e
/ det
;
903 if (ctm
== DCTM
&& areawin
->redraw_ongoing
)
904 xc_cairo_set_matrix(ctm
);
905 #endif /* HAVE_CAIRO */
908 /*------------------------------------------------------------------------*/
910 void UCopyCTM(fctm
, tctm
)
921 if (tctm
== DCTM
&& areawin
->redraw_ongoing
)
922 xc_cairo_set_matrix(tctm
);
923 #endif /* HAVE_CAIRO */
926 /*-------------------------------------------------------------------------*/
927 /* Multiply CTM by current screen position and scale to get transformation */
928 /* matrix from a user point to the X11 window */
929 /*-------------------------------------------------------------------------*/
931 void UMakeWCTM(Matrix
*ctm
)
933 ctm
->a
*= areawin
->vscale
;
934 ctm
->b
*= areawin
->vscale
;
935 ctm
->c
= (ctm
->c
- (float)areawin
->pcorner
.x
) * areawin
->vscale
938 ctm
->d
*= -areawin
->vscale
;
939 ctm
->e
*= -areawin
->vscale
;
940 ctm
->f
= (float)areawin
->height
+ ((float)areawin
->pcorner
.y
- ctm
->f
) *
941 areawin
->vscale
+ areawin
->pany
;
944 if (ctm
== DCTM
&& areawin
->redraw_ongoing
)
945 xc_cairo_set_matrix(ctm
);
946 #endif /* HAVE_CAIRO */
949 /*------------------------------------------------------------------------*/
951 void UMultCTM(Matrix
*ctm
, XPoint position
, float scale
, float rotate
)
953 float tmpa
, tmpb
, tmpd
, tmpe
, yscale
;
954 float mata
, matb
, matc
;
955 double drot
= (double)rotate
* RADFAC
;
957 yscale
= abs(scale
); /* -scale implies flip in x direction only */
959 tmpa
= scale
* cos(drot
);
960 tmpb
= yscale
* sin(drot
);
961 tmpd
= -scale
* sin(drot
);
962 tmpe
= yscale
* cos(drot
);
964 mata
= ctm
->a
* tmpa
+ ctm
->d
* tmpb
;
965 matb
= ctm
->b
* tmpa
+ ctm
->e
* tmpb
;
966 matc
= ctm
->c
* tmpa
+ ctm
->f
* tmpb
+ position
.x
;
968 ctm
->d
= ctm
->d
* tmpe
+ ctm
->a
* tmpd
;
969 ctm
->e
= ctm
->e
* tmpe
+ ctm
->b
* tmpd
;
970 ctm
->f
= ctm
->f
* tmpe
+ ctm
->c
* tmpd
+ position
.y
;
977 if (ctm
== DCTM
&& areawin
->redraw_ongoing
)
978 xc_cairo_set_matrix(ctm
);
979 #endif /* HAVE_CAIRO */
982 /*----------------------------------------------------------------------*/
983 /* Slanting function x' = x + beta * y, y' = y */
984 /*----------------------------------------------------------------------*/
986 void USlantCTM(Matrix
*ctm
, float beta
)
988 ctm
->b
+= ctm
->a
* beta
;
989 ctm
->e
+= ctm
->d
* beta
;
992 if (ctm
== DCTM
&& areawin
->redraw_ongoing
)
993 xc_cairo_set_matrix(ctm
);
994 #endif /* HAVE_CAIRO */
998 /*----------------------------------------------------------------------*/
999 /* Transform text to make it right-side up within 90 degrees of page */
1000 /* NOTE: This is not yet resolved, as xcircuit does not agree with */
1001 /* PostScript in a few cases! */
1002 /*----------------------------------------------------------------------*/
1004 void UPreScaleCTM(Matrix
*ctm
)
1006 /* negative X scale (-1, +1) */
1007 if ((ctm
->a
< -EPS
) || ((ctm
->a
< EPS
) && (ctm
->a
> -EPS
) &&
1008 ((ctm
->d
* ctm
->b
) < 0))) {
1013 /* negative Y scale (+1, -1) */
1019 /* At 90, 270 degrees need special attention to avoid discrepencies */
1020 /* with the PostScript output due to roundoff error. This code */
1021 /* matches what PostScript produces. */
1024 if (ctm
== DCTM
&& areawin
->redraw_ongoing
)
1025 xc_cairo_set_matrix(ctm
);
1026 #endif /* HAVE_CAIRO */
1029 /*----------------------------------------------------------------------*/
1030 /* Adjust anchoring and CTM as necessary for flip invariance */
1031 /*----------------------------------------------------------------------*/
1033 short flipadjust(short anchor
)
1035 short tmpanchor
= anchor
& (~FLIPINV
);
1037 if (anchor
& FLIPINV
) {
1038 if (((DCTM
)->a
< -EPS
) || (((DCTM
)->a
< EPS
) && ((DCTM
)->a
> -EPS
) &&
1039 (((DCTM
)->d
* (DCTM
)->b
) < 0))) {
1040 if ((tmpanchor
& (RIGHT
| NOTLEFT
)) != NOTLEFT
)
1041 tmpanchor
^= (RIGHT
| NOTLEFT
);
1043 /* NOTE: Justification does not change under flip invariance. */
1045 if ((DCTM
)->e
> EPS
) {
1046 if ((tmpanchor
& (TOP
| NOTBOTTOM
)) != NOTBOTTOM
)
1047 tmpanchor
^= (TOP
| NOTBOTTOM
);
1054 /*------------------------------------------------------------------------*/
1056 void UPreMultCTM(Matrix
*ctm
, XPoint position
, float scale
, float rotate
)
1058 float tmpa
, tmpb
, tmpd
, tmpe
, yscale
;
1060 double drot
= (double)rotate
* RADFAC
;
1062 yscale
= abs(scale
); /* negative scale value implies flip in x only */
1064 tmpa
= scale
* cos(drot
);
1065 tmpb
= yscale
* sin(drot
);
1066 tmpd
= -scale
* sin(drot
);
1067 tmpe
= yscale
* cos(drot
);
1069 ctm
->c
+= ctm
->a
* position
.x
+ ctm
->b
* position
.y
;
1070 ctm
->f
+= ctm
->d
* position
.x
+ ctm
->e
* position
.y
;
1072 mata
= ctm
->a
* tmpa
+ ctm
->b
* tmpd
;
1073 ctm
->b
= ctm
->a
* tmpb
+ ctm
->b
* tmpe
;
1075 matd
= ctm
->d
* tmpa
+ ctm
->e
* tmpd
;
1076 ctm
->e
= ctm
->d
* tmpb
+ ctm
->e
* tmpe
;
1082 if (ctm
== DCTM
&& areawin
->redraw_ongoing
)
1083 xc_cairo_set_matrix(ctm
);
1084 #endif /* HAVE_CAIRO */
1087 /*----------------------------------------------------------------------*/
1088 /* Direct Matrix-Matrix multiplication */
1089 /*----------------------------------------------------------------------*/
1091 void UPreMultCTMbyMat(Matrix
*ctm
, Matrix
*pre
)
1095 mata
= pre
->a
* ctm
->a
+ pre
->d
* ctm
->b
;
1096 ctm
->c
+= pre
->c
* ctm
->a
+ pre
->f
* ctm
->b
;
1097 ctm
->b
= pre
->b
* ctm
->a
+ pre
->e
* ctm
->b
;
1100 matd
= pre
->a
* ctm
->d
+ pre
->d
* ctm
->e
;
1101 ctm
->f
+= pre
->c
* ctm
->d
+ pre
->f
* ctm
->e
;
1102 ctm
->e
= pre
->b
* ctm
->d
+ pre
->e
* ctm
->e
;
1106 if (ctm
== DCTM
&& areawin
->redraw_ongoing
)
1107 xc_cairo_set_matrix(ctm
);
1108 #endif /* HAVE_CAIRO */
1111 /*------------------------------------------------------------------------*/
1113 void UTransformbyCTM(Matrix
*ctm
, XPoint
*ipoints
, XPoint
*points
, short number
)
1115 pointlist current
, ptptr
= points
;
1117 /* short tmpx; (jdk) */
1119 for (current
= ipoints
; current
< ipoints
+ number
; current
++, ptptr
++) {
1120 fx
= ctm
->a
* (float)current
->x
+ ctm
->b
* (float)current
->y
+ ctm
->c
;
1121 fy
= ctm
->d
* (float)current
->x
+ ctm
->e
* (float)current
->y
+ ctm
->f
;
1123 ptptr
->x
= (fx
>= 0) ? (short)(fx
+ 0.5) : (short)(fx
- 0.5);
1124 ptptr
->y
= (fy
>= 0) ? (short)(fy
+ 0.5) : (short)(fy
- 0.5);
1128 /*------------------------------------------------------------------------*/
1129 /* (same as above routine but using type (float) for point values; this */
1130 /* is for calculation of Bezier curve internal points. */
1131 /*------------------------------------------------------------------------*/
1133 void UfTransformbyCTM(Matrix
*ctm
, XfPoint
*fpoints
, XPoint
*points
, short number
)
1136 pointlist
new = points
;
1139 for (current
= fpoints
; current
< fpoints
+ number
; current
++, new++) {
1140 fx
= ctm
->a
* current
->x
+ ctm
->b
* current
->y
+ ctm
->c
;
1141 fy
= ctm
->d
* current
->x
+ ctm
->e
* current
->y
+ ctm
->f
;
1142 new->x
= (fx
>= 0) ? (short)(fx
+ 0.5) : (short)(fx
- 0.5);
1143 new->y
= (fy
>= 0) ? (short)(fy
+ 0.5) : (short)(fy
- 0.5);
1147 /*------------------------------------------------------------------------*/
1151 Matrixptr lastmatrix
;
1153 if (areawin
->MatStack
== NULL
) {
1154 Wprintf("Matrix stack pop error");
1157 lastmatrix
= areawin
->MatStack
->nextmatrix
;
1158 free(areawin
->MatStack
);
1159 areawin
->MatStack
= lastmatrix
;
1162 if (areawin
->area
) {
1163 xc_cairo_set_matrix(lastmatrix
);
1165 #endif /* HAVE_CAIRO */
1168 /*------------------------------------------------------------------------*/
1174 nmatrix
= (Matrixptr
)malloc(sizeof(Matrix
));
1175 if (areawin
->MatStack
== NULL
)
1178 UCopyCTM(areawin
->MatStack
, nmatrix
);
1179 nmatrix
->nextmatrix
= areawin
->MatStack
;
1180 areawin
->MatStack
= nmatrix
;
1183 /*------------------------------------------------------------------------*/
1185 void UTransformPoints(XPoint
*points
, XPoint
*newpoints
, short number
,
1186 XPoint atpt
, float scale
, float rotate
)
1191 UMultCTM(&LCTM
, atpt
, scale
, rotate
);
1192 UTransformbyCTM(&LCTM
, points
, newpoints
, number
);
1195 /*----------------------------------------------------*/
1196 /* Transform points inward to next hierarchical level */
1197 /*----------------------------------------------------*/
1199 void InvTransformPoints(XPoint
*points
, XPoint
*newpoints
, short number
,
1200 XPoint atpt
, float scale
, float rotate
)
1205 UPreMultCTM(&LCTM
, atpt
, scale
, rotate
);
1207 UTransformbyCTM(&LCTM
, points
, newpoints
, number
);
1210 /*----------------------------------------------------------------------*/
1211 /* Adjust wire coords to force a wire to a horizontal or vertical */
1213 /* "pospt" is the target position for the point of interest. */
1214 /* "cycle" is the point number in the polygon of the point of interest. */
1215 /* cycle == -1 is equivalent to the last point of the polygon. */
1216 /* If "strict" is TRUE then single-segment wires are forced manhattan */
1217 /* even if that means that the endpoint drifts from the target point. */
1218 /* If "strict" is FALSE then single-segment wires will become non- */
1219 /* manhattan so that the target point is reached. */
1220 /* NOTE: It might be preferable to add a segment to maintain a */
1221 /* manhattan layout, except that we want to avoid merging nets */
1223 /*----------------------------------------------------------------------*/
1225 void manhattanize(XPoint
*pospt
, polyptr newpoly
, short cycle
, Boolean strict
)
1227 XPoint
*curpt
, *bpt
, *bbpt
, *fpt
, *ffpt
;
1230 if (newpoly
->number
== 1) return; /* sanity check */
1232 if (cycle
== -1 || cycle
== newpoly
->number
- 1) {
1233 curpt
= newpoly
->points
+ newpoly
->number
- 1;
1234 bpt
= newpoly
->points
+ newpoly
->number
- 2;
1237 if (newpoly
->number
> 2)
1238 bbpt
= newpoly
->points
+ newpoly
->number
- 3;
1242 else if (cycle
== 0) {
1243 curpt
= newpoly
->points
;
1244 fpt
= newpoly
->points
+ 1;
1247 if (newpoly
->number
> 2)
1248 ffpt
= newpoly
->points
+ 2;
1253 curpt
= newpoly
->points
+ cycle
;
1254 fpt
= newpoly
->points
+ cycle
+ 1;
1255 bpt
= newpoly
->points
+ cycle
- 1;
1257 bbpt
= newpoly
->points
+ cycle
- 2;
1261 if (cycle
< newpoly
->number
- 2)
1262 ffpt
= newpoly
->points
+ cycle
+ 2;
1267 /* enforce constraints on point behind cycle position */
1271 if (bpt
->x
== bbpt
->x
) bpt
->y
= pospt
->y
;
1272 if (bpt
->y
== bbpt
->y
) bpt
->x
= pospt
->x
;
1275 deltax
= abs(bpt
->x
- pospt
->x
);
1276 deltay
= abs(bpt
->y
- pospt
->y
);
1278 /* Only one segment---just make sure it's horizontal or vertical */
1279 if (deltay
> deltax
) pospt
->x
= bpt
->x
;
1280 else pospt
->y
= bpt
->y
;
1284 /* enforce constraints on point forward of cycle position */
1288 if (fpt
->x
== ffpt
->x
) fpt
->y
= pospt
->y
;
1289 if (fpt
->y
== ffpt
->y
) fpt
->x
= pospt
->x
;
1292 deltax
= abs(fpt
->x
- pospt
->x
);
1293 deltay
= abs(fpt
->y
- pospt
->y
);
1295 /* Only one segment---just make sure it's horizontal or vertical */
1296 if (deltay
> deltax
) pospt
->x
= fpt
->x
;
1297 else pospt
->y
= fpt
->y
;
1302 /*----------------------------------------------------------------------*/
1303 /* Bounding box calculation routines */
1304 /*----------------------------------------------------------------------*/
1306 void bboxcalc(short testval
, short *lowerval
, short *upperval
)
1308 if (testval
< *lowerval
) *lowerval
= testval
;
1309 if (testval
> *upperval
) *upperval
= testval
;
1312 /*----------------------------------------------------------------------*/
1313 /* Bounding box calculation for elements which can be part of a path */
1314 /*----------------------------------------------------------------------*/
1316 void calcextents(genericptr
*bboxgen
, short *llx
, short *lly
,
1317 short *urx
, short *ury
)
1319 switch (ELEMENTTYPE(*bboxgen
)) {
1322 for (bboxpts
= TOPOLY(bboxgen
)->points
; bboxpts
< TOPOLY(bboxgen
)->points
1323 + TOPOLY(bboxgen
)->number
; bboxpts
++) {
1324 bboxcalc(bboxpts
->x
, llx
, urx
);
1325 bboxcalc(bboxpts
->y
, lly
, ury
);
1331 bboxcalc(TOSPLINE(bboxgen
)->ctrl
[0].x
, llx
, urx
);
1332 bboxcalc(TOSPLINE(bboxgen
)->ctrl
[0].y
, lly
, ury
);
1333 bboxcalc(TOSPLINE(bboxgen
)->ctrl
[3].x
, llx
, urx
);
1334 bboxcalc(TOSPLINE(bboxgen
)->ctrl
[3].y
, lly
, ury
);
1335 for (bboxpts
= TOSPLINE(bboxgen
)->points
; bboxpts
<
1336 TOSPLINE(bboxgen
)->points
+ INTSEGS
; bboxpts
++) {
1337 bboxcalc((short)(bboxpts
->x
), llx
, urx
);
1338 bboxcalc((short)(bboxpts
->y
), lly
, ury
);
1344 for (bboxpts
= TOARC(bboxgen
)->points
; bboxpts
< TOARC(bboxgen
)->points
+
1345 TOARC(bboxgen
)->number
; bboxpts
++) {
1346 bboxcalc((short)(bboxpts
->x
), llx
, urx
);
1347 bboxcalc((short)(bboxpts
->y
), lly
, ury
);
1353 /*----------------------------------------------------------------------*/
1354 /* Calculate the bounding box of an object instance */
1355 /*----------------------------------------------------------------------*/
1357 void objinstbbox(objinstptr obbox
, XPoint
*npoints
, int extend
)
1361 points
[0].x
= points
[1].x
= obbox
->bbox
.lowerleft
.x
- extend
;
1362 points
[1].y
= points
[2].y
= obbox
->bbox
.lowerleft
.y
+ obbox
->bbox
.height
1364 points
[2].x
= points
[3].x
= obbox
->bbox
.lowerleft
.x
+ obbox
->bbox
.width
1366 points
[0].y
= points
[3].y
= obbox
->bbox
.lowerleft
.y
- extend
;
1368 UTransformPoints(points
, npoints
, 4, obbox
->position
,
1369 obbox
->scale
, obbox
->rotation
);
1372 /*----------------------------------------------------------------------*/
1373 /* Calculate the bounding box of a label */
1374 /*----------------------------------------------------------------------*/
1376 void labelbbox(labelptr labox
, XPoint
*npoints
, objinstptr callinst
)
1382 tmpext
= ULength(labox
, callinst
, NULL
);
1383 points
[0].x
= points
[1].x
= (labox
->anchor
& NOTLEFT
?
1384 (labox
->anchor
& RIGHT
? -tmpext
.maxwidth
:
1385 -tmpext
.maxwidth
/ 2) : 0);
1386 points
[2].x
= points
[3].x
= points
[0].x
+ tmpext
.maxwidth
;
1387 points
[0].y
= points
[3].y
= (labox
->anchor
& NOTBOTTOM
?
1388 (labox
->anchor
& TOP
? -tmpext
.ascent
:
1389 -(tmpext
.ascent
+ tmpext
.base
) / 2) : -tmpext
.base
)
1391 points
[1].y
= points
[2].y
= points
[0].y
+ tmpext
.ascent
- tmpext
.descent
;
1393 /* separate bounding box for pinlabels and infolabels */
1396 for (j
= 0; j
< 4; j
++)
1397 pinadjust(labox
->anchor
, &points
[j
].x
, &points
[j
].y
, 1);
1399 UTransformPoints(points
, npoints
, 4, labox
->position
,
1400 labox
->scale
, labox
->rotation
);
1403 /*----------------------------------------------------------------------*/
1404 /* Calculate the bounding box of a graphic image */
1405 /*----------------------------------------------------------------------*/
1407 void graphicbbox(graphicptr gp
, XPoint
*npoints
)
1410 int hw
= xcImageGetWidth(gp
->source
) >> 1;
1411 int hh
= xcImageGetHeight(gp
->source
) >> 1;
1413 points
[1].x
= points
[2].x
= hw
;
1414 points
[0].x
= points
[3].x
= -hw
;
1416 points
[0].y
= points
[1].y
= -hh
;
1417 points
[2].y
= points
[3].y
= hh
;
1419 UTransformPoints(points
, npoints
, 4, gp
->position
,
1420 gp
->scale
, gp
->rotation
);
1423 /*--------------------------------------------------------------*/
1424 /* Wrapper for single call to calcbboxsingle() in the netlister */
1425 /*--------------------------------------------------------------*/
1427 void calcinstbbox(genericptr
*bboxgen
, short *llx
, short *lly
, short *urx
,
1430 *llx
= *lly
= 32767;
1431 *urx
= *ury
= -32768;
1433 calcbboxsingle(bboxgen
, areawin
->topinstance
, llx
, lly
, urx
, ury
);
1436 /*----------------------------------------------------------------------*/
1437 /* Bounding box calculation for a single generic element */
1438 /*----------------------------------------------------------------------*/
1440 void calcbboxsingle(genericptr
*bboxgen
, objinstptr thisinst
,
1441 short *llx
, short *lly
, short *urx
, short *ury
)
1446 /* For each screen element, compute the extents and revise bounding */
1447 /* box points, if necessary. */
1449 switch(ELEMENTTYPE(*bboxgen
)) {
1452 objinstbbox(TOOBJINST(bboxgen
), npoints
, 0);
1454 for (j
= 0; j
< 4; j
++) {
1455 bboxcalc(npoints
[j
].x
, llx
, urx
);
1456 bboxcalc(npoints
[j
].y
, lly
, ury
);
1461 /* because a pin is offset from its position point, include */
1462 /* that point in the bounding box. */
1464 if (TOLABEL(bboxgen
)->pin
) {
1465 bboxcalc(TOLABEL(bboxgen
)->position
.x
, llx
, urx
);
1466 bboxcalc(TOLABEL(bboxgen
)->position
.y
, lly
, ury
);
1468 labelbbox(TOLABEL(bboxgen
), npoints
, thisinst
);
1470 for (j
= 0; j
< 4; j
++) {
1471 bboxcalc(npoints
[j
].x
, llx
, urx
);
1472 bboxcalc(npoints
[j
].y
, lly
, ury
);
1477 graphicbbox(TOGRAPHIC(bboxgen
), npoints
);
1478 for (j
= 0; j
< 4; j
++) {
1479 bboxcalc(npoints
[j
].x
, llx
, urx
);
1480 bboxcalc(npoints
[j
].y
, lly
, ury
);
1486 for (pathc
= TOPATH(bboxgen
)->plist
; pathc
< TOPATH(bboxgen
)->plist
1487 + TOPATH(bboxgen
)->parts
; pathc
++)
1488 calcextents(pathc
, llx
, lly
, urx
, ury
);
1492 calcextents(bboxgen
, llx
, lly
, urx
, ury
);
1496 /*------------------------------------------------------*/
1497 /* Find if an object is in the specified library */
1498 /*------------------------------------------------------*/
1500 Boolean
object_in_library(short libnum
, objectptr thisobject
)
1504 for (i
= 0; i
< xobjs
.userlibs
[libnum
].number
; i
++) {
1505 if (*(xobjs
.userlibs
[libnum
].library
+ i
) == thisobject
)
1511 /*-----------------------------------------------------------*/
1512 /* Find if an object is in the hierarchy of the given object */
1513 /* Returns the number (position in plist) or -1 if not found */
1514 /*-----------------------------------------------------------*/
1516 short find_object(objectptr pageobj
, objectptr thisobject
)
1521 for (i
= 0; i
< pageobj
->parts
; i
++) {
1522 pelem
= pageobj
->plist
+ i
;
1523 if (IS_OBJINST(*pelem
)) {
1524 if ((TOOBJINST(pelem
))->thisobject
== thisobject
)
1526 else if ((j
= find_object((TOOBJINST(pelem
))->thisobject
, thisobject
)) >= 0)
1527 return i
; /* was j---is this the right fix? */
1533 /*------------------------------------------------------*/
1534 /* Find all pages and libraries containing this object */
1535 /* and update accordingly. If this object is a page, */
1536 /* just update the page directory. */
1537 /*------------------------------------------------------*/
1539 void updatepagebounds(objectptr thisobject
)
1544 if ((i
= is_page(thisobject
)) >= 0) {
1545 if (xobjs
.pagelist
[i
]->background
.name
!= (char *)NULL
)
1547 updatepagelib(PAGELIB
, i
);
1550 for (i
= 0; i
< xobjs
.pages
; i
++) {
1551 if (xobjs
.pagelist
[i
]->pageinst
!= NULL
) {
1552 pageobj
= xobjs
.pagelist
[i
]->pageinst
->thisobject
;
1553 if ((j
= find_object(pageobj
, thisobject
)) >= 0) {
1554 calcbboxvalues(xobjs
.pagelist
[i
]->pageinst
,
1555 (genericptr
*)(pageobj
->plist
+ j
));
1556 updatepagelib(PAGELIB
, i
);
1560 for (i
= 0; i
< xobjs
.numlibs
; i
++)
1561 if (object_in_library(i
, thisobject
))
1562 composelib(i
+ LIBRARY
);
1566 /*--------------------------------------------------------------*/
1567 /* Free memory for the schematic bounding box */
1568 /*--------------------------------------------------------------*/
1570 void invalidateschembbox(objinstptr thisinst
)
1572 if (thisinst
->schembbox
!= NULL
) {
1573 free(thisinst
->schembbox
);
1574 thisinst
->schembbox
= NULL
;
1578 /*--------------------------------------------------------------*/
1579 /* Calculate the bounding box for an object instance. Use the */
1580 /* existing bbox and finish calculation on all the elements */
1581 /* which have parameters not taking default values. */
1582 /* This finishes the calculation partially done by */
1583 /* calcbboxvalues(). */
1584 /*--------------------------------------------------------------*/
1586 void calcbboxinst(objinstptr thisinst
)
1590 short llx
, lly
, urx
, ury
;
1592 short pllx
, plly
, purx
, pury
;
1593 Boolean hasschembbox
= FALSE
;
1594 Boolean didparamsubs
= FALSE
;
1596 if (thisinst
== NULL
) return;
1598 thisobj
= thisinst
->thisobject
;
1600 llx
= thisobj
->bbox
.lowerleft
.x
;
1601 lly
= thisobj
->bbox
.lowerleft
.y
;
1602 urx
= llx
+ thisobj
->bbox
.width
;
1603 ury
= lly
+ thisobj
->bbox
.height
;
1605 pllx
= plly
= 32767;
1606 purx
= pury
= -32768;
1608 for (gelem
= thisobj
->plist
; gelem
< thisobj
->plist
+ thisobj
->parts
;
1610 /* pins which do not appear outside of the object */
1611 /* contribute to the objects "schembbox". */
1613 if (IS_LABEL(*gelem
)) {
1614 labelptr btext
= TOLABEL(gelem
);
1615 if (btext
->pin
&& !(btext
->anchor
& PINVISIBLE
)) {
1616 hasschembbox
= TRUE
;
1617 calcbboxsingle(gelem
, thisinst
, &pllx
, &plly
, &purx
, &pury
);
1622 if (has_param(*gelem
)) {
1623 if (didparamsubs
== FALSE
) {
1624 psubstitute(thisinst
);
1625 didparamsubs
= TRUE
;
1627 calcbboxsingle(gelem
, thisinst
, &llx
, &lly
, &urx
, &ury
);
1630 /* If we have a clipmask, the clipmask is used to calculate the */
1631 /* bounding box, not the element it is masking. */
1633 switch(ELEMENTTYPE(*gelem
)) {
1634 case POLYGON
: case SPLINE
: case ARC
: case PATH
:
1635 if (TOPOLY(gelem
)->style
& CLIPMASK
) gelem
++;
1640 thisinst
->bbox
.lowerleft
.x
= llx
;
1641 thisinst
->bbox
.lowerleft
.y
= lly
;
1642 thisinst
->bbox
.width
= urx
- llx
;
1643 thisinst
->bbox
.height
= ury
- lly
;
1646 if (thisinst
->schembbox
== NULL
)
1647 thisinst
->schembbox
= (BBox
*)malloc(sizeof(BBox
));
1649 thisinst
->schembbox
->lowerleft
.x
= pllx
;
1650 thisinst
->schembbox
->lowerleft
.y
= plly
;
1651 thisinst
->schembbox
->width
= purx
- pllx
;
1652 thisinst
->schembbox
->height
= pury
- plly
;
1655 invalidateschembbox(thisinst
);
1658 /*--------------------------------------------------------------*/
1659 /* Update things based on a changed instance bounding box. */
1660 /* If the parameter was a single-instance */
1661 /* substitution, only the page should be updated. If the */
1662 /* parameter was a default value, the library should be updated */
1663 /* and any pages containing the object where the parameter */
1664 /* takes the default value. */
1665 /*--------------------------------------------------------------*/
1667 void updateinstparam(objectptr bobj
)
1672 /* change bounds on pagelib and all pages */
1673 /* containing this *object* if and only if the object */
1674 /* instance takes the default value. Also update the */
1677 for (i
= 0; i
< xobjs
.pages
; i
++)
1678 if (xobjs
.pagelist
[i
]->pageinst
!= NULL
) {
1679 pageobj
= xobjs
.pagelist
[i
]->pageinst
->thisobject
;
1680 if ((j
= find_object(pageobj
, topobject
)) >= 0) {
1682 /* Really, we'd like to recalculate the bounding box only if the */
1683 /* parameter value is the default value which was just changed. */
1684 /* However, then any non-default values may contain the wrong */
1685 /* substitutions. */
1687 objinstptr cinst
= TOOBJINST(pageobj
->plist
+ j
);
1688 if (cinst
->thisobject
->params
== NULL
) {
1689 calcbboxvalues(xobjs
.pagelist
[i
]->pageinst
, pageobj
->plist
+ j
);
1690 updatepagelib(PAGELIB
, i
);
1695 for (i
= 0; i
< xobjs
.numlibs
; i
++)
1696 if (object_in_library(i
, topobject
))
1697 composelib(i
+ LIBRARY
);
1700 /*--------------------------------------------------------------*/
1701 /* Calculate bbox on all elements of the given object */
1702 /*--------------------------------------------------------------*/
1704 void calcbbox(objinstptr binst
)
1706 calcbboxvalues(binst
, (genericptr
*)NULL
);
1707 if (binst
== areawin
->topinstance
) {
1708 updatepagebounds(topobject
);
1712 /*--------------------------------------------------------------*/
1713 /* Calculate bbox on the given element of the specified object. */
1714 /* This is a wrapper for calcbboxvalues() assuming that we're */
1715 /* on the top-level, and that page bounds need to be updated. */
1716 /*--------------------------------------------------------------*/
1718 void singlebbox(genericptr
*gelem
)
1720 calcbboxvalues(areawin
->topinstance
, (genericptr
*)gelem
);
1721 updatepagebounds(topobject
);
1724 /*----------------------------------------------------------------------*/
1725 /* Extend bounding box based on selected elements only */
1726 /*----------------------------------------------------------------------*/
1728 void calcbboxselect()
1731 for (bsel
= areawin
->selectlist
; bsel
< areawin
->selectlist
+
1732 areawin
->selects
; bsel
++)
1733 calcbboxvalues(areawin
->topinstance
, topobject
->plist
+ *bsel
);
1735 updatepagebounds(topobject
);
1738 /*--------------------------------------------------------------*/
1739 /* Update Bounding box for an object. */
1740 /* If newelement == NULL, calculate bounding box from scratch. */
1741 /* Otherwise, expand bounding box to enclose newelement. */
1742 /*--------------------------------------------------------------*/
1744 void calcbboxvalues(objinstptr thisinst
, genericptr
*newelement
)
1746 genericptr
*bboxgen
;
1747 short llx
, lly
, urx
, ury
;
1748 objectptr thisobj
= thisinst
->thisobject
;
1750 /* no action if there are no elements */
1751 if (thisobj
->parts
== 0) return;
1753 /* If this object has parameters, then we will do a separate */
1754 /* bounding box calculation on parameterized parts. This */
1755 /* calculation ignores them, and the result is a base that the */
1756 /* instance bounding-box computation can use as a starting point. */
1758 /* set starting bounds as maximum bounds of screen */
1762 for (bboxgen
= thisobj
->plist
; bboxgen
< thisobj
->plist
+
1763 thisobj
->parts
; bboxgen
++) {
1765 /* override the "for" loop if we're doing a single element */
1766 if (newelement
!= NULL
) bboxgen
= newelement
;
1768 if ((thisobj
->params
== NULL
) || (!has_param(*bboxgen
))) {
1769 /* pins which do not appear outside of the object */
1770 /* are ignored now---will be computed per instance. */
1772 if (IS_LABEL(*bboxgen
)) {
1773 labelptr btext
= TOLABEL(bboxgen
);
1774 if (btext
->pin
&& !(btext
->anchor
& PINVISIBLE
)) {
1778 calcbboxsingle(bboxgen
, thisinst
, &llx
, &lly
, &urx
, &ury
);
1780 if (newelement
== NULL
)
1781 switch(ELEMENTTYPE(*bboxgen
)) {
1782 case POLYGON
: case SPLINE
: case ARC
: case PATH
:
1783 if (TOPOLY(bboxgen
)->style
& CLIPMASK
)
1789 if (newelement
!= NULL
) break;
1792 /* if this is a single-element calculation and its bounding box */
1793 /* turned out to be smaller than the object's, then we need to */
1794 /* recompute the entire object's bounding box in case it got */
1795 /* smaller. This is not recursive, in spite of looks. */
1797 if (newelement
!= NULL
) {
1798 if (llx
> thisobj
->bbox
.lowerleft
.x
&&
1799 lly
> thisobj
->bbox
.lowerleft
.y
&&
1800 urx
< (thisobj
->bbox
.lowerleft
.x
+ thisobj
->bbox
.width
) &&
1801 ury
< (thisobj
->bbox
.lowerleft
.y
+ thisobj
->bbox
.height
)) {
1802 calcbboxvalues(thisinst
, NULL
);
1806 bboxcalc(thisobj
->bbox
.lowerleft
.x
, &llx
, &urx
);
1807 bboxcalc(thisobj
->bbox
.lowerleft
.y
, &lly
, &ury
);
1808 bboxcalc(thisobj
->bbox
.lowerleft
.x
+ thisobj
->bbox
.width
, &llx
, &urx
);
1809 bboxcalc(thisobj
->bbox
.lowerleft
.y
+ thisobj
->bbox
.height
, &lly
, &ury
);
1813 /* Set the new bounding box. In pathological cases, such as a page */
1814 /* with only pin labels, the bounds may not have been changed from */
1815 /* their initial values. If so, then don't touch the bounding box. */
1817 if ((llx
<= urx
) && (lly
<= ury
)) {
1818 thisobj
->bbox
.lowerleft
.x
= llx
;
1819 thisobj
->bbox
.lowerleft
.y
= lly
;
1820 thisobj
->bbox
.width
= urx
- llx
;
1821 thisobj
->bbox
.height
= ury
- lly
;
1824 /* calculate instance-specific values */
1825 calcbboxinst(thisinst
);
1828 /*------------------------------------------------------*/
1829 /* Center an object in the viewing window */
1830 /*------------------------------------------------------*/
1832 void centerview(objinstptr tinst
)
1834 XPoint origin
, corner
;
1835 Dimension width
, height
;
1836 float fitwidth
, fitheight
;
1837 objectptr tobj
= tinst
->thisobject
;
1839 origin
= tinst
->bbox
.lowerleft
;
1840 corner
.x
= origin
.x
+ tinst
->bbox
.width
;
1841 corner
.y
= origin
.y
+ tinst
->bbox
.height
;
1843 extendschembbox(tinst
, &origin
, &corner
);
1845 width
= corner
.x
- origin
.x
;
1846 height
= corner
.y
- origin
.y
;
1848 fitwidth
= (float)areawin
->width
/ ((float)width
+ 2 * DEFAULTGRIDSPACE
);
1849 fitheight
= (float)areawin
->height
/ ((float)height
+ 2 * DEFAULTGRIDSPACE
);
1851 tobj
->viewscale
= (fitwidth
< fitheight
) ?
1852 min(MINAUTOSCALE
, fitwidth
) : min(MINAUTOSCALE
, fitheight
);
1854 tobj
->pcorner
.x
= origin
.x
- (areawin
->width
1855 / tobj
->viewscale
- width
) / 2;
1856 tobj
->pcorner
.y
= origin
.y
- (areawin
->height
1857 / tobj
->viewscale
- height
) / 2;
1859 /* Copy new position values to the current window */
1861 if ((areawin
->topinstance
!= NULL
) && (tobj
== topobject
)) {
1862 areawin
->pcorner
= tobj
->pcorner
;
1863 areawin
->vscale
= tobj
->viewscale
;
1867 /*-----------------------------------------------------------*/
1868 /* Refresh the window and scrollbars and write the page name */
1869 /*-----------------------------------------------------------*/
1871 void refresh(xcWidget bw
, caddr_t clientdata
, caddr_t calldata
)
1873 areawin
->redraw_needed
= True
;
1874 drawarea(NULL
, NULL
, NULL
);
1875 if (areawin
->scrollbarh
)
1876 drawhbar(areawin
->scrollbarh
, NULL
, NULL
);
1877 if (areawin
->scrollbarv
)
1878 drawvbar(areawin
->scrollbarv
, NULL
, NULL
);
1879 printname(topobject
);
1882 /*------------------------------------------------------*/
1883 /* Center the current page in the viewing window */
1884 /*------------------------------------------------------*/
1886 void zoomview(xcWidget w
, caddr_t clientdata
, caddr_t calldata
)
1888 if (eventmode
== NORMAL_MODE
|| eventmode
== COPY_MODE
||
1889 eventmode
== MOVE_MODE
|| eventmode
== CATALOG_MODE
||
1890 eventmode
== FONTCAT_MODE
|| eventmode
== EFONTCAT_MODE
||
1891 eventmode
== CATMOVE_MODE
) {
1893 if (areawin
->topinstance
)
1894 centerview(areawin
->topinstance
);
1895 areawin
->lastbackground
= NULL
;
1897 refresh(NULL
, NULL
, NULL
);
1901 /*---------------------------------------------------------*/
1902 /* Basic X Graphics Routines in the User coordinate system */
1903 /*---------------------------------------------------------*/
1906 void UDrawSimpleLine(XPoint
*pt1
, XPoint
*pt2
)
1908 XPoint newpt1
, newpt2
;
1910 if (!areawin
->redraw_ongoing
) {
1911 areawin
->redraw_needed
= True
;
1915 UTransformbyCTM(DCTM
, pt1
, &newpt1
, 1);
1916 UTransformbyCTM(DCTM
, pt2
, &newpt2
, 1);
1918 DrawLine(dpy
, areawin
->window
, areawin
->gc
,
1919 newpt1
.x
, newpt1
.y
, newpt2
.x
, newpt2
.y
);
1921 #endif /* !HAVE_CAIRO */
1923 /*-------------------------------------------------------------------------*/
1926 void UDrawLine(XPoint
*pt1
, XPoint
*pt2
)
1928 float tmpwidth
= UTopTransScale(xobjs
.pagelist
[areawin
->page
]->wirewidth
);
1930 if (!areawin
->redraw_ongoing
) {
1931 areawin
->redraw_needed
= True
;
1935 SetLineAttributes(dpy
, areawin
->gc
, tmpwidth
, LineSolid
, CapRound
, JoinBevel
);
1936 UDrawSimpleLine(pt1
, pt2
);
1938 #endif /* !HAVE_CAIRO */
1940 /*----------------------------------------------------------------------*/
1941 /* Add circle at given point to indicate that the point is a parameter. */
1942 /* The circle is divided into quarters. For parameterized y-coordinate */
1943 /* the top and bottom quarters are drawn. For parameterized x- */
1944 /* coordinate, the left and right quarters are drawn. A full circle */
1945 /* indicates either both x- and y-coordinates are parameterized, or */
1946 /* else any other kind of parameterization (presently, not used). */
1948 /* (note that the two angles in XDrawArc() are 1) the start angle, */
1949 /* measured in absolute 64th degrees from 0 (3 o'clock), and 2) the */
1950 /* path length, in relative 64th degrees (positive = counterclockwise, */
1951 /* negative = clockwise)). */
1952 /*----------------------------------------------------------------------*/
1955 void UDrawCircle(XPoint
*upt
, u_char which
)
1959 if (!areawin
->redraw_ongoing
) {
1960 areawin
->redraw_needed
= True
;
1964 user_to_window(*upt
, &wpt
);
1965 SetThinLineAttributes(dpy
, areawin
->gc
, 0, LineSolid
, CapButt
, JoinMiter
);
1969 XDrawArc(dpy
, areawin
->window
, areawin
->gc
, wpt
.x
- 4,
1970 wpt
.y
- 4, 8, 8, -(45 * 64), (90 * 64));
1971 XDrawArc(dpy
, areawin
->window
, areawin
->gc
, wpt
.x
- 4,
1972 wpt
.y
- 4, 8, 8, (135 * 64), (90 * 64));
1975 XDrawArc(dpy
, areawin
->window
, areawin
->gc
, wpt
.x
- 4,
1976 wpt
.y
- 4, 8, 8, (45 * 64), (90 * 64));
1977 XDrawArc(dpy
, areawin
->window
, areawin
->gc
, wpt
.x
- 4,
1978 wpt
.y
- 4, 8, 8, (225 * 64), (90 * 64));
1981 XDrawArc(dpy
, areawin
->window
, areawin
->gc
, wpt
.x
- 4,
1982 wpt
.y
- 4, 8, 8, 0, (360 * 64));
1986 #endif /* !HAVE_CAIRO */
1988 /*----------------------------------------------------------------------*/
1989 /* Add "X" at string origin */
1990 /*----------------------------------------------------------------------*/
1993 void UDrawXAt(XPoint
*wpt
)
1995 if (!areawin
->redraw_ongoing
) {
1996 areawin
->redraw_needed
= True
;
2000 SetThinLineAttributes(dpy
, areawin
->gc
, 0, LineSolid
, CapButt
, JoinMiter
);
2001 DrawLine(dpy
, areawin
->window
, areawin
->gc
, wpt
->x
- 3,
2002 wpt
->y
- 3, wpt
->x
+ 3, wpt
->y
+ 3);
2003 DrawLine(dpy
, areawin
->window
, areawin
->gc
, wpt
->x
+ 3,
2004 wpt
->y
- 3, wpt
->x
- 3, wpt
->y
+ 3);
2006 #endif /* !HAVE_CAIRO */
2008 /*----------------------------------------------------------------------*/
2009 /* Draw "X" on current level */
2010 /*----------------------------------------------------------------------*/
2012 void UDrawX(labelptr curlabel
)
2016 user_to_window(curlabel
->position
, &wpt
);
2020 /*----------------------------------------------------------------------*/
2021 /* Draw "X" on top level (only for LOCAL and GLOBAL pin labels) */
2022 /*----------------------------------------------------------------------*/
2024 void UDrawXDown(labelptr curlabel
)
2028 UTransformbyCTM(DCTM
, &curlabel
->position
, &wpt
, 1);
2032 /*----------------------------------------------------------------------*/
2033 /* Find the "real" width, height, and origin of an object including pin */
2034 /* labels and so forth that only show up on a schematic when it is the */
2035 /* top-level object. */
2036 /*----------------------------------------------------------------------*/
2038 int toplevelwidth(objinstptr bbinst
, short *rllx
)
2041 short origin
, corner
;
2043 if (bbinst
->schembbox
== NULL
) {
2044 if (rllx
) *rllx
= bbinst
->bbox
.lowerleft
.x
;
2045 return bbinst
->bbox
.width
;
2048 origin
= bbinst
->bbox
.lowerleft
.x
;
2049 corner
= origin
+ bbinst
->bbox
.width
;
2051 llx
= bbinst
->schembbox
->lowerleft
.x
;
2052 urx
= llx
+ bbinst
->schembbox
->width
;
2054 bboxcalc(llx
, &origin
, &corner
);
2055 bboxcalc(urx
, &origin
, &corner
);
2057 if (rllx
) *rllx
= origin
;
2058 return(corner
- origin
);
2061 /*----------------------------------------------------------------------*/
2063 int toplevelheight(objinstptr bbinst
, short *rlly
)
2066 short origin
, corner
;
2068 if (bbinst
->schembbox
== NULL
) {
2069 if (rlly
) *rlly
= bbinst
->bbox
.lowerleft
.y
;
2070 return bbinst
->bbox
.height
;
2073 origin
= bbinst
->bbox
.lowerleft
.y
;
2074 corner
= origin
+ bbinst
->bbox
.height
;
2076 lly
= bbinst
->schembbox
->lowerleft
.y
;
2077 ury
= lly
+ bbinst
->schembbox
->height
;
2079 bboxcalc(lly
, &origin
, &corner
);
2080 bboxcalc(ury
, &origin
, &corner
);
2082 if (rlly
) *rlly
= origin
;
2083 return(corner
- origin
);
2086 /*----------------------------------------------------------------------*/
2087 /* Add dimensions of schematic pins to an object's bounding box */
2088 /*----------------------------------------------------------------------*/
2090 void extendschembbox(objinstptr bbinst
, XPoint
*origin
, XPoint
*corner
)
2092 short llx
, lly
, urx
, ury
;
2094 if ((bbinst
== NULL
) || (bbinst
->schembbox
== NULL
)) return;
2096 llx
= bbinst
->schembbox
->lowerleft
.x
;
2097 lly
= bbinst
->schembbox
->lowerleft
.y
;
2098 urx
= llx
+ bbinst
->schembbox
->width
;
2099 ury
= lly
+ bbinst
->schembbox
->height
;
2101 bboxcalc(llx
, &(origin
->x
), &(corner
->x
));
2102 bboxcalc(lly
, &(origin
->y
), &(corner
->y
));
2103 bboxcalc(urx
, &(origin
->x
), &(corner
->x
));
2104 bboxcalc(ury
, &(origin
->y
), &(corner
->y
));
2107 /*----------------------------------------------------------------------*/
2108 /* Adjust a pinlabel position to account for pad spacing */
2109 /*----------------------------------------------------------------------*/
2111 void pinadjust (short anchor
, short *xpoint
, short *ypoint
, short dir
)
2115 dely
= (anchor
& NOTBOTTOM
) ?
2116 ((anchor
& TOP
) ? -PADSPACE
: 0) : PADSPACE
;
2117 delx
= (anchor
& NOTLEFT
) ?
2118 ((anchor
& RIGHT
) ? -PADSPACE
: 0) : PADSPACE
;
2120 if (xpoint
!= NULL
) *xpoint
+= (dir
> 0) ? delx
: -delx
;
2121 if (ypoint
!= NULL
) *ypoint
+= (dir
> 0) ? dely
: -dely
;
2124 /*----------------------------------------------------------------------*/
2125 /* Draw line for editing text (position of cursor in string is given by */
2126 /* tpos (2nd parameter) */
2127 /*----------------------------------------------------------------------*/
2129 void UDrawTextLine(labelptr curlabel
, short tpos
)
2131 XPoint points
[2]; /* top and bottom of text cursor line */
2132 short tmpanchor
, xbase
;
2135 TextLinesInfo tlinfo
;
2137 if (!areawin
->redraw_ongoing
) {
2138 areawin
->redraw_needed
= True
;
2142 /* correct for position, rotation, scale, and flip invariance of text */
2145 UPreMultCTM(DCTM
, curlabel
->position
, curlabel
->scale
, curlabel
->rotation
);
2146 tmpanchor
= flipadjust(curlabel
->anchor
);
2148 SetForeground(dpy
, areawin
->gc
, AUXCOLOR
);
2151 tlinfo
.tbreak
= NULL
;
2152 tlinfo
.padding
= NULL
;
2154 tmpext
= ULength(curlabel
, areawin
->topinstance
, &tlinfo
);
2155 maxwidth
= tmpext
.maxwidth
;
2156 xbase
= tmpext
.base
;
2157 tlinfo
.dostop
= tpos
;
2158 tmpext
= ULength(curlabel
, areawin
->topinstance
, &tlinfo
);
2160 points
[0].x
= (tmpanchor
& NOTLEFT
?
2161 (tmpanchor
& RIGHT
? -maxwidth
: -maxwidth
>> 1) : 0) + tmpext
.width
;
2162 if ((tmpanchor
& JUSTIFYRIGHT
) && tlinfo
.padding
)
2163 points
[0].x
+= tlinfo
.padding
[tlinfo
.line
];
2164 else if ((tmpanchor
& TEXTCENTERED
) && tlinfo
.padding
)
2165 points
[0].x
+= 0.5 * tlinfo
.padding
[tlinfo
.line
];
2166 points
[0].y
= (tmpanchor
& NOTBOTTOM
?
2167 (tmpanchor
& TOP
? -tmpext
.ascent
: -(tmpext
.ascent
+ xbase
) / 2)
2168 : -xbase
) + tmpext
.base
- 3;
2169 points
[1].x
= points
[0].x
;
2170 points
[1].y
= points
[0].y
+ TEXTHEIGHT
+ 6;
2172 if (curlabel
->pin
) {
2173 pinadjust(tmpanchor
, &(points
[0].x
), &(points
[0].y
), 1);
2174 pinadjust(tmpanchor
, &(points
[1].x
), &(points
[1].y
), 1);
2176 if (tlinfo
.padding
!= NULL
) free(tlinfo
.padding
);
2180 UDrawLine(&points
[0], &points
[1]);
2186 /*-----------------------------------------------------------------*/
2187 /* Draw lines for editing text when multiple characters are chosen */
2188 /*-----------------------------------------------------------------*/
2190 void UDrawTLine(labelptr curlabel
)
2192 UDrawTextLine(curlabel
, areawin
->textpos
);
2193 if ((areawin
->textend
> 0) && (areawin
->textend
< areawin
->textpos
)) {
2194 UDrawTextLine(curlabel
, areawin
->textend
);
2198 /*----------------------*/
2200 /*----------------------*/
2203 void UDrawXLine(XPoint opt
, XPoint cpt
)
2207 if (!areawin
->redraw_ongoing
) {
2208 areawin
->redraw_needed
= True
;
2212 SetForeground(dpy
, areawin
->gc
, AUXCOLOR
);
2214 user_to_window(cpt
, &upt
);
2215 user_to_window(opt
, &vpt
);
2217 SetThinLineAttributes(dpy
, areawin
->gc
, 0, LineOnOffDash
, CapButt
, JoinMiter
);
2218 DrawLine(dpy
, areawin
->window
, areawin
->gc
, vpt
.x
, vpt
.y
, upt
.x
, upt
.y
);
2220 SetThinLineAttributes(dpy
, areawin
->gc
, 0, LineSolid
, CapButt
, JoinMiter
);
2221 DrawLine(dpy
, areawin
->window
, areawin
->gc
, upt
.x
- 3, upt
.y
- 3,
2222 upt
.x
+ 3, upt
.y
+ 3);
2223 DrawLine(dpy
, areawin
->window
, areawin
->gc
, upt
.x
+ 3, upt
.y
- 3,
2224 upt
.x
- 3, upt
.y
+ 3);
2226 SetForeground(dpy
, areawin
->gc
, areawin
->gccolor
);
2228 #endif /* HAVE_CAIRO */
2230 /*-------------------------------------------------------------------------*/
2233 void UDrawBox(XPoint origin
, XPoint corner
)
2235 XPoint worig
, wcorn
;
2237 if (!areawin
->redraw_ongoing
) {
2238 areawin
->redraw_needed
= True
;
2242 user_to_window(origin
, &worig
);
2243 user_to_window(corner
, &wcorn
);
2245 SetForeground(dpy
, areawin
->gc
, AUXCOLOR
);
2246 SetThinLineAttributes(dpy
, areawin
->gc
, 0, LineSolid
, CapRound
, JoinBevel
);
2247 DrawLine(dpy
, areawin
->window
, areawin
->gc
, worig
.x
, worig
.y
,
2249 DrawLine(dpy
, areawin
->window
, areawin
->gc
, worig
.x
, wcorn
.y
,
2251 DrawLine(dpy
, areawin
->window
, areawin
->gc
, wcorn
.x
, wcorn
.y
,
2253 DrawLine(dpy
, areawin
->window
, areawin
->gc
, wcorn
.x
, worig
.y
,
2256 #endif /* HAVE_CAIRO */
2258 /*----------------------------------------------------------------------*/
2259 /* Get a box indicating the dimensions of the edit element that most */
2260 /* closely reach the position "corner". */
2261 /*----------------------------------------------------------------------*/
2263 float UGetRescaleBox(XPoint
*corner
, XPoint
*newpoints
)
2266 float savescale
, newscale
;
2267 long mindist
, testdist
, refdist
;
2273 if (!areawin
->redraw_ongoing
) {
2274 areawin
->redraw_needed
= True
;
2278 if (areawin
->selects
== 0) return 0.0;
2280 /* Use only the 1st selection as a reference to set the scale */
2282 rgen
= SELTOGENERIC(areawin
->selectlist
);
2284 switch(ELEMENTTYPE(rgen
)) {
2286 rlab
= (labelptr
)rgen
;
2287 labelbbox(rlab
, newpoints
, areawin
->topinstance
);
2288 newpoints
[4] = newpoints
[0];
2290 for (i
= 0; i
< 4; i
++) {
2291 testdist
= finddist(&newpoints
[i
], &newpoints
[i
+1], corner
);
2292 if (testdist
< mindist
)
2295 refdist
= wirelength(corner
, &(rlab
->position
));
2296 mindist
= (int)sqrt(abs((double)mindist
));
2297 savescale
= rlab
->scale
;
2298 if (!test_insideness((int)corner
->x
, (int)corner
->y
, newpoints
))
2300 if (refdist
== mindist
) refdist
= 1 - mindist
;
2301 if (rlab
->scale
< 0) rlab
->scale
= -rlab
->scale
;
2302 newscale
= fabs(rlab
->scale
* (float)refdist
/ (float)(refdist
+ mindist
));
2303 if (newscale
> 10 * rlab
->scale
) newscale
= 10 * rlab
->scale
;
2304 if (areawin
->snapto
) {
2305 float snapstep
= 2 * (float)xobjs
.pagelist
[areawin
->page
]->gridspace
2306 / (float)xobjs
.pagelist
[areawin
->page
]->snapspace
;
2307 newscale
= (float)((int)(newscale
* snapstep
)) / snapstep
;
2308 if (newscale
< (1.0 / snapstep
)) newscale
= (1.0 / snapstep
);
2310 else if (newscale
< 0.1 * rlab
->scale
) newscale
= 0.1 * rlab
->scale
;
2311 rlab
->scale
= (savescale
< 0) ? -newscale
: newscale
;
2312 labelbbox(rlab
, newpoints
, areawin
->topinstance
);
2313 rlab
->scale
= savescale
;
2314 if (savescale
< 0) newscale
= -newscale
;
2318 rgraph
= (graphicptr
)rgen
;
2319 graphicbbox(rgraph
, newpoints
);
2320 newpoints
[4] = newpoints
[0];
2322 for (i
= 0; i
< 4; i
++) {
2323 testdist
= finddist(&newpoints
[i
], &newpoints
[i
+1], corner
);
2324 if (testdist
< mindist
)
2327 refdist
= wirelength(corner
, &(rgraph
->position
));
2328 mindist
= (int)sqrt(abs((double)mindist
));
2329 savescale
= rgraph
->scale
;
2330 if (!test_insideness((int)corner
->x
, (int)corner
->y
, newpoints
))
2332 if (refdist
== mindist
) refdist
= 1 - mindist
; /* avoid inf result */
2333 if (rgraph
->scale
< 0) rgraph
->scale
= -rgraph
->scale
;
2334 newscale
= fabs(rgraph
->scale
* (float)refdist
/ (float)(refdist
+ mindist
));
2335 if (newscale
> 10 * rgraph
->scale
) newscale
= 10 * rgraph
->scale
;
2336 if (areawin
->snapto
) {
2337 float snapstep
= 2 * (float)xobjs
.pagelist
[areawin
->page
]->gridspace
2338 / (float)xobjs
.pagelist
[areawin
->page
]->snapspace
;
2339 newscale
= (float)((int)(newscale
* snapstep
)) / snapstep
;
2340 if (newscale
< (1.0 / snapstep
)) newscale
= (1.0 / snapstep
);
2342 else if (newscale
< 0.1 * rgraph
->scale
) newscale
= 0.1 * rgraph
->scale
;
2343 rgraph
->scale
= (savescale
< 0) ? -newscale
: newscale
;
2344 graphicbbox(rgraph
, newpoints
);
2345 rgraph
->scale
= savescale
;
2346 if (savescale
< 0) newscale
= -newscale
;
2350 rinst
= (objinstptr
)rgen
;
2351 objinstbbox(rinst
, newpoints
, 0);
2352 newpoints
[4] = newpoints
[0];
2354 for (i
= 0; i
< 4; i
++) {
2355 testdist
= finddist(&newpoints
[i
], &newpoints
[i
+1], corner
);
2356 if (testdist
< mindist
)
2359 refdist
= wirelength(corner
, &(rinst
->position
));
2360 mindist
= (int)sqrt(abs((double)mindist
));
2361 savescale
= rinst
->scale
;
2362 if (!test_insideness((int)corner
->x
, (int)corner
->y
, newpoints
))
2364 if (refdist
== mindist
) refdist
= 1 - mindist
; /* avoid inf result */
2365 if (rinst
->scale
< 0) rinst
->scale
= -rinst
->scale
;
2366 newscale
= fabs(rinst
->scale
* (float)refdist
/ (float)(refdist
+ mindist
));
2367 if (newscale
> 10 * rinst
->scale
) newscale
= 10 * rinst
->scale
;
2368 if (areawin
->snapto
) {
2369 float snapstep
= 2 * (float)xobjs
.pagelist
[areawin
->page
]->gridspace
2370 / (float)xobjs
.pagelist
[areawin
->page
]->snapspace
;
2371 newscale
= (float)((int)(newscale
* snapstep
)) / snapstep
;
2372 if (newscale
< (1.0 / snapstep
)) newscale
= (1.0 / snapstep
);
2374 else if (newscale
< 0.1 * rinst
->scale
) newscale
= 0.1 * rinst
->scale
;
2375 rinst
->scale
= (savescale
< 0) ? -newscale
: newscale
;
2376 objinstbbox(rinst
, newpoints
, 0);
2377 rinst
->scale
= savescale
;
2378 if (savescale
< 0) newscale
= -newscale
;
2385 /*----------------------------------------------------------------------*/
2386 /* Draw a box indicating the dimensions of the edit element that most */
2387 /* closely reach the position "corner". */
2388 /*----------------------------------------------------------------------*/
2391 void UDrawRescaleBox(XPoint
*corner
)
2393 XPoint origpoints
[5], newpoints
[5];
2395 if (!areawin
->redraw_ongoing
) {
2396 areawin
->redraw_needed
= True
;
2400 if (areawin
->selects
== 0)
2403 UGetRescaleBox(corner
, newpoints
);
2405 SetForeground(dpy
, areawin
->gc
, AUXCOLOR
);
2406 SetThinLineAttributes(dpy
, areawin
->gc
, 0, LineSolid
, CapRound
, JoinBevel
);
2408 UTransformbyCTM(DCTM
, newpoints
, origpoints
, 4);
2409 strokepath(origpoints
, 4, 0, 1);
2411 #endif /* HAVE_CAIRO */
2413 /*-------------------------------------------------------------------------*/
2419 XPoint worig
, wcorn
, corner
;
2420 objinstptr bbinst
= areawin
->topinstance
;
2422 if (!areawin
->redraw_ongoing
) {
2423 areawin
->redraw_needed
= True
;
2427 if ((!areawin
->bboxon
) || (checkforbbox(topobject
) != NULL
)) return;
2429 origin
= bbinst
->bbox
.lowerleft
;
2430 corner
.x
= origin
.x
+ bbinst
->bbox
.width
;
2431 corner
.y
= origin
.y
+ bbinst
->bbox
.height
;
2433 /* Include any schematic labels in the bounding box. */
2434 extendschembbox(bbinst
, &origin
, &corner
);
2436 user_to_window(origin
, &worig
);
2437 user_to_window(corner
, &wcorn
);
2439 SetForeground(dpy
, areawin
->gc
, BBOXCOLOR
);
2440 DrawLine(dpy
, areawin
->window
, areawin
->gc
, worig
.x
, worig
.y
,
2442 DrawLine(dpy
, areawin
->window
, areawin
->gc
, worig
.x
, wcorn
.y
,
2444 DrawLine(dpy
, areawin
->window
, areawin
->gc
, wcorn
.x
, wcorn
.y
,
2446 DrawLine(dpy
, areawin
->window
, areawin
->gc
, wcorn
.x
, worig
.y
,
2449 #endif /* !HAVE_CAIRO */
2451 /*----------------------------------------------------------------------*/
2452 /* Fill and/or draw a border around the stroking path */
2453 /*----------------------------------------------------------------------*/
2456 void strokepath(XPoint
*pathlist
, short number
, short style
, float width
)
2460 tmpwidth
= UTopTransScale(width
);
2462 if (!(style
& CLIPMASK
) || (areawin
->showclipmasks
== TRUE
) ||
2463 (areawin
->clipped
< 0)) {
2464 if (style
& FILLED
|| (!(style
& FILLED
) && style
& OPAQUE
)) {
2465 if ((style
& FILLSOLID
) == FILLSOLID
)
2466 SetFillStyle(dpy
, areawin
->gc
, FillSolid
);
2467 else if (!(style
& FILLED
)) {
2468 SetFillStyle(dpy
, areawin
->gc
, FillOpaqueStippled
);
2469 SetStipple(dpy
, areawin
->gc
, 7);
2473 SetFillStyle(dpy
, areawin
->gc
, FillOpaqueStippled
);
2475 SetFillStyle(dpy
, areawin
->gc
, FillStippled
);
2476 SetStipple(dpy
, areawin
->gc
, ((style
& FILLSOLID
) >> 5));
2478 FillPolygon(dpy
, areawin
->window
, areawin
->gc
, pathlist
, number
, Nonconvex
,
2480 /* return to original state */
2481 SetFillStyle(dpy
, areawin
->gc
, FillSolid
);
2483 if (!(style
& NOBORDER
)) {
2484 if (style
& (DASHED
| DOTTED
)) {
2485 /* Set up dots or dashes */
2487 /* prevent values greater than 255 from folding back into */
2488 /* type char. Limit to 63 (=255/4) to keep at least the */
2489 /* dot/gap ratio to scale when 'gap' is at its maximum */
2491 unsigned char dotsize
= min(63, max(1, (short)tmpwidth
));
2493 dashstring
[0] = 4 * dotsize
;
2494 else if (style
& DOTTED
)
2495 dashstring
[0] = dotsize
;
2496 dashstring
[1] = 4 * dotsize
;
2497 SetDashes(dpy
, areawin
->gc
, 0, dashstring
, 2);
2498 SetLineAttributes(dpy
, areawin
->gc
, tmpwidth
, LineOnOffDash
,
2499 CapButt
, (style
& SQUARECAP
) ? JoinMiter
: JoinBevel
);
2502 SetLineAttributes(dpy
, areawin
->gc
, tmpwidth
, LineSolid
,
2503 (style
& SQUARECAP
) ? CapProjecting
: CapRound
,
2504 (style
& SQUARECAP
) ? JoinMiter
: JoinBevel
);
2506 /* draw the spline and close off if so specified */
2507 DrawLines(dpy
, areawin
->window
, areawin
->gc
, pathlist
,
2508 number
, CoordModeOrigin
);
2509 if (!(style
& UNCLOSED
))
2510 DrawLine(dpy
, areawin
->window
, areawin
->gc
, pathlist
[0].x
,
2511 pathlist
[0].y
, pathlist
[number
- 1].x
, pathlist
[number
- 1].y
);
2515 if (style
& CLIPMASK
) {
2516 if (areawin
->clipped
== 0) {
2517 XSetForeground(dpy
, areawin
->cmgc
, 0);
2518 XFillRectangle(dpy
, areawin
->clipmask
, areawin
->cmgc
, 0, 0,
2519 areawin
->width
, areawin
->height
);
2520 XSetForeground(dpy
, areawin
->cmgc
, 1);
2521 FillPolygon(dpy
, areawin
->clipmask
, areawin
->cmgc
, pathlist
,
2522 number
, Nonconvex
, CoordModeOrigin
);
2523 XSetClipMask(dpy
, areawin
->gc
, areawin
->clipmask
);
2524 // printf("level 0: Clip to clipmask\n"); // Diagnostic
2527 else if ((areawin
->clipped
> 0) && (areawin
->clipped
& 1) == 0) {
2528 if (areawin
->pbuf
== (Pixmap
)NULL
) {
2529 areawin
->pbuf
= XCreatePixmap (dpy
, areawin
->window
,
2530 areawin
->width
, areawin
->height
, 1);
2532 XCopyArea(dpy
, areawin
->clipmask
, areawin
->pbuf
, areawin
->cmgc
,
2533 0, 0, areawin
->width
, areawin
->height
, 0, 0);
2534 XSetForeground(dpy
, areawin
->cmgc
, 0);
2535 XFillRectangle(dpy
, areawin
->clipmask
, areawin
->cmgc
, 0, 0,
2536 areawin
->width
, areawin
->height
);
2537 XSetForeground(dpy
, areawin
->cmgc
, 1);
2538 FillPolygon(dpy
, areawin
->clipmask
, areawin
->cmgc
, pathlist
,
2539 number
, Nonconvex
, CoordModeOrigin
);
2540 XSetFunction(dpy
, areawin
->cmgc
, GXand
);
2541 XCopyArea(dpy
, areawin
->pbuf
, areawin
->clipmask
, areawin
->cmgc
,
2542 0, 0, areawin
->width
, areawin
->height
, 0, 0);
2543 XSetFunction(dpy
, areawin
->cmgc
, GXcopy
);
2544 XSetClipMask(dpy
, areawin
->gc
, areawin
->clipmask
);
2545 // printf("level X: Clip to clipmask\n"); // Diagnostic
2550 #endif /* !HAVE_CAIRO */
2552 /*-------------------------------------------------------------------------*/
2554 void makesplinepath(splineptr thespline
, XPoint
*pathlist
)
2556 XPoint
*tmpptr
= pathlist
;
2558 UTransformbyCTM(DCTM
, &(thespline
->ctrl
[0]), tmpptr
, 1);
2559 UfTransformbyCTM(DCTM
, thespline
->points
, ++tmpptr
, INTSEGS
);
2560 UTransformbyCTM(DCTM
, &(thespline
->ctrl
[3]), tmpptr
+ INTSEGS
, 1);
2563 /*-------------------------------------------------------------------------*/
2566 void UDrawSpline(splineptr thespline
, float passwidth
)
2568 XPoint tmppoints
[SPLINESEGS
];
2571 if (!areawin
->redraw_ongoing
) {
2572 areawin
->redraw_needed
= True
;
2576 scaledwidth
= thespline
->width
* passwidth
;
2578 makesplinepath(thespline
, tmppoints
);
2579 strokepath(tmppoints
, SPLINESEGS
, thespline
->style
, scaledwidth
);
2581 #endif /* HAVE_CAIRO */
2583 /*-------------------------------------------------------------------------*/
2586 void UDrawPolygon(polyptr thepoly
, float passwidth
)
2588 XPoint
*tmppoints
= (pointlist
) malloc(thepoly
->number
* sizeof(XPoint
));
2591 if (!areawin
->redraw_ongoing
) {
2592 areawin
->redraw_needed
= True
;
2596 scaledwidth
= thepoly
->width
* passwidth
;
2598 UTransformbyCTM(DCTM
, thepoly
->points
, tmppoints
, thepoly
->number
);
2599 strokepath(tmppoints
, thepoly
->number
, thepoly
->style
, scaledwidth
);
2602 #endif /* HAVE_CAIRO */
2604 /*-------------------------------------------------------------------------*/
2607 void UDrawArc(arcptr thearc
, float passwidth
)
2609 XPoint tmppoints
[RSTEPS
+ 2];
2612 if (!areawin
->redraw_ongoing
) {
2613 areawin
->redraw_needed
= True
;
2617 scaledwidth
= thearc
->width
* passwidth
;
2619 UfTransformbyCTM(DCTM
, thearc
->points
, tmppoints
, thearc
->number
);
2620 strokepath(tmppoints
, thearc
->number
, thearc
->style
, scaledwidth
);
2622 #endif /* HAVE_CAIRO */
2624 /*-------------------------------------------------------------------------*/
2627 void UDrawPath(pathptr thepath
, float passwidth
)
2629 XPoint
*tmppoints
= (pointlist
) malloc(sizeof(XPoint
));
2630 genericptr
*genpath
;
2632 splineptr thespline
;
2633 int pathsegs
= 0, curseg
= 0;
2636 if (!areawin
->redraw_ongoing
) {
2637 areawin
->redraw_needed
= True
;
2641 for (genpath
= thepath
->plist
; genpath
< thepath
->plist
+ thepath
->parts
;
2643 switch(ELEMENTTYPE(*genpath
)) {
2645 thepoly
= TOPOLY(genpath
);
2646 pathsegs
+= thepoly
->number
;
2647 tmppoints
= (pointlist
) realloc(tmppoints
, pathsegs
* sizeof(XPoint
));
2648 UTransformbyCTM(DCTM
, thepoly
->points
, tmppoints
+ curseg
, thepoly
->number
);
2652 thespline
= TOSPLINE(genpath
);
2653 pathsegs
+= SPLINESEGS
;
2654 tmppoints
= (pointlist
) realloc(tmppoints
, pathsegs
* sizeof(XPoint
));
2655 makesplinepath(thespline
, tmppoints
+ curseg
);
2660 scaledwidth
= thepath
->width
* passwidth
;
2662 strokepath(tmppoints
, pathsegs
, thepath
->style
, scaledwidth
);
2665 #endif /* HAVE_CAIRO */
2667 /*----------------------------------------------------------------------*/
2668 /* Main recursive object instance drawing routine. */
2669 /* context is the instance information passed down from above */
2670 /* theinstance is the object instance to be drawn */
2671 /* level is the level of recursion */
2672 /* passcolor is the inherited color value passed to object */
2673 /* passwidth is the inherited linewidth value passed to the object */
2674 /* stack contains graphics context information */
2675 /*----------------------------------------------------------------------*/
2678 void UDrawObject(objinstptr theinstance
, short level
, int passcolor
,
2679 float passwidth
, pushlistptr
*stack
)
2681 genericptr
*areagen
;
2683 int defaultcolor
= passcolor
;
2684 int curcolor
= passcolor
;
2687 XPoint bboxin
[2], bboxout
[2];
2689 objectptr theobject
= theinstance
->thisobject
;
2691 if (!areawin
->redraw_ongoing
) {
2692 areawin
->redraw_needed
= True
;
2696 /* Save the number of selections and set it to zero while we do the */
2697 /* object drawing. */
2699 savesel
= areawin
->selects
;
2700 areawin
->selects
= 0;
2702 /* All parts are given in the coordinate system of the object, unless */
2703 /* this is the top-level object, in which they will be interpreted as */
2704 /* relative to the screen. */
2709 /* Save the current clipping mask and push it on the stack */
2710 if (areawin
->clipped
> 0) {
2711 push_stack((pushlistptr
*)stack
, theinstance
, (char *)areawin
->clipmask
);
2712 areawin
->clipmask
= XCreatePixmap(dpy
, areawin
->window
, areawin
->width
,
2713 areawin
->height
, 1);
2714 XCopyArea(dpy
, (Pixmap
)(*stack
)->clientdata
, areawin
->clipmask
, areawin
->cmgc
,
2715 0, 0, areawin
->width
, areawin
->height
, 0, 0);
2718 push_stack((pushlistptr
*)stack
, theinstance
, (char *)NULL
);
2721 UPreMultCTM(DCTM
, theinstance
->position
, theinstance
->scale
,
2722 theinstance
->rotation
);
2724 if (theinstance
->style
& LINE_INVARIANT
)
2725 passwidth
/= fabs(theinstance
->scale
);
2727 /* do a quick test for intersection with the display window */
2729 bboxin
[0].x
= theobject
->bbox
.lowerleft
.x
;
2730 bboxin
[0].y
= theobject
->bbox
.lowerleft
.y
;
2731 bboxin
[1].x
= theobject
->bbox
.lowerleft
.x
+ theobject
->bbox
.width
;
2732 bboxin
[1].y
= theobject
->bbox
.lowerleft
.y
+ theobject
->bbox
.height
;
2734 extendschembbox(theinstance
, &(bboxin
[0]), &(bboxin
[1]));
2735 UTransformbyCTM(DCTM
, bboxin
, bboxout
, 2);
2737 xm
= (bboxout
[0].x
< bboxout
[1].x
) ? 0 : 1;
2738 ym
= (bboxout
[0].y
< bboxout
[1].y
) ? 0 : 1;
2740 if (bboxout
[xm
].x
< areawin
->width
&& bboxout
[ym
].y
< areawin
->height
&&
2741 bboxout
[1 - xm
].x
> 0 && bboxout
[1 - ym
].y
> 0) {
2743 /* make parameter substitutions */
2744 psubstitute(theinstance
);
2746 /* draw all of the elements */
2748 tmpwidth
= UTopTransScale(passwidth
);
2749 SetLineAttributes(dpy
, areawin
->gc
, tmpwidth
, LineSolid
, CapRound
,
2752 /* guard against plist being regenerated during a redraw by the */
2753 /* expression parameter mechanism (should that be prohibited?) */
2755 for (thispart
= 0; thispart
< theobject
->parts
; thispart
++) {
2756 areagen
= theobject
->plist
+ thispart
;
2757 if ((*areagen
)->type
& DRAW_HIDE
) continue;
2759 if (defaultcolor
!= DOFORALL
) {
2760 Boolean clipcolor
= FALSE
;
2761 switch(ELEMENTTYPE(*areagen
)) {
2762 case(POLYGON
): case(SPLINE
): case(ARC
): case(PATH
):
2763 if (TOPOLY(areagen
)->style
& CLIPMASK
)
2767 if (((*areagen
)->color
!= curcolor
) || (clipcolor
== TRUE
)) {
2769 curcolor
= CLIPMASKCOLOR
;
2770 else if ((*areagen
)->color
== DEFAULTCOLOR
)
2771 curcolor
= defaultcolor
;
2773 curcolor
= (*areagen
)->color
;
2775 XcTopSetForeground(curcolor
);
2779 switch(ELEMENTTYPE(*areagen
)) {
2781 if (level
== 0 || !((TOPOLY(areagen
))->style
& BBOX
))
2782 UDrawPolygon(TOPOLY(areagen
), passwidth
);
2786 UDrawSpline(TOSPLINE(areagen
), passwidth
);
2790 UDrawArc(TOARC(areagen
), passwidth
);
2794 UDrawPath(TOPATH(areagen
), passwidth
);
2798 UDrawGraphic(TOGRAPHIC(areagen
));
2802 if (areawin
->editinplace
&& stack
&& (TOOBJINST(areagen
)
2803 == areawin
->topinstance
)) {
2804 /* If stack matches areawin->stack, then don't draw */
2805 /* because it would be redundant. */
2806 pushlistptr alist
= *stack
, blist
= areawin
->stack
;
2807 while (alist
&& blist
) {
2808 if (alist
->thisinst
!= blist
->thisinst
) break;
2809 alist
= alist
->next
;
2810 blist
= blist
->next
;
2812 if ((!alist
) || (!blist
)) break;
2814 if (areawin
->clipped
> 0) areawin
->clipped
+= 2;
2815 UDrawObject(TOOBJINST(areagen
), level
+ 1, curcolor
, passwidth
, stack
);
2816 if (areawin
->clipped
> 0) areawin
->clipped
-= 2;
2820 if (level
== 0 || TOLABEL(areagen
)->pin
== False
)
2821 UDrawString(TOLABEL(areagen
), curcolor
, theinstance
);
2822 else if ((TOLABEL(areagen
)->anchor
& PINVISIBLE
) && areawin
->pinpointon
)
2823 UDrawString(TOLABEL(areagen
), curcolor
, theinstance
);
2824 else if (TOLABEL(areagen
)->anchor
& PINVISIBLE
)
2825 UDrawStringNoX(TOLABEL(areagen
), curcolor
, theinstance
);
2826 else if (level
== 1 && TOLABEL(areagen
)->pin
&&
2827 TOLABEL(areagen
)->pin
!= INFO
&& areawin
->pinpointon
)
2828 UDrawXDown(TOLABEL(areagen
));
2831 if (areawin
->clipped
> 0) {
2832 if ((areawin
->clipped
& 3) == 1) {
2835 else if ((areawin
->clipped
& 3) == 2) {
2836 areawin
->clipped
-= 2;
2837 if ((!stack
) || ((*stack
)->clientdata
== (char *)NULL
)) {
2838 XSetClipMask(dpy
, areawin
->gc
, None
);
2839 // printf("1: Clear clipmask\n"); // Diagnostic
2842 XSetClipMask(dpy
, areawin
->gc
, (Pixmap
)((*stack
)->clientdata
));
2843 // printf("1: Set to pushed clipmask\n"); // Diagnostic
2849 /* restore the color passed to the object, if different from current color */
2851 if ((defaultcolor
!= DOFORALL
) && (passcolor
!= curcolor
)) {
2852 XTopSetForeground(passcolor
);
2854 if (areawin
->clipped
> 0) {
2855 if ((areawin
->clipped
& 3) != 3) {
2856 if ((!stack
) || ((*stack
)->clientdata
== (char *)NULL
)) {
2857 XSetClipMask(dpy
, areawin
->gc
, None
);
2858 // printf("2: Clear clipmask\n"); // Diagnostic
2861 XSetClipMask(dpy
, areawin
->gc
, (Pixmap
)((*stack
)->clientdata
));
2862 // printf("2: Set to pushed clipmask\n"); // Diagnostic
2865 areawin
->clipped
&= ~3;
2869 /* restore the selection list (if any) */
2870 areawin
->selects
= savesel
;
2873 if ((*stack
) != NULL
) {
2874 if ((*stack
)->clientdata
!= (char *)NULL
) {
2875 XFreePixmap(dpy
, areawin
->clipmask
);
2876 areawin
->clipmask
= (Pixmap
)(*stack
)->clientdata
;
2877 // printf("3: Restore clipmask\n"); // Diagnostic
2883 #endif /* HAVE_CAIRO */
2885 /*----------------------------------------------------------------------*/
2886 /* Recursively run through the current page and find any labels which */
2887 /* are declared to be style LATEX. If "checkonly" is present, we set */
2888 /* it to TRUE or FALSE depending on whether or not LATEX labels have */
2889 /* been encountered. If NULL, then we write LATEX output appropriately */
2890 /* to a file named with the page filename + suffix ".tex". */
2891 /*----------------------------------------------------------------------*/
2893 void UDoLatex(objinstptr theinstance
, short level
, FILE *f
,
2894 float scale
, float scale2
, int tx
, int ty
, Boolean
*checkonly
)
2899 genericptr
*areagen
;
2900 objectptr theobject
= theinstance
->thisobject
;
2902 int lranchor
, tbanchor
;
2906 UPreMultCTM(DCTM
, theinstance
->position
, theinstance
->scale
,
2907 theinstance
->rotation
);
2909 /* make parameter substitutions */
2910 psubstitute(theinstance
);
2912 /* find all of the elements */
2914 for (areagen
= theobject
->plist
; areagen
< theobject
->plist
+
2915 theobject
->parts
; areagen
++) {
2917 switch(ELEMENTTYPE(*areagen
)) {
2919 UDoLatex(TOOBJINST(areagen
), level
+ 1, f
, scale
, scale2
, tx
, ty
, checkonly
);
2923 thislabel
= TOLABEL(areagen
);
2924 if (level
== 0 || thislabel
->pin
== False
||
2925 (thislabel
->anchor
& PINVISIBLE
))
2926 if (thislabel
->anchor
& LATEXLABEL
) {
2932 lpos
.x
= thislabel
->position
.x
;
2933 lpos
.y
= thislabel
->position
.y
;
2934 UTransformbyCTM(DCTM
, &lpos
, &xlpos
, 1);
2937 xfpos
.x
= (float)xlpos
.x
* scale
;
2938 xfpos
.y
= (float)xlpos
.y
* scale
;
2947 ltext
= textprinttex(thislabel
->string
, theinstance
);
2948 tbanchor
= thislabel
->anchor
& (NOTBOTTOM
| TOP
);
2949 lranchor
= thislabel
->anchor
& (NOTLEFT
| RIGHT
);
2951 /* The 1.2 factor accounts for the difference between */
2952 /* Xcircuit's label scale of "1" and LaTeX's "normalsize" */
2954 fprintf(f
, " \\putbox{%3.2fin}{%3.2fin}{%3.2f}{",
2955 xfpos
.x
, xfpos
.y
, 1.2 * thislabel
->scale
);
2956 if (thislabel
->rotation
!= 0)
2957 fprintf(f
, "\\rotatebox{-%d}{", thislabel
->rotation
);
2958 if (lranchor
== (NOTLEFT
| RIGHT
)) fprintf(f
, "\\rightbox{");
2959 else if (lranchor
== NOTLEFT
) fprintf(f
, "\\centbox{");
2960 if (tbanchor
== (NOTBOTTOM
| TOP
)) fprintf(f
, "\\topbox{");
2961 else if (tbanchor
== NOTBOTTOM
) fprintf(f
, "\\midbox{");
2962 fprintf(f
, "%s", ltext
);
2963 if (lranchor
!= NORMAL
) fprintf(f
, "}");
2964 if (tbanchor
!= NORMAL
) fprintf(f
, "}");
2965 if (thislabel
->rotation
!= 0) fprintf(f
, "}");
2966 fprintf(f
, "}%%\n");
2976 /*----------------------------------------------------------------------*/
2977 /* Top level routine for writing LATEX output. */
2978 /*----------------------------------------------------------------------*/
2983 float psscale
, outscale
;
2984 int tx
, ty
, width
, height
;
2987 Boolean checklatex
= FALSE
;
2988 char filename
[100], extension
[10], *dotptr
;
2990 UDoLatex(areawin
->topinstance
, 0, NULL
, 1.0, 1.0, 0, 0, &checklatex
);
2992 if (checklatex
== FALSE
) return; /* No LaTeX labels to write */
2994 /* Handle cases where the file might have a ".eps" extension. */
2995 /* Thanks to Graham Sheward for pointing this out. */
2997 /* Modified file path routines: */
2998 /* Solved problems with incomplete paths, NULL file pointers, */
2999 /* added tilde and variable expansion by AgustÃn Campeny, April 2020 */
3001 sprintf(filename
, "%s", xobjs
.pagelist
[areawin
->page
]->filename
);
3003 xc_tilde_expand(filename
, 100);
3004 while(xc_variable_expand(filename
, 100));
3006 dotptr
= strrchr(filename
, '.');
3007 sprintf(extension
, "%s", dotptr
);
3008 filename
[dotptr
- filename
] = '\0';
3009 sprintf(filename
, "%s.tex", filename
);
3011 f
= fopen(filename
, "w");
3014 Wprintf("Couldn't save .tex file. Check file path");
3020 fprintf(f
, "%% XCircuit output \"%s.tex\" for LaTeX input from %s%s\n",
3021 filename
, filename
, extension
);
3022 fprintf(f
, "\\def\\putbox#1#2#3#4{\\makebox[0in][l]{\\makebox[#1][l]{}"
3023 "\\raisebox{\\baselineskip}[0in][0in]"
3024 "{\\raisebox{#2}[0in][0in]{\\scalebox{#3}{#4}}}}}\n");
3025 fprintf(f
, "\\def\\rightbox#1{\\makebox[0in][r]{#1}}\n");
3026 fprintf(f
, "\\def\\centbox#1{\\makebox[0in]{#1}}\n");
3027 fprintf(f
, "\\def\\topbox#1{\\raisebox{-0.60\\baselineskip}[0in][0in]{#1}}\n");
3028 fprintf(f
, "\\def\\midbox#1{\\raisebox{-0.20\\baselineskip}[0in][0in]{#1}}\n");
3030 /* Modified to use \scalebox and \parbox by Alex Tercete, June 2008 */
3032 // fprintf(f, "\\begin{center}\n");
3034 outscale
= xobjs
.pagelist
[areawin
->page
]->outscale
;
3035 psscale
= getpsscale(outscale
, areawin
->page
);
3037 width
= toplevelwidth(areawin
->topinstance
, &origin
.x
);
3038 height
= toplevelheight(areawin
->topinstance
, &origin
.y
);
3040 /* Added 10/19/10: If there is a specified bounding box, let it */
3041 /* determine the figure origin; otherwise, the labels will be */
3042 /* mismatched to the bounding box. */
3044 if ((framebox
= checkforbbox(topobject
)) != NULL
) {
3047 origin
.x
= maxx
= framebox
->points
[0].x
;
3048 origin
.y
= maxy
= framebox
->points
[0].y
;
3049 for (i
= 1; i
< framebox
->number
; i
++) {
3050 if (framebox
->points
[i
].x
< origin
.x
) origin
.x
= framebox
->points
[i
].x
;
3051 if (framebox
->points
[i
].x
> maxx
) maxx
= framebox
->points
[i
].x
;
3052 if (framebox
->points
[i
].y
< origin
.y
) origin
.y
= framebox
->points
[i
].y
;
3053 if (framebox
->points
[i
].y
> maxy
) maxy
= framebox
->points
[i
].y
;
3055 origin
.x
-= ((width
- maxx
+ origin
.x
) / 2);
3056 origin
.y
-= ((height
- maxy
+ origin
.y
) / 2);
3059 tx
= (int)(72 / psscale
) - origin
.x
,
3060 ty
= (int)(72 / psscale
) - origin
.y
;
3062 fprintf(f
, " \\scalebox{%g}{\n", outscale
);
3063 fprintf(f
, " \\normalsize\n");
3064 fprintf(f
, " \\parbox{%gin}{\n", (((float)width
* psscale
) / 72.0) / outscale
);
3065 fprintf(f
, " \\includegraphics[scale=%g]{%s%s}\\\\\n", 1.0 / outscale
,
3066 filename
, extension
);
3067 fprintf(f
, " %% translate x=%d y=%d scale %3.2f\n", tx
, ty
, psscale
);
3069 UPushCTM(); /* Save current state */
3070 UResetCTM(DCTM
); /* Set to identity matrix */
3071 UDoLatex(areawin
->topinstance
, 0, f
, psscale
, outscale
, tx
, ty
, NULL
);
3072 UPopCTM(); /* Restore state */
3074 fprintf(f
, " } %% close \'parbox\'\n");
3075 fprintf(f
, " } %% close \'scalebox\'\n");
3076 fprintf(f
, " \\vspace{-\\baselineskip} %% this is not"
3077 " necessary, but looks better\n");
3078 // fprintf(f, "\\end{center}\n");
3081 Wprintf("Wrote auxiliary file %s.tex", filename
);