1
2 /*
3 * bltGrMisc.c --
4 *
5 * This module implements miscellaneous routines for the BLT
6 * graph widget.
7 *
8 * Copyright 1993-1998 Lucent Technologies, Inc.
9 *
10 * Permission to use, copy, modify, and distribute this software and
11 * its documentation for any purpose and without fee is hereby
12 * granted, provided that the above copyright notice appear in all
13 * copies and that both that the copyright notice and warranty
14 * disclaimer appear in supporting documentation, and that the names
15 * of Lucent Technologies any of their entities not be used in
16 * advertising or publicity pertaining to distribution of the software
17 * without specific, written prior permission.
18 *
19 * Lucent Technologies disclaims all warranties with regard to this
20 * software, including all implied warranties of merchantability and
21 * fitness. In no event shall Lucent Technologies be liable for any
22 * special, indirect or consequential damages or any damages
23 * whatsoever resulting from loss of use, data or profits, whether in
24 * an action of contract, negligence or other tortuous action, arising
25 * out of or in connection with the use or performance of this
26 * software.
27 */
28
29 #include "bltGraph.h"
30 #include <X11/Xutil.h>
31
32 #if defined(__STDC__)
33 #include <stdarg.h>
34 #else
35 #include <varargs.h>
36 #endif
37
38
39 static Tk_OptionParseProc StringToPoint;
40 static Tk_OptionPrintProc PointToString;
41 static Tk_OptionParseProc StringToColorPair;
42 static Tk_OptionPrintProc ColorPairToString;
43 Tk_CustomOption bltPointOption =
44 {
45 StringToPoint, PointToString, (ClientData)0
46 };
47 Tk_CustomOption bltColorPairOption =
48 {
49 StringToColorPair, ColorPairToString, (ClientData)0
50 };
51
52 /* ----------------------------------------------------------------------
53 * Custom option parse and print procedures
54 * ----------------------------------------------------------------------
55 */
56
57 /*
58 *----------------------------------------------------------------------
59 *
60 * Blt_GetXY --
61 *
62 * Converts a string in the form "@x,y" into an XPoint structure
63 * of the x and y coordinates.
64 *
65 * Results:
66 * A standard Tcl result. If the string represents a valid position
67 * *pointPtr* will contain the converted x and y coordinates and
68 * TCL_OK is returned. Otherwise, TCL_ERROR is returned and
69 * interp->result will contain an error message.
70 *
71 *----------------------------------------------------------------------
72 */
73 int
Blt_GetXY(interp,tkwin,string,xPtr,yPtr)74 Blt_GetXY(interp, tkwin, string, xPtr, yPtr)
75 Tcl_Interp *interp;
76 Tk_Window tkwin;
77 char *string;
78 int *xPtr, *yPtr;
79 {
80 char *comma;
81 int result;
82 int x, y;
83
84 if ((string == NULL) || (*string == '\0')) {
85 *xPtr = *yPtr = -SHRT_MAX;
86 return TCL_OK;
87 }
88 if (*string != '@') {
89 goto badFormat;
90 }
91 comma = strchr(string + 1, ',');
92 if (comma == NULL) {
93 goto badFormat;
94 }
95 *comma = '\0';
96 result = ((Tk_GetPixels(interp, tkwin, string + 1, &x) == TCL_OK) &&
97 (Tk_GetPixels(interp, tkwin, comma + 1, &y) == TCL_OK));
98 *comma = ',';
99 if (!result) {
100 Tcl_AppendResult(interp, ": can't parse position \"", string, "\"",
101 (char *)NULL);
102 return TCL_ERROR;
103 }
104 *xPtr = x, *yPtr = y;
105 return TCL_OK;
106
107 badFormat:
108 Tcl_AppendResult(interp, "bad position \"", string,
109 "\": should be \"@x,y\"", (char *)NULL);
110 return TCL_ERROR;
111 }
112
113 /*
114 *----------------------------------------------------------------------
115 *
116 * StringToPoint --
117 *
118 * Convert the string representation of a legend XY position into
119 * window coordinates. The form of the string must be "@x,y" or
120 * none.
121 *
122 * Results:
123 * A standard Tcl result. The symbol type is written into the
124 * widget record.
125 *
126 *----------------------------------------------------------------------
127 */
128 /*ARGSUSED*/
129 static int
StringToPoint(clientData,interp,tkwin,string,widgRec,offset)130 StringToPoint(clientData, interp, tkwin, string, widgRec, offset)
131 ClientData clientData; /* Not used. */
132 Tcl_Interp *interp; /* Interpreter to send results back to */
133 Tk_Window tkwin; /* Not used. */
134 char *string; /* New legend position string */
135 char *widgRec; /* Widget record */
136 int offset; /* offset to XPoint structure */
137 {
138 XPoint *pointPtr = (XPoint *)(widgRec + offset);
139 int x, y;
140
141 if (Blt_GetXY(interp, tkwin, string, &x, &y) != TCL_OK) {
142 return TCL_ERROR;
143 }
144 pointPtr->x = x, pointPtr->y = y;
145 return TCL_OK;
146 }
147
148 /*
149 *----------------------------------------------------------------------
150 *
151 * PointToString --
152 *
153 * Convert the window coordinates into a string.
154 *
155 * Results:
156 * The string representing the coordinate position is returned.
157 *
158 *----------------------------------------------------------------------
159 */
160 /*ARGSUSED*/
161 static char *
PointToString(clientData,tkwin,widgRec,offset,freeProcPtr)162 PointToString(clientData, tkwin, widgRec, offset, freeProcPtr)
163 ClientData clientData; /* Not used. */
164 Tk_Window tkwin; /* Not used. */
165 char *widgRec; /* Widget record */
166 int offset; /* offset of XPoint in record */
167 Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
168 {
169 char *result;
170 XPoint *pointPtr = (XPoint *)(widgRec + offset);
171
172 result = "";
173 if ((pointPtr->x != -SHRT_MAX) && (pointPtr->y != -SHRT_MAX)) {
174 char string[200];
175
176 sprintf(string, "@%d,%d", pointPtr->x, pointPtr->y);
177 result = Blt_Strdup(string);
178 assert(result);
179 *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
180 }
181 return result;
182 }
183
184 /*LINTLIBRARY*/
185 static int
GetColorPair(interp,tkwin,fgStr,bgStr,pairPtr,allowDefault)186 GetColorPair(interp, tkwin, fgStr, bgStr, pairPtr, allowDefault)
187 Tcl_Interp *interp;
188 Tk_Window tkwin;
189 char *fgStr, *bgStr;
190 ColorPair *pairPtr;
191 int allowDefault;
192 {
193 unsigned int length;
194 XColor *fgColor, *bgColor;
195
196 length = strlen(fgStr);
197 if (fgStr[0] == '\0') {
198 fgColor = NULL;
199 } else if ((allowDefault) && (fgStr[0] == 'd') &&
200 (strncmp(fgStr, "defcolor", length) == 0)) {
201 fgColor = COLOR_DEFAULT;
202 } else {
203 fgColor = Tk_GetColor(interp, tkwin, Tk_GetUid(fgStr));
204 if (fgColor == NULL) {
205 return TCL_ERROR;
206 }
207 }
208 length = strlen(bgStr);
209 if (bgStr[0] == '\0') {
210 bgColor = NULL;
211 } else if ((allowDefault) && (bgStr[0] == 'd') &&
212 (strncmp(bgStr, "defcolor", length) == 0)) {
213 bgColor = COLOR_DEFAULT;
214 } else {
215 bgColor = Tk_GetColor(interp, tkwin, Tk_GetUid(bgStr));
216 if (bgColor == NULL) {
217 return TCL_ERROR;
218 }
219 }
220 pairPtr->fgColor = fgColor;
221 pairPtr->bgColor = bgColor;
222 return TCL_OK;
223 }
224
225 void
Blt_FreeColorPair(pairPtr)226 Blt_FreeColorPair(pairPtr)
227 ColorPair *pairPtr;
228 {
229 if ((pairPtr->bgColor != NULL) && (pairPtr->bgColor != COLOR_DEFAULT)) {
230 Tk_FreeColor(pairPtr->bgColor);
231 }
232 if ((pairPtr->fgColor != NULL) && (pairPtr->fgColor != COLOR_DEFAULT)) {
233 Tk_FreeColor(pairPtr->fgColor);
234 }
235 pairPtr->bgColor = pairPtr->fgColor = NULL;
236 }
237
238 /*
239 *----------------------------------------------------------------------
240 *
241 * StringToColorPair --
242 *
243 * Convert the color names into pair of XColor pointers.
244 *
245 * Results:
246 * A standard Tcl result. The color pointer is written into the
247 * widget record.
248 *
249 *----------------------------------------------------------------------
250 */
251 /*ARGSUSED*/
252 static int
StringToColorPair(clientData,interp,tkwin,string,widgRec,offset)253 StringToColorPair(clientData, interp, tkwin, string, widgRec, offset)
254 ClientData clientData; /* Not used. */
255 Tcl_Interp *interp; /* Interpreter to send results back to */
256 Tk_Window tkwin; /* Not used. */
257 char *string; /* String representing color */
258 char *widgRec; /* Widget record */
259 int offset; /* Offset of color field in record */
260 {
261 ColorPair *pairPtr = (ColorPair *)(widgRec + offset);
262 ColorPair sample;
263 int allowDefault = (int)clientData;
264
265 sample.fgColor = sample.bgColor = NULL;
266 if ((string != NULL) && (*string != '\0')) {
267 int nColors;
268 char **colors;
269 int result;
270
271 if (Tcl_SplitList(interp, string, &nColors, &colors) != TCL_OK) {
272 return TCL_ERROR;
273 }
274 switch (nColors) {
275 case 0:
276 result = TCL_OK;
277 break;
278 case 1:
279 result = GetColorPair(interp, tkwin, colors[0], "", &sample,
280 allowDefault);
281 break;
282 case 2:
283 result = GetColorPair(interp, tkwin, colors[0], colors[1],
284 &sample, allowDefault);
285 break;
286 default:
287 result = TCL_ERROR;
288 Tcl_AppendResult(interp, "too many names in colors list",
289 (char *)NULL);
290 }
291 Blt_Free(colors);
292 if (result != TCL_OK) {
293 return TCL_ERROR;
294 }
295 }
296 Blt_FreeColorPair(pairPtr);
297 *pairPtr = sample;
298 return TCL_OK;
299 }
300
301 /*
302 *----------------------------------------------------------------------
303 *
304 * NameOfColor --
305 *
306 * Convert the color option value into a string.
307 *
308 * Results:
309 * The static string representing the color option is returned.
310 *
311 *----------------------------------------------------------------------
312 */
313 static char *
NameOfColor(colorPtr)314 NameOfColor(colorPtr)
315 XColor *colorPtr;
316 {
317 if (colorPtr == NULL) {
318 return "";
319 } else if (colorPtr == COLOR_DEFAULT) {
320 return "defcolor";
321 } else {
322 return Tk_NameOfColor(colorPtr);
323 }
324 }
325
326 /*
327 *----------------------------------------------------------------------
328 *
329 * ColorPairToString --
330 *
331 * Convert the color pairs into color names.
332 *
333 * Results:
334 * The string representing the symbol color is returned.
335 *
336 *----------------------------------------------------------------------
337 */
338 /*ARGSUSED*/
339 static char *
ColorPairToString(clientData,tkwin,widgRec,offset,freeProcPtr)340 ColorPairToString(clientData, tkwin, widgRec, offset, freeProcPtr)
341 ClientData clientData; /* Not used. */
342 Tk_Window tkwin; /* Not used. */
343 char *widgRec; /* Element information record */
344 int offset; /* Offset of symbol type field in record */
345 Tcl_FreeProc **freeProcPtr; /* Not used. */
346 {
347 ColorPair *pairPtr = (ColorPair *)(widgRec + offset);
348 Tcl_DString dString;
349 char *result;
350
351 Tcl_DStringInit(&dString);
352 Tcl_DStringAppendElement(&dString, NameOfColor(pairPtr->fgColor));
353 Tcl_DStringAppendElement(&dString, NameOfColor(pairPtr->bgColor));
354 result = Tcl_DStringValue(&dString);
355 if (result == dString.staticSpace) {
356 result = Blt_Strdup(result);
357 }
358 *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
359 return result;
360 }
361
362 int
Blt_PointInSegments(samplePtr,segments,nSegments,halo)363 Blt_PointInSegments(samplePtr, segments, nSegments, halo)
364 Point2D *samplePtr;
365 Segment2D *segments;
366 int nSegments;
367 double halo;
368 {
369 register Segment2D *segPtr, *endPtr;
370 double left, right, top, bottom;
371 Point2D p, t;
372 double dist, minDist;
373
374 minDist = DBL_MAX;
375 for (segPtr = segments, endPtr = segments + nSegments; segPtr < endPtr;
376 segPtr++) {
377 t = Blt_GetProjection((int)samplePtr->x, (int)samplePtr->y,
378 &segPtr->p, &segPtr->q);
379 if (segPtr->p.x > segPtr->q.x) {
380 right = segPtr->p.x, left = segPtr->q.x;
381 } else {
382 right = segPtr->q.x, left = segPtr->p.x;
383 }
384 if (segPtr->p.y > segPtr->q.y) {
385 bottom = segPtr->p.y, top = segPtr->q.y;
386 } else {
387 bottom = segPtr->q.y, top = segPtr->p.y;
388 }
389 p.x = BOUND(t.x, left, right);
390 p.y = BOUND(t.y, top, bottom);
391 dist = hypot(p.x - samplePtr->x, p.y - samplePtr->y);
392 if (dist < minDist) {
393 minDist = dist;
394 }
395 }
396 return (minDist < halo);
397 }
398
399 int
Blt_PointInPolygon(samplePtr,points,nPoints)400 Blt_PointInPolygon(samplePtr, points, nPoints)
401 Point2D *samplePtr;
402 Point2D *points;
403 int nPoints;
404 {
405 double b;
406 register Point2D *p, *q, *endPtr;
407 register int count;
408
409 count = 0;
410 for (p = points, q = p + 1, endPtr = p + nPoints; q < endPtr; p++, q++) {
411 if (((p->y <= samplePtr->y) && (samplePtr->y < q->y)) ||
412 ((q->y <= samplePtr->y) && (samplePtr->y < p->y))) {
413 b = (q->x - p->x) * (samplePtr->y - p->y) / (q->y - p->y) + p->x;
414 if (samplePtr->x < b) {
415 count++; /* Count the number of intersections. */
416 }
417 }
418 }
419 return (count & 0x01);
420 }
421
422 int
Blt_RegionInPolygon(extsPtr,points,nPoints,enclosed)423 Blt_RegionInPolygon(extsPtr, points, nPoints, enclosed)
424 Extents2D *extsPtr;
425 Point2D *points;
426 int nPoints;
427 int enclosed;
428 {
429 register Point2D *pointPtr, *endPtr;
430
431 if (enclosed) {
432 /*
433 * All points of the polygon must be inside the rectangle.
434 */
435 for (pointPtr = points, endPtr = points + nPoints; pointPtr < endPtr;
436 pointPtr++) {
437 if ((pointPtr->x < extsPtr->left) ||
438 (pointPtr->x > extsPtr->right) ||
439 (pointPtr->y < extsPtr->top) ||
440 (pointPtr->y > extsPtr->bottom)) {
441 return FALSE; /* One point is exterior. */
442 }
443 }
444 return TRUE;
445 } else {
446 Point2D p, q;
447
448 /*
449 * If any segment of the polygon clips the bounding region, the
450 * polygon overlaps the rectangle.
451 */
452 points[nPoints] = points[0];
453 for (pointPtr = points, endPtr = points + nPoints; pointPtr < endPtr;
454 pointPtr++) {
455 p = *pointPtr;
456 q = *(pointPtr + 1);
457 if (Blt_LineRectClip(extsPtr, &p, &q)) {
458 return TRUE;
459 }
460 }
461 /*
462 * Otherwise the polygon and rectangle are either disjoint
463 * or enclosed. Check if one corner of the rectangle is
464 * inside the polygon.
465 */
466 p.x = extsPtr->left;
467 p.y = extsPtr->top;
468 return Blt_PointInPolygon(&p, points, nPoints);
469 }
470 }
471
472 /*
473 *----------------------------------------------------------------------
474 *
475 * Blt_GraphExtents --
476 *
477 * Generates a bounding box representing the plotting area of
478 * the graph. This data structure is used to clip the points and
479 * line segments of the line element.
480 *
481 * The clip region is the plotting area plus such arbitrary extra
482 * space. The reason we clip with a bounding box larger than the
483 * plot area is so that symbols will be drawn even if their center
484 * point isn't in the plotting area.
485 *
486 * Results:
487 * None.
488 *
489 * Side Effects:
490 * The bounding box is filled with the dimensions of the plotting
491 * area.
492 *
493 *----------------------------------------------------------------------
494 */
495 void
Blt_GraphExtents(graphPtr,extsPtr)496 Blt_GraphExtents(graphPtr, extsPtr)
497 Graph *graphPtr;
498 Extents2D *extsPtr;
499 {
500 extsPtr->left = (double)(graphPtr->hOffset - graphPtr->padX.side1);
501 extsPtr->top = (double)(graphPtr->vOffset - graphPtr->padY.side1);
502 extsPtr->right = (double)(graphPtr->hOffset + graphPtr->hRange +
503 graphPtr->padX.side2);
504 extsPtr->bottom = (double)(graphPtr->vOffset + graphPtr->vRange +
505 graphPtr->padY.side2);
506 }
507
508 static int
ClipTest(double ds,double dr,double * t1,double * t2)509 ClipTest (double ds, double dr, double *t1, double *t2)
510 {
511 double t;
512
513 if (ds < 0.0) {
514 t = dr / ds;
515 if (t > *t2) {
516 return FALSE;
517 }
518 if (t > *t1) {
519 *t1 = t;
520 }
521 } else if (ds > 0.0) {
522 t = dr / ds;
523 if (t < *t1) {
524 return FALSE;
525 }
526 if (t < *t2) {
527 *t2 = t;
528 }
529 } else {
530 /* d = 0, so line is parallel to this clipping edge */
531 if (dr < 0.0) { /* Line is outside clipping edge */
532 return FALSE;
533 }
534 }
535 return TRUE;
536 }
537
538 /*
539 *----------------------------------------------------------------------
540 *
541 * Blt_LineRectClip --
542 *
543 * Clips the given line segment to a rectangular region. The
544 * coordinates of the clipped line segment are returned. The
545 * original coordinates are overwritten.
546 *
547 * Reference: Liang-Barsky Line Clipping Algorithm.
548 *
549 * Results:
550 * Returns if line segment is visible within the region. The
551 * coordinates of the original line segment are overwritten
552 * by the clipped coordinates.
553 *
554 *----------------------------------------------------------------------
555 */
556 int
Blt_LineRectClip(extsPtr,p,q)557 Blt_LineRectClip(extsPtr, p, q)
558 Extents2D *extsPtr; /* Rectangular region to clip. */
559 Point2D *p, *q; /* (in/out) Coordinates of original
560 * and clipped line segment. */
561 {
562 double t1, t2;
563 double dx, dy;
564
565 t1 = 0.0;
566 t2 = 1.0;
567 dx = q->x - p->x;
568 if ((ClipTest (-dx, p->x - extsPtr->left, &t1, &t2)) &&
569 (ClipTest (dx, extsPtr->right - p->x, &t1, &t2))) {
570 dy = q->y - p->y;
571 if ((ClipTest (-dy, p->y - extsPtr->top, &t1, &t2)) &&
572 (ClipTest (dy, extsPtr->bottom - p->y, &t1, &t2))) {
573 if (t2 < 1.0) {
574 q->x = p->x + t2 * dx;
575 q->y = p->y + t2 * dy;
576 }
577 if (t1 > 0.0) {
578 p->x += t1 * dx;
579 p->y += t1 * dy;
580 }
581 return TRUE;
582 }
583 }
584 return FALSE;
585 }
586
587 /*
588 *----------------------------------------------------------------------
589 *
590 * Blt_PolyRectClip --
591 *
592 * Clips the given polygon to a rectangular region. The resulting
593 * polygon is returned. Note that the resulting polyon may be
594 * complex, connected by zero width/height segments. The drawing
595 * routine (such as XFillPolygon) will not draw a connecting
596 * segment.
597 *
598 * Reference: Liang-Barsky Polygon Clipping Algorithm
599 *
600 * Results:
601 * Returns the number of points in the clipped polygon. The
602 * points of the clipped polygon are stored in *outputPts*.
603 *
604 *----------------------------------------------------------------------
605 */
606 #define EPSILON FLT_EPSILON
607 #define AddVertex(vx, vy) r->x=(vx), r->y=(vy), r++, count++
608 #define LastVertex(vx, vy) r->x=(vx), r->y=(vy), count++
609
610 int
Blt_PolyRectClip(extsPtr,points,nPoints,clipPts)611 Blt_PolyRectClip(extsPtr, points, nPoints, clipPts)
612 Extents2D *extsPtr;
613 Point2D *points;
614 int nPoints;
615 Point2D *clipPts;
616 {
617 Point2D *endPtr;
618 double dx, dy;
619 double tin1, tin2;
620 double tinx, tiny;
621 double xin, yin, xout, yout;
622 int count;
623 register Point2D *p; /* First vertex of input polygon edge. */
624 register Point2D *q; /* Last vertex of input polygon edge. */
625 register Point2D *r;
626
627 r = clipPts;
628 count = 0; /* Counts # of vertices in output polygon. */
629
630 points[nPoints] = points[0];
631
632 for (p = points, q = p + 1, endPtr = p + nPoints; p < endPtr; p++, q++) {
633 dx = q->x - p->x; /* X-direction */
634 dy = q->y - p->y; /* Y-direction */
635
636 if (FABS(dx) < EPSILON) {
637 dx = (p->x > extsPtr->left) ? -EPSILON : EPSILON ;
638 }
639 if (FABS(dy) < EPSILON) {
640 dy = (p->y > extsPtr->top) ? -EPSILON : EPSILON ;
641 }
642
643 if (dx > 0.0) { /* Left */
644 xin = extsPtr->left;
645 xout = extsPtr->right + 1.0;
646 } else { /* Right */
647 xin = extsPtr->right + 1.0;
648 xout = extsPtr->left;
649 }
650 if (dy > 0.0) { /* Top */
651 yin = extsPtr->top;
652 yout = extsPtr->bottom + 1.0;
653 } else { /* Bottom */
654 yin = extsPtr->bottom + 1.0;
655 yout = extsPtr->top;
656 }
657
658 tinx = (xin - p->x) / dx;
659 tiny = (yin - p->y) / dy;
660
661 if (tinx < tiny) { /* Hits x first */
662 tin1 = tinx;
663 tin2 = tiny;
664 } else { /* Hits y first */
665 tin1 = tiny;
666 tin2 = tinx;
667 }
668
669 if (tin1 <= 1.0) {
670 if (tin1 > 0.0) {
671 AddVertex(xin, yin);
672 }
673 if (tin2 <= 1.0) {
674 double toutx, touty, tout1;
675
676 toutx = (xout - p->x) / dx;
677 touty = (yout - p->y) / dy;
678 tout1 = MIN(toutx, touty);
679
680 if ((tin2 > 0.0) || (tout1 > 0.0)) {
681 if (tin2 <= tout1) {
682 if (tin2 > 0.0) {
683 if (tinx > tiny) {
684 AddVertex(xin, p->y + tinx * dy);
685 } else {
686 AddVertex(p->x + tiny * dx, yin);
687 }
688 }
689 if (tout1 < 1.0) {
690 if (toutx < touty) {
691 AddVertex(xout, p->y + toutx * dy);
692 } else {
693 AddVertex(p->x + touty * dx, yout);
694 }
695 } else {
696 AddVertex(q->x, q->y);
697 }
698 } else {
699 if (tinx > tiny) {
700 AddVertex(xin, yout);
701 } else {
702 AddVertex(xout, yin);
703 }
704 }
705 }
706 }
707 }
708 }
709 if (count > 0) {
710 LastVertex(clipPts[0].x, clipPts[0].y);
711 }
712 return count;
713 }
714
715 /*
716 *----------------------------------------------------------------------
717 *
718 * Blt_GetProjection --
719 *
720 * Computes the projection of a point on a line. The line (given
721 * by two points), is assumed the be infinite.
722 *
723 * Compute the slope (angle) of the line and rotate it 90 degrees.
724 * Using the slope-intercept method (we know the second line from
725 * the sample test point and the computed slope), then find the
726 * intersection of both lines. This will be the projection of the
727 * sample point on the first line.
728 *
729 * Results:
730 * Returns the coordinates of the projection on the line.
731 *
732 *----------------------------------------------------------------------
733 */
734 Point2D
Blt_GetProjection(x,y,p,q)735 Blt_GetProjection(x, y, p, q)
736 int x, y; /* Screen coordinates of the sample point. */
737 Point2D *p, *q; /* Line segment to project point onto */
738 {
739 double dx, dy;
740 Point2D t;
741
742 dx = p->x - q->x;
743 dy = p->y - q->y;
744
745 /* Test for horizontal and vertical lines */
746 if (FABS(dx) < DBL_EPSILON) {
747 t.x = p->x, t.y = (double)y;
748 } else if (FABS(dy) < DBL_EPSILON) {
749 t.x = (double)x, t.y = p->y;
750 } else {
751 double m1, m2; /* Slope of both lines */
752 double b1, b2; /* y-intercepts */
753 double midX, midY; /* Midpoint of line segment. */
754 double ax, ay, bx, by;
755
756 /* Compute the slop and intercept of the line segment. */
757 m1 = (dy / dx);
758 b1 = p->y - (p->x * m1);
759
760 /*
761 * Compute the slope and intercept of a second line segment:
762 * one that intersects through sample X-Y coordinate with a
763 * slope perpendicular to original line.
764 */
765
766 /* Find midpoint of original segment. */
767 midX = (p->x + q->x) * 0.5;
768 midY = (p->y + q->y) * 0.5;
769
770 /* Rotate the line 90 degrees */
771 ax = midX - (0.5 * dy);
772 ay = midY - (0.5 * -dx);
773 bx = midX + (0.5 * dy);
774 by = midY + (0.5 * -dx);
775
776 m2 = (ay - by) / (ax - bx);
777 b2 = y - (x * m2);
778
779 /*
780 * Given the equations of two lines which contain the same point,
781 *
782 * y = m1 * x + b1
783 * y = m2 * x + b2
784 *
785 * solve for the intersection.
786 *
787 * x = (b2 - b1) / (m1 - m2)
788 * y = m1 * x + b1
789 *
790 */
791
792 t.x = (b2 - b1) / (m1 - m2);
793 t.y = m1 * t.x + b1;
794 }
795 return t;
796 }
797
798 typedef struct {
799 double hue, sat, val;
800 } HSV;
801
802 #define SetColor(c,r,g,b) ((c)->red = (int)((r) * 65535.0), \
803 (c)->green = (int)((g) * 65535.0), \
804 (c)->blue = (int)((b) * 65535.0))
805
806 void
Blt_XColorToHSV(colorPtr,hsvPtr)807 Blt_XColorToHSV(colorPtr, hsvPtr)
808 XColor *colorPtr;
809 HSV *hsvPtr;
810 {
811 unsigned short max, min;
812 double range;
813 unsigned short *colorValues;
814
815 /* Find the minimum and maximum RGB intensities */
816 colorValues = (unsigned short *)&colorPtr->red;
817 max = MAX3(colorValues[0], colorValues[1], colorValues[2]);
818 min = MIN3(colorValues[0], colorValues[1], colorValues[2]);
819
820 hsvPtr->val = (double)max / 65535.0;
821 hsvPtr->hue = hsvPtr->sat = 0.0;
822
823 range = (double)(max - min);
824 if (max != min) {
825 hsvPtr->sat = range / (double)max;
826 }
827 if (hsvPtr->sat > 0.0) {
828 double red, green, blue;
829
830 /* Normalize the RGB values */
831 red = (double)(max - colorPtr->red) / range;
832 green = (double)(max - colorPtr->green) / range;
833 blue = (double)(max - colorPtr->blue) / range;
834
835 if (colorPtr->red == max) {
836 hsvPtr->hue = (blue - green);
837 } else if (colorPtr->green == max) {
838 hsvPtr->hue = 2 + (red - blue);
839 } else if (colorPtr->blue == max) {
840 hsvPtr->hue = 4 + (green - red);
841 }
842 hsvPtr->hue *= 60.0;
843 } else {
844 hsvPtr->sat = 0.5;
845 }
846 if (hsvPtr->hue < 0.0) {
847 hsvPtr->hue += 360.0;
848 }
849 }
850
851 void
Blt_HSVToXColor(hsvPtr,colorPtr)852 Blt_HSVToXColor(hsvPtr, colorPtr)
853 HSV *hsvPtr;
854 XColor *colorPtr;
855 {
856 double hue, p, q, t;
857 double frac;
858 int quadrant;
859
860 if (hsvPtr->val < 0.0) {
861 hsvPtr->val = 0.0;
862 } else if (hsvPtr->val > 1.0) {
863 hsvPtr->val = 1.0;
864 }
865 if (hsvPtr->sat == 0.0) {
866 SetColor(colorPtr, hsvPtr->val, hsvPtr->val, hsvPtr->val);
867 return;
868 }
869 hue = FMOD(hsvPtr->hue, 360.0) / 60.0;
870 quadrant = (int)floor(hue);
871 frac = hsvPtr->hue - quadrant;
872 p = hsvPtr->val * (1 - (hsvPtr->sat));
873 q = hsvPtr->val * (1 - (hsvPtr->sat * frac));
874 t = hsvPtr->val * (1 - (hsvPtr->sat * (1 - frac)));
875
876 switch (quadrant) {
877 case 0:
878 SetColor(colorPtr, hsvPtr->val, t, p);
879 break;
880 case 1:
881 SetColor(colorPtr, q, hsvPtr->val, p);
882 break;
883 case 2:
884 SetColor(colorPtr, p, hsvPtr->val, t);
885 break;
886 case 3:
887 SetColor(colorPtr, p, q, hsvPtr->val);
888 break;
889 case 4:
890 SetColor(colorPtr, t, p, hsvPtr->val);
891 break;
892 case 5:
893 SetColor(colorPtr, hsvPtr->val, p, q);
894 break;
895 }
896 }
897
898 /*
899 *----------------------------------------------------------------------
900 *
901 * Blt_AdjustViewport --
902 *
903 * Adjusts the offsets of the viewport according to the scroll mode.
904 * This is to accommodate both "listbox" and "canvas" style scrolling.
905 *
906 * "canvas" The viewport scrolls within the range of world
907 * coordinates. This way the viewport always displays
908 * a full page of the world. If the world is smaller
909 * than the viewport, then (bizarrely) the world and
910 * viewport are inverted so that the world moves up
911 * and down within the viewport.
912 *
913 * "listbox" The viewport can scroll beyond the range of world
914 * coordinates. Every entry can be displayed at the
915 * top of the viewport. This also means that the
916 * scrollbar thumb weirdly shrinks as the last entry
917 * is scrolled upward.
918 *
919 * Results:
920 * The corrected offset is returned.
921 *
922 *----------------------------------------------------------------------
923 */
924 int
Blt_AdjustViewport(offset,worldSize,windowSize,scrollUnits,scrollMode)925 Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits, scrollMode)
926 int offset, worldSize, windowSize;
927 int scrollUnits;
928 int scrollMode;
929 {
930 switch (scrollMode) {
931 case BLT_SCROLL_MODE_CANVAS:
932
933 /*
934 * Canvas-style scrolling allows the world to be scrolled
935 * within the window.
936 */
937
938 if (worldSize < windowSize) {
939 if ((worldSize - offset) > windowSize) {
940 offset = worldSize - windowSize;
941 }
942 if (offset > 0) {
943 offset = 0;
944 }
945 } else {
946 if ((offset + windowSize) > worldSize) {
947 offset = worldSize - windowSize;
948 }
949 if (offset < 0) {
950 offset = 0;
951 }
952 }
953 break;
954
955 case BLT_SCROLL_MODE_LISTBOX:
956 if (offset < 0) {
957 offset = 0;
958 }
959 if (offset >= worldSize) {
960 offset = worldSize - scrollUnits;
961 }
962 break;
963
964 case BLT_SCROLL_MODE_HIERBOX:
965
966 /*
967 * Hierbox-style scrolling allows the world to be scrolled
968 * within the window.
969 */
970 if ((offset + windowSize) > worldSize) {
971 offset = worldSize - windowSize;
972 }
973 if (offset < 0) {
974 offset = 0;
975 }
976 break;
977 }
978 return offset;
979 }
980
981 int
Blt_GetScrollInfo(interp,argc,argv,offsetPtr,worldSize,windowSize,scrollUnits,scrollMode)982 Blt_GetScrollInfo(interp, argc, argv, offsetPtr, worldSize, windowSize,
983 scrollUnits, scrollMode)
984 Tcl_Interp *interp;
985 int argc;
986 char **argv;
987 int *offsetPtr;
988 int worldSize, windowSize;
989 int scrollUnits;
990 int scrollMode;
991 {
992 char c;
993 unsigned int length;
994 int offset;
995 int count;
996 double fract;
997
998 offset = *offsetPtr;
999 c = argv[0][0];
1000 length = strlen(argv[0]);
1001 if ((c == 's') && (strncmp(argv[0], "scroll", length) == 0)) {
1002 if (argc != 3) {
1003 return TCL_ERROR;
1004 }
1005 /* scroll number unit/page */
1006 if (Tcl_GetInt(interp, argv[1], &count) != TCL_OK) {
1007 return TCL_ERROR;
1008 }
1009 c = argv[2][0];
1010 length = strlen(argv[2]);
1011 if ((c == 'u') && (strncmp(argv[2], "units", length) == 0)) {
1012 fract = (double)count *scrollUnits;
1013 } else if ((c == 'p') && (strncmp(argv[2], "pixels", length) == 0)) {
1014 fract = (double)count;
1015 } else if ((c == 'p') && (strncmp(argv[2], "pages", length) == 0)) {
1016 /* A page is 90% of the view-able window. */
1017 fract = (double)count *windowSize * 0.9;
1018 } else {
1019 Tcl_AppendResult(interp, "unknown \"scroll\" units \"", argv[2],
1020 "\"", (char *)NULL);
1021 return TCL_ERROR;
1022 }
1023 offset += (int)fract;
1024 } else if ((c == 'm') && (strncmp(argv[0], "moveto", length) == 0)) {
1025 if (argc != 2) {
1026 return TCL_ERROR;
1027 }
1028 /* moveto fraction */
1029 if (Tcl_GetDouble(interp, argv[1], &fract) != TCL_OK) {
1030 return TCL_ERROR;
1031 }
1032 offset = (int)(worldSize * fract);
1033 } else {
1034 /* Treat like "scroll units" */
1035 if (Tcl_GetInt(interp, argv[0], &count) != TCL_OK) {
1036 return TCL_ERROR;
1037 }
1038 fract = (double)count *scrollUnits;
1039 offset += (int)fract;
1040 }
1041 *offsetPtr = Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits,
1042 scrollMode);
1043 return TCL_OK;
1044 }
1045
1046 #if (TCL_MAJOR_VERSION >= 8)
1047 int
Blt_GetScrollInfoFromObj(interp,objc,objv,offsetPtr,worldSize,windowSize,scrollUnits,scrollMode)1048 Blt_GetScrollInfoFromObj(interp, objc, objv, offsetPtr, worldSize, windowSize,
1049 scrollUnits, scrollMode)
1050 Tcl_Interp *interp;
1051 int objc;
1052 Tcl_Obj *CONST *objv;
1053 int *offsetPtr;
1054 int worldSize, windowSize;
1055 int scrollUnits;
1056 int scrollMode;
1057 {
1058 char c;
1059 unsigned int length;
1060 int offset;
1061 int count;
1062 double fract;
1063 char *string;
1064
1065 offset = *offsetPtr;
1066
1067 string = Tcl_GetString(objv[0]);
1068 c = string[0];
1069 length = strlen(string);
1070 if ((c == 's') && (strncmp(string, "scroll", length) == 0)) {
1071 if (objc != 3) {
1072 return TCL_ERROR;
1073 }
1074 /* scroll number unit/page */
1075 if (Tcl_GetIntFromObj(interp, objv[1], &count) != TCL_OK) {
1076 return TCL_ERROR;
1077 }
1078 string = Tcl_GetString(objv[2]);
1079 c = string[0];
1080 length = strlen(string);
1081 if ((c == 'u') && (strncmp(string, "units", length) == 0)) {
1082 fract = (double)count *scrollUnits;
1083 } else if ((c == 'p') && (strncmp(string, "pixels", length) == 0)) {
1084 fract = (double)count;
1085 } else if ((c == 'p') && (strncmp(string, "pages", length) == 0)) {
1086 /* A page is 90% of the view-able window. */
1087 fract = (double)count *windowSize * 0.9;
1088 } else {
1089 Tcl_AppendResult(interp, "unknown \"scroll\" units \"",
1090 Tcl_GetString(objv[2]), "\"", (char *)NULL);
1091 return TCL_ERROR;
1092 }
1093 offset += (int)fract;
1094 } else if ((c == 'm') && (strncmp(string, "moveto", length) == 0)) {
1095 if (objc != 2) {
1096 return TCL_ERROR;
1097 }
1098 /* moveto fraction */
1099 if (Tcl_GetDoubleFromObj(interp, objv[1], &fract) != TCL_OK) {
1100 return TCL_ERROR;
1101 }
1102 offset = (int)(worldSize * fract);
1103 } else {
1104 /* Treat like "scroll units" */
1105 if (Tcl_GetIntFromObj(interp, objv[0], &count) != TCL_OK) {
1106 return TCL_ERROR;
1107 }
1108 fract = (double)count *scrollUnits;
1109 offset += (int)fract;
1110 }
1111 *offsetPtr = Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits,
1112 scrollMode);
1113 return TCL_OK;
1114 }
1115 #endif /* TCL_MAJOR_VERSION >= 8 */
1116
1117 /*
1118 * ----------------------------------------------------------------------
1119 *
1120 * Blt_UpdateScrollbar --
1121 *
1122 * Invoke a Tcl command to the scrollbar, defining the new
1123 * position and length of the scroll. See the Tk documentation
1124 * for further information on the scrollbar. It is assumed the
1125 * scrollbar command prefix is valid.
1126 *
1127 * Results:
1128 * None.
1129 *
1130 * Side Effects:
1131 * Scrollbar is commanded to change position and/or size.
1132 *
1133 * ----------------------------------------------------------------------
1134 */
1135 void
Blt_UpdateScrollbar(interp,scrollCmd,firstFract,lastFract)1136 Blt_UpdateScrollbar(interp, scrollCmd, firstFract, lastFract)
1137 Tcl_Interp *interp;
1138 char *scrollCmd; /* scrollbar command */
1139 double firstFract, lastFract;
1140 {
1141 char string[200];
1142 Tcl_DString dString;
1143
1144 Tcl_DStringInit(&dString);
1145 Tcl_DStringAppend(&dString, scrollCmd, -1);
1146 if (firstFract<0.0) { firstFract = 0; }
1147 if (lastFract>1.0) { lastFract = 1; }
1148 sprintf(string, " %f %f", firstFract, lastFract);
1149 Tcl_DStringAppend(&dString, string, -1);
1150 if (Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)) != TCL_OK) {
1151 Tcl_BackgroundError(interp);
1152 }
1153 Tcl_DStringFree(&dString);
1154 }
1155
1156 /* -------------- */
1157 /*
1158 *----------------------------------------------------------------------
1159 *
1160 * Blt_GetPrivateGCFromDrawable --
1161 *
1162 * Like Tk_GetGC, but doesn't share the GC with any other widget.
1163 * This is needed because the certain GC parameters (like dashes)
1164 * can not be set via XCreateGC, therefore there is no way for
1165 * Tk's hashing mechanism to recognize that two such GCs differ.
1166 *
1167 * Results:
1168 * A new GC is returned.
1169 *
1170 *----------------------------------------------------------------------
1171 */
1172 GC
Blt_GetPrivateGCFromDrawable(display,drawable,gcMask,valuePtr)1173 Blt_GetPrivateGCFromDrawable(display, drawable, gcMask, valuePtr)
1174 Display *display;
1175 Drawable drawable;
1176 unsigned long gcMask;
1177 XGCValues *valuePtr;
1178 {
1179 GC newGC;
1180
1181 #ifdef WIN32
1182 newGC = Blt_EmulateXCreateGC(display, drawable, gcMask, valuePtr);
1183 #else
1184 newGC = XCreateGC(display, drawable, gcMask, valuePtr);
1185 #endif
1186 return newGC;
1187 }
1188
1189 /*
1190 *----------------------------------------------------------------------
1191 *
1192 * Blt_GetPrivateGC --
1193 *
1194 * Like Tk_GetGC, but doesn't share the GC with any other widget.
1195 * This is needed because the certain GC parameters (like dashes)
1196 * can not be set via XCreateGC, therefore there is no way for
1197 * Tk's hashing mechanism to recognize that two such GCs differ.
1198 *
1199 * Results:
1200 * A new GC is returned.
1201 *
1202 *----------------------------------------------------------------------
1203 */
1204 GC
Blt_GetPrivateGC(tkwin,gcMask,valuePtr)1205 Blt_GetPrivateGC(tkwin, gcMask, valuePtr)
1206 Tk_Window tkwin;
1207 unsigned long gcMask;
1208 XGCValues *valuePtr;
1209 {
1210 GC gc;
1211 Pixmap pixmap;
1212 Drawable drawable;
1213 Display *display;
1214
1215 pixmap = None;
1216 drawable = Tk_WindowId(tkwin);
1217 display = Tk_Display(tkwin);
1218
1219 if (drawable == None) {
1220 Drawable root;
1221 int depth;
1222
1223 root = RootWindow(display, Tk_ScreenNumber(tkwin));
1224 depth = Tk_Depth(tkwin);
1225
1226 if (depth == DefaultDepth(display, Tk_ScreenNumber(tkwin))) {
1227 drawable = root;
1228 } else {
1229 pixmap = Tk_GetPixmap(display, root, 1, 1, depth);
1230 drawable = pixmap;
1231 }
1232 }
1233 gc = Blt_GetPrivateGCFromDrawable(display, drawable, gcMask, valuePtr);
1234 if (pixmap != None) {
1235 Tk_FreePixmap(display, pixmap);
1236 }
1237 return gc;
1238 }
1239
1240 void
Blt_FreePrivateGC(display,gc)1241 Blt_FreePrivateGC(display, gc)
1242 Display *display;
1243 GC gc;
1244 {
1245 Tk_FreeXId(display, (XID) XGContextFromGC(gc));
1246 XFreeGC(display, gc);
1247 }
1248
1249 #ifndef WIN32
1250 void
Blt_SetDashes(display,gc,dashesPtr)1251 Blt_SetDashes(display, gc, dashesPtr)
1252 Display *display;
1253 GC gc;
1254 Blt_Dashes *dashesPtr;
1255 {
1256 XSetDashes(display, gc, dashesPtr->offset,
1257 (CONST char *)dashesPtr->values, strlen((char *)dashesPtr->values));
1258 }
1259 #endif
1260
1261
1262 static double
FindSplit(points,i,j,split)1263 FindSplit(points, i, j, split)
1264 Point2D points[];
1265 int i, j; /* Indices specifying the range of points. */
1266 int *split; /* (out) Index of next split. */
1267 { double maxDist;
1268
1269 maxDist = -1.0;
1270 if ((i + 1) < j) {
1271 register int k;
1272 double a, b, c;
1273 double sqDist;
1274
1275 /*
1276 *
1277 * sqDist P(k) = | 1 P(i).x P(i).y |
1278 * | 1 P(j).x P(j).y |
1279 * | 1 P(k).x P(k).y |
1280 * ---------------------------
1281 * (P(i).x - P(j).x)^2 + (P(i).y - P(j).y)^2
1282 */
1283
1284 a = points[i].y - points[j].y;
1285 b = points[j].x - points[i].x;
1286 c = (points[i].x * points[j].y) - (points[i].y * points[j].x);
1287 for (k = (i + 1); k < j; k++) {
1288 sqDist = (points[k].x * a) + (points[k].y * b) + c;
1289 if (sqDist < 0.0) {
1290 sqDist = -sqDist;
1291 }
1292 if (sqDist > maxDist) {
1293 maxDist = sqDist; /* Track the maximum. */
1294 *split = k;
1295 }
1296 }
1297 /* Correction for segment length---should be redone if can == 0 */
1298 maxDist *= maxDist / (a * a + b * b);
1299 }
1300 return maxDist;
1301 }
1302
1303
1304 /* Douglas-Peucker line simplification algorithm */
1305 int
Blt_SimplifyLine(inputPts,low,high,tolerance,indices)1306 Blt_SimplifyLine(inputPts, low, high, tolerance, indices)
1307 Point2D inputPts[];
1308 int low, high;
1309 double tolerance;
1310 int indices[];
1311 {
1312 #define StackPush(a) s++, stack[s] = (a)
1313 #define StackPop(a) (a) = stack[s], s--
1314 #define StackEmpty() (s < 0)
1315 #define StackTop() stack[s]
1316 int *stack;
1317 int split = -1;
1318 double sqDist, sqTolerance;
1319 int s = -1; /* Points to top stack item. */
1320 int count;
1321
1322 stack = Blt_Malloc(sizeof(int) * (high - low + 1));
1323 StackPush(high);
1324 count = 0;
1325 indices[count++] = 0;
1326 sqTolerance = tolerance * tolerance;
1327 while (!StackEmpty()) {
1328 sqDist = FindSplit(inputPts, low, StackTop(), &split);
1329 if (sqDist > sqTolerance) {
1330 StackPush(split);
1331 } else {
1332 indices[count++] = StackTop();
1333 StackPop(low);
1334 }
1335 }
1336 Blt_Free(stack);
1337 return count;
1338 }
1339
1340 void
Blt_Draw2DSegments(display,drawable,gc,segPtr,nSegments)1341 Blt_Draw2DSegments(display, drawable, gc, segPtr, nSegments)
1342 Display *display;
1343 Drawable drawable;
1344 GC gc;
1345 register Segment2D *segPtr;
1346 int nSegments;
1347 {
1348 XSegment *xSegPtr, *xSegArr;
1349 Segment2D *endPtr;
1350
1351 xSegArr = Blt_Malloc(nSegments * sizeof(XSegment));
1352 if (xSegArr == NULL) {
1353 return;
1354 }
1355 xSegPtr = xSegArr;
1356 for (endPtr = segPtr + nSegments; segPtr < endPtr; segPtr++) {
1357 xSegPtr->x1 = (short int)segPtr->p.x;
1358 xSegPtr->y1 = (short int)segPtr->p.y;
1359 xSegPtr->x2 = (short int)segPtr->q.x;
1360 xSegPtr->y2 = (short int)segPtr->q.y;
1361 xSegPtr++;
1362 }
1363 XDrawSegments(display, drawable, gc, xSegArr, nSegments);
1364 Blt_Free(xSegArr);
1365 }
1366
1367 void
Blt_DrawArrow(display,drawable,gc,x,y,arrowHeight,orientation)1368 Blt_DrawArrow(display, drawable, gc, x, y, arrowHeight, orientation)
1369 Display *display;
1370 Drawable drawable;
1371 GC gc;
1372 int x, y;
1373 int arrowHeight;
1374 int orientation;
1375 {
1376 XPoint arrow[5];
1377 int a, b;
1378
1379 a = arrowHeight / 2 + 1;
1380 b = arrowHeight;
1381 switch (orientation) {
1382 case ARROW_UP:
1383 /*
1384 * 0
1385 * +
1386 * / \
1387 * / \
1388 * / \ a
1389 * / \
1390 * x,y / \
1391 * +-----------+
1392 * 1 b 2
1393 */
1394 arrow[0].x = x;
1395 arrow[0].y = y - a;
1396 arrow[1].x = arrow[0].x - b;
1397 arrow[1].y = arrow[0].y + b;
1398 arrow[2].x = arrow[0].x + b;
1399 arrow[2].y = arrow[0].y + b;
1400 arrow[3].x = arrow[0].x;
1401 arrow[3].y = arrow[0].y;
1402 break;
1403
1404 case ARROW_DOWN:
1405 /*
1406 * 1 b 2
1407 * +-----------+
1408 * \ /
1409 * \ x,y /
1410 * \ / a
1411 * \ /
1412 * \ /
1413 * +
1414 * 0
1415 */
1416 arrow[0].x = x;
1417 arrow[0].y = y + a;
1418 arrow[1].x = arrow[0].x - b;
1419 arrow[1].y = arrow[0].y - b;
1420 arrow[2].x = arrow[0].x + b;
1421 arrow[2].y = arrow[0].y - b;
1422 arrow[3].x = arrow[0].x;
1423 arrow[3].y = arrow[0].y;
1424 break;
1425
1426 case ARROW_RIGHT:
1427 /*
1428 * 2
1429 * +
1430 * |\
1431 * | \
1432 * | \
1433 * | \
1434 * | \
1435 * | x,y + 0
1436 * | /
1437 * | /
1438 * | /
1439 * | /
1440 * |/
1441 * +
1442 * 1
1443 */
1444 arrow[0].x = x + a;
1445 arrow[0].y = y;
1446 arrow[1].x = arrow[0].x - b;
1447 arrow[1].y = arrow[0].y + b;
1448 arrow[2].x = arrow[0].x - b;
1449 arrow[2].y = arrow[0].y - b;
1450 arrow[3].x = arrow[0].x;
1451 arrow[3].y = arrow[0].y;
1452 break;
1453
1454 case ARROW_LEFT:
1455 /*
1456 * 2
1457 * +
1458 * /|
1459 * / |
1460 * / |
1461 * / |
1462 * / |
1463 * 0+ x,y |
1464 * \ |
1465 * \ |
1466 * \ |
1467 * \ |
1468 * \|
1469 * +
1470 * 1
1471 */
1472 arrow[0].x = x - a;
1473 arrow[0].y = y;
1474 arrow[1].x = arrow[0].x + b;
1475 arrow[1].y = arrow[0].y + b;
1476 arrow[2].x = arrow[0].x + b;
1477 arrow[2].y = arrow[0].y - b;
1478 arrow[3].x = arrow[0].x;
1479 arrow[3].y = arrow[0].y;
1480 break;
1481
1482 }
1483 XFillPolygon(display, drawable, gc, arrow, 4, Convex, CoordModeOrigin);
1484 XDrawLines(display, drawable, gc, arrow, 4, CoordModeOrigin);
1485 }
1486
1487 int
Blt_MaxRequestSize(Display * display,unsigned int elemSize)1488 Blt_MaxRequestSize(Display *display, unsigned int elemSize)
1489 {
1490 long size;
1491
1492 #ifdef HAVE_XEXTENDEDMAXREQUESTSIZE
1493 size = XExtendedMaxRequestSize(display);
1494 if (size == 0) {
1495 size = XMaxRequestSize(display);
1496 }
1497 #else
1498 size = XMaxRequestSize(display);
1499 #endif
1500 size -= 4;
1501 return ((size * 4) / elemSize);
1502 }
1503
1504 #undef Blt_Fill3DRectangle
1505 void
Blt_Fill3DRectangle(tkwin,drawable,border,x,y,width,height,borderWidth,relief)1506 Blt_Fill3DRectangle(tkwin, drawable, border, x, y, width,
1507 height, borderWidth, relief)
1508 Tk_Window tkwin; /* Window for which border was allocated. */
1509 Drawable drawable; /* X window or pixmap in which to draw. */
1510 Tk_3DBorder border; /* Token for border to draw. */
1511 int x, y, width, height; /* Outside area of rectangular region. */
1512 int borderWidth; /* Desired width for border, in
1513 * pixels. Border will be *inside* region. */
1514 int relief; /* Indicates 3D effect: TK_RELIEF_FLAT,
1515 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
1516 {
1517 #ifndef notdef
1518 if ((borderWidth > 1) && (width > 2) && (height > 2) &&
1519 ((relief == TK_RELIEF_SUNKEN) || (relief == TK_RELIEF_RAISED))) {
1520 GC lightGC, darkGC;
1521 int x2, y2;
1522
1523 x2 = x + width - 1;
1524 y2 = y + height - 1;
1525 #define TK_3D_LIGHT2_GC TK_3D_DARK_GC+1
1526 #define TK_3D_DARK2_GC TK_3D_DARK_GC+2
1527 if (relief == TK_RELIEF_RAISED) {
1528 lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
1529 #ifdef WIN32
1530 darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
1531 #else
1532 darkGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
1533 #endif
1534 } else {
1535 #ifdef WIN32
1536 lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
1537 #else
1538 lightGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
1539 #endif
1540 darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
1541 }
1542 XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x2, y);
1543 XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x2, y);
1544 XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x, y2);
1545 XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x, y2);
1546 x++, y++, width -= 2, height -= 2, borderWidth--;
1547 }
1548 #endif
1549 Tk_Fill3DRectangle(tkwin, drawable, border, x, y, width, height,
1550 borderWidth, relief);
1551 }
1552
1553
1554 #undef Blt_Draw3DRectangle
1555 void
Blt_Draw3DRectangle(tkwin,drawable,border,x,y,width,height,borderWidth,relief)1556 Blt_Draw3DRectangle(tkwin, drawable, border, x, y, width,
1557 height, borderWidth, relief)
1558 Tk_Window tkwin; /* Window for which border was allocated. */
1559 Drawable drawable; /* X window or pixmap in which to draw. */
1560 Tk_3DBorder border; /* Token for border to draw. */
1561 int x, y, width, height; /* Outside area of rectangular region. */
1562 int borderWidth; /* Desired width for border, in
1563 * pixels. Border will be *inside* region. */
1564 int relief; /* Indicates 3D effect: TK_RELIEF_FLAT,
1565 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
1566 {
1567 #ifndef notdef
1568 if ((borderWidth > 1) && (width > 2) && (height > 2) &&
1569 ((relief == TK_RELIEF_SUNKEN) || (relief == TK_RELIEF_RAISED))) {
1570 GC lightGC, darkGC;
1571 int x2, y2;
1572
1573 x2 = x + width - 1;
1574 y2 = y + height - 1;
1575 if (relief == TK_RELIEF_RAISED) {
1576 lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
1577 #ifdef WIN32
1578 darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
1579 #else
1580 darkGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
1581 #endif
1582 } else {
1583 #ifdef WIN32
1584 lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
1585 #else
1586 lightGC = DefaultGC(Tk_Display(tkwin), Tk_ScreenNumber(tkwin));
1587 #endif
1588 darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
1589 }
1590 XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x2, y);
1591 XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x2, y);
1592 XDrawLine(Tk_Display(tkwin), drawable, darkGC, x2, y2, x, y2);
1593 XDrawLine(Tk_Display(tkwin), drawable, lightGC, x, y, x, y2);
1594 x++, y++, width -= 2, height -= 2, borderWidth--;
1595 }
1596 #endif
1597 Tk_Draw3DRectangle(tkwin, drawable, border, x, y, width, height,
1598 borderWidth, relief);
1599 }
1600
1601 #ifdef notdef
1602 typedef struct {
1603 Screen *screen;
1604 Visual *visual;
1605 Colormap colormap;
1606 Tk_Uid nameUid;
1607 } BorderKey;
1608
1609 typedef struct {
1610 Screen *screen; /* Screen on which the border will be used. */
1611 Visual *visual; /* Visual for all windows and pixmaps using
1612 * the border. */
1613 int depth; /* Number of bits per pixel of drawables where
1614 * the border will be used. */
1615 Colormap colormap; /* Colormap out of which pixels are
1616 * allocated. */
1617 int refCount; /* Number of active uses of this color
1618 * (each active use corresponds to a
1619 * call to Blt_Get3DBorder). If this
1620 * count is 0, then this structure is
1621 * no longer valid and it isn't
1622 * present in borderTable: it is being
1623 * kept around only because there are
1624 * objects referring to it. The
1625 * structure is freed when refCount is
1626 * 0. */
1627
1628 XColor *bgColorPtr; /* Color of face. */
1629 XColor *shadows[4];
1630
1631 Pixmap darkStipple; /* Stipple pattern to use for drawing
1632 * shadows areas. Used for displays with
1633 * <= 64 colors or where colormap has filled
1634 * up. */
1635 Pixmap lightStipple; /* Stipple pattern to use for drawing
1636 * shadows areas. Used for displays with
1637 * <= 64 colors or where colormap has filled
1638 * up. */
1639 GC bgGC; /* Used (if necessary) to draw areas in
1640 * the background color. */
1641 GC lightGC, darkGC;
1642 Tcl_HashEntry *hashPtr; /* Entry in borderTable (needed in
1643 * order to delete structure). */
1644
1645 struct Blt_3DBorderStruct *nextPtr;
1646 } Border, *Blt_3DBorder;
1647
1648
1649 void
Blt_Draw3DRectangle(tkwin,drawable,border,x,y,width,height,borderWidth,relief)1650 Blt_Draw3DRectangle(tkwin, drawable, border, x, y, width,
1651 height, borderWidth, relief)
1652 Tk_Window tkwin; /* Window for which border was allocated. */
1653 Drawable drawable; /* X window or pixmap in which to draw. */
1654 Blt_3DBorder *borderPtr; /* Border to draw. */
1655 int x, y, width, height; /* Outside area of rectangular region. */
1656 int borderWidth; /* Desired width for border, in
1657 * pixels. Border will be *inside* region. */
1658 int relief; /* Indicates 3D effect: TK_RELIEF_FLAT,
1659 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
1660 {
1661 if ((width > (2 * borderWidth)) && (height > (2 * borderWidth))) {
1662 int x2, y2;
1663 int i;
1664
1665 x2 = x + width - 1;
1666 y2 = y + height - 1;
1667
1668 XSetForeground(borderPtr->lightGC, borderPtr->shadows[0]);
1669 XSetForeground(borderPtr->darkGC, borderPtr->shadows[3]);
1670 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC,
1671 x, y, x2, y);
1672 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC,
1673 x, y, x, y2);
1674 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC,
1675 x2, y, x2, y2);
1676 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC,
1677 x2, y2, x, y2);
1678 XSetForeground(borderPtr->lightGC, borderPtr->shadows[1]);
1679 XSetForeground(borderPtr->darkGC, borderPtr->shadows[2]);
1680 for (i = 1; i < (borderWidth - 1); i++) {
1681
1682 /*
1683 * +---------
1684 * |+-------
1685 * ||+-----
1686 * |||
1687 * |||
1688 * ||
1689 * |
1690 */
1691 x++, y++, x2--, y2--;
1692 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC,
1693 x, y, x2, y);
1694 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->lightGC,
1695 x, y, x, y2);
1696 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC,
1697 x2, y, x2, y2);
1698 XDrawLine(Tk_Display(tkwin), drawable, borderPtr->darkGC,
1699 x2, y2, x, y2);
1700 }
1701 }
1702 }
1703
1704 void
Blt_Fill3DRectangle(tkwin,drawable,border,x,y,width,height,borderWidth,relief)1705 Blt_Fill3DRectangle(tkwin, drawable, border, x, y, width, height, borderWidth,
1706 relief)
1707 Tk_Window tkwin; /* Window for which border was allocated. */
1708 Drawable drawable; /* X window or pixmap in which to draw. */
1709 Tk_3DBorder border; /* Token for border to draw. */
1710 int x, y, width, height; /* Outside area of rectangular region. */
1711 int borderWidth; /* Desired width for border, in
1712 * pixels. Border will be *inside* region. */
1713 int relief; /* Indicates 3D effect: TK_RELIEF_FLAT,
1714 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
1715 {
1716 Blt_3DBorder *borderPtr;
1717
1718 XFillRectangle(Tk_Display(tkwin), drawable, borderPtr->bgGC, x, y, width,
1719 height);
1720 if ((borderWidth > 0) && (relief != BLT_RELIEF_FLAT)) {
1721 Blt_Draw3DRectangle(tkwin, drawable, borderPtr, x, y, width, height,
1722 borderWidth, relief);
1723 }
1724 }
1725
1726
1727 void
FreeBorder(display,borderPtr)1728 FreeBorder(display, borderPtr)
1729 Display *display;
1730 Border *borderPtr;
1731 {
1732 int i;
1733
1734 if (borderPtr->bgColorPtr != NULL) {
1735 Tk_FreeColor(display, borderPtr->bgColorPtr);
1736 }
1737 for (i = 0; i < 4; i++) {
1738 Tk_FreeColor(display, borderPtr->shadows[i]);
1739 }
1740 if (borderPtr->tile != NULL) {
1741 Blt_FreeTile(tile);
1742 }
1743 if (borderPtr->darkGC != NULL) {
1744 Blt_FreePrivateGC(display, borderPtr->darkGC);
1745 }
1746 if (borderPtr->lightGC != NULL) {
1747 Blt_FreePrivateGC(tkwin, borderPtr->lightGC);
1748 }
1749 if (borderPtr->bgGC != NULL) {
1750 Blt_FreePrivateGC(tkwin, borderPtr->bgGC);
1751 }
1752 Blt_Free(borderPtr);
1753 }
1754
1755 void
Blt_Free3DBorder(display,border)1756 Blt_Free3DBorder(display, border)
1757 Display *display;
1758 Blt_3DBorder border;
1759 {
1760 Border *borderPtr = (Border *)border;
1761
1762 borderPtr->refCount--;
1763 if (borderPtr->refCount >= 0) {
1764 /* Search for the border in the bucket. Start at the head. */
1765 headPtr = Blt_GetHashValue(borderPtr->hashPtr);
1766 lastPtr = NULL;
1767 while ((headPtr != borderPtr) && (headPtr != NULL)) {
1768 lastPtr = headPtr;
1769 headPtr = headPtr->next;
1770 }
1771 if (headPtr == NULL) {
1772 return; /* This can't happen. It means that
1773 * we could not find the border. */
1774 }
1775 if (lastPtr != NULL) {
1776 lastPtr->next = borderPtr->next;
1777 } else {
1778 Tcl_DeleteHashEntry(borderPtr->hashPtr);
1779 }
1780 FreeBorder(display, borderPtr);
1781 }
1782 }
1783
1784 Blt_3DBorder *
Blt_Get3DBorder(interp,tkwin,borderName)1785 Blt_Get3DBorder(interp, tkwin, borderName)
1786 Tcl_Interp *interp;
1787 Tk_Window tkwin;
1788 char *borderName;
1789 {
1790 Blt_3DBorder *borderPtr, *lastBorderPtr;
1791 Blt_HashEntry *hPtr;
1792 Blt_Tile tile;
1793 XColor *bgColorPtr;
1794 char **argv;
1795 char *colorName;
1796 int argc;
1797 int isNew;
1798
1799 lastBorderPtr = NULL;
1800 hPtr = Tcl_CreateHashEntry(&dataPtr->borderTable, borderName, &isNew);
1801 if (!isNew) {
1802 borderPtr = lastBorderPtr = Blt_GetHashValue(hPtr);
1803 while (borderPtr != NULL) {
1804 if ((Tk_Screen(tkwin) == borderPtr->screen) &&
1805 (Tk_Colormap(tkwin) == borderPtr->colormap)) {
1806 borderPtr->refCount++;
1807 return borderPtr;
1808 }
1809 borderPtr = borderPtr->nextPtr;
1810 }
1811 }
1812 /* Create a new border. */
1813 argv = NULL;
1814 bgColorPtr = NULL;
1815 tile = NULL;
1816
1817 if (Tcl_SplitList(interp, borderName, &argc, &argv) != TCL_OK) {
1818 goto error;
1819 }
1820 colorName = borderName;
1821 if ((argc == 2) && (Blt_GetTile(interp, tkwin, argv[0], &tile) == TCL_OK)) {
1822 colorName = argv[1];
1823 }
1824 bgColorPtr = Tk_GetColor(interp, tkwin, colorName);
1825 if (bgColorPtr == NULL) {
1826 goto error;
1827 }
1828
1829 /* Create a new border */
1830 borderPtr = Blt_Calloc(1, sizeof(Blt_3DBorder));
1831 assert(borderPtr);
1832 borderPtr->screen = Tk_Screen(tkwin);
1833 borderPtr->visual = Tk_Visual(tkwin);
1834 borderPtr->depth = Tk_Depth(tkwin);
1835 borderPtr->colormap = Tk_Colormap(tkwin);
1836 borderPtr->refCount = 1;
1837 borderPtr->bgColorPtr = bgColorPtr;
1838 borderPtr->tile = tile;
1839 borderPtr->darkGC = Blt_GetPrivateGC(tkwin, 0, NULL);
1840 borderPtr->lightGC = Blt_GetPrivateGC(tkwin, 0, NULL);
1841 borderPtr->hashPtr = lastBorderPtr->hashPtr;
1842 lastBorderPtr->nextPtr = lastBorderPtr;
1843 {
1844 HSV hsv;
1845 XColor color;
1846 double sat, sat0, diff, step, hstep;
1847 int count;
1848
1849 /* Convert the face (background) color to HSV */
1850 Blt_XColorToHSV(borderPtr->bgColorPtr, &hsv);
1851
1852 /* Using the color as the baseline intensity, pick a set of
1853 * colors around the intensity. */
1854 #define UFLOOR(x,u) (floor((x)*(u))/(u))
1855 diff = hsv.sat - UFLOOR(hsv.sat, 0.2);
1856 sat = 0.1 + (diff - 0.1);
1857 sat0 = hsv.sat;
1858 count = 0;
1859 for (sat = 0.1 + (diff - 0.1); sat <= 1.0; sat += 0.2) {
1860 if (FABS(sat0 - sat) >= 0.1) {
1861 hsv.sat = sat;
1862 Blt_HSVToXColor(&hsv, &color);
1863 borderPtr->shadows[count] = Tk_GetColorByValue(tkwin, &color);
1864 count++;
1865 }
1866 }
1867 }
1868 Blt_SetHashValue(hPtr, borderPtr);
1869 if (argv != NULL) {
1870 Blt_Free(argv);
1871 }
1872 return TCL_OK;
1873
1874 error:
1875 if (argv != NULL) {
1876 Blt_Free(argv);
1877 }
1878 if (tile != NULL) {
1879 Blt_FreeTile(tile);
1880 }
1881 if (bgColorPtr != NULL) {
1882 Tk_FreeColor(bgColorPtr);
1883 }
1884 if (isNew) {
1885 Blt_DeleteHashEntry(&borderTable, hPtr);
1886 }
1887 return NULL;
1888 }
1889
1890 #endif
1891