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