summaryrefslogtreecommitdiff
path: root/blt/src/bltGrMisc.c
diff options
context:
space:
mode:
Diffstat (limited to 'blt/src/bltGrMisc.c')
-rw-r--r--blt/src/bltGrMisc.c1372
1 files changed, 1372 insertions, 0 deletions
diff --git a/blt/src/bltGrMisc.c b/blt/src/bltGrMisc.c
new file mode 100644
index 00000000000..d4eb9d09a9c
--- /dev/null
+++ b/blt/src/bltGrMisc.c
@@ -0,0 +1,1372 @@
+
+/*
+ * bltGrMisc.c --
+ *
+ * This module implements miscellaneous routines for the BLT
+ * graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+#include "bltGraph.h"
+#include <X11/Xutil.h>
+
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+
+static Tk_OptionParseProc StringToPoint;
+static Tk_OptionPrintProc PointToString;
+static Tk_OptionParseProc StringToColorPair;
+static Tk_OptionPrintProc ColorPairToString;
+Tk_CustomOption bltPointOption =
+{
+ StringToPoint, PointToString, (ClientData)0
+};
+Tk_CustomOption bltColorPairOption =
+{
+ StringToColorPair, ColorPairToString, (ClientData)0
+};
+
+/* ----------------------------------------------------------------------
+ * Custom option parse and print procedures
+ * ----------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetXY --
+ *
+ * Converts a string in the form "@x,y" into an XPoint structure
+ * of the x and y coordinates.
+ *
+ * Results:
+ * A standard Tcl result. If the string represents a valid position
+ * *pointPtr* will contain the converted x and y coordinates and
+ * TCL_OK is returned. Otherwise, TCL_ERROR is returned and
+ * interp->result will contain an error message.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_GetXY(interp, tkwin, string, xPtr, yPtr)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ char *string;
+ int *xPtr, *yPtr;
+{
+ char *comma;
+ int result;
+ int x, y;
+
+ if ((string == NULL) || (*string == '\0')) {
+ *xPtr = *yPtr = -SHRT_MAX;
+ return TCL_OK;
+ }
+ if (*string != '@') {
+ goto badFormat;
+ }
+ comma = strchr(string + 1, ',');
+ if (comma == NULL) {
+ goto badFormat;
+ }
+ *comma = '\0';
+ result = ((Tk_GetPixels(interp, tkwin, string + 1, &x) == TCL_OK) &&
+ (Tk_GetPixels(interp, tkwin, comma + 1, &y) == TCL_OK));
+ *comma = ',';
+ if (!result) {
+ Tcl_AppendResult(interp, ": can't parse position \"", string, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ *xPtr = x, *yPtr = y;
+ return TCL_OK;
+
+ badFormat:
+ Tcl_AppendResult(interp, "bad position \"", string,
+ "\": should be \"@x,y\"", (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToPoint --
+ *
+ * Convert the string representation of a legend XY position into
+ * window coordinates. The form of the string must be "@x,y" or
+ * none.
+ *
+ * Results:
+ * A standard Tcl result. The symbol type is written into the
+ * widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToPoint(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* New legend position string */
+ char *widgRec; /* Widget record */
+ int offset; /* offset to XPoint structure */
+{
+ XPoint *pointPtr = (XPoint *)(widgRec + offset);
+ int x, y;
+
+ if (Blt_GetXY(interp, tkwin, string, &x, &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ pointPtr->x = x, pointPtr->y = y;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PointToString --
+ *
+ * Convert the window coordinates into a string.
+ *
+ * Results:
+ * The string representing the coordinate position is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+PointToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Widget record */
+ int offset; /* offset of XPoint in record */
+ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */
+{
+ char *result;
+ XPoint *pointPtr = (XPoint *)(widgRec + offset);
+
+ result = "";
+ if ((pointPtr->x != -SHRT_MAX) && (pointPtr->y != -SHRT_MAX)) {
+ char string[200];
+
+ sprintf(string, "@%d,%d", pointPtr->x, pointPtr->y);
+ result = Blt_Strdup(string);
+ assert(result);
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ }
+ return result;
+}
+
+/*LINTLIBRARY*/
+static int
+GetColorPair(interp, tkwin, fgStr, bgStr, pairPtr, allowDefault)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ char *fgStr, *bgStr;
+ ColorPair *pairPtr;
+ int allowDefault;
+{
+ unsigned int length;
+ XColor *fgColor, *bgColor;
+
+ fgColor = bgColor = NULL;
+ length = strlen(fgStr);
+ if (fgStr[0] == '\0') {
+ fgColor = NULL;
+ } else if ((allowDefault) && (fgStr[0] == 'd') &&
+ (strncmp(fgStr, "defcolor", length) == 0)) {
+ fgColor = COLOR_DEFAULT;
+ } else {
+ fgColor = Tk_GetColor(interp, tkwin, Tk_GetUid(fgStr));
+ if (fgColor == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ length = strlen(bgStr);
+ if (bgStr[0] == '\0') {
+ bgColor = NULL;
+ } else if ((allowDefault) && (bgStr[0] == 'd') &&
+ (strncmp(bgStr, "defcolor", length) == 0)) {
+ bgColor = COLOR_DEFAULT;
+ } else {
+ bgColor = Tk_GetColor(interp, tkwin, Tk_GetUid(bgStr));
+ if (bgColor == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ pairPtr->fgColor = fgColor;
+ pairPtr->bgColor = bgColor;
+ return TCL_OK;
+}
+
+void
+Blt_FreeColorPair(pairPtr)
+ ColorPair *pairPtr;
+{
+ if ((pairPtr->bgColor != NULL) && (pairPtr->bgColor != COLOR_DEFAULT)) {
+ Tk_FreeColor(pairPtr->bgColor);
+ }
+ if ((pairPtr->fgColor != NULL) && (pairPtr->fgColor != COLOR_DEFAULT)) {
+ Tk_FreeColor(pairPtr->fgColor);
+ }
+ pairPtr->bgColor = pairPtr->fgColor = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToColorPair --
+ *
+ * Convert the color names into pair of XColor pointers.
+ *
+ * Results:
+ * A standard Tcl result. The color pointer is written into the
+ * widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToColorPair(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing color */
+ char *widgRec; /* Widget record */
+ int offset; /* Offset of color field in record */
+{
+ ColorPair *pairPtr = (ColorPair *)(widgRec + offset);
+ ColorPair sample;
+ int allowDefault = (int)clientData;
+
+ sample.fgColor = sample.bgColor = NULL;
+ if ((string != NULL) && (*string != '\0')) {
+ int nColors;
+ char **colors;
+ int result;
+
+ if (Tcl_SplitList(interp, string, &nColors, &colors) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ result = TCL_ERROR;
+ switch (nColors) {
+ case 0:
+ result = TCL_OK;
+ break;
+ case 1:
+ result = GetColorPair(interp, tkwin, colors[0], "", &sample,
+ allowDefault);
+ break;
+ case 2:
+ result = GetColorPair(interp, tkwin, colors[0], colors[1],
+ &sample, allowDefault);
+ break;
+ default:
+ result = TCL_ERROR;
+ Tcl_AppendResult(interp, "too many names in colors list",
+ (char *)NULL);
+ }
+ Blt_Free(colors);
+ if (result != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ Blt_FreeColorPair(pairPtr);
+ *pairPtr = sample;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfColor --
+ *
+ * Convert the color option value into a string.
+ *
+ * Results:
+ * The static string representing the color option is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfColor(colorPtr)
+ XColor *colorPtr;
+{
+ if (colorPtr == NULL) {
+ return "";
+ } else if (colorPtr == COLOR_DEFAULT) {
+ return "defcolor";
+ } else {
+ return Tk_NameOfColor(colorPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColorPairToString --
+ *
+ * Convert the color pairs into color names.
+ *
+ * Results:
+ * The string representing the symbol color is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+ColorPairToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of symbol type field in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ ColorPair *pairPtr = (ColorPair *)(widgRec + offset);
+ Tcl_DString dString;
+ char *result;
+
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppendElement(&dString, NameOfColor(pairPtr->fgColor));
+ Tcl_DStringAppendElement(&dString, NameOfColor(pairPtr->bgColor));
+ result = Tcl_DStringValue(&dString);
+ if (result == dString.staticSpace) {
+ result = Blt_Strdup(result);
+ }
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ return result;
+}
+
+int
+Blt_PointInSegments(samplePtr, segments, nSegments, halo)
+ Point2D *samplePtr;
+ Segment2D *segments;
+ int nSegments;
+ double halo;
+{
+ register Segment2D *segPtr, *endPtr;
+ double left, right, top, bottom;
+ Point2D p, t;
+ double dist, minDist;
+
+ minDist = DBL_MAX;
+ for (segPtr = segments, endPtr = segments + nSegments; segPtr < endPtr;
+ segPtr++) {
+ t = Blt_GetProjection((int)samplePtr->x, (int)samplePtr->y,
+ &segPtr->p, &segPtr->q);
+ if (segPtr->p.x > segPtr->q.x) {
+ right = segPtr->p.x, left = segPtr->q.x;
+ } else {
+ right = segPtr->q.x, left = segPtr->p.x;
+ }
+ if (segPtr->p.y > segPtr->q.y) {
+ bottom = segPtr->p.y, top = segPtr->q.y;
+ } else {
+ bottom = segPtr->q.y, top = segPtr->p.y;
+ }
+ p.x = BOUND(t.x, left, right);
+ p.y = BOUND(t.y, top, bottom);
+ dist = hypot(p.x - samplePtr->x, p.y - samplePtr->y);
+ if (dist < minDist) {
+ minDist = dist;
+ }
+ }
+ return (minDist < halo);
+}
+
+int
+Blt_PointInPolygon(samplePtr, points, nPoints)
+ Point2D *samplePtr;
+ Point2D *points;
+ int nPoints;
+{
+ double b;
+ register Point2D *p, *q, *endPtr;
+ register int count;
+
+ count = 0;
+ for (p = points, q = p + 1, endPtr = p + nPoints; q < endPtr; p++, q++) {
+ if (((p->y <= samplePtr->y) && (samplePtr->y < q->y)) ||
+ ((q->y <= samplePtr->y) && (samplePtr->y < p->y))) {
+ b = (q->x - p->x) * (samplePtr->y - p->y) / (q->y - p->y) + p->x;
+ if (samplePtr->x < b) {
+ count++; /* Count the number of intersections. */
+ }
+ }
+ }
+ return (count & 0x01);
+}
+
+int
+Blt_RegionInPolygon(extsPtr, points, nPoints, enclosed)
+ Extents2D *extsPtr;
+ Point2D *points;
+ int nPoints;
+ int enclosed;
+{
+ register Point2D *pointPtr, *endPtr;
+
+ if (enclosed) {
+ /*
+ * All points of the polygon must be inside the rectangle.
+ */
+ for (pointPtr = points, endPtr = points + nPoints; pointPtr < endPtr;
+ pointPtr++) {
+ if ((pointPtr->x < extsPtr->left) ||
+ (pointPtr->x > extsPtr->right) ||
+ (pointPtr->y < extsPtr->top) ||
+ (pointPtr->y > extsPtr->bottom)) {
+ return FALSE; /* One point is exterior. */
+ }
+ }
+ return TRUE;
+ } else {
+ Point2D p, q;
+
+ /*
+ * If any segment of the polygon clips the bounding region, the
+ * polygon overlaps the rectangle.
+ */
+ points[nPoints] = points[0];
+ for (pointPtr = points, endPtr = points + nPoints; pointPtr < endPtr;
+ pointPtr++) {
+ p = *pointPtr;
+ q = *(pointPtr + 1);
+ if (Blt_LineRectClip(extsPtr, &p, &q)) {
+ return TRUE;
+ }
+ }
+ /*
+ * Otherwise the polygon and rectangle are either disjoint
+ * or enclosed. Check if one corner of the rectangle is
+ * inside the polygon.
+ */
+ p.x = extsPtr->left;
+ p.y = extsPtr->top;
+ return Blt_PointInPolygon(&p, points, nPoints);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GraphExtents --
+ *
+ * Generates a bounding box representing the plotting area of
+ * the graph. This data structure is used to clip the points and
+ * line segments of the line element.
+ *
+ * The clip region is the plotting area plus such arbitrary extra
+ * space. The reason we clip with a bounding box larger than the
+ * plot area is so that symbols will be drawn even if their center
+ * point isn't in the plotting area.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The bounding box is filled with the dimensions of the plotting
+ * area.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+Blt_GraphExtents(graphPtr, extsPtr)
+ Graph *graphPtr;
+ Extents2D *extsPtr;
+{
+ extsPtr->left = (double)(graphPtr->hOffset - graphPtr->padX.side1);
+ extsPtr->top = (double)(graphPtr->vOffset - graphPtr->padY.side1);
+ extsPtr->right = (double)(graphPtr->hOffset + graphPtr->hRange +
+ graphPtr->padX.side2);
+ extsPtr->bottom = (double)(graphPtr->vOffset + graphPtr->vRange +
+ graphPtr->padY.side2);
+}
+
+static int
+ClipTest (double ds, double dr, double *t1, double *t2)
+{
+ double t;
+
+ if (ds < 0.0) {
+ t = dr / ds;
+ if (t > *t2) {
+ return FALSE;
+ }
+ if (t > *t1) {
+ *t1 = t;
+ }
+ } else if (ds > 0.0) {
+ t = dr / ds;
+ if (t < *t1) {
+ return FALSE;
+ }
+ if (t < *t2) {
+ *t2 = t;
+ }
+ } else {
+ /* d = 0, so line is parallel to this clipping edge */
+ if (dr < 0.0) { /* Line is outside clipping edge */
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_LineRectClip --
+ *
+ * Clips the given line segment to a rectangular region. The
+ * coordinates of the clipped line segment are returned. The
+ * original coordinates are overwritten.
+ *
+ * Reference: Liang-Barsky Line Clipping Algorithm.
+ *
+ * Results:
+ * Returns if line segment is visible within the region. The
+ * coordinates of the original line segment are overwritten
+ * by the clipped coordinates.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_LineRectClip(extsPtr, p, q)
+ Extents2D *extsPtr; /* Rectangular region to clip. */
+ Point2D *p, *q; /* (in/out) Coordinates of original
+ * and clipped line segment. */
+{
+ double t1, t2;
+ double dx, dy;
+
+ t1 = 0.0;
+ t2 = 1.0;
+ dx = q->x - p->x;
+ if ((ClipTest (-dx, p->x - extsPtr->left, &t1, &t2)) &&
+ (ClipTest (dx, extsPtr->right - p->x, &t1, &t2))) {
+ dy = q->y - p->y;
+ if ((ClipTest (-dy, p->y - extsPtr->top, &t1, &t2)) &&
+ (ClipTest (dy, extsPtr->bottom - p->y, &t1, &t2))) {
+ if (t2 < 1.0) {
+ q->x = p->x + t2 * dx;
+ q->y = p->y + t2 * dy;
+ }
+ if (t1 > 0.0) {
+ p->x += t1 * dx;
+ p->y += t1 * dy;
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_PolyRectClip --
+ *
+ * Clips the given polygon to a rectangular region. The resulting
+ * polygon is returned. Note that the resulting polyon may be
+ * complex, connected by zero width/height segments. The drawing
+ * routine (such as XFillPolygon) will not draw a connecting
+ * segment.
+ *
+ * Reference: Liang-Barsky Polygon Clipping Algorithm
+ *
+ * Results:
+ * Returns the number of points in the clipped polygon. The
+ * points of the clipped polygon are stored in *outputPts*.
+ *
+ *----------------------------------------------------------------------
+ */
+#define EPSILON FLT_EPSILON
+#define AddVertex(vx, vy) r->x=(vx), r->y=(vy), r++, count++
+
+int
+Blt_PolyRectClip(extsPtr, points, nPoints, clipPts)
+ Extents2D *extsPtr;
+ Point2D *points;
+ int nPoints;
+ Point2D *clipPts;
+{
+ Point2D *endPtr;
+ double dx, dy;
+ double tin1, tin2;
+ double tinx, tiny;
+ double xin, yin, xout, yout;
+ int count;
+ register Point2D *p; /* First vertex of input polygon edge. */
+ register Point2D *q; /* Last vertex of input polygon edge. */
+ register Point2D *r;
+
+ r = clipPts;
+ count = 0; /* Counts # of vertices in output polygon. */
+
+ points[nPoints] = points[0];
+
+ for (p = points, q = p + 1, endPtr = p + nPoints; p < endPtr; p++, q++) {
+ dx = q->x - p->x; /* X-direction */
+ dy = q->y - p->y; /* Y-direction */
+
+ if (FABS(dx) < EPSILON) {
+ dx = (p->x > extsPtr->left) ? -EPSILON : EPSILON ;
+ }
+ if (FABS(dy) < EPSILON) {
+ dy = (p->y > extsPtr->top) ? -EPSILON : EPSILON ;
+ }
+
+ if (dx > 0.0) { /* Left */
+ xin = extsPtr->left;
+ xout = extsPtr->right + 1.0;
+ } else { /* Right */
+ xin = extsPtr->right + 1.0;
+ xout = extsPtr->left;
+ }
+ if (dy > 0.0) { /* Top */
+ yin = extsPtr->top;
+ yout = extsPtr->bottom + 1.0;
+ } else { /* Bottom */
+ yin = extsPtr->bottom + 1.0;
+ yout = extsPtr->top;
+ }
+
+ tinx = (xin - p->x) / dx;
+ tiny = (yin - p->y) / dy;
+
+ if (tinx < tiny) { /* Hits x first */
+ tin1 = tinx;
+ tin2 = tiny;
+ } else { /* Hits y first */
+ tin1 = tiny;
+ tin2 = tinx;
+ }
+
+ if (tin1 <= 1.0) {
+ if (tin1 > 0.0) {
+ AddVertex(xin, yin);
+ }
+ if (tin2 <= 1.0) {
+ double toutx, touty, tout1;
+
+ toutx = (xout - p->x) / dx;
+ touty = (yout - p->y) / dy;
+ tout1 = MIN(toutx, touty);
+
+ if ((tin2 > 0.0) || (tout1 > 0.0)) {
+ if (tin2 <= tout1) {
+ if (tin2 > 0.0) {
+ if (tinx > tiny) {
+ AddVertex(xin, p->y + tinx * dy);
+ } else {
+ AddVertex(p->x + tiny * dx, yin);
+ }
+ }
+ if (tout1 < 1.0) {
+ if (toutx < touty) {
+ AddVertex(xout, p->y + toutx * dy);
+ } else {
+ AddVertex(p->x + touty * dx, yout);
+ }
+ } else {
+ AddVertex(q->x, q->y);
+ }
+ } else {
+ if (tinx > tiny) {
+ AddVertex(xin, yout);
+ } else {
+ AddVertex(xout, yin);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (count > 0) {
+ AddVertex(clipPts[0].x, clipPts[0].y);
+ }
+ return count;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetProjection --
+ *
+ * Computes the projection of a point on a line. The line (given
+ * by two points), is assumed the be infinite.
+ *
+ * Compute the slope (angle) of the line and rotate it 90 degrees.
+ * Using the slope-intercept method (we know the second line from
+ * the sample test point and the computed slope), then find the
+ * intersection of both lines. This will be the projection of the
+ * sample point on the first line.
+ *
+ * Results:
+ * Returns the coordinates of the projection on the line.
+ *
+ *----------------------------------------------------------------------
+ */
+Point2D
+Blt_GetProjection(x, y, p, q)
+ int x, y; /* Screen coordinates of the sample point. */
+ Point2D *p, *q; /* Line segment to project point onto */
+{
+ double dx, dy;
+ Point2D t;
+
+ dx = p->x - q->x;
+ dy = p->y - q->y;
+
+ /* Test for horizontal and vertical lines */
+ if (FABS(dx) < DBL_EPSILON) {
+ t.x = p->x, t.y = (double)y;
+ } else if (FABS(dy) < DBL_EPSILON) {
+ t.x = (double)x, t.y = p->y;
+ } else {
+ double m1, m2; /* Slope of both lines */
+ double b1, b2; /* y-intercepts */
+ double midX, midY; /* Midpoint of line segment. */
+ double ax, ay, bx, by;
+
+ /* Compute the slop and intercept of the line segment. */
+ m1 = (dy / dx);
+ b1 = p->y - (p->x * m1);
+
+ /*
+ * Compute the slope and intercept of a second line segment:
+ * one that intersects through sample X-Y coordinate with a
+ * slope perpendicular to original line.
+ */
+
+ /* Find midpoint of original segment. */
+ midX = (p->x + q->x) * 0.5;
+ midY = (p->y + q->y) * 0.5;
+
+ /* Rotate the line 90 degrees */
+ ax = midX - (0.5 * dy);
+ ay = midY - (0.5 * -dx);
+ bx = midX + (0.5 * dy);
+ by = midY + (0.5 * -dx);
+
+ m2 = (ay - by) / (ax - bx);
+ b2 = y - (x * m2);
+
+ /*
+ * Given the equations of two lines which contain the same point,
+ *
+ * y = m1 * x + b1
+ * y = m2 * x + b2
+ *
+ * solve for the intersection.
+ *
+ * x = (b2 - b1) / (m1 - m2)
+ * y = m1 * x + b1
+ *
+ */
+
+ t.x = (b2 - b1) / (m1 - m2);
+ t.y = m1 * t.x + b1;
+ }
+ return t;
+}
+
+
+#define SetColor(c,r,g,b) ((c)->red = (int)((r) * 65535.0), \
+ (c)->green = (int)((g) * 65535.0), \
+ (c)->blue = (int)((b) * 65535.0))
+
+void
+Blt_HSV(colorPtr, huePtr, valPtr, satPtr)
+ XColor *colorPtr;
+ double *huePtr, *valPtr, *satPtr;
+{
+ unsigned short iMax, iMin;
+ double range;
+ unsigned short *colorValues;
+ register int i;
+ double hue, sat, val;
+
+ /* Find the minimum and maximum RGB intensities */
+ colorValues = (unsigned short *)&colorPtr->red;
+ iMax = iMin = colorValues[0];
+ for (i = 1; i < 3; i++) {
+ if (iMax < colorValues[i]) {
+ iMax = colorValues[i];
+ } else if (iMin > colorValues[i]) {
+ iMin = colorValues[i];
+ }
+ }
+
+ val = (double)iMax / 65535.0;
+ hue = 0.0, sat = 0.0;
+
+ range = (double)iMax - (double)iMin;
+ if (iMax != iMin) {
+ sat = range / (double)iMax;
+ }
+ if (sat > 0.0) {
+ double r, g, b;
+
+ /* Normalize the RGB values */
+ r = ((double)iMax - (double)colorPtr->red) / range;
+ g = ((double)iMax - (double)colorPtr->green) / range;
+ b = ((double)iMax - (double)colorPtr->blue) / range;
+
+ if (colorPtr->red == iMax) {
+ hue = (double)(b - g);
+ } else if (colorPtr->green == iMax) {
+ hue = (double)(2 + (r - b));
+ } else if (colorPtr->blue == iMax) {
+ hue = (double)(4 + (g - r));
+ }
+ hue *= 60.0;
+ } else {
+ sat = 0.5;
+ }
+ if (hue < 0.0) {
+ hue += 360.0;
+ }
+ *huePtr = hue;
+ *valPtr = val;
+ *satPtr = sat;
+}
+
+void
+Blt_RGB(hue, sat, val, colorPtr)
+ double hue, sat, val;
+ XColor *colorPtr;
+{
+ double p, q, t;
+ double frac;
+ int ihue;
+
+ if (val < 0.0) {
+ val = 0.0;
+ } else if (val > 1.0) {
+ val = 1.0;
+ }
+ if (sat == 0.0) {
+ SetColor(colorPtr, val, val, val);
+ return;
+ }
+ hue = FMOD(hue, 360.0) / 60.0;
+ ihue = (int)floor(hue);
+ frac = hue - ihue;
+ p = val * (1 - sat);
+ q = val * (1 - (sat * frac));
+ t = val * (1 - (sat * (1 - frac)));
+
+ switch (ihue) {
+ case 0:
+ SetColor(colorPtr, val, t, p);
+ break;
+ case 1:
+ SetColor(colorPtr, q, val, p);
+ break;
+ case 2:
+ SetColor(colorPtr, p, val, t);
+ break;
+ case 3:
+ SetColor(colorPtr, p, q, val);
+ break;
+ case 4:
+ SetColor(colorPtr, t, p, val);
+ break;
+ case 5:
+ SetColor(colorPtr, val, p, q);
+ break;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_AdjustViewport --
+ *
+ * Adjusts the offsets of the viewport according to the scroll mode.
+ * This is to accommodate both "listbox" and "canvas" style scrolling.
+ *
+ * "canvas" The viewport scrolls within the range of world
+ * coordinates. This way the viewport always displays
+ * a full page of the world. If the world is smaller
+ * than the viewport, then (bizarrely) the world and
+ * viewport are inverted so that the world moves up
+ * and down within the viewport.
+ *
+ * "listbox" The viewport can scroll beyond the range of world
+ * coordinates. Every entry can be displayed at the
+ * top of the viewport. This also means that the
+ * scrollbar thumb weirdly shrinks as the last entry
+ * is scrolled upward.
+ *
+ * Results:
+ * The corrected offset is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits, scrollMode)
+ int offset, worldSize, windowSize;
+ int scrollUnits;
+ int scrollMode;
+{
+ switch (scrollMode) {
+ case BLT_SCROLL_MODE_CANVAS:
+
+ /*
+ * Canvas-style scrolling allows the world to be scrolled
+ * within the window.
+ */
+
+ if (worldSize < windowSize) {
+ if ((worldSize - offset) > windowSize) {
+ offset = worldSize - windowSize;
+ }
+ if (offset > 0) {
+ offset = 0;
+ }
+ } else {
+ if ((offset + windowSize) > worldSize) {
+ offset = worldSize - windowSize;
+ }
+ if (offset < 0) {
+ offset = 0;
+ }
+ }
+ break;
+
+ case BLT_SCROLL_MODE_LISTBOX:
+ if (offset < 0) {
+ offset = 0;
+ }
+ if (offset >= worldSize) {
+ offset = worldSize - scrollUnits;
+ }
+ break;
+
+ case BLT_SCROLL_MODE_HIERBOX:
+
+ /*
+ * Hierbox-style scrolling allows the world to be scrolled
+ * within the window.
+ */
+ if ((offset + windowSize) > worldSize) {
+ offset = worldSize - windowSize;
+ }
+ if (offset < 0) {
+ offset = 0;
+ }
+ break;
+ }
+ return offset;
+}
+
+int
+Blt_GetScrollInfo(interp, argc, argv, offsetPtr, worldSize, windowSize,
+ scrollUnits, scrollMode)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+ int *offsetPtr;
+ int worldSize, windowSize;
+ int scrollUnits;
+ int scrollMode;
+{
+ char c;
+ unsigned int length;
+ int offset;
+ int count;
+ double fract;
+
+ offset = *offsetPtr;
+ c = argv[0][0];
+ length = strlen(argv[0]);
+ if ((c == 's') && (strncmp(argv[0], "scroll", length) == 0)) {
+ if (argc != 3) {
+ return TCL_ERROR;
+ }
+ /* scroll number unit/page */
+ if (Tcl_GetInt(interp, argv[1], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'u') && (strncmp(argv[2], "units", length) == 0)) {
+ fract = (double)count *scrollUnits;
+ } else if ((c == 'p') && (strncmp(argv[2], "pages", length) == 0)) {
+ /* A page is 90% of the view-able window. */
+ fract = (double)count *windowSize * 0.9;
+ } else {
+ Tcl_AppendResult(interp, "unknown \"scroll\" units \"", argv[2],
+ "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ offset += (int)fract;
+ } else if ((c == 'm') && (strncmp(argv[0], "moveto", length) == 0)) {
+ if (argc != 2) {
+ return TCL_ERROR;
+ }
+ /* moveto fraction */
+ if (Tcl_GetDouble(interp, argv[1], &fract) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ offset = (int)(worldSize * fract);
+ } else {
+ /* Treat like "scroll units" */
+ if (Tcl_GetInt(interp, argv[0], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ fract = (double)count *scrollUnits;
+ offset += (int)fract;
+ return TCL_OK;
+ }
+ *offsetPtr = Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits,
+ scrollMode);
+ return TCL_OK;
+}
+
+#if (TCL_MAJOR_VERSION >= 8)
+int
+Blt_GetScrollInfoFromObj(interp, objc, objv, offsetPtr, worldSize, windowSize,
+ scrollUnits, scrollMode)
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST *objv;
+ int *offsetPtr;
+ int worldSize, windowSize;
+ int scrollUnits;
+ int scrollMode;
+{
+ char c;
+ unsigned int length;
+ int offset;
+ int count;
+ double fract;
+ char *string;
+
+ offset = *offsetPtr;
+
+ string = Tcl_GetString(objv[0]);
+ c = string[0];
+ length = strlen(string);
+ if ((c == 's') && (strncmp(string, "scroll", length) == 0)) {
+ if (objc != 3) {
+ return TCL_ERROR;
+ }
+ /* scroll number unit/page */
+ if (Tcl_GetIntFromObj(interp, objv[1], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ string = Tcl_GetString(objv[2]);
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'u') && (strncmp(string, "units", length) == 0)) {
+ fract = (double)count *scrollUnits;
+ } else if ((c == 'p') && (strncmp(string, "pages", length) == 0)) {
+ /* A page is 90% of the view-able window. */
+ fract = (double)count *windowSize * 0.9;
+ } else {
+ Tcl_AppendResult(interp, "unknown \"scroll\" units \"",
+ Tcl_GetString(objv[2]), "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ offset += (int)fract;
+ } else if ((c == 'm') && (strncmp(string, "moveto", length) == 0)) {
+ if (objc != 2) {
+ return TCL_ERROR;
+ }
+ /* moveto fraction */
+ if (Tcl_GetDoubleFromObj(interp, objv[1], &fract) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ offset = (int)(worldSize * fract);
+ } else {
+ /* Treat like "scroll units" */
+ if (Tcl_GetIntFromObj(interp, objv[0], &count) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ fract = (double)count *scrollUnits;
+ offset += (int)fract;
+ return TCL_OK;
+ }
+ *offsetPtr = Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits,
+ scrollMode);
+ return TCL_OK;
+}
+#endif /* TCL_MAJOR_VERSION >= 8 */
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_UpdateScrollbar --
+ *
+ * Invoke a Tcl command to the scrollbar, defining the new
+ * position and length of the scroll. See the Tk documentation
+ * for further information on the scrollbar. It is assumed the
+ * scrollbar command prefix is valid.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Scrollbar is commanded to change position and/or size.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_UpdateScrollbar(interp, scrollCmd, firstFract, lastFract)
+ Tcl_Interp *interp;
+ char *scrollCmd; /* scrollbar command */
+ double firstFract, lastFract;
+{
+ char string[200];
+ Tcl_DString dString;
+
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppend(&dString, scrollCmd, -1);
+ sprintf(string, " %f %f", firstFract, lastFract);
+ Tcl_DStringAppend(&dString, string, -1);
+ if (Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)) != TCL_OK) {
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_DStringFree(&dString);
+}
+
+/* -------------- */
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetPrivateGCFromDrawable --
+ *
+ * Like Tk_GetGC, but doesn't share the GC with any other widget.
+ * This is needed because the certain GC parameters (like dashes)
+ * can not be set via XCreateGC, therefore there is no way for
+ * Tk's hashing mechanism to recognize that two such GCs differ.
+ *
+ * Results:
+ * A new GC is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+GC
+Blt_GetPrivateGCFromDrawable(display, drawable, gcMask, valuePtr)
+ Display *display;
+ Drawable drawable;
+ unsigned long gcMask;
+ XGCValues *valuePtr;
+{
+ GC newGC;
+
+#ifdef WIN32
+ newGC = Blt_EmulateXCreateGC(display, drawable, gcMask, valuePtr);
+#else
+ newGC = XCreateGC(display, drawable, gcMask, valuePtr);
+#endif
+ return newGC;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_GetPrivateGC --
+ *
+ * Like Tk_GetGC, but doesn't share the GC with any other widget.
+ * This is needed because the certain GC parameters (like dashes)
+ * can not be set via XCreateGC, therefore there is no way for
+ * Tk's hashing mechanism to recognize that two such GCs differ.
+ *
+ * Results:
+ * A new GC is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+GC
+Blt_GetPrivateGC(tkwin, gcMask, valuePtr)
+ Tk_Window tkwin;
+ unsigned long gcMask;
+ XGCValues *valuePtr;
+{
+ GC gc;
+ Pixmap pixmap;
+ Drawable drawable;
+ Display *display;
+
+ pixmap = None;
+ drawable = Tk_WindowId(tkwin);
+ display = Tk_Display(tkwin);
+
+ if (drawable == None) {
+ Drawable root;
+ int depth;
+
+ root = RootWindow(display, Tk_ScreenNumber(tkwin));
+ depth = Tk_Depth(tkwin);
+
+ if (depth == DefaultDepth(display, Tk_ScreenNumber(tkwin))) {
+ drawable = root;
+ } else {
+ pixmap = Tk_GetPixmap(display, root, 1, 1, depth);
+ drawable = pixmap;
+ }
+ }
+ gc = Blt_GetPrivateGCFromDrawable(display, drawable, gcMask, valuePtr);
+ if (pixmap != None) {
+ Tk_FreePixmap(display, pixmap);
+ }
+ return gc;
+}
+
+void
+Blt_FreePrivateGC(display, gc)
+ Display *display;
+ GC gc;
+{
+ Tk_FreeXId(display, (XID) XGContextFromGC(gc));
+ XFreeGC(display, gc);
+}
+
+#ifndef WIN32
+void
+Blt_SetDashes(display, gc, dashesPtr)
+ Display *display;
+ GC gc;
+ Blt_Dashes *dashesPtr;
+{
+ XSetDashes(display, gc, dashesPtr->offset,
+ (CONST char *)dashesPtr->values, strlen((char *)dashesPtr->values));
+}
+#endif
+
+
+static double
+FindSplit(points, i, j, split)
+ Point2D points[];
+ int i, j; /* Indices specifying the range of points. */
+ int *split; /* (out) Index of next split. */
+{
+ double maxDist;
+
+ maxDist = -1.0;
+ if ((i + 1) < j) {
+ register int k;
+ double a, b, c;
+ double sqDist;
+
+ /*
+ *
+ * sqDist P(k) = | 1 P(i).x P(i).y |
+ * | 1 P(j).x P(j).y |
+ * | 1 P(k).x P(k).y |
+ * ---------------------------
+ * (P(i).x - P(j).x)^2 + (P(i).y - P(j).y)^2
+ */
+
+ a = points[i].y - points[j].y;
+ b = points[j].x - points[i].x;
+ c = (points[i].x * points[j].y) - (points[i].y * points[j].x);
+ for (k = (i + 1); k < j; k++) {
+ sqDist = (points[k].x * a) + (points[k].y * b) + c;
+ if (sqDist < 0.0) {
+ sqDist = -sqDist;
+ }
+ if (sqDist > maxDist) {
+ maxDist = sqDist; /* Track the maximum. */
+ *split = k;
+ }
+ }
+ /* Correction for segment length---should be redone if can == 0 */
+ maxDist *= maxDist / (a * a + b * b);
+ }
+ return maxDist;
+}
+
+
+/* Douglas-Peucker line simplification algorithm */
+int
+Blt_SimplifyLine(inputPts, low, high, tolerance, indices)
+ Point2D inputPts[];
+ int low, high;
+ double tolerance;
+ int indices[];
+{
+#define StackPush(a) s++, stack[s] = (a)
+#define StackPop(a) (a) = stack[s], s--
+#define StackEmpty() (s < 0)
+#define StackTop() stack[s]
+ int *stack;
+ int split = -1;
+ double sqDist, sqTolerance;
+ int s = -1; /* Points to top stack item. */
+ int count;
+
+ stack = Blt_Malloc(sizeof(int) * (high - low + 1));
+ StackPush(high);
+ count = 0;
+ indices[count++] = 0;
+ sqTolerance = tolerance * tolerance;
+ while (!StackEmpty()) {
+ sqDist = FindSplit(inputPts, low, StackTop(), &split);
+ if (sqDist > sqTolerance) {
+ StackPush(split);
+ } else {
+ indices[count++] = StackTop();
+ StackPop(low);
+ }
+ }
+ Blt_Free(stack);
+ return count;
+}
+
+void
+Blt_DrawSegments2D(display, drawable, gc, segPtr, nSegments)
+ Display *display;
+ Drawable drawable;
+ GC gc;
+ register Segment2D *segPtr;
+ int nSegments;
+{
+ XSegment *xSegPtr, *xSegArr;
+ Segment2D *endPtr;
+
+ xSegArr = Blt_Malloc(nSegments * sizeof(XSegment));
+ if (xSegArr == NULL) {
+ return;
+ }
+ xSegPtr = xSegArr;
+ for (endPtr = segPtr + nSegments; segPtr < endPtr; segPtr++) {
+ xSegPtr->x1 = (short int)segPtr->p.x;
+ xSegPtr->y1 = (short int)segPtr->p.y;
+ xSegPtr->x2 = (short int)segPtr->q.x;
+ xSegPtr->y2 = (short int)segPtr->q.y;
+ xSegPtr++;
+ }
+ XDrawSegments(display, drawable, gc, xSegArr, nSegments);
+ Blt_Free(xSegArr);
+}
+