summaryrefslogtreecommitdiffstats
path: root/tkblt/generic/tkbltGraphOp.C
diff options
context:
space:
mode:
Diffstat (limited to 'tkblt/generic/tkbltGraphOp.C')
-rw-r--r--tkblt/generic/tkbltGraphOp.C462
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;
+}
+