/* * bltGrElem.c -- * * This module implements generic elements 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 "bltVecInt.h" #include "bltGraph.h" #include "bltChain.h" #include static Tk_OptionParseProc StringToData; static Tk_OptionPrintProc DataToString; static Tk_OptionParseProc StringToDataPairs; static Tk_OptionPrintProc DataPairsToString; static Tk_OptionParseProc StringToAlong; static Tk_OptionPrintProc AlongToString; static Tk_CustomOption alongOption = { StringToAlong, AlongToString, (ClientData)0 }; Tk_CustomOption bltDataOption = { StringToData, DataToString, (ClientData)0 }; Tk_CustomOption bltDataPairsOption = { StringToDataPairs, DataPairsToString, (ClientData)0 }; extern Tk_CustomOption bltDistanceOption; static int counter; #include "bltGrElem.h" extern Element *Blt_BarElement(); extern Element *Blt_LineElement(); static Blt_VectorChangedProc VectorChangedProc; EXTERN int Blt_VectorExists2 _ANSI_ARGS_((Tcl_Interp *interp, char *vecName)); /* * ---------------------------------------------------------------------- * Custom option parse and print procedures * ---------------------------------------------------------------------- */ static int GetPenStyle(graphPtr, string, type, stylePtr) Graph *graphPtr; char *string; Blt_Uid type; PenStyle *stylePtr; { Pen *penPtr; Tcl_Interp *interp = graphPtr->interp; char **elemArr; int nElem; elemArr = NULL; if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) { return TCL_ERROR; } if ((nElem != 1) && (nElem != 3)) { Tcl_AppendResult(interp, "bad style \"", string, "\": should be ", "\"penName\" or \"penName min max\"", (char *)NULL); if (elemArr != NULL) { Blt_Free(elemArr); } return TCL_ERROR; } if (Blt_GetPen(graphPtr, elemArr[0], type, &penPtr) != TCL_OK) { Blt_Free(elemArr); return TCL_ERROR; } if (nElem == 3) { double min, max; if ((Tcl_GetDouble(interp, elemArr[1], &min) != TCL_OK) || (Tcl_GetDouble(interp, elemArr[2], &max) != TCL_OK)) { Blt_Free(elemArr); return TCL_ERROR; } SetWeight(stylePtr->weight, min, max); } stylePtr->penPtr = penPtr; Blt_Free(elemArr); return TCL_OK; } double Blt_VecMin(vecPtr) Blt_Vector *vecPtr; { VectorObject *vPtr = (VectorObject *)vecPtr; if (!FINITE(vPtr->min)) { double min; register int i; min = bltNaN; for (i = 0; i < vPtr->length; i++) { if (FINITE(vPtr->valueArr[i])) { min = vPtr->valueArr[i]; break; } } for (/* empty */; i < vPtr->length; i++) { if (FINITE(vPtr->valueArr[i])) { if (min > vPtr->valueArr[i]) { min = vPtr->valueArr[i]; } } } vPtr->min = min; } return vPtr->min; } double Blt_VecMax(vecPtr) Blt_Vector *vecPtr; { VectorObject *vPtr = (VectorObject *)vecPtr; if (!FINITE(vPtr->max)) { double max; register int i; max = bltNaN; for (i = 0; i < vPtr->length; i++) { if (FINITE(vPtr->valueArr[i])) { max = vPtr->valueArr[i]; break; } } for (/* empty */; i < vPtr->length; i++) { if (FINITE(vPtr->valueArr[i])) { if (max < vPtr->valueArr[i]) { max = vPtr->valueArr[i]; } } } vPtr->max = max; } return vPtr->max; } static void SyncElemVector(vPtr) ElemVector *vPtr; { vPtr->nValues = Blt_VecLength(vPtr->vecPtr); vPtr->valueArr = Blt_VecData(vPtr->vecPtr); vPtr->min = Blt_VecMin(vPtr->vecPtr); vPtr->max = Blt_VecMax(vPtr->vecPtr); } /* *---------------------------------------------------------------------- * * FindRange -- * * Find the minimum, positive minimum, and maximum values in a * given vector and store the results in the vector structure. * * Results: * None. * * Side Effects: * Minimum, positive minimum, and maximum values are stored in * the vector. * *---------------------------------------------------------------------- */ static void FindRange(vPtr) ElemVector *vPtr; { register int i; register double *x; register double min, max; if ((vPtr->nValues < 1) || (vPtr->valueArr == NULL)) { return; /* This shouldn't ever happen. */ } x = vPtr->valueArr; min = DBL_MAX, max = -DBL_MAX; for(i = 0; i < vPtr->nValues; i++) { if (FINITE(x[i])) { min = max = x[i]; break; } } /* Initialize values to track the vector range */ for (/* empty */; i < vPtr->nValues; i++) { if (FINITE(x[i])) { if (x[i] < min) { min = x[i]; } else if (x[i] > max) { max = x[i]; } } } vPtr->min = min, vPtr->max = max; } /* *---------------------------------------------------------------------- * * Blt_FindElemVectorMinimum -- * * Find the minimum, positive minimum, and maximum values in a * given vector and store the results in the vector structure. * * Results: * None. * * Side Effects: * Minimum, positive minimum, and maximum values are stored in * the vector. * *---------------------------------------------------------------------- */ double Blt_FindElemVectorMinimum(vPtr, minLimit) ElemVector *vPtr; double minLimit; { register int i; register double *arr; register double min, x; min = DBL_MAX; arr = vPtr->valueArr; for (i = 0; i < vPtr->nValues; i++) { x = arr[i]; if (x < 0.0) { /* What do you do about negative values when using log * scale values seems like a grey area. Mirror. */ x = -x; } if ((x > minLimit) && (min > x)) { min = x; } } if (min == DBL_MAX) { min = minLimit; } return min; } static void FreeDataVector(vPtr) ElemVector *vPtr; { if (vPtr->clientId != NULL) { Blt_FreeVectorId(vPtr->clientId); /* Free the old vector */ vPtr->clientId = NULL; } else if (vPtr->valueArr != NULL) { Blt_Free(vPtr->valueArr); } vPtr->valueArr = NULL; vPtr->nValues = 0; } /* *---------------------------------------------------------------------- * * VectorChangedProc -- * * * Results: * None. * * Side Effects: * Graph is redrawn. * *---------------------------------------------------------------------- */ static void VectorChangedProc(interp, clientData, notify) Tcl_Interp *interp; ClientData clientData; Blt_VectorNotify notify; { ElemVector *vPtr = clientData; Element *elemPtr = vPtr->elemPtr; Graph *graphPtr = elemPtr->graphPtr; switch (notify) { case BLT_VECTOR_NOTIFY_DESTROY: vPtr->clientId = NULL; vPtr->valueArr = NULL; vPtr->nValues = 0; break; case BLT_VECTOR_NOTIFY_UPDATE: default: Blt_GetVectorById(interp, vPtr->clientId, &vPtr->vecPtr); SyncElemVector(vPtr); break; } graphPtr->flags |= RESET_AXES; elemPtr->flags |= MAP_ITEM; if (!elemPtr->hidden) { graphPtr->flags |= REDRAW_BACKING_STORE; Blt_EventuallyRedrawGraph(graphPtr); } } static int EvalExprList(interp, list, nElemPtr, arrayPtr) Tcl_Interp *interp; char *list; int *nElemPtr; double **arrayPtr; { int nElem; char **elemArr; double *array; int result; result = TCL_ERROR; elemArr = NULL; if (Tcl_SplitList(interp, list, &nElem, &elemArr) != TCL_OK) { return TCL_ERROR; } array = NULL; if (nElem > 0) { register double *valuePtr; register int i; counter++; array = Blt_Malloc(sizeof(double) * nElem); if (array == NULL) { Tcl_AppendResult(interp, "can't allocate new vector", (char *)NULL); goto badList; } valuePtr = array; for (i = 0; i < nElem; i++) { if (Tcl_ExprDouble(interp, elemArr[i], valuePtr) != TCL_OK) { goto badList; } valuePtr++; } } result = TCL_OK; badList: Blt_Free(elemArr); *arrayPtr = array; *nElemPtr = nElem; if (result != TCL_OK) { Blt_Free(array); } return result; } /* *---------------------------------------------------------------------- * * StringToData -- * * Given a Tcl list of numeric expression representing the element * values, convert into an array of double precision values. In * addition, the minimum and maximum values are saved. Since * elastic values are allow (values which translate to the * min/max of the graph), we must try to get the non-elastic * minimum and maximum. * * Results: * The return value is a standard Tcl result. The vector is passed * back via the vPtr. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToData(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Type of axis vector to fill */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Not used. */ char *string; /* Tcl list of expressions */ char *widgRec; /* Element record */ int offset; /* Offset of vector in Element record */ { Element *elemPtr = (Element *)(widgRec); ElemVector *vPtr = (ElemVector *)(widgRec + offset); FreeDataVector(vPtr); if (Blt_VectorExists2(interp, string)) { Blt_VectorId clientId; clientId = Blt_AllocVectorId(interp, string); if (Blt_GetVectorById(interp, clientId, &vPtr->vecPtr) != TCL_OK) { return TCL_ERROR; } Blt_SetVectorChangedProc(clientId, VectorChangedProc, vPtr); vPtr->elemPtr = elemPtr; vPtr->clientId = clientId; SyncElemVector(vPtr); elemPtr->flags |= MAP_ITEM; } else { double *newArr; int nValues; if (EvalExprList(interp, string, &nValues, &newArr) != TCL_OK) { return TCL_ERROR; } if (nValues > 0) { vPtr->valueArr = newArr; } vPtr->nValues = nValues; FindRange(vPtr); } return TCL_OK; } /* *---------------------------------------------------------------------- * * DataToString -- * * Convert the vector of floating point values into a Tcl list. * * Results: * The string representation of the vector is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * DataToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Type of axis vector to print */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Element record */ int offset; /* Offset of vector in Element record */ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */ { ElemVector *vPtr = (ElemVector *)(widgRec + offset); Element *elemPtr = (Element *)(widgRec); Tcl_DString dString; char *result; char string[TCL_DOUBLE_SPACE + 1]; double *p, *endPtr; if (vPtr->clientId != NULL) { return Blt_NameOfVectorId(vPtr->clientId); } if (vPtr->nValues == 0) { return ""; } Tcl_DStringInit(&dString); endPtr = vPtr->valueArr + vPtr->nValues; for (p = vPtr->valueArr; p < endPtr; p++) { Tcl_PrintDouble(elemPtr->graphPtr->interp, *p, string); Tcl_DStringAppendElement(&dString, string); } result = Tcl_DStringValue(&dString); /* * If memory wasn't allocated for the dynamic string, do it here (it's * currently on the stack), so that Tcl can free it normally. */ if (result == dString.staticSpace) { result = Blt_Strdup(result); } *freeProcPtr = (Tcl_FreeProc *)Blt_Free; return result; } /* *---------------------------------------------------------------------- * * StringToDataPairs -- * * This procedure is like StringToData except that it interprets * the list of numeric expressions as X Y coordinate pairs. The * minimum and maximum for both the X and Y vectors are * determined. * * Results: * The return value is a standard Tcl result. The vectors are * passed back via the widget record (elemPtr). * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToDataPairs(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; /* Tcl list of numeric expressions */ char *widgRec; /* Element record */ int offset; /* Not used. */ { Element *elemPtr = (Element *)widgRec; int nElem; unsigned int newSize; double *newArr; if (EvalExprList(interp, string, &nElem, &newArr) != TCL_OK) { return TCL_ERROR; } if (nElem & 1) { Tcl_AppendResult(interp, "odd number of data points", (char *)NULL); Blt_Free(newArr); return TCL_ERROR; } nElem /= 2; newSize = nElem * sizeof(double); FreeDataVector(&elemPtr->x); FreeDataVector(&elemPtr->y); elemPtr->x.valueArr = Blt_Malloc(newSize); elemPtr->y.valueArr = Blt_Malloc(newSize); assert(elemPtr->x.valueArr && elemPtr->y.valueArr); elemPtr->x.nValues = elemPtr->y.nValues = nElem; if (newSize > 0) { register double *dataPtr; register int i; for (dataPtr = newArr, i = 0; i < nElem; i++) { elemPtr->x.valueArr[i] = *dataPtr++; elemPtr->y.valueArr[i] = *dataPtr++; } Blt_Free(newArr); FindRange(&elemPtr->x); FindRange(&elemPtr->y); } return TCL_OK; } /* *---------------------------------------------------------------------- * * DataPairsToString -- * * Convert pairs of floating point values in the X and Y arrays * into a Tcl list. * * Results: * The return value is a string (Tcl list). * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * DataPairsToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Element information record */ int offset; /* Not used. */ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */ { Element *elemPtr = (Element *)widgRec; Tcl_Interp *interp = elemPtr->graphPtr->interp; int i; int length; char *result; char string[TCL_DOUBLE_SPACE + 1]; Tcl_DString dString; length = NumberOfPoints(elemPtr); if (length < 1) { return ""; } Tcl_DStringInit(&dString); for (i = 0; i < length; i++) { Tcl_PrintDouble(interp, elemPtr->x.valueArr[i], string); Tcl_DStringAppendElement(&dString, string); Tcl_PrintDouble(interp, elemPtr->y.valueArr[i], string); Tcl_DStringAppendElement(&dString, string); } result = Tcl_DStringValue(&dString); /* * If memory wasn't allocated for the dynamic string, do it here * (it's currently on the stack), so that Tcl can free it * normally. */ if (result == dString.staticSpace) { result = Blt_Strdup(result); } *freeProcPtr = (Tcl_FreeProc *)Blt_Free; return result; } /* *---------------------------------------------------------------------- * * StringToAlong -- * * Given a Tcl list of numeric expression representing the element * values, convert into an array of double precision values. In * addition, the minimum and maximum values are saved. Since * elastic values are allow (values which translate to the * min/max of the graph), we must try to get the non-elastic * minimum and maximum. * * Results: * The return value is a standard Tcl result. The vector is passed * back via the vPtr. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToAlong(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 representation of value. */ char *widgRec; /* Widget record. */ int offset; /* Offset of field in widget record. */ { int *intPtr = (int *)(widgRec + offset); if ((string[0] == 'x') && (string[1] == '\0')) { *intPtr = SEARCH_X; } else if ((string[0] == 'y') && (string[1] == '\0')) { *intPtr = SEARCH_Y; } else if ((string[0] == 'b') && (strcmp(string, "both") == 0)) { *intPtr = SEARCH_BOTH; } else { Tcl_AppendResult(interp, "bad along value \"", string, "\"", (char *)NULL); return TCL_ERROR; } return TCL_OK; } /* *---------------------------------------------------------------------- * * AlongToString -- * * Convert the vector of floating point values into a Tcl list. * * Results: * The string representation of the vector is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * AlongToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Widget record */ int offset; /* Offset of field in widget record */ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */ { int along = *(int *)(widgRec + offset); switch (along) { case SEARCH_X: return "x"; case SEARCH_Y: return "y"; case SEARCH_BOTH: return "both"; default: return "unknown along value"; } } void Blt_FreePalette(graphPtr, palette) Graph *graphPtr; Blt_Chain *palette; { Blt_ChainLink *linkPtr; /* Skip the first slot. It contains the built-in "normal" pen of * the element. */ linkPtr = Blt_ChainFirstLink(palette); if (linkPtr != NULL) { register PenStyle *stylePtr; Blt_ChainLink *nextPtr; for (linkPtr = Blt_ChainNextLink(linkPtr); linkPtr != NULL; linkPtr = nextPtr) { nextPtr = Blt_ChainNextLink(linkPtr); stylePtr = Blt_ChainGetValue(linkPtr); Blt_FreePen(graphPtr, stylePtr->penPtr); Blt_ChainDeleteLink(palette, linkPtr); } } } /* *---------------------------------------------------------------------- * * Blt_StringToStyles -- * * Parse the list of style names. * * Results: * The return value is a standard Tcl result. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ int Blt_StringToStyles(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 style list */ char *widgRec; /* Element information record */ int offset; /* Offset of symbol type field in record */ { Blt_Chain *palette = *(Blt_Chain **)(widgRec + offset); Blt_ChainLink *linkPtr; Element *elemPtr = (Element *)(widgRec); PenStyle *stylePtr; char **elemArr; int nStyles; register int i; size_t size = (size_t)clientData; elemArr = NULL; Blt_FreePalette(elemPtr->graphPtr, palette); if ((string == NULL) || (*string == '\0')) { nStyles = 0; } else if (Tcl_SplitList(interp, string, &nStyles, &elemArr) != TCL_OK) { return TCL_ERROR; } /* Reserve the first entry for the "normal" pen. We'll set the * style later */ linkPtr = Blt_ChainFirstLink(palette); if (linkPtr == NULL) { linkPtr = Blt_ChainAllocLink(size); Blt_ChainLinkBefore(palette, linkPtr, NULL); } stylePtr = Blt_ChainGetValue(linkPtr); stylePtr->penPtr = elemPtr->normalPenPtr; for (i = 0; i < nStyles; i++) { linkPtr = Blt_ChainAllocLink(size); stylePtr = Blt_ChainGetValue(linkPtr); stylePtr->weight.min = (double)i; stylePtr->weight.max = (double)i + 1.0; stylePtr->weight.range = 1.0; if (GetPenStyle(elemPtr->graphPtr, elemArr[i], elemPtr->classUid, (PenStyle *)stylePtr) != TCL_OK) { Blt_Free(elemArr); Blt_FreePalette(elemPtr->graphPtr, palette); return TCL_ERROR; } Blt_ChainLinkBefore(palette, linkPtr, NULL); } if (elemArr != NULL) { Blt_Free(elemArr); } return TCL_OK; } /* *---------------------------------------------------------------------- * * Blt_StylesToString -- * * Convert the style information into a string. * * Results: * The string representing the style information is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ char * Blt_StylesToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Element information record */ int offset; /* Not used. */ Tcl_FreeProc **freeProcPtr; /* Not used. */ { Blt_Chain *palette = *(Blt_Chain **)(widgRec + offset); Tcl_DString dString; char *result; Blt_ChainLink *linkPtr; Tcl_DStringInit(&dString); linkPtr = Blt_ChainFirstLink(palette); if (linkPtr != NULL) { Element *elemPtr = (Element *)(widgRec); char string[TCL_DOUBLE_SPACE]; Tcl_Interp *interp; PenStyle *stylePtr; interp = elemPtr->graphPtr->interp; for (linkPtr = Blt_ChainNextLink(linkPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { stylePtr = Blt_ChainGetValue(linkPtr); Tcl_DStringStartSublist(&dString); Tcl_DStringAppendElement(&dString, stylePtr->penPtr->name); Tcl_PrintDouble(interp, stylePtr->weight.min, string); Tcl_DStringAppendElement(&dString, string); Tcl_PrintDouble(interp, stylePtr->weight.max, string); Tcl_DStringAppendElement(&dString, string); Tcl_DStringEndSublist(&dString); } } result = Blt_Strdup(Tcl_DStringValue(&dString)); *freeProcPtr = (Tcl_FreeProc *)Blt_Free; return result; } /* *---------------------------------------------------------------------- * * Blt_StyleMap -- * * Creates an array of style indices and fills it based on the weight * of each data point. * * Results: * None. * * Side effects: * Memory is freed and allocated for the index array. * *---------------------------------------------------------------------- */ PenStyle ** Blt_StyleMap(elemPtr) Element *elemPtr; { register int i; int nWeights; /* Number of weights to be examined. * If there are more data points than * weights, they will default to the * normal pen. */ PenStyle **dataToStyle; /* Directory of styles. Each array * element represents the style for * the data point at that index */ Blt_ChainLink *linkPtr; PenStyle *stylePtr; double *w; /* Weight vector */ int nPoints; nPoints = NumberOfPoints(elemPtr); nWeights = MIN(elemPtr->w.nValues, nPoints); w = elemPtr->w.valueArr; linkPtr = Blt_ChainFirstLink(elemPtr->palette); stylePtr = Blt_ChainGetValue(linkPtr); /* * Create a style mapping array (data point index to style), * initialized to the default style. */ dataToStyle = Blt_Malloc(nPoints * sizeof(PenStyle *)); assert(dataToStyle); for (i = 0; i < nPoints; i++) { dataToStyle[i] = stylePtr; } for (i = 0; i < nWeights; i++) { for (linkPtr = Blt_ChainLastLink(elemPtr->palette); linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) { stylePtr = Blt_ChainGetValue(linkPtr); if (stylePtr->weight.range > 0.0) { double norm; norm = (w[i] - stylePtr->weight.min) / stylePtr->weight.range; if (((norm - 1.0) <= DBL_EPSILON) && (((1.0 - norm) - 1.0) <= DBL_EPSILON)) { dataToStyle[i] = stylePtr; break; /* Done: found range that matches. */ } } } } return dataToStyle; } /* *---------------------------------------------------------------------- * * Blt_MapErrorBars -- * * Creates two arrays of points and pen indices, filled with * the screen coordinates of the visible * * Results: * None. * * Side effects: * Memory is freed and allocated for the index array. * *---------------------------------------------------------------------- */ void Blt_MapErrorBars(graphPtr, elemPtr, dataToStyle) Graph *graphPtr; Element *elemPtr; PenStyle **dataToStyle; { int n, nPoints; Extents2D exts; PenStyle *stylePtr; Blt_GraphExtents(graphPtr, &exts); nPoints = NumberOfPoints(elemPtr); if (elemPtr->xError.nValues > 0) { n = MIN(elemPtr->xError.nValues, nPoints); } else { n = MIN3(elemPtr->xHigh.nValues, elemPtr->xLow.nValues, nPoints); } if (n > 0) { Segment2D *errorBars; Segment2D *segPtr; double high, low; double x, y; int *errorToData; int *indexPtr; register int i; segPtr = errorBars = Blt_Malloc(n * 3 * sizeof(Segment2D)); indexPtr = errorToData = Blt_Malloc(n * 3 * sizeof(int)); for (i = 0; i < n; i++) { x = elemPtr->x.valueArr[i]; y = elemPtr->y.valueArr[i]; stylePtr = dataToStyle[i]; if ((FINITE(x)) && (FINITE(y))) { if (elemPtr->xError.nValues > 0) { high = x + elemPtr->xError.valueArr[i]; low = x - elemPtr->xError.valueArr[i]; } else { high = elemPtr->xHigh.valueArr[i]; low = elemPtr->xLow.valueArr[i]; } if ((FINITE(high)) && (FINITE(low))) { Point2D p, q; p = Blt_Map2D(graphPtr, high, y, &elemPtr->axes); q = Blt_Map2D(graphPtr, low, y, &elemPtr->axes); segPtr->p = p; segPtr->q = q; if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) { segPtr++; *indexPtr++ = i; } /* Left cap */ segPtr->p.x = segPtr->q.x = p.x; segPtr->p.y = p.y - stylePtr->errorBarCapWidth; segPtr->q.y = p.y + stylePtr->errorBarCapWidth; if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) { segPtr++; *indexPtr++ = i; } /* Right cap */ segPtr->p.x = segPtr->q.x = q.x; segPtr->p.y = q.y - stylePtr->errorBarCapWidth; segPtr->q.y = q.y + stylePtr->errorBarCapWidth; if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) { segPtr++; *indexPtr++ = i; } } } } elemPtr->xErrorBars = errorBars; elemPtr->xErrorBarCnt = segPtr - errorBars; elemPtr->xErrorToData = errorToData; } if (elemPtr->yError.nValues > 0) { n = MIN(elemPtr->yError.nValues, nPoints); } else { n = MIN3(elemPtr->yHigh.nValues, elemPtr->yLow.nValues, nPoints); } if (n > 0) { Segment2D *errorBars; Segment2D *segPtr; double high, low; double x, y; int *errorToData; int *indexPtr; register int i; segPtr = errorBars = Blt_Malloc(n * 3 * sizeof(Segment2D)); indexPtr = errorToData = Blt_Malloc(n * 3 * sizeof(int)); for (i = 0; i < n; i++) { x = elemPtr->x.valueArr[i]; y = elemPtr->y.valueArr[i]; stylePtr = dataToStyle[i]; if ((FINITE(x)) && (FINITE(y))) { if (elemPtr->yError.nValues > 0) { high = y + elemPtr->yError.valueArr[i]; low = y - elemPtr->yError.valueArr[i]; } else { high = elemPtr->yHigh.valueArr[i]; low = elemPtr->yLow.valueArr[i]; } if ((FINITE(high)) && (FINITE(low))) { Point2D p, q; p = Blt_Map2D(graphPtr, x, high, &elemPtr->axes); q = Blt_Map2D(graphPtr, x, low, &elemPtr->axes); segPtr->p = p; segPtr->q = q; if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) { segPtr++; *indexPtr++ = i; } /* Top cap. */ segPtr->p.y = segPtr->q.y = p.y; segPtr->p.x = p.x - stylePtr->errorBarCapWidth; segPtr->q.x = p.x + stylePtr->errorBarCapWidth; if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) { segPtr++; *indexPtr++ = i; } /* Bottom cap. */ segPtr->p.y = segPtr->q.y = q.y; segPtr->p.x = q.x - stylePtr->errorBarCapWidth; segPtr->q.x = q.x + stylePtr->errorBarCapWidth; if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) { segPtr++; *indexPtr++ = i; } } } } elemPtr->yErrorBars = errorBars; elemPtr->yErrorBarCnt = segPtr - errorBars; elemPtr->yErrorToData = errorToData; } } /* *---------------------------------------------------------------------- * * GetIndex -- * * Given a string representing the index of a pair of x,y * coordinates, return the numeric index. * * Results: * A standard TCL result. * *---------------------------------------------------------------------- */ static int GetIndex(interp, elemPtr, string, indexPtr) Tcl_Interp *interp; Element *elemPtr; char *string; int *indexPtr; { long ielem; int last; last = NumberOfPoints(elemPtr) - 1; if ((*string == 'e') && (strcmp("end", string) == 0)) { ielem = last; } else if (Tcl_ExprLong(interp, string, &ielem) != TCL_OK) { return TCL_ERROR; } *indexPtr = (int)ielem; return TCL_OK; } /* *---------------------------------------------------------------------- * * NameToElement -- * * Find the element represented the given name, returning * a pointer to its data structure via elemPtrPtr. * * Results: * A standard TCL result. * *---------------------------------------------------------------------- */ static int NameToElement(graphPtr, name, elemPtrPtr) Graph *graphPtr; char *name; Element **elemPtrPtr; { Blt_HashEntry *hPtr; if (name == NULL) { return TCL_ERROR; } hPtr = Blt_FindHashEntry(&graphPtr->elements.table, name); if (hPtr == NULL) { Tcl_AppendResult(graphPtr->interp, "can't find element \"", name, "\" in \"", Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL); return TCL_ERROR; } *elemPtrPtr = (Element *)Blt_GetHashValue(hPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * DestroyElement -- * * Add a new element to the graph. * * Results: * The return value is a standard Tcl result. * *---------------------------------------------------------------------- */ static void DestroyElement(graphPtr, elemPtr) Graph *graphPtr; Element *elemPtr; { Blt_ChainLink *linkPtr; Blt_DeleteBindings(graphPtr->bindTable, elemPtr); Blt_LegendRemoveElement(graphPtr->legend, elemPtr); Tk_FreeOptions(elemPtr->specsPtr, (char *)elemPtr, graphPtr->display, 0); /* * Call the element's own destructor to release the memory and * resources allocated for it. */ (*elemPtr->procsPtr->destroyProc) (graphPtr, elemPtr); /* Remove it also from the element display list */ for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { if (elemPtr == Blt_ChainGetValue(linkPtr)) { Blt_ChainDeleteLink(graphPtr->elements.displayList, linkPtr); if (!elemPtr->hidden) { graphPtr->flags |= RESET_WORLD; Blt_EventuallyRedrawGraph(graphPtr); } break; } } /* Remove the element for the graph's hash table of elements */ if (elemPtr->hashPtr != NULL) { Blt_DeleteHashEntry(&graphPtr->elements.table, elemPtr->hashPtr); } if (elemPtr->name != NULL) { Blt_Free(elemPtr->name); } Blt_Free(elemPtr); } /* *---------------------------------------------------------------------- * * CreateElement -- * * Add a new element to the graph. * * Results: * The return value is a standard Tcl result. * *---------------------------------------------------------------------- */ static int CreateElement(graphPtr, interp, argc, argv, classUid) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; Blt_Uid classUid; { Element *elemPtr; Blt_HashEntry *hPtr; int isNew; if (argv[3][0] == '-') { Tcl_AppendResult(graphPtr->interp, "name of element \"", argv[3], "\" can't start with a '-'", (char *)NULL); return TCL_ERROR; } hPtr = Blt_CreateHashEntry(&graphPtr->elements.table, argv[3], &isNew); if (!isNew) { Tcl_AppendResult(interp, "element \"", argv[3], "\" already exists in \"", argv[0], "\"", (char *)NULL); return TCL_ERROR; } if (classUid == bltBarElementUid) { elemPtr = Blt_BarElement(graphPtr, argv[3], classUid); } else { /* Stripcharts are line graphs with some options enabled. */ elemPtr = Blt_LineElement(graphPtr, argv[3], classUid); } elemPtr->hashPtr = hPtr; Blt_SetHashValue(hPtr, elemPtr); if (Blt_ConfigureWidgetComponent(interp, graphPtr->tkwin, elemPtr->name, "Element", elemPtr->specsPtr, argc - 4, argv + 4, (char *)elemPtr, 0) != TCL_OK) { DestroyElement(graphPtr, elemPtr); return TCL_ERROR; } (*elemPtr->procsPtr->configProc) (graphPtr, elemPtr); Blt_ChainPrepend(graphPtr->elements.displayList, elemPtr); if (!elemPtr->hidden) { /* If the new element isn't hidden then redraw the graph. */ graphPtr->flags |= REDRAW_BACKING_STORE; Blt_EventuallyRedrawGraph(graphPtr); } elemPtr->flags |= MAP_ITEM; graphPtr->flags |= RESET_AXES; Tcl_SetResult(interp, elemPtr->name, TCL_VOLATILE); return TCL_OK; } /* *---------------------------------------------------------------------- * * RebuildDisplayList -- * * Given a Tcl list of element names, this procedure rebuilds the * display list, ignoring invalid element names. This list describes * not only only which elements to draw, but in what order. This is * only important for bar and pie charts. * * Results: * The return value is a standard Tcl result. Only if the Tcl list * can not be split, a TCL_ERROR is returned and interp->result contains * an error message. * * Side effects: * The graph is eventually redrawn using the new display list. * *---------------------------------------------------------------------- */ static int RebuildDisplayList(graphPtr, newList) Graph *graphPtr; /* Graph widget record */ char *newList; /* Tcl list of element names */ { int nNames; /* Number of names found in Tcl name list */ char **nameArr; /* Broken out array of element names */ register int i; Element *elemPtr; /* Element information record */ if (Tcl_SplitList(graphPtr->interp, newList, &nNames, &nameArr) != TCL_OK) { Tcl_AppendResult(graphPtr->interp, "can't split name list \"", newList, "\"", (char *)NULL); return TCL_ERROR; } /* Clear the display list and mark all elements as hidden. */ Blt_ChainReset(graphPtr->elements.displayList); /* Rebuild the display list, checking that each name it exists * (currently ignoring invalid element names). */ for (i = 0; i < nNames; i++) { if (NameToElement(graphPtr, nameArr[i], &elemPtr) == TCL_OK) { Blt_ChainAppend(graphPtr->elements.displayList, elemPtr); } } Blt_Free(nameArr); graphPtr->flags |= RESET_WORLD; Blt_EventuallyRedrawGraph(graphPtr); Tcl_ResetResult(graphPtr->interp); return TCL_OK; } /* *---------------------------------------------------------------------- * * Blt_DestroyElements -- * * Removes all the graph's elements. This routine is called when * the graph is destroyed. * * Results: * None. * * Side effects: * Memory allocated for the graph's elements is freed. * *---------------------------------------------------------------------- */ void Blt_DestroyElements(graphPtr) Graph *graphPtr; { Blt_HashEntry *hPtr; Blt_HashSearch cursor; Element *elemPtr; for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.table, &cursor); hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { elemPtr = (Element *)Blt_GetHashValue(hPtr); elemPtr->hashPtr = NULL; DestroyElement(graphPtr, elemPtr); } Blt_DeleteHashTable(&graphPtr->elements.table); Blt_DeleteHashTable(&graphPtr->elements.tagTable); Blt_ChainDestroy(graphPtr->elements.displayList); } void Blt_MapElements(graphPtr) Graph *graphPtr; { Element *elemPtr; Blt_ChainLink *linkPtr; if (graphPtr->mode != MODE_INFRONT) { Blt_ResetStacks(graphPtr); } for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { elemPtr = Blt_ChainGetValue(linkPtr); if (elemPtr->hidden) { continue; } if ((graphPtr->flags & MAP_ALL) || (elemPtr->flags & MAP_ITEM)) { (*elemPtr->procsPtr->mapProc) (graphPtr, elemPtr); elemPtr->flags &= ~MAP_ITEM; } } } /* * ----------------------------------------------------------------- * * Blt_DrawElements -- * * Calls the individual element drawing routines for each * element. * * Results: * None * * Side Effects: * Elements are drawn into the drawable (pixmap) which will * eventually be displayed in the graph window. * * ----------------------------------------------------------------- */ void Blt_DrawElements(graphPtr, drawable) Graph *graphPtr; Drawable drawable; /* Pixmap or window to draw into */ { Blt_ChainLink *linkPtr; Element *elemPtr; for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { elemPtr = Blt_ChainGetValue(linkPtr); if (!elemPtr->hidden) { (*elemPtr->procsPtr->drawNormalProc) (graphPtr, drawable, elemPtr); } } } /* * ----------------------------------------------------------------- * * Blt_DrawActiveElements -- * * Calls the individual element drawing routines to display * the active colors for each element. * * Results: * None * * Side Effects: * Elements are drawn into the drawable (pixmap) which will * eventually be displayed in the graph window. * * ----------------------------------------------------------------- */ void Blt_DrawActiveElements(graphPtr, drawable) Graph *graphPtr; Drawable drawable; /* Pixmap or window to draw into */ { Blt_ChainLink *linkPtr; Element *elemPtr; for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { elemPtr = Blt_ChainGetValue(linkPtr); if ((!elemPtr->hidden) && (elemPtr->flags & ELEM_ACTIVE)) { (*elemPtr->procsPtr->drawActiveProc) (graphPtr, drawable, elemPtr); } } } /* * ----------------------------------------------------------------- * * Blt_ElementsToPostScript -- * * Generates PostScript output for each graph element in the * element display list. * * ----------------------------------------------------------------- */ void Blt_ElementsToPostScript(graphPtr, psToken) Graph *graphPtr; PsToken psToken; { Blt_ChainLink *linkPtr; Element *elemPtr; for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { elemPtr = Blt_ChainGetValue(linkPtr); if (!elemPtr->hidden) { /* Comment the PostScript to indicate the start of the element */ Blt_FormatToPostScript(psToken, "\n%% Element \"%s\"\n\n", elemPtr->name); (*elemPtr->procsPtr->printNormalProc) (graphPtr, psToken, elemPtr); } } } /* * ----------------------------------------------------------------- * * Blt_ActiveElementsToPostScript -- * * ----------------------------------------------------------------- */ void Blt_ActiveElementsToPostScript(graphPtr, psToken) Graph *graphPtr; PsToken psToken; { Blt_ChainLink *linkPtr; Element *elemPtr; for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { elemPtr = Blt_ChainGetValue(linkPtr); if ((!elemPtr->hidden) && (elemPtr->flags & ELEM_ACTIVE)) { Blt_FormatToPostScript(psToken, "\n%% Active Element \"%s\"\n\n", elemPtr->name); (*elemPtr->procsPtr->printActiveProc) (graphPtr, psToken, elemPtr); } } } int Blt_GraphUpdateNeeded(graphPtr) Graph *graphPtr; { Blt_ChainLink *linkPtr; Element *elemPtr; for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { elemPtr = Blt_ChainGetValue(linkPtr); if (elemPtr->hidden) { continue; } /* Check if the x or y vectors have notifications pending */ if ((Blt_VectorNotifyPending(elemPtr->x.clientId)) || (Blt_VectorNotifyPending(elemPtr->y.clientId))) { return 1; } } return 0; } /* *---------------------------------------------------------------------- * * ActivateOp -- * * Marks data points of elements (given by their index) as active. * * Results: * Returns TCL_OK if no errors occurred. * *---------------------------------------------------------------------- */ static int ActivateOp(graphPtr, interp, argc, argv) Graph *graphPtr; /* Graph widget */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; /* Number of element names */ char **argv; /* List of element names */ { Element *elemPtr; register int i; int *activeArr; int nActiveIndices; if (argc == 3) { register Blt_HashEntry *hPtr; Blt_HashSearch cursor; /* List all the currently active elements */ for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.table, &cursor); hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { elemPtr = (Element *)Blt_GetHashValue(hPtr); if (elemPtr->flags & ELEM_ACTIVE) { Tcl_AppendElement(graphPtr->interp, elemPtr->name); } } return TCL_OK; } if (NameToElement(graphPtr, argv[3], &elemPtr) != TCL_OK) { return TCL_ERROR; /* Can't find named element */ } elemPtr->flags |= ELEM_ACTIVE | ACTIVE_PENDING; activeArr = NULL; nActiveIndices = -1; if (argc > 4) { register int *activePtr; nActiveIndices = argc - 4; activePtr = activeArr = Blt_Malloc(sizeof(int) * nActiveIndices); assert(activeArr); for (i = 4; i < argc; i++) { if (GetIndex(interp, elemPtr, argv[i], activePtr) != TCL_OK) { return TCL_ERROR; } activePtr++; } } if (elemPtr->activeIndices != NULL) { Blt_Free(elemPtr->activeIndices); } elemPtr->nActiveIndices = nActiveIndices; elemPtr->activeIndices = activeArr; Blt_EventuallyRedrawGraph(graphPtr); return TCL_OK; } ClientData Blt_MakeElementTag(graphPtr, tagName) Graph *graphPtr; char *tagName; { Blt_HashEntry *hPtr; int isNew; hPtr = Blt_CreateHashEntry(&graphPtr->elements.tagTable, tagName, &isNew); assert(hPtr); return Blt_GetHashKey(&graphPtr->elements.tagTable, hPtr); } /* *---------------------------------------------------------------------- * * BindOp -- * * .g element bind elemName sequence command * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int BindOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { if (argc == 3) { Blt_HashEntry *hPtr; Blt_HashSearch cursor; char *tagName; for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.tagTable, &cursor); hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { tagName = Blt_GetHashKey(&graphPtr->elements.tagTable, hPtr); Tcl_AppendElement(interp, tagName); } return TCL_OK; } return Blt_ConfigureBindings(interp, graphPtr->bindTable, Blt_MakeElementTag(graphPtr, argv[3]), argc - 4, argv + 4); } /* *---------------------------------------------------------------------- * * CreateOp -- * * Add a new element to the graph (using the default type of the * graph). * * Results: * The return value is a standard Tcl result. * *---------------------------------------------------------------------- */ static int CreateOp(graphPtr, interp, argc, argv, type) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; Blt_Uid type; { return CreateElement(graphPtr, interp, argc, argv, type); } /* *---------------------------------------------------------------------- * * CgetOp -- * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int CgetOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char *argv[]; { Element *elemPtr; if (NameToElement(graphPtr, argv[3], &elemPtr) != TCL_OK) { return TCL_ERROR; /* Can't find named element */ } if (Tk_ConfigureValue(interp, graphPtr->tkwin, elemPtr->specsPtr, (char *)elemPtr, argv[4], 0) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } /* *---------------------------------------------------------------------- * * ClosestOp -- * * Find the element closest to the specified screen coordinates. * Options: * -halo Consider points only with this maximum distance * from the picked coordinate. * -interpolate Find closest point along element traces, not just * data points. * -along * * Results: * A standard Tcl result. If an element could be found within * the halo distance, the interpreter result is "1", otherwise * "0". If a closest element exists, the designated Tcl array * variable will be set with the following information: * * 1) the element name, * 2) the index of the closest point, * 3) the distance (in screen coordinates) from the picked X-Y * coordinate and the closest point, * 4) the X coordinate (graph coordinate) of the closest point, * 5) and the Y-coordinate. * *---------------------------------------------------------------------- */ static Tk_ConfigSpec closestSpecs[] = { {TK_CONFIG_CUSTOM, "-halo", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(ClosestSearch, halo), 0, &bltDistanceOption}, {TK_CONFIG_BOOLEAN, "-interpolate", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(ClosestSearch, mode), 0 }, {TK_CONFIG_CUSTOM, "-along", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(ClosestSearch, along), 0, &alongOption}, {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL, 0, 0} }; static int ClosestOp(graphPtr, interp, argc, argv) Graph *graphPtr; /* Graph widget */ Tcl_Interp *interp; /* Interpreter to report results to */ int argc; /* Number of element names */ char **argv; /* List of element names */ { Element *elemPtr; ClosestSearch search; int i, x, y; int flags = TCL_LEAVE_ERR_MSG; int found; if (graphPtr->flags & RESET_AXES) { Blt_ResetAxes(graphPtr); } if (Tk_GetPixels(interp, graphPtr->tkwin, argv[3], &x) != TCL_OK) { Tcl_AppendResult(interp, ": bad window x-coordinate", (char *)NULL); return TCL_ERROR; } if (Tk_GetPixels(interp, graphPtr->tkwin, argv[4], &y) != TCL_OK) { Tcl_AppendResult(interp, ": bad window y-coordinate", (char *)NULL); return TCL_ERROR; } if (graphPtr->inverted) { int temp; temp = x, x = y, y = temp; } for (i = 6; i < argc; i += 2) { /* Count switches-value pairs */ if ((argv[i][0] != '-') || ((argv[i][1] == '-') && (argv[i][2] == '\0'))) { break; } } if (i > argc) { i = argc; } search.mode = SEARCH_POINTS; search.halo = graphPtr->halo; search.index = -1; search.along = SEARCH_BOTH; search.x = x; search.y = y; if (Tk_ConfigureWidget(interp, graphPtr->tkwin, closestSpecs, i - 6, argv + 6, (char *)&search, TK_CONFIG_ARGV_ONLY) != TCL_OK) { return TCL_ERROR; /* Error occurred processing an option. */ } if ((i < argc) && (argv[i][0] == '-')) { i++; /* Skip "--" */ } search.dist = (double)(search.halo + 1); if (i < argc) { Blt_ChainLink *linkPtr; for ( /* empty */ ; i < argc; i++) { if (NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) { return TCL_ERROR; /* Can't find named element */ } found = FALSE; for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList); linkPtr == NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { if (elemPtr == Blt_ChainGetValue(linkPtr)) { found = TRUE; break; } } if ((!found) || (elemPtr->hidden)) { Tcl_AppendResult(interp, "element \"", argv[i], "\" is hidden", (char *)NULL); return TCL_ERROR; /* Element isn't visible */ } /* Check if the X or Y vectors have notifications pending */ if ((elemPtr->flags & MAP_ITEM) || (Blt_VectorNotifyPending(elemPtr->x.clientId)) || (Blt_VectorNotifyPending(elemPtr->y.clientId))) { continue; } (*elemPtr->procsPtr->closestProc) (graphPtr, elemPtr, &search); } } else { Blt_ChainLink *linkPtr; /* * Find the closest point from the set of displayed elements, * searching the display list from back to front. That way if * the points from two different elements overlay each other * exactly, the last one picked will be the topmost. */ for (linkPtr = Blt_ChainLastLink(graphPtr->elements.displayList); linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) { elemPtr = Blt_ChainGetValue(linkPtr); /* Check if the X or Y vectors have notifications pending */ if ((elemPtr->hidden) || (elemPtr->flags & MAP_ITEM) || (Blt_VectorNotifyPending(elemPtr->x.clientId)) || (Blt_VectorNotifyPending(elemPtr->y.clientId))) { continue; } (*elemPtr->procsPtr->closestProc)(graphPtr, elemPtr, &search); } } if (search.dist < (double)search.halo) { char string[200]; /* * Return an array of 5 elements */ if (Tcl_SetVar2(interp, argv[5], "name", search.elemPtr->name, flags) == NULL) { return TCL_ERROR; } sprintf(string, "%d", search.index); if (Tcl_SetVar2(interp, argv[5], "index", string, flags) == NULL) { return TCL_ERROR; } Tcl_PrintDouble(interp, search.point.x, string); if (Tcl_SetVar2(interp, argv[5], "x", string, flags) == NULL) { return TCL_ERROR; } Tcl_PrintDouble(interp, search.point.y, string); if (Tcl_SetVar2(interp, argv[5], "y", string, flags) == NULL) { return TCL_ERROR; } Tcl_PrintDouble(interp, search.dist, string); if (Tcl_SetVar2(interp, argv[5], "dist", string, flags) == NULL) { return TCL_ERROR; } Tcl_SetResult(interp, "1", TCL_STATIC); } else { if (Tcl_SetVar2(interp, argv[5], "name", "", flags) == NULL) { return TCL_ERROR; } Tcl_SetResult(interp, "0", TCL_STATIC); } return TCL_OK; } /* *---------------------------------------------------------------------- * * ConfigureOp -- * * Sets the element specifications by the given the command line * arguments and calls the element specification configuration * routine. If zero or one command line options are given, only * information about the option(s) is returned in interp->result. * If the element configuration has changed and the element is * currently displayed, the axis limits are updated and * recomputed. * * Results: * The return value is a standard Tcl result. * * Side Effects: * Graph will be redrawn to reflect the new display list. * *---------------------------------------------------------------------- */ static int ConfigureOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char *argv[]; { Element *elemPtr; int flags; int numNames, numOpts; char **options; register int i; /* Figure out where the option value pairs begin */ argc -= 3; argv += 3; for (i = 0; i < argc; i++) { if (argv[i][0] == '-') { break; } if (NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) { return TCL_ERROR; /* Can't find named element */ } } numNames = i; /* Number of element names specified */ numOpts = argc - i; /* Number of options specified */ options = argv + numNames; /* Start of options in argv */ for (i = 0; i < numNames; i++) { NameToElement(graphPtr, argv[i], &elemPtr); flags = TK_CONFIG_ARGV_ONLY; if (numOpts == 0) { return Tk_ConfigureInfo(interp, graphPtr->tkwin, elemPtr->specsPtr, (char *)elemPtr, (char *)NULL, flags); } else if (numOpts == 1) { return Tk_ConfigureInfo(interp, graphPtr->tkwin, elemPtr->specsPtr, (char *)elemPtr, options[0], flags); } if (Tk_ConfigureWidget(interp, graphPtr->tkwin, elemPtr->specsPtr, numOpts, options, (char *)elemPtr, flags) != TCL_OK) { return TCL_ERROR; } if ((*elemPtr->procsPtr->configProc) (graphPtr, elemPtr) != TCL_OK) { return TCL_ERROR; /* Failed to configure element */ } if (Blt_ConfigModified(elemPtr->specsPtr, "-hide", (char *)NULL)) { graphPtr->flags |= RESET_AXES; elemPtr->flags |= MAP_ITEM; } /* If data points or axes have changed, reset the axes (may * affect autoscaling) and recalculate the screen points of * the element. */ if (Blt_ConfigModified(elemPtr->specsPtr, "-*data", "-map*", "-x", "-y", (char *)NULL)) { graphPtr->flags |= RESET_WORLD; elemPtr->flags |= MAP_ITEM; } /* The new label may change the size of the legend */ if (Blt_ConfigModified(elemPtr->specsPtr, "-label", (char *)NULL)) { graphPtr->flags |= (MAP_WORLD | REDRAW_WORLD); } } /* Update the pixmap if any configuration option changed */ graphPtr->flags |= (REDRAW_BACKING_STORE | DRAW_MARGINS); Blt_EventuallyRedrawGraph(graphPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * DeactivateOp -- * * Clears the active bit for the named elements. * * Results: * Returns TCL_OK if no errors occurred. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int DeactivateOp(graphPtr, interp, argc, argv) Graph *graphPtr; /* Graph widget */ Tcl_Interp *interp; /* Not used. */ int argc; /* Number of element names */ char **argv; /* List of element names */ { Element *elemPtr; register int i; for (i = 3; i < argc; i++) { if (NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) { return TCL_ERROR; /* Can't find named element */ } elemPtr->flags &= ~ELEM_ACTIVE; if (elemPtr->activeIndices != NULL) { Blt_Free(elemPtr->activeIndices); elemPtr->activeIndices = NULL; } elemPtr->nActiveIndices = 0; } Blt_EventuallyRedrawGraph(graphPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * DeleteOp -- * * Delete the named elements from the graph. * * Results: * TCL_ERROR is returned if any of the named elements can not be * found. Otherwise TCL_OK is returned; * * Side Effects: * If the element is currently displayed, the plotting area of * the graph is redrawn. Memory and resources allocated by the * elements are released. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int DeleteOp(graphPtr, interp, argc, argv) Graph *graphPtr; /* Graph widget */ Tcl_Interp *interp; /* Not used. */ int argc; /* Number of element names */ char **argv; /* List of element names */ { Element *elemPtr; register int i; for (i = 3; i < argc; i++) { if (NameToElement(graphPtr, argv[i], &elemPtr) != TCL_OK) { return TCL_ERROR; /* Can't find named element */ } DestroyElement(graphPtr, elemPtr); } Blt_EventuallyRedrawGraph(graphPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * ExistsOp -- * * Indicates if the named element exists in the graph. * * Results: * The return value is a standard Tcl result. The interpreter * result will contain "1" or "0". * *---------------------------------------------------------------------- */ /* ARGSUSED */ static int ExistsOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { Blt_HashEntry *hPtr; hPtr = Blt_FindHashEntry(&graphPtr->elements.table, argv[3]); Blt_SetBooleanResult(interp, (hPtr != NULL)); return TCL_OK; } /* *---------------------------------------------------------------------- * * GetOp -- * * Returns the name of the picked element (using the element * bind operation). Right now, the only name accepted is * "current". * * Results: * A standard Tcl result. The interpreter result will contain * the name of the element. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int GetOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; /* Not used. */ char *argv[]; { register Element *elemPtr; if ((argv[3][0] == 'c') && (strcmp(argv[3], "current") == 0)) { elemPtr = (Element *)Blt_GetCurrentItem(graphPtr->bindTable); /* Report only on elements. */ if ((elemPtr != NULL) && ((elemPtr->classUid == bltBarElementUid) || (elemPtr->classUid == bltLineElementUid) || (elemPtr->classUid == bltStripElementUid))) { Tcl_SetResult(interp, elemPtr->name, TCL_VOLATILE); } } return TCL_OK; } /* *---------------------------------------------------------------------- * * NamesOp -- * * Returns the names of the elements is the graph matching * one of more patterns provided. If no pattern arguments * are given, then all element names will be returned. * * Results: * The return value is a standard Tcl result. The interpreter * result will contain a Tcl list of the element names. * *---------------------------------------------------------------------- */ static int NamesOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { Element *elemPtr; Blt_HashSearch cursor; register Blt_HashEntry *hPtr; register int i; for (hPtr = Blt_FirstHashEntry(&graphPtr->elements.table, &cursor); hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { elemPtr = (Element *)Blt_GetHashValue(hPtr); if (argc == 3) { Tcl_AppendElement(graphPtr->interp, elemPtr->name); continue; } for (i = 3; i < argc; i++) { if (Tcl_StringMatch(elemPtr->name, argv[i])) { Tcl_AppendElement(interp, elemPtr->name); break; } } } return TCL_OK; } /* *---------------------------------------------------------------------- * * ShowOp -- * * Queries or resets the element display list. * * Results: * The return value is a standard Tcl result. The interpreter * result will contain the new display list of element names. * *---------------------------------------------------------------------- */ static int ShowOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { Element *elemPtr; Blt_ChainLink *linkPtr; if (argc == 4) { if (RebuildDisplayList(graphPtr, argv[3]) != TCL_OK) { return TCL_ERROR; } } for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { elemPtr = Blt_ChainGetValue(linkPtr); Tcl_AppendElement(interp, elemPtr->name); } return TCL_OK; } /* *---------------------------------------------------------------------- * * TypeOp -- * * Returns the name of the type of the element given by some * element name. * * Results: * A standard Tcl result. Returns the type of the element in * interp->result. If the identifier given doesn't represent an * element, then an error message is left in interp->result. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int TypeOp(graphPtr, interp, argc, argv) Graph *graphPtr; /* Graph widget */ Tcl_Interp *interp; int argc; /* Not used. */ char **argv; /* Element name */ { Element *elemPtr; if (NameToElement(graphPtr, argv[3], &elemPtr) != TCL_OK) { return TCL_ERROR; /* Can't find named element */ } Tcl_SetResult(interp, elemPtr->classUid, TCL_STATIC); return TCL_OK; } /* * Global routines: */ static Blt_OpSpec elemOps[] = { {"activate", 1, (Blt_Op)ActivateOp, 3, 0, "?elemName? ?index...?",}, {"bind", 1, (Blt_Op)BindOp, 3, 6, "elemName sequence command",}, {"cget", 2, (Blt_Op)CgetOp, 5, 5, "elemName option",}, {"closest", 2, (Blt_Op)ClosestOp, 6, 0, "x y varName ?option value?... ?elemName?...",}, {"configure", 2, (Blt_Op)ConfigureOp, 4, 0, "elemName ?elemName?... ?option value?...",}, {"create", 2, (Blt_Op)CreateOp, 4, 0, "elemName ?option value?...",}, {"deactivate", 3, (Blt_Op)DeactivateOp, 3, 0, "?elemName?...",}, {"delete", 3, (Blt_Op)DeleteOp, 3, 0, "?elemName?...",}, {"exists", 1, (Blt_Op)ExistsOp, 4, 4, "elemName",}, {"get", 1, (Blt_Op)GetOp, 4, 4, "name",}, {"names", 1, (Blt_Op)NamesOp, 3, 0, "?pattern?...",}, {"show", 1, (Blt_Op)ShowOp, 3, 4, "?elemList?",}, {"type", 1, (Blt_Op)TypeOp, 4, 4, "elemName",}, }; static int numElemOps = sizeof(elemOps) / sizeof(Blt_OpSpec); /* * ---------------------------------------------------------------- * * Blt_ElementOp -- * * This procedure is invoked to process the Tcl command that * corresponds to a widget managed by this module. See the user * documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * * ---------------------------------------------------------------- */ int Blt_ElementOp(graphPtr, interp, argc, argv, type) Graph *graphPtr; /* Graph widget record */ Tcl_Interp *interp; int argc; /* # arguments */ char **argv; /* Argument list */ Blt_Uid type; { Blt_Op proc; int result; proc = Blt_GetOp(interp, numElemOps, elemOps, BLT_OP_ARG2, argc, argv, 0); if (proc == NULL) { return TCL_ERROR; } if (proc == CreateOp) { result = CreateOp(graphPtr, interp, argc, argv, type); } else { result = (*proc) (graphPtr, interp, argc, argv); } return result; }