diff options
Diffstat (limited to 'tkblt/generic/tkbltGraphOp.C')
-rw-r--r-- | tkblt/generic/tkbltGraphOp.C | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/tkblt/generic/tkbltGraphOp.C b/tkblt/generic/tkbltGraphOp.C new file mode 100644 index 0000000..ada2758 --- /dev/null +++ b/tkblt/generic/tkbltGraphOp.C @@ -0,0 +1,462 @@ +/* + * Smithsonian Astrophysical Observatory, Cambridge, MA, USA + * This code has been modified under the terms listed below and is made + * available under the same terms. + */ + +/* + * Copyright 1991-2004 George A Howlett. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <string.h> + +#include "tkbltGraph.h" +#include "tkbltGraphLine.h" +#include "tkbltGraphBar.h" +#include "tkbltGraphOp.h" + +#include "tkbltGrAxis.h" +#include "tkbltGrAxisOp.h" +#include "tkbltGrElem.h" +#include "tkbltGrElemOp.h" +#include "tkbltGrHairs.h" +#include "tkbltGrHairsOp.h" +#include "tkbltGrLegd.h" +#include "tkbltGrLegdOp.h" +#include "tkbltGrMarker.h" +#include "tkbltGrMarkerOp.h" +#include "tkbltGrPostscript.h" +#include "tkbltGrPostscriptOp.h" +#include "tkbltGrPen.h" +#include "tkbltGrPenOp.h" +#include "tkbltGrXAxisOp.h" + +using namespace Blt; + +static Tcl_ObjCmdProc BarchartObjCmd; +static Tcl_ObjCmdProc GraphObjCmd; + +static Axis* GetFirstAxis(Chain* chain); + +int GraphObjConfigure(Graph* graphPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Tk_SavedOptions savedOptions; + int mask =0; + int error; + Tcl_Obj* errorResult; + + for (error=0; error<=1; error++) { + if (!error) { + if (Tk_SetOptions(interp, (char*)graphPtr->ops_, graphPtr->optionTable_, + objc, objv, graphPtr->tkwin_, &savedOptions, &mask) + != TCL_OK) + continue; + } + else { + errorResult = Tcl_GetObjResult(interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + } + + if (graphPtr->configure() != TCL_OK) + return TCL_ERROR; + graphPtr->flags |= mask; + graphPtr->eventuallyRedraw(); + + break; + } + + if (!error) { + Tk_FreeSavedOptions(&savedOptions); + return TCL_OK; + } + else { + Tcl_SetObjResult(interp, errorResult); + Tcl_DecrRefCount(errorResult); + return TCL_ERROR; + } +} + +static int CgetOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc != 3) { + Tcl_WrongNumArgs(interp, 1, objv, "cget option"); + return TCL_ERROR; + } + Tcl_Obj* objPtr = Tk_GetOptionValue(interp, + (char*)graphPtr->ops_, + graphPtr->optionTable_, + objv[2], graphPtr->tkwin_); + if (objPtr == NULL) + return TCL_ERROR; + else + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; +} + +static int ConfigureOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc <= 3) { + Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)graphPtr->ops_, + graphPtr->optionTable_, + (objc == 3) ? objv[2] : NULL, + graphPtr->tkwin_); + if (objPtr == NULL) + return TCL_ERROR; + else + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; + } + else + return GraphObjConfigure(graphPtr, interp, objc-2, objv+2); +} + +/* + *--------------------------------------------------------------------------- + * + * ExtentsOp -- + * + * Reports the size of one of several items within the graph. The + * following are valid items: + * + * "bottommargin" Height of the bottom margin + * "leftmargin" Width of the left margin + * "legend" x y w h of the legend + * "plotarea" x y w h of the plotarea + * "plotheight" Height of the plot area + * "rightmargin" Width of the right margin + * "topmargin" Height of the top margin + * "plotwidth" Width of the plot area + * + * Results: + * Always returns TCL_OK. + * + *--------------------------------------------------------------------------- + */ + +static int ExtentsOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + GraphOptions* ops = (GraphOptions*)graphPtr->ops_; + int length; + const char* string = Tcl_GetStringFromObj(objv[2], &length); + char c = string[0]; + if ((c == 'p') && (length > 4) && + (strncmp("plotheight", string, length) == 0)) { + int height = graphPtr->bottom_ - graphPtr->top_ + 1; + Tcl_SetIntObj(Tcl_GetObjResult(interp), height); + } + else if ((c == 'p') && (length > 4) && + (strncmp("plotwidth", string, length) == 0)) { + int width = graphPtr->right_ - graphPtr->left_ + 1; + Tcl_SetIntObj(Tcl_GetObjResult(interp), width); + } + else if ((c == 'p') && (length > 4) && + (strncmp("plotarea", string, length) == 0)) { + Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewIntObj(graphPtr->left_)); + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewIntObj(graphPtr->top_)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(graphPtr->right_ - graphPtr->left_+1)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(graphPtr->bottom_ - graphPtr->top_+1)); + Tcl_SetObjResult(interp, listObjPtr); + } + else if ((c == 'l') && (length > 2) && + (strncmp("legend", string, length) == 0)) { + Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewIntObj(graphPtr->legend_->x_)); + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewIntObj(graphPtr->legend_->y_)); + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewIntObj(graphPtr->legend_->width_)); + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewIntObj(graphPtr->legend_->height_)); + Tcl_SetObjResult(interp, listObjPtr); + } + else if ((c == 'l') && (length > 2) && + (strncmp("leftmargin", string, length) == 0)) { + Tcl_SetIntObj(Tcl_GetObjResult(interp), ops->leftMargin.width); + } + else if ((c == 'r') && (length > 1) && + (strncmp("rightmargin", string, length) == 0)) { + Tcl_SetIntObj(Tcl_GetObjResult(interp), ops->rightMargin.width); + } + else if ((c == 't') && (length > 1) && + (strncmp("topmargin", string, length) == 0)) { + Tcl_SetIntObj(Tcl_GetObjResult(interp), ops->topMargin.height); + } + else if ((c == 'b') && (length > 1) && + (strncmp("bottommargin", string, length) == 0)) { + Tcl_SetIntObj(Tcl_GetObjResult(interp), ops->bottomMargin.height); + } + else { + Tcl_AppendResult(interp, "bad extent item \"", objv[2], + "\": should be plotheight, plotwidth, leftmargin, rightmargin, \ +topmargin, bottommargin, plotarea, or legend", (char*)NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +static int InsideOp(ClientData clientData, Tcl_Interp* interp, int objc, + Tcl_Obj* const objv[]) +{ + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "x y"); + return TCL_ERROR; + } + + Graph* graphPtr = (Graph*)clientData; + + int x; + if (Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) + return TCL_ERROR; + + int y; + if (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK) + return TCL_ERROR; + + Region2d exts; + graphPtr->extents(&exts); + + int result = (x<=exts.right && x>=exts.left && y<=exts.bottom && y>=exts.top); + Tcl_SetBooleanObj(Tcl_GetObjResult(interp), result); + + return TCL_OK; +} + +static int InvtransformOp(ClientData clientData, Tcl_Interp* interp, int objc, + Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + double x, y; + if ((Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK) || + (Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK)) + return TCL_ERROR; + + if (graphPtr->flags & RESET) + graphPtr->resetAxes(); + + // Perform the reverse transformation, converting from window coordinates + // to graph data coordinates. Note that the point is always mapped to the + // bottom and left axes (which may not be what the user wants) + Axis* xAxis = GetFirstAxis(graphPtr->axisChain_[0]); + Axis* yAxis = GetFirstAxis(graphPtr->axisChain_[1]); + Point2d point = graphPtr->invMap2D(x, y, xAxis, yAxis); + + Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(point.x)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(point.y)); + Tcl_SetObjResult(interp, listObjPtr); + + return TCL_OK; +} + +static int TransformOp(ClientData clientData, Tcl_Interp* interp, int objc, + Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + double x, y; + if ((Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK) || + (Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK)) + return TCL_ERROR; + + if (graphPtr->flags & RESET) + graphPtr->resetAxes(); + + // Perform the transformation from window to graph coordinates. Note that + // the points are always mapped onto the bottom and left axes (which may + // not be the what the user wants + Axis* xAxis = GetFirstAxis(graphPtr->axisChain_[0]); + Axis* yAxis = GetFirstAxis(graphPtr->axisChain_[1]); + + Point2d point = graphPtr->map2D(x, y, xAxis, yAxis); + + Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj((int)point.x)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj((int)point.y)); + Tcl_SetObjResult(interp, listObjPtr); + + return TCL_OK; +} + +static const Ensemble graphEnsemble[] = { + {"axis", 0, Blt::axisEnsemble}, + {"bar", 0, Blt::elementEnsemble}, + {"cget", CgetOp, 0}, + {"configure", ConfigureOp, 0}, + {"crosshairs", 0, Blt::crosshairsEnsemble}, + {"element", 0, Blt::elementEnsemble}, + {"extents", ExtentsOp, 0}, + {"inside", InsideOp, 0}, + {"invtransform",InvtransformOp, 0}, + {"legend", 0, Blt::legendEnsemble}, + {"line", 0, Blt::elementEnsemble}, + {"marker", 0, Blt::markerEnsemble}, + {"pen", 0, Blt::penEnsemble}, + {"postscript", 0, Blt::postscriptEnsemble}, + {"transform", TransformOp, 0}, + {"xaxis", 0, Blt::xaxisEnsemble}, + {"yaxis", 0, Blt::xaxisEnsemble}, + {"x2axis", 0, Blt::xaxisEnsemble}, + {"y2axis", 0, Blt::xaxisEnsemble}, + { 0,0,0 } +}; + +// Support + +static Axis* GetFirstAxis(Chain* chain) +{ + ChainLink* link = Chain_FirstLink(chain); + if (!link) + return NULL; + + return (Axis*)Chain_GetValue(link); +} + +// Tk Interface + +int Blt_GraphCmdInitProc(Tcl_Interp* interp) +{ + Tcl_Namespace* nsPtr = Tcl_FindNamespace(interp, "::blt", NULL, + TCL_LEAVE_ERR_MSG); + if (nsPtr == NULL) + return TCL_ERROR; + + { + const char* cmdPath = "::blt::graph"; + Tcl_Command cmdToken = Tcl_FindCommand(interp, cmdPath, NULL, 0); + if (cmdToken) + return TCL_OK; + cmdToken = Tcl_CreateObjCommand(interp, cmdPath, GraphObjCmd, NULL, NULL); + if (Tcl_Export(interp, nsPtr, "graph", 0) != TCL_OK) + return TCL_ERROR; + } + + { + const char* cmdPath = "::blt::barchart"; + Tcl_Command cmdToken = Tcl_FindCommand(interp, cmdPath, NULL, 0); + if (cmdToken) + return TCL_OK; + cmdToken = Tcl_CreateObjCommand(interp, cmdPath, BarchartObjCmd, NULL,NULL); + if (Tcl_Export(interp, nsPtr, "barchart", 0) != TCL_OK) + return TCL_ERROR; + } + + return TCL_OK; +} + +static int GraphObjCmd(ClientData clientData, Tcl_Interp* interp, int objc, + Tcl_Obj* const objv[]) +{ + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); + return TCL_ERROR; + } + + Graph* graphPtr = new LineGraph(clientData, interp, objc, objv); + return graphPtr->valid_ ? TCL_OK : TCL_ERROR; +} + +static int BarchartObjCmd(ClientData clientData, Tcl_Interp* interp, int objc, + Tcl_Obj* const objv[]) +{ + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); + return TCL_ERROR; + } + + Graph* graphPtr = new BarGraph(clientData, interp, objc, objv); + return graphPtr->valid_ ? TCL_OK : TCL_ERROR; +} + +int GraphInstCmdProc(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Tcl_Preserve(graphPtr); + int result = graphPtr->invoke(graphEnsemble, 1, objc, objv); + Tcl_Release(graphPtr); + return result; +} + +// called by Tcl_DeleteCommand +void GraphInstCmdDeleteProc(ClientData clientData) +{ + Graph* graphPtr = (Graph*)clientData; + if (!(graphPtr->flags & GRAPH_DELETED)) + Tk_DestroyWindow(graphPtr->tkwin_); +} + +void GraphEventProc(ClientData clientData, XEvent* eventPtr) +{ + Graph* graphPtr = (Graph*)clientData; + + if (eventPtr->type == Expose) { + if (eventPtr->xexpose.count == 0) + graphPtr->eventuallyRedraw(); + } + else if (eventPtr->type == FocusIn || eventPtr->type == FocusOut) { + if (eventPtr->xfocus.detail != NotifyInferior) { + if (eventPtr->type == FocusIn) + graphPtr->flags |= FOCUS; + else + graphPtr->flags &= ~FOCUS; + graphPtr->eventuallyRedraw(); + } + } + else if (eventPtr->type == DestroyNotify) { + if (!(graphPtr->flags & GRAPH_DELETED)) { + graphPtr->flags |= GRAPH_DELETED; + Tcl_DeleteCommandFromToken(graphPtr->interp_, graphPtr->cmdToken_); + if (graphPtr->flags & REDRAW_PENDING) + Tcl_CancelIdleCall(DisplayGraph, graphPtr); + Tcl_EventuallyFree(graphPtr, DestroyGraph); + } + } + else if (eventPtr->type == ConfigureNotify) { + graphPtr->flags |= RESET; + graphPtr->eventuallyRedraw(); + } +} + +void DisplayGraph(ClientData clientData) +{ + Graph* graphPtr = (Graph*)clientData; + graphPtr->draw(); +} + +// called by Tcl_EventuallyFree and others +void DestroyGraph(char* dataPtr) +{ + Graph* graphPtr = (Graph*)dataPtr; + delete graphPtr; +} + |