From 4773cc1128f60e83ba5721fe1ad3497b15078361 Mon Sep 17 00:00:00 2001 From: William Joye Date: Tue, 8 Aug 2017 16:06:26 -0400 Subject: Squashed 'tkblt/' changes from e1747e4c..c79bffe0 c79bffe0 comment out MSVB flags, need a better way f2bb9074 Merge pull request #6 from prs-de/master 95101fc9 Interpret empty strings as NaN e4521220 Add -tickformat axis option, rename -command option to -tickformatcommand d2c07875 Stop checking for NULL before free e8635888 Prevent dangling pointer 796f2e81 Make sure enough arguments are passed before accessing them dc04360d Update axis' margin when using it 04d02fd5 Use axis' own classid to determine the type 452da1d8 Don't remove pkgIndex.tcl on 'make clean' 314ade3a Import std namespace 6983f2fa Don't remove tkbltConfig.sh on 'make clean' ac38d758 Correct function name fbf29e98 Fix MSVC's C2375 "redefinition with different linkage" f10a85ad Add cmath and printf wrappers for Windows 75db3cd1 Fix polymorphic variant in jump table f14d9dc7 Enforce explicit downcasting of numeric values 095e27e5 Use double for comparing with GraphOption's aspect 9ffae9ee Upgrade BarGroup's lastY to double 16f221ec Upgrade BarGroup's sum to double ce7e3c4d Remove superfluous literal suffixes 0383f13a Upgrade getBoundingBox angle to double 2655a6b4 Upgrade vScale_/hScale_ to double 642cb149 Add Windows-specific CFLAGS 1a9e6fba Place axes within their area when using multiple axis in a single margin 71312d1e Better align with TEA's expected file layout ce90eafc fix win64 issue git-subtree-dir: tkblt git-subtree-split: c79bffe0a0e6c587473708c778b2744c37d8abba --- configure | 116 +- configure.ac | 113 +- doc/graph.html | 42 +- doc/graph.n | 38 +- generic/tkblt.decls | 92 ++ generic/tkbltChain.C | 194 +++ generic/tkbltChain.h | 91 ++ generic/tkbltConfig.C | 218 ++++ generic/tkbltConfig.h | 43 + generic/tkbltDecls.h | 152 +++ generic/tkbltGrAxis.C | 1962 ++++++++++++++++++++++++++++++ generic/tkbltGrAxis.h | 266 +++++ generic/tkbltGrAxisOp.C | 648 ++++++++++ generic/tkbltGrAxisOp.h | 60 + generic/tkbltGrAxisOption.C | 264 +++++ generic/tkbltGrAxisOption.h | 41 + generic/tkbltGrBind.C | 227 ++++ generic/tkbltGrBind.h | 72 ++ generic/tkbltGrDef.h | 45 + generic/tkbltGrElem.C | 284 +++++ generic/tkbltGrElem.h | 202 ++++ generic/tkbltGrElemBar.C | 1315 +++++++++++++++++++++ generic/tkbltGrElemBar.h | 132 +++ generic/tkbltGrElemLine.C | 2470 ++++++++++++++++++++++++++++++++++++++ generic/tkbltGrElemLine.h | 184 +++ generic/tkbltGrElemLineSpline.C | 1086 +++++++++++++++++ generic/tkbltGrElemOp.C | 597 ++++++++++ generic/tkbltGrElemOp.h | 41 + generic/tkbltGrElemOption.C | 396 +++++++ generic/tkbltGrElemOption.h | 41 + generic/tkbltGrHairs.C | 145 +++ generic/tkbltGrHairs.h | 78 ++ generic/tkbltGrHairsOp.C | 164 +++ generic/tkbltGrHairsOp.h | 42 + generic/tkbltGrLegd.C | 1070 +++++++++++++++++ generic/tkbltGrLegd.h | 178 +++ generic/tkbltGrLegdOp.C | 496 ++++++++ generic/tkbltGrLegdOp.h | 40 + generic/tkbltGrMarker.C | 177 +++ generic/tkbltGrMarker.h | 103 ++ generic/tkbltGrMarkerLine.C | 298 +++++ generic/tkbltGrMarkerLine.h | 82 ++ generic/tkbltGrMarkerOp.C | 458 ++++++++ generic/tkbltGrMarkerOp.h | 39 + generic/tkbltGrMarkerOption.C | 209 ++++ generic/tkbltGrMarkerOption.h | 39 + generic/tkbltGrMarkerPolygon.C | 298 +++++ generic/tkbltGrMarkerPolygon.h | 84 ++ generic/tkbltGrMarkerText.C | 276 +++++ generic/tkbltGrMarkerText.h | 82 ++ generic/tkbltGrMisc.C | 335 ++++++ generic/tkbltGrMisc.h | 118 ++ generic/tkbltGrPSOutput.C | 931 +++++++++++++++ generic/tkbltGrPSOutput.h | 90 ++ generic/tkbltGrPen.C | 62 + generic/tkbltGrPen.h | 79 ++ generic/tkbltGrPenBar.C | 174 +++ generic/tkbltGrPenBar.h | 73 ++ generic/tkbltGrPenLine.C | 243 ++++ generic/tkbltGrPenLine.h | 88 ++ generic/tkbltGrPenOp.C | 217 ++++ generic/tkbltGrPenOp.h | 42 + generic/tkbltGrPenOption.C | 89 ++ generic/tkbltGrPostscript.C | 84 ++ generic/tkbltGrPostscript.h | 73 ++ generic/tkbltGrPostscriptOp.C | 183 +++ generic/tkbltGrPostscriptOp.h | 41 + generic/tkbltGrText.C | 249 ++++ generic/tkbltGrText.h | 79 ++ generic/tkbltGrXAxisOp.C | 222 ++++ generic/tkbltGrXAxisOp.h | 39 + generic/tkbltGraph.C | 1458 +++++++++++++++++++++++ generic/tkbltGraph.h | 256 ++++ generic/tkbltGraphBar.C | 516 ++++++++ generic/tkbltGraphBar.h | 119 ++ generic/tkbltGraphLine.C | 267 +++++ generic/tkbltGraphLine.h | 76 ++ generic/tkbltGraphOp.C | 462 ++++++++ generic/tkbltGraphOp.h | 44 + generic/tkbltGraphSup.C | 686 +++++++++++ generic/tkbltInt.C | 74 ++ generic/tkbltInt.h | 58 + generic/tkbltNsUtil.C | 129 ++ generic/tkbltNsUtil.h | 64 + generic/tkbltOp.C | 171 +++ generic/tkbltOp.h | 67 ++ generic/tkbltParse.C | 388 ++++++ generic/tkbltParse.h | 55 + generic/tkbltStubInit.c | 30 + generic/tkbltStubLib.C | 15 + generic/tkbltSwitch.C | 407 +++++++ generic/tkbltSwitch.h | 129 ++ generic/tkbltVecCmd.C | 1820 ++++++++++++++++++++++++++++ generic/tkbltVecInt.h | 202 ++++ generic/tkbltVecMath.C | 1612 +++++++++++++++++++++++++ generic/tkbltVecOp.C | 56 + generic/tkbltVector.C | 1875 +++++++++++++++++++++++++++++ generic/tkbltVector.h | 140 +++ src/tkblt.decls | 92 -- src/tkbltChain.C | 194 --- src/tkbltChain.h | 91 -- src/tkbltConfig.C | 218 ---- src/tkbltConfig.h | 43 - src/tkbltDecls.h | 152 --- src/tkbltGrAxis.C | 1971 ------------------------------- src/tkbltGrAxis.h | 264 ----- src/tkbltGrAxisOp.C | 643 ---------- src/tkbltGrAxisOp.h | 60 - src/tkbltGrAxisOption.C | 260 ---- src/tkbltGrAxisOption.h | 41 - src/tkbltGrBind.C | 228 ---- src/tkbltGrBind.h | 72 -- src/tkbltGrDef.h | 45 - src/tkbltGrElem.C | 288 ----- src/tkbltGrElem.h | 202 ---- src/tkbltGrElemBar.C | 1323 --------------------- src/tkbltGrElemBar.h | 132 --- src/tkbltGrElemLine.C | 2486 --------------------------------------- src/tkbltGrElemLine.h | 184 --- src/tkbltGrElemLineSpline.C | 1086 ----------------- src/tkbltGrElemOp.C | 600 ---------- src/tkbltGrElemOp.h | 41 - src/tkbltGrElemOption.C | 399 ------- src/tkbltGrElemOption.h | 41 - src/tkbltGrHairs.C | 145 --- src/tkbltGrHairs.h | 78 -- src/tkbltGrHairsOp.C | 164 --- src/tkbltGrHairsOp.h | 42 - src/tkbltGrLegd.C | 1070 ----------------- src/tkbltGrLegd.h | 178 --- src/tkbltGrLegdOp.C | 496 -------- src/tkbltGrLegdOp.h | 40 - src/tkbltGrMarker.C | 178 --- src/tkbltGrMarker.h | 103 -- src/tkbltGrMarkerLine.C | 299 ----- src/tkbltGrMarkerLine.h | 82 -- src/tkbltGrMarkerOp.C | 458 -------- src/tkbltGrMarkerOp.h | 39 - src/tkbltGrMarkerOption.C | 210 ---- src/tkbltGrMarkerOption.h | 39 - src/tkbltGrMarkerPolygon.C | 301 ----- src/tkbltGrMarkerPolygon.h | 84 -- src/tkbltGrMarkerText.C | 276 ----- src/tkbltGrMarkerText.h | 82 -- src/tkbltGrMisc.C | 335 ------ src/tkbltGrMisc.h | 109 -- src/tkbltGrPSOutput.C | 931 --------------- src/tkbltGrPSOutput.h | 90 -- src/tkbltGrPen.C | 63 - src/tkbltGrPen.h | 79 -- src/tkbltGrPenBar.C | 174 --- src/tkbltGrPenBar.h | 73 -- src/tkbltGrPenLine.C | 243 ---- src/tkbltGrPenLine.h | 88 -- src/tkbltGrPenOp.C | 217 ---- src/tkbltGrPenOp.h | 42 - src/tkbltGrPenOption.C | 89 -- src/tkbltGrPostscript.C | 84 -- src/tkbltGrPostscript.h | 73 -- src/tkbltGrPostscriptOp.C | 183 --- src/tkbltGrPostscriptOp.h | 41 - src/tkbltGrText.C | 240 ---- src/tkbltGrText.h | 77 -- src/tkbltGrXAxisOp.C | 220 ---- src/tkbltGrXAxisOp.h | 39 - src/tkbltGraph.C | 1457 ----------------------- src/tkbltGraph.h | 256 ---- src/tkbltGraphBar.C | 518 -------- src/tkbltGraphBar.h | 119 -- src/tkbltGraphLine.C | 267 ----- src/tkbltGraphLine.h | 76 -- src/tkbltGraphOp.C | 456 ------- src/tkbltGraphOp.h | 44 - src/tkbltGraphSup.C | 686 ----------- src/tkbltInt.C | 74 -- src/tkbltNsUtil.C | 129 -- src/tkbltNsUtil.h | 64 - src/tkbltOp.C | 171 --- src/tkbltOp.h | 67 -- src/tkbltParse.C | 388 ------ src/tkbltParse.h | 55 - src/tkbltStubInit.c | 30 - src/tkbltStubLib.C | 15 - src/tkbltSwitch.C | 407 ------- src/tkbltSwitch.h | 129 -- src/tkbltVecCmd.C | 1811 ---------------------------- src/tkbltVecInt.h | 202 ---- src/tkbltVecMath.C | 1609 ------------------------- src/tkbltVecOp.C | 56 - src/tkbltVector.C | 1874 ----------------------------- src/tkbltVector.h | 129 -- 191 files changed, 30330 insertions(+), 30246 deletions(-) create mode 100644 generic/tkblt.decls create mode 100644 generic/tkbltChain.C create mode 100644 generic/tkbltChain.h create mode 100644 generic/tkbltConfig.C create mode 100644 generic/tkbltConfig.h create mode 100644 generic/tkbltDecls.h create mode 100644 generic/tkbltGrAxis.C create mode 100644 generic/tkbltGrAxis.h create mode 100644 generic/tkbltGrAxisOp.C create mode 100644 generic/tkbltGrAxisOp.h create mode 100644 generic/tkbltGrAxisOption.C create mode 100644 generic/tkbltGrAxisOption.h create mode 100644 generic/tkbltGrBind.C create mode 100644 generic/tkbltGrBind.h create mode 100644 generic/tkbltGrDef.h create mode 100644 generic/tkbltGrElem.C create mode 100644 generic/tkbltGrElem.h create mode 100644 generic/tkbltGrElemBar.C create mode 100644 generic/tkbltGrElemBar.h create mode 100644 generic/tkbltGrElemLine.C create mode 100644 generic/tkbltGrElemLine.h create mode 100644 generic/tkbltGrElemLineSpline.C create mode 100644 generic/tkbltGrElemOp.C create mode 100644 generic/tkbltGrElemOp.h create mode 100644 generic/tkbltGrElemOption.C create mode 100644 generic/tkbltGrElemOption.h create mode 100644 generic/tkbltGrHairs.C create mode 100644 generic/tkbltGrHairs.h create mode 100644 generic/tkbltGrHairsOp.C create mode 100644 generic/tkbltGrHairsOp.h create mode 100644 generic/tkbltGrLegd.C create mode 100644 generic/tkbltGrLegd.h create mode 100644 generic/tkbltGrLegdOp.C create mode 100644 generic/tkbltGrLegdOp.h create mode 100644 generic/tkbltGrMarker.C create mode 100644 generic/tkbltGrMarker.h create mode 100644 generic/tkbltGrMarkerLine.C create mode 100644 generic/tkbltGrMarkerLine.h create mode 100644 generic/tkbltGrMarkerOp.C create mode 100644 generic/tkbltGrMarkerOp.h create mode 100644 generic/tkbltGrMarkerOption.C create mode 100644 generic/tkbltGrMarkerOption.h create mode 100644 generic/tkbltGrMarkerPolygon.C create mode 100644 generic/tkbltGrMarkerPolygon.h create mode 100644 generic/tkbltGrMarkerText.C create mode 100644 generic/tkbltGrMarkerText.h create mode 100644 generic/tkbltGrMisc.C create mode 100644 generic/tkbltGrMisc.h create mode 100644 generic/tkbltGrPSOutput.C create mode 100644 generic/tkbltGrPSOutput.h create mode 100644 generic/tkbltGrPen.C create mode 100644 generic/tkbltGrPen.h create mode 100644 generic/tkbltGrPenBar.C create mode 100644 generic/tkbltGrPenBar.h create mode 100644 generic/tkbltGrPenLine.C create mode 100644 generic/tkbltGrPenLine.h create mode 100644 generic/tkbltGrPenOp.C create mode 100644 generic/tkbltGrPenOp.h create mode 100644 generic/tkbltGrPenOption.C create mode 100644 generic/tkbltGrPostscript.C create mode 100644 generic/tkbltGrPostscript.h create mode 100644 generic/tkbltGrPostscriptOp.C create mode 100644 generic/tkbltGrPostscriptOp.h create mode 100644 generic/tkbltGrText.C create mode 100644 generic/tkbltGrText.h create mode 100644 generic/tkbltGrXAxisOp.C create mode 100644 generic/tkbltGrXAxisOp.h create mode 100644 generic/tkbltGraph.C create mode 100644 generic/tkbltGraph.h create mode 100644 generic/tkbltGraphBar.C create mode 100644 generic/tkbltGraphBar.h create mode 100644 generic/tkbltGraphLine.C create mode 100644 generic/tkbltGraphLine.h create mode 100644 generic/tkbltGraphOp.C create mode 100644 generic/tkbltGraphOp.h create mode 100644 generic/tkbltGraphSup.C create mode 100644 generic/tkbltInt.C create mode 100644 generic/tkbltInt.h create mode 100644 generic/tkbltNsUtil.C create mode 100644 generic/tkbltNsUtil.h create mode 100644 generic/tkbltOp.C create mode 100644 generic/tkbltOp.h create mode 100644 generic/tkbltParse.C create mode 100644 generic/tkbltParse.h create mode 100644 generic/tkbltStubInit.c create mode 100644 generic/tkbltStubLib.C create mode 100644 generic/tkbltSwitch.C create mode 100644 generic/tkbltSwitch.h create mode 100644 generic/tkbltVecCmd.C create mode 100644 generic/tkbltVecInt.h create mode 100644 generic/tkbltVecMath.C create mode 100644 generic/tkbltVecOp.C create mode 100644 generic/tkbltVector.C create mode 100644 generic/tkbltVector.h delete mode 100644 src/tkblt.decls delete mode 100644 src/tkbltChain.C delete mode 100644 src/tkbltChain.h delete mode 100644 src/tkbltConfig.C delete mode 100644 src/tkbltConfig.h delete mode 100644 src/tkbltDecls.h delete mode 100644 src/tkbltGrAxis.C delete mode 100644 src/tkbltGrAxis.h delete mode 100644 src/tkbltGrAxisOp.C delete mode 100644 src/tkbltGrAxisOp.h delete mode 100644 src/tkbltGrAxisOption.C delete mode 100644 src/tkbltGrAxisOption.h delete mode 100644 src/tkbltGrBind.C delete mode 100644 src/tkbltGrBind.h delete mode 100644 src/tkbltGrDef.h delete mode 100644 src/tkbltGrElem.C delete mode 100644 src/tkbltGrElem.h delete mode 100644 src/tkbltGrElemBar.C delete mode 100644 src/tkbltGrElemBar.h delete mode 100644 src/tkbltGrElemLine.C delete mode 100644 src/tkbltGrElemLine.h delete mode 100644 src/tkbltGrElemLineSpline.C delete mode 100644 src/tkbltGrElemOp.C delete mode 100644 src/tkbltGrElemOp.h delete mode 100644 src/tkbltGrElemOption.C delete mode 100644 src/tkbltGrElemOption.h delete mode 100644 src/tkbltGrHairs.C delete mode 100644 src/tkbltGrHairs.h delete mode 100644 src/tkbltGrHairsOp.C delete mode 100644 src/tkbltGrHairsOp.h delete mode 100644 src/tkbltGrLegd.C delete mode 100644 src/tkbltGrLegd.h delete mode 100644 src/tkbltGrLegdOp.C delete mode 100644 src/tkbltGrLegdOp.h delete mode 100644 src/tkbltGrMarker.C delete mode 100644 src/tkbltGrMarker.h delete mode 100644 src/tkbltGrMarkerLine.C delete mode 100644 src/tkbltGrMarkerLine.h delete mode 100644 src/tkbltGrMarkerOp.C delete mode 100644 src/tkbltGrMarkerOp.h delete mode 100644 src/tkbltGrMarkerOption.C delete mode 100644 src/tkbltGrMarkerOption.h delete mode 100644 src/tkbltGrMarkerPolygon.C delete mode 100644 src/tkbltGrMarkerPolygon.h delete mode 100644 src/tkbltGrMarkerText.C delete mode 100644 src/tkbltGrMarkerText.h delete mode 100644 src/tkbltGrMisc.C delete mode 100644 src/tkbltGrMisc.h delete mode 100644 src/tkbltGrPSOutput.C delete mode 100644 src/tkbltGrPSOutput.h delete mode 100644 src/tkbltGrPen.C delete mode 100644 src/tkbltGrPen.h delete mode 100644 src/tkbltGrPenBar.C delete mode 100644 src/tkbltGrPenBar.h delete mode 100644 src/tkbltGrPenLine.C delete mode 100644 src/tkbltGrPenLine.h delete mode 100644 src/tkbltGrPenOp.C delete mode 100644 src/tkbltGrPenOp.h delete mode 100644 src/tkbltGrPenOption.C delete mode 100644 src/tkbltGrPostscript.C delete mode 100644 src/tkbltGrPostscript.h delete mode 100644 src/tkbltGrPostscriptOp.C delete mode 100644 src/tkbltGrPostscriptOp.h delete mode 100644 src/tkbltGrText.C delete mode 100644 src/tkbltGrText.h delete mode 100644 src/tkbltGrXAxisOp.C delete mode 100644 src/tkbltGrXAxisOp.h delete mode 100644 src/tkbltGraph.C delete mode 100644 src/tkbltGraph.h delete mode 100644 src/tkbltGraphBar.C delete mode 100644 src/tkbltGraphBar.h delete mode 100644 src/tkbltGraphLine.C delete mode 100644 src/tkbltGraphLine.h delete mode 100644 src/tkbltGraphOp.C delete mode 100644 src/tkbltGraphOp.h delete mode 100644 src/tkbltGraphSup.C delete mode 100644 src/tkbltInt.C delete mode 100644 src/tkbltNsUtil.C delete mode 100644 src/tkbltNsUtil.h delete mode 100644 src/tkbltOp.C delete mode 100644 src/tkbltOp.h delete mode 100644 src/tkbltParse.C delete mode 100644 src/tkbltParse.h delete mode 100644 src/tkbltStubInit.c delete mode 100644 src/tkbltStubLib.C delete mode 100644 src/tkbltSwitch.C delete mode 100644 src/tkbltSwitch.h delete mode 100644 src/tkbltVecCmd.C delete mode 100644 src/tkbltVecInt.h delete mode 100644 src/tkbltVecMath.C delete mode 100644 src/tkbltVecOp.C delete mode 100644 src/tkbltVector.C delete mode 100644 src/tkbltVector.h diff --git a/configure b/configure index 1ac516e..052a60b 100755 --- a/configure +++ b/configure @@ -5644,55 +5644,55 @@ done vars=" -src/tkbltChain.C -src/tkbltConfig.C -src/tkbltGrAxis.C -src/tkbltGrAxisOp.C -src/tkbltGrAxisOption.C -src/tkbltGrBind.C -src/tkbltGrElemOp.C -src/tkbltGrElemOption.C -src/tkbltGrElem.C -src/tkbltGrElemBar.C -src/tkbltGrElemLine.C -src/tkbltGrElemLineSpline.C -src/tkbltGrHairs.C -src/tkbltGrHairsOp.C -src/tkbltGrLegd.C -src/tkbltGrLegdOp.C -src/tkbltGrMarkerOp.C -src/tkbltGrMarkerOption.C -src/tkbltGrMarker.C -src/tkbltGrMarkerLine.C -src/tkbltGrMarkerPolygon.C -src/tkbltGrMarkerText.C -src/tkbltGrMisc.C -src/tkbltGrPenOp.C -src/tkbltGrPenOption.C -src/tkbltGrPen.C -src/tkbltGrPenBar.C -src/tkbltGrPenLine.C -src/tkbltGrPostscript.C -src/tkbltGrPostscriptOp.C -src/tkbltGrPSOutput.C -src/tkbltGrText.C -src/tkbltGrXAxisOp.C -src/tkbltGraph.C -src/tkbltGraphBar.C -src/tkbltGraphLine.C -src/tkbltGraphOp.C -src/tkbltGraphSup.C -src/tkbltInt.C -src/tkbltNsUtil.C -src/tkbltParse.C -src/tkbltOp.C -src/tkbltStubInit.c -src/tkbltStubLib.C -src/tkbltSwitch.C -src/tkbltVecCmd.C -src/tkbltVecOp.C -src/tkbltVecMath.C -src/tkbltVector.C +tkbltChain.C +tkbltConfig.C +tkbltGrAxis.C +tkbltGrAxisOp.C +tkbltGrAxisOption.C +tkbltGrBind.C +tkbltGrElemOp.C +tkbltGrElemOption.C +tkbltGrElem.C +tkbltGrElemBar.C +tkbltGrElemLine.C +tkbltGrElemLineSpline.C +tkbltGrHairs.C +tkbltGrHairsOp.C +tkbltGrLegd.C +tkbltGrLegdOp.C +tkbltGrMarkerOp.C +tkbltGrMarkerOption.C +tkbltGrMarker.C +tkbltGrMarkerLine.C +tkbltGrMarkerPolygon.C +tkbltGrMarkerText.C +tkbltGrMisc.C +tkbltGrPenOp.C +tkbltGrPenOption.C +tkbltGrPen.C +tkbltGrPenBar.C +tkbltGrPenLine.C +tkbltGrPostscript.C +tkbltGrPostscriptOp.C +tkbltGrPSOutput.C +tkbltGrText.C +tkbltGrXAxisOp.C +tkbltGraph.C +tkbltGraphBar.C +tkbltGraphLine.C +tkbltGraphOp.C +tkbltGraphSup.C +tkbltInt.C +tkbltNsUtil.C +tkbltParse.C +tkbltOp.C +tkbltStubInit.c +tkbltStubLib.C +tkbltSwitch.C +tkbltVecCmd.C +tkbltVecOp.C +tkbltVecMath.C +tkbltVector.C " for i in $vars; do case $i in @@ -5729,8 +5729,8 @@ src/tkbltVector.C vars=" -src/tkbltVector.h -src/tkbltDecls.h +generic/tkbltVector.h +generic/tkbltDecls.h " for i in $vars; do # check for existence, be strict because it is installed @@ -5742,7 +5742,7 @@ src/tkbltDecls.h - vars="-I./src" + vars="" for i in $vars; do PKG_INCLUDES="$PKG_INCLUDES $i" done @@ -5763,12 +5763,11 @@ src/tkbltDecls.h done +#if test "windows" = "${TEA_PLATFORM}"; then +#TEA_ADD_CFLAGS([-TP -EHsc -D_CRT_SECURE_NO_WARNINGS -D_USE_MATH_DEFINES]) +#fi - PKG_CFLAGS="$PKG_CFLAGS " - - - - vars="src/tkbltStubLib.C" + vars="tkbltStubLib.C" for i in $vars; do # check for existence - allows for generic/win/unix VPATH if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \ @@ -5815,8 +5814,7 @@ src/tkbltDecls.h # TEA_ADD_* any platform specific compiler/build info here. #-------------------------------------------------------------------- -#CLEANFILES="$CLEANFILES pkgIndex.tcl" -CLEANFILES="$CLEANFILES tkbltConfig.sh" +CLEANFILES="$CLEANFILES" if test "${TEA_PLATFORM}" = "windows" ; then # Ensure no empty if clauses : diff --git a/configure.ac b/configure.ac index da04b23..7e2d152 100644 --- a/configure.ac +++ b/configure.ac @@ -72,64 +72,66 @@ TEA_SETUP_COMPILER #----------------------------------------------------------------------- TEA_ADD_SOURCES([ -src/tkbltChain.C -src/tkbltConfig.C -src/tkbltGrAxis.C -src/tkbltGrAxisOp.C -src/tkbltGrAxisOption.C -src/tkbltGrBind.C -src/tkbltGrElemOp.C -src/tkbltGrElemOption.C -src/tkbltGrElem.C -src/tkbltGrElemBar.C -src/tkbltGrElemLine.C -src/tkbltGrElemLineSpline.C -src/tkbltGrHairs.C -src/tkbltGrHairsOp.C -src/tkbltGrLegd.C -src/tkbltGrLegdOp.C -src/tkbltGrMarkerOp.C -src/tkbltGrMarkerOption.C -src/tkbltGrMarker.C -src/tkbltGrMarkerLine.C -src/tkbltGrMarkerPolygon.C -src/tkbltGrMarkerText.C -src/tkbltGrMisc.C -src/tkbltGrPenOp.C -src/tkbltGrPenOption.C -src/tkbltGrPen.C -src/tkbltGrPenBar.C -src/tkbltGrPenLine.C -src/tkbltGrPostscript.C -src/tkbltGrPostscriptOp.C -src/tkbltGrPSOutput.C -src/tkbltGrText.C -src/tkbltGrXAxisOp.C -src/tkbltGraph.C -src/tkbltGraphBar.C -src/tkbltGraphLine.C -src/tkbltGraphOp.C -src/tkbltGraphSup.C -src/tkbltInt.C -src/tkbltNsUtil.C -src/tkbltParse.C -src/tkbltOp.C -src/tkbltStubInit.c -src/tkbltStubLib.C -src/tkbltSwitch.C -src/tkbltVecCmd.C -src/tkbltVecOp.C -src/tkbltVecMath.C -src/tkbltVector.C +tkbltChain.C +tkbltConfig.C +tkbltGrAxis.C +tkbltGrAxisOp.C +tkbltGrAxisOption.C +tkbltGrBind.C +tkbltGrElemOp.C +tkbltGrElemOption.C +tkbltGrElem.C +tkbltGrElemBar.C +tkbltGrElemLine.C +tkbltGrElemLineSpline.C +tkbltGrHairs.C +tkbltGrHairsOp.C +tkbltGrLegd.C +tkbltGrLegdOp.C +tkbltGrMarkerOp.C +tkbltGrMarkerOption.C +tkbltGrMarker.C +tkbltGrMarkerLine.C +tkbltGrMarkerPolygon.C +tkbltGrMarkerText.C +tkbltGrMisc.C +tkbltGrPenOp.C +tkbltGrPenOption.C +tkbltGrPen.C +tkbltGrPenBar.C +tkbltGrPenLine.C +tkbltGrPostscript.C +tkbltGrPostscriptOp.C +tkbltGrPSOutput.C +tkbltGrText.C +tkbltGrXAxisOp.C +tkbltGraph.C +tkbltGraphBar.C +tkbltGraphLine.C +tkbltGraphOp.C +tkbltGraphSup.C +tkbltInt.C +tkbltNsUtil.C +tkbltParse.C +tkbltOp.C +tkbltStubInit.c +tkbltStubLib.C +tkbltSwitch.C +tkbltVecCmd.C +tkbltVecOp.C +tkbltVecMath.C +tkbltVector.C ]) TEA_ADD_HEADERS([ -src/tkbltVector.h -src/tkbltDecls.h +generic/tkbltVector.h +generic/tkbltDecls.h ]) -TEA_ADD_INCLUDES([-I./src]) +TEA_ADD_INCLUDES([]) TEA_ADD_LIBS([-lstdc++]) -TEA_ADD_CFLAGS([]) -TEA_ADD_STUB_SOURCES([src/tkbltStubLib.C]) +#if test "windows" = "${TEA_PLATFORM}"; then +#TEA_ADD_CFLAGS([-TP -EHsc -D_CRT_SECURE_NO_WARNINGS -D_USE_MATH_DEFINES]) +#fi +TEA_ADD_STUB_SOURCES([tkbltStubLib.C]) TEA_ADD_TCL_SOURCES([library/graph.tcl]) #-------------------------------------------------------------------- @@ -144,8 +146,7 @@ TEA_ADD_TCL_SOURCES([library/graph.tcl]) # TEA_ADD_* any platform specific compiler/build info here. #-------------------------------------------------------------------- -#CLEANFILES="$CLEANFILES pkgIndex.tcl" -CLEANFILES="$CLEANFILES tkbltConfig.sh" +CLEANFILES="$CLEANFILES" if test "${TEA_PLATFORM}" = "windows" ; then # Ensure no empty if clauses : diff --git a/doc/graph.html b/doc/graph.html index 412c817..70f1e6f 100644 --- a/doc/graph.html +++ b/doc/graph.html @@ -438,23 +438,6 @@ Sets the color of the axis and tick labels. The default is black. - -command prefix - Specifies a Tcl command to be invoked when formatting the - axis tick labels. Prefix is a string containing the name - of a Tcl proc and any extra arguments for the procedure. - This command is invoked for each major tick on the axis. - Two additional arguments are passed to the procedure: the - pathname of the widget and the current the numeric value - of the tick. The procedure returns the formatted tick - label. If "" is returned, no label will appear next to - the tick. You can get the standard tick labels again by - setting prefix to "". The default is "". - - Please note that this procedure is invoked while the - graph is redrawn. You may query configuration options. - But do not them, because this can have unexpected - results. - -descending boolean Indicates whether the values along the axis are monotoni- cally increasing or decreasing. If boolean is true, the @@ -542,6 +525,31 @@ Specifies the font for axis tick labels. The default is *-Courier-Bold-R-Normal-*-100-*. + -tickformat formatStr + Specifies a printf-like description to format teh axis + tick labels. You can get the standard tick labels again by + setting formatStr to "". The default is "". + + -tickformatcommand, -command prefix + Specifies a Tcl command to be invoked when formatting the + axis tick labels. Prefix is a string containing the name + of a Tcl proc and any extra arguments for the procedure. + This command is invoked for each major tick on the axis. + Two additional arguments are passed to the procedure: the + pathname of the widget and the current the numeric value + of the tick. The procedure returns the formatted tick + label. If "" is returned, no label will appear next to + the tick. You can get the standard tick labels again by + setting prefix to "". The default is "". + + The numeric value for the tick might change when using the + -logscale and -tickformat options. + + Please note that this procedure is invoked while the + graph is redrawn. You may query configuration options. + But do not them, because this can have unexpected + results. + -ticklength pixels Sets the length of major and minor ticks (minor ticks are half the length of major ticks). If pixels is less than diff --git a/doc/graph.n b/doc/graph.n index 72df726..fbbbb9b 100644 --- a/doc/graph.n +++ b/doc/graph.n @@ -686,21 +686,6 @@ the element is always the first tag in the list. The default value is Sets the color of the axis and tick labels. The default is \f(CWblack\fR. .TP -\fB\-command \fIprefix\fR -Specifies a Tcl command to be invoked when formatting the axis tick -labels. \fIPrefix\fR is a string containing the name of a Tcl proc and -any extra arguments for the procedure. This command is invoked for each -major tick on the axis. Two additional arguments are passed to the -procedure: the pathname of the widget and the current the numeric -value of the tick. The procedure returns the formatted tick label. If -\f(CW""\fR is returned, no label will appear next to the tick. You can -get the standard tick labels again by setting \fIprefix\fR to -\f(CW""\fR. The default is \f(CW""\fR. -.sp 1 -Please note that this procedure is invoked while the graph is redrawn. -You may query configuration options. But do not them, because this -can have unexpected results. -.TP \fB\-descending \fIboolean\fR Indicates whether the values along the axis are monotonically increasing or decreasing. If \fIboolean\fR is true, the axis values will be @@ -810,6 +795,29 @@ displayed. The default is \f(CW2\fR. Specifies the font for axis tick labels. The default is \f(CW*-Courier-Bold-R-Normal-*-100-*\fR. .TP +\fB\-tickformat\fR \fIformatStr\fR +Specifies a printf-like description to format teh axis +tick labels. You can get the standard tick labels again by +setting \fIformatStr\fR to \f(CW""\fR. The default is \f(CW""\fR. +.TP +\fB\-tickformatcommand\fR, \fB\-command \fIprefix\fR +Specifies a Tcl command to be invoked when formatting the axis tick +labels. \fIPrefix\fR is a string containing the name of a Tcl proc and +any extra arguments for the procedure. This command is invoked for each +major tick on the axis. Two additional arguments are passed to the +procedure: the pathname of the widget and the current the numeric +value of the tick. The procedure returns the formatted tick label. If +\f(CW""\fR is returned, no label will appear next to the tick. You can +get the standard tick labels again by setting \fIprefix\fR to +\f(CW""\fR. The default is \f(CW""\fR. +.sp 1 +The numeric value for the tick might change when using the +\fB\-logscale\fR and \fB\-tickformat\fR options. +.sp 1 +Please note that this procedure is invoked while the graph is redrawn. +You may query configuration options. But do not them, because this +can have unexpected results. +.TP \fB\-ticklength \fIpixels\fR Sets the length of major and minor ticks (minor ticks are half the length of major ticks). If \fIpixels\fR is less than zero, the axis diff --git a/generic/tkblt.decls b/generic/tkblt.decls new file mode 100644 index 0000000..b4b5c67 --- /dev/null +++ b/generic/tkblt.decls @@ -0,0 +1,92 @@ +library tkblt +interface tkblt + +declare 0 generic { + int Blt_CreateVector(Tcl_Interp* interp, const char *vecName, + int size, Blt_Vector** vecPtrPtr) +} + +declare 1 generic { + int Blt_CreateVector2(Tcl_Interp* interp, const char *vecName, + const char *cmdName, const char *varName, + int initialSize, Blt_Vector **vecPtrPtr) +} + +declare 2 generic { + int Blt_DeleteVectorByName(Tcl_Interp* interp, const char *vecName) +} + +declare 3 generic { + int Blt_DeleteVector(Blt_Vector *vecPtr) +} + +declare 4 generic { + int Blt_GetVector(Tcl_Interp* interp, const char *vecName, + Blt_Vector **vecPtrPtr) +} + +declare 5 generic { + int Blt_GetVectorFromObj(Tcl_Interp* interp, Tcl_Obj *objPtr, + Blt_Vector **vecPtrPtr) +} + +declare 6 generic { + int Blt_ResetVector(Blt_Vector *vecPtr, double *dataArr, int n, + int arraySize, Tcl_FreeProc *freeProc) +} + +declare 7 generic { + int Blt_ResizeVector(Blt_Vector *vecPtr, int n) +} + +declare 8 generic { + int Blt_VectorExists(Tcl_Interp* interp, const char *vecName) +} + +declare 9 generic { + int Blt_VectorExists2(Tcl_Interp* interp, const char *vecName) +} + +declare 10 generic { + Blt_VectorId Blt_AllocVectorId(Tcl_Interp* interp, const char *vecName) +} + +declare 11 generic { + int Blt_GetVectorById(Tcl_Interp* interp, Blt_VectorId clientId, + Blt_Vector **vecPtrPtr) +} + +declare 12 generic { + void Blt_SetVectorChangedProc(Blt_VectorId clientId, + Blt_VectorChangedProc *proc, + ClientData clientData) +} + +declare 13 generic { + void Blt_FreeVectorId(Blt_VectorId clientId) +} + +declare 14 generic { + const char *Blt_NameOfVectorId(Blt_VectorId clientId) +} + +declare 15 generic { + const char *Blt_NameOfVector(Blt_Vector *vecPtr) +} + +declare 16 generic { + int Blt_ExprVector(Tcl_Interp* interp, char *expr, Blt_Vector *vecPtr) +} + +declare 17 generic { + void Blt_InstallIndexProc(Tcl_Interp* interp, const char *indexName, + Blt_VectorIndexProc * procPtr) +} + +declare 18 generic { + double Blt_VecMin(Blt_Vector *vPtr) +} + +declare 19 generic { + double Blt_VecMax(Blt_Vector *vPtr) +} diff --git a/generic/tkbltChain.C b/generic/tkbltChain.C new file mode 100644 index 0000000..dbd317c --- /dev/null +++ b/generic/tkbltChain.C @@ -0,0 +1,194 @@ +/* + * 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 + +#include "tkbltChain.h" + +using namespace Blt; + +// ChainLink + +ChainLink::ChainLink(void* clientData) +{ + prev_ =NULL; + next_ =NULL; + manage_ =0; + clientData_ = clientData; +} + +ChainLink::ChainLink(size_t ss) +{ + prev_ =NULL; + next_ =NULL; + manage_ =1; + clientData_ = (void*)calloc(1,ss); +} + +ChainLink::~ChainLink() +{ + if (manage_ && clientData_) + free(clientData_); +} + +// Chain + +Chain::Chain() +{ + head_ =NULL; + tail_ =NULL; + nLinks_ =0; +} + +Chain::~Chain() +{ + ChainLink* linkPtr = head_; + while (linkPtr) { + ChainLink* oldPtr =linkPtr; + linkPtr = linkPtr->next_; + delete oldPtr; + } +} + +void Chain::reset() +{ + ChainLink* linkPtr = head_; + while (linkPtr) { + ChainLink* oldPtr = linkPtr; + linkPtr = linkPtr->next_; + delete oldPtr; + } + head_ =NULL; + tail_ =NULL; + nLinks_ =0; +} + +void Chain::linkAfter(ChainLink* linkPtr, ChainLink* afterPtr) +{ + if (!head_) { + head_ = linkPtr; + tail_ = linkPtr; + } + else { + if (!afterPtr) { + linkPtr->next_ = NULL; + linkPtr->prev_ = tail_; + tail_->next_ = linkPtr; + tail_ = linkPtr; + } + else { + linkPtr->next_ = afterPtr->next_; + linkPtr->prev_ = afterPtr; + if (afterPtr == tail_) + tail_ = linkPtr; + else + afterPtr->next_->prev_ = linkPtr; + afterPtr->next_ = linkPtr; + } + } + + nLinks_++; +} + +void Chain::linkBefore(ChainLink* linkPtr, ChainLink* beforePtr) +{ + if (!head_) { + head_ = linkPtr; + tail_ = linkPtr; + } + else { + if (beforePtr == NULL) { + linkPtr->next_ = head_; + linkPtr->prev_ = NULL; + head_->prev_ = linkPtr; + head_ = linkPtr; + } + else { + linkPtr->prev_ = beforePtr->prev_; + linkPtr->next_ = beforePtr; + if (beforePtr == head_) + head_ = linkPtr; + else + beforePtr->prev_->next_ = linkPtr; + beforePtr->prev_ = linkPtr; + } + } + + nLinks_++; +} + +void Chain::unlinkLink(ChainLink* linkPtr) +{ + // Indicates if the link is actually remove from the chain + int unlinked; + + unlinked = 0; + if (head_ == linkPtr) { + head_ = linkPtr->next_; + unlinked = 1; + } + if (tail_ == linkPtr) { + tail_ = linkPtr->prev_; + unlinked = 1; + } + if (linkPtr->next_) { + linkPtr->next_->prev_ = linkPtr->prev_; + unlinked = 1; + } + if (linkPtr->prev_) { + linkPtr->prev_->next_ = linkPtr->next_; + unlinked = 1; + } + if (unlinked) + nLinks_--; + + linkPtr->prev_ =NULL; + linkPtr->next_ =NULL; +} + +void Chain::deleteLink(ChainLink* link) +{ + unlinkLink(link); + delete link; + link = NULL; +} + +ChainLink* Chain::append(void* clientData) +{ + ChainLink* link = new ChainLink(clientData); + linkAfter(link, NULL); + return link; +} + +ChainLink* Chain::prepend(void* clientData) +{ + ChainLink* link = new ChainLink(clientData); + linkBefore(link, NULL); + return link; +} diff --git a/generic/tkbltChain.h b/generic/tkbltChain.h new file mode 100644 index 0000000..6e254f9 --- /dev/null +++ b/generic/tkbltChain.h @@ -0,0 +1,91 @@ +/* + * 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 1993-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. + */ +#ifndef _BLT_CHAIN_H +#define _BLT_CHAIN_H + +#define Chain_GetLength(c) (((c) == NULL) ? 0 : (c)->nLinks()) +#define Chain_FirstLink(c) (((c) == NULL) ? NULL : (c)->head()) +#define Chain_LastLink(c) (((c) == NULL) ? NULL : (c)->tail()) + +#define Chain_PrevLink(l) ((l)->prev()) +#define Chain_NextLink(l) ((l)->next()) +#define Chain_GetValue(l) ((l)->clientData()) + +namespace Blt { + + class Chain; + + class ChainLink { + friend class Chain; + + protected: + ChainLink* prev_; + ChainLink* next_; + int manage_; + void* clientData_; + + public: + ChainLink(void*); + ChainLink(size_t); + virtual ~ChainLink(); + + ChainLink* prev() {return prev_;} + ChainLink* next() {return next_;} + void* clientData() {return clientData_;} + void setClientData(void* d) {clientData_ =d;} + }; + + class Chain { + protected: + ChainLink* head_; + ChainLink* tail_; + long nLinks_; + + public: + Chain(); + virtual ~Chain(); + + ChainLink* head() {return head_;} + ChainLink* tail() {return tail_;} + long nLinks() {return nLinks_;} + + void reset(); + void linkAfter(ChainLink* link, ChainLink* after); + void linkBefore(ChainLink* link, ChainLink* before); + void unlinkLink(ChainLink* linkPtr); + void deleteLink(ChainLink* link); + ChainLink* append(void* clientData); + ChainLink* prepend(void* clientData); + }; +}; + +#endif diff --git a/generic/tkbltConfig.C b/generic/tkbltConfig.C new file mode 100644 index 0000000..82fea4e --- /dev/null +++ b/generic/tkbltConfig.C @@ -0,0 +1,218 @@ +/* + * 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 (c) 1990-1994 The Regents of the University of California. + * Copyright (c) 1994-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Copyright 2003-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 +#include + +#include "tkbltConfig.h" +#include "tkbltGrMisc.h" + +using namespace Blt; + +void RestoreProc(ClientData clientData, Tk_Window tkwin, + char *ptr, char *savePtr) +{ + *(double*)ptr = *(double*)savePtr; +} + +// Fill +const char* fillObjOption[] = {"none", "x", "y", "both", NULL}; + +// Dashes +static Tk_CustomOptionSetProc DashesSetProc; +static Tk_CustomOptionGetProc DashesGetProc; +Tk_ObjCustomOption dashesObjOption = + { + "dashes", DashesSetProc, DashesGetProc, NULL, NULL, NULL + }; + +static int DashesSetProc(ClientData clientData, Tcl_Interp *interp, + Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, + int offset, char* save, int flags) +{ + Dashes* dashesPtr = (Dashes*)(widgRec + offset); + + int length; + const char* string = Tcl_GetStringFromObj(*objPtr, &length); + if (!string || !string[0]) { + dashesPtr->values[0] = 0; + return TCL_OK; + } + + if (!strncmp(string, "dot", length)) { + dashesPtr->values[0] = 1; + dashesPtr->values[1] = 0; + } + else if (!strncmp(string, "dash", length)) { + dashesPtr->values[0] = 5; + dashesPtr->values[1] = 2; + dashesPtr->values[2] = 0; + } + else if (!strncmp(string, "dashdot", length)) { + dashesPtr->values[0] = 2; + dashesPtr->values[1] = 4; + dashesPtr->values[2] = 2; + dashesPtr->values[3] = 0; + } + else if (!strncmp(string, "dashdotdot", length)) { + dashesPtr->values[0] = 2; + dashesPtr->values[1] = 4; + dashesPtr->values[2] = 2; + dashesPtr->values[3] = 2; + dashesPtr->values[4] = 0; + } + else { + int objc; + Tcl_Obj** objv; + if (Tcl_ListObjGetElements(interp, *objPtr, &objc, &objv) != TCL_OK) + return TCL_ERROR; + + // This is the postscript limit + if (objc > 11) { + Tcl_AppendResult(interp, "too many values in dash list \"", + string, "\"", (char *)NULL); + return TCL_ERROR; + } + + int ii; + for (ii=0; ii 255)) { + Tcl_AppendResult(interp, "dash value \"", + Tcl_GetString(objv[ii]), "\" is out of range", + (char *)NULL); + return TCL_ERROR; + } + dashesPtr->values[ii] = (unsigned char)value; + } + + // Make sure the array ends with a NULL byte + dashesPtr->values[ii] = 0; + } + + return TCL_OK; +}; + +static Tcl_Obj* DashesGetProc(ClientData clientData, Tk_Window tkwin, + char *widgRec, int offset) +{ + Dashes* dashesPtr = (Dashes*)(widgRec + offset); + + // count how many + int cnt =0; + while (dashesPtr->values[cnt]) + cnt++; + + if (!cnt) + return Tcl_NewListObj(0, (Tcl_Obj**)NULL); + + Tcl_Obj** ll = new Tcl_Obj*[cnt]; + for (int ii=0; iivalues[ii]); + Tcl_Obj* listObjPtr = Tcl_NewListObj(cnt, ll); + delete [] ll; + + return listObjPtr; +}; + +// List +static Tk_CustomOptionSetProc ListSetProc; +static Tk_CustomOptionGetProc ListGetProc; +static Tk_CustomOptionFreeProc ListFreeProc; +Tk_ObjCustomOption listObjOption = + { + "list", ListSetProc, ListGetProc, RestoreProc, ListFreeProc, NULL + }; + +static int ListSetProc(ClientData clientData, Tcl_Interp *interp, + Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, + int offset, char* savePtr, int flags) +{ + const char*** listPtr = (const char***)(widgRec + offset); + *(double*)savePtr = *(double*)listPtr; + + if (!listPtr) + return TCL_OK; + + const char** argv; + int argc; + if (Tcl_SplitList(interp, Tcl_GetString(*objPtr), &argc, &argv) != TCL_OK) + return TCL_ERROR; + + *listPtr = argv; + + return TCL_OK; +}; + +static Tcl_Obj* ListGetProc(ClientData clientData, Tk_Window tkwin, + char *widgRec, int offset) +{ + const char*** listPtr = (const char***)(widgRec + offset); + + if (!listPtr || !(*listPtr)) + return Tcl_NewListObj(0, NULL); + + // count how many + int cnt=0; + for (const char** pp=*listPtr; *pp; pp++,cnt++) {} + if (!cnt) + return Tcl_NewListObj(0, NULL); + + Tcl_Obj** ll = new Tcl_Obj*[cnt]; + for (int ii=0; ii + +extern const char* fillObjOption[]; +extern Tk_ObjCustomOption dashesObjOption; +extern Tk_ObjCustomOption listObjOption; +extern Tk_CustomOptionRestoreProc RestoreProc; + +#endif diff --git a/generic/tkbltDecls.h b/generic/tkbltDecls.h new file mode 100644 index 0000000..4d7c679 --- /dev/null +++ b/generic/tkbltDecls.h @@ -0,0 +1,152 @@ +/* !BEGIN!: Do not edit below this line. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Exported function declarations: + */ + +/* 0 */ +TKBLT_STORAGE_CLASS int Blt_CreateVector(Tcl_Interp*interp, + const char *vecName, int size, + Blt_Vector**vecPtrPtr); +/* 1 */ +TKBLT_STORAGE_CLASS int Blt_CreateVector2(Tcl_Interp*interp, + const char *vecName, const char *cmdName, + const char *varName, int initialSize, + Blt_Vector **vecPtrPtr); +/* 2 */ +TKBLT_STORAGE_CLASS int Blt_DeleteVectorByName(Tcl_Interp*interp, + const char *vecName); +/* 3 */ +TKBLT_STORAGE_CLASS int Blt_DeleteVector(Blt_Vector *vecPtr); +/* 4 */ +TKBLT_STORAGE_CLASS int Blt_GetVector(Tcl_Interp*interp, const char *vecName, + Blt_Vector **vecPtrPtr); +/* 5 */ +TKBLT_STORAGE_CLASS int Blt_GetVectorFromObj(Tcl_Interp*interp, + Tcl_Obj *objPtr, Blt_Vector **vecPtrPtr); +/* 6 */ +TKBLT_STORAGE_CLASS int Blt_ResetVector(Blt_Vector *vecPtr, double *dataArr, + int n, int arraySize, Tcl_FreeProc *freeProc); +/* 7 */ +TKBLT_STORAGE_CLASS int Blt_ResizeVector(Blt_Vector *vecPtr, int n); +/* 8 */ +TKBLT_STORAGE_CLASS int Blt_VectorExists(Tcl_Interp*interp, + const char *vecName); +/* 9 */ +TKBLT_STORAGE_CLASS int Blt_VectorExists2(Tcl_Interp*interp, + const char *vecName); +/* 10 */ +TKBLT_STORAGE_CLASS Blt_VectorId Blt_AllocVectorId(Tcl_Interp*interp, + const char *vecName); +/* 11 */ +TKBLT_STORAGE_CLASS int Blt_GetVectorById(Tcl_Interp*interp, + Blt_VectorId clientId, + Blt_Vector **vecPtrPtr); +/* 12 */ +TKBLT_STORAGE_CLASS void Blt_SetVectorChangedProc(Blt_VectorId clientId, + Blt_VectorChangedProc *proc, + ClientData clientData); +/* 13 */ +TKBLT_STORAGE_CLASS void Blt_FreeVectorId(Blt_VectorId clientId); +/* 14 */ +TKBLT_STORAGE_CLASS const char * Blt_NameOfVectorId(Blt_VectorId clientId); +/* 15 */ +TKBLT_STORAGE_CLASS const char * Blt_NameOfVector(Blt_Vector *vecPtr); +/* 16 */ +TKBLT_STORAGE_CLASS int Blt_ExprVector(Tcl_Interp*interp, char *expr, + Blt_Vector *vecPtr); +/* 17 */ +TKBLT_STORAGE_CLASS void Blt_InstallIndexProc(Tcl_Interp*interp, + const char *indexName, + Blt_VectorIndexProc *procPtr); +/* 18 */ +TKBLT_STORAGE_CLASS double Blt_VecMin(Blt_Vector *vPtr); +/* 19 */ +TKBLT_STORAGE_CLASS double Blt_VecMax(Blt_Vector *vPtr); + +typedef struct TkbltStubs { + int magic; + void *hooks; + + int (*blt_CreateVector) (Tcl_Interp*interp, const char *vecName, int size, Blt_Vector**vecPtrPtr); /* 0 */ + int (*blt_CreateVector2) (Tcl_Interp*interp, const char *vecName, const char *cmdName, const char *varName, int initialSize, Blt_Vector **vecPtrPtr); /* 1 */ + int (*blt_DeleteVectorByName) (Tcl_Interp*interp, const char *vecName); /* 2 */ + int (*blt_DeleteVector) (Blt_Vector *vecPtr); /* 3 */ + int (*blt_GetVector) (Tcl_Interp*interp, const char *vecName, Blt_Vector **vecPtrPtr); /* 4 */ + int (*blt_GetVectorFromObj) (Tcl_Interp*interp, Tcl_Obj *objPtr, Blt_Vector **vecPtrPtr); /* 5 */ + int (*blt_ResetVector) (Blt_Vector *vecPtr, double *dataArr, int n, int arraySize, Tcl_FreeProc *freeProc); /* 6 */ + int (*blt_ResizeVector) (Blt_Vector *vecPtr, int n); /* 7 */ + int (*blt_VectorExists) (Tcl_Interp*interp, const char *vecName); /* 8 */ + int (*blt_VectorExists2) (Tcl_Interp*interp, const char *vecName); /* 9 */ + Blt_VectorId (*blt_AllocVectorId) (Tcl_Interp*interp, const char *vecName); /* 10 */ + int (*blt_GetVectorById) (Tcl_Interp*interp, Blt_VectorId clientId, Blt_Vector **vecPtrPtr); /* 11 */ + void (*blt_SetVectorChangedProc) (Blt_VectorId clientId, Blt_VectorChangedProc *proc, ClientData clientData); /* 12 */ + void (*blt_FreeVectorId) (Blt_VectorId clientId); /* 13 */ + const char * (*blt_NameOfVectorId) (Blt_VectorId clientId); /* 14 */ + const char * (*blt_NameOfVector) (Blt_Vector *vecPtr); /* 15 */ + int (*blt_ExprVector) (Tcl_Interp*interp, char *expr, Blt_Vector *vecPtr); /* 16 */ + void (*blt_InstallIndexProc) (Tcl_Interp*interp, const char *indexName, Blt_VectorIndexProc *procPtr); /* 17 */ + double (*blt_VecMin) (Blt_Vector *vPtr); /* 18 */ + double (*blt_VecMax) (Blt_Vector *vPtr); /* 19 */ +} TkbltStubs; + +extern const TkbltStubs *tkbltStubsPtr; + +#ifdef __cplusplus +} +#endif + +#if defined(USE_TKBLT_STUBS) + +/* + * Inline function declarations: + */ + +#define Blt_CreateVector \ + (tkbltStubsPtr->blt_CreateVector) /* 0 */ +#define Blt_CreateVector2 \ + (tkbltStubsPtr->blt_CreateVector2) /* 1 */ +#define Blt_DeleteVectorByName \ + (tkbltStubsPtr->blt_DeleteVectorByName) /* 2 */ +#define Blt_DeleteVector \ + (tkbltStubsPtr->blt_DeleteVector) /* 3 */ +#define Blt_GetVector \ + (tkbltStubsPtr->blt_GetVector) /* 4 */ +#define Blt_GetVectorFromObj \ + (tkbltStubsPtr->blt_GetVectorFromObj) /* 5 */ +#define Blt_ResetVector \ + (tkbltStubsPtr->blt_ResetVector) /* 6 */ +#define Blt_ResizeVector \ + (tkbltStubsPtr->blt_ResizeVector) /* 7 */ +#define Blt_VectorExists \ + (tkbltStubsPtr->blt_VectorExists) /* 8 */ +#define Blt_VectorExists2 \ + (tkbltStubsPtr->blt_VectorExists2) /* 9 */ +#define Blt_AllocVectorId \ + (tkbltStubsPtr->blt_AllocVectorId) /* 10 */ +#define Blt_GetVectorById \ + (tkbltStubsPtr->blt_GetVectorById) /* 11 */ +#define Blt_SetVectorChangedProc \ + (tkbltStubsPtr->blt_SetVectorChangedProc) /* 12 */ +#define Blt_FreeVectorId \ + (tkbltStubsPtr->blt_FreeVectorId) /* 13 */ +#define Blt_NameOfVectorId \ + (tkbltStubsPtr->blt_NameOfVectorId) /* 14 */ +#define Blt_NameOfVector \ + (tkbltStubsPtr->blt_NameOfVector) /* 15 */ +#define Blt_ExprVector \ + (tkbltStubsPtr->blt_ExprVector) /* 16 */ +#define Blt_InstallIndexProc \ + (tkbltStubsPtr->blt_InstallIndexProc) /* 17 */ +#define Blt_VecMin \ + (tkbltStubsPtr->blt_VecMin) /* 18 */ +#define Blt_VecMax \ + (tkbltStubsPtr->blt_VecMax) /* 19 */ + +#endif /* defined(USE_TKBLT_STUBS) */ + +/* !END!: Do not edit above this line. */ diff --git a/generic/tkbltGrAxis.C b/generic/tkbltGrAxis.C new file mode 100644 index 0000000..bac6316 --- /dev/null +++ b/generic/tkbltGrAxis.C @@ -0,0 +1,1962 @@ +/* + * 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 1993-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 +#include +#include + +#include + +#include "tkbltGraph.h" +#include "tkbltGrBind.h" +#include "tkbltGrAxis.h" +#include "tkbltGrAxisOption.h" +#include "tkbltGrPostscript.h" +#include "tkbltGrMisc.h" +#include "tkbltGrDef.h" +#include "tkbltConfig.h" +#include "tkbltGrPSOutput.h" +#include "tkbltInt.h" + +using namespace Blt; + +#define AXIS_PAD_TITLE 2 +#define EXP10(x) (pow(10.0,(x))) + +AxisName Blt::axisNames[] = { + { "x", CID_AXIS_X }, + { "y", CID_AXIS_Y }, + { "x2", CID_AXIS_X }, + { "y2", CID_AXIS_Y } +} ; + +// Defs + +extern double AdjustViewport(double offset, double windowSize); + +static Tk_OptionSpec optionSpecs[] = { + {TK_OPTION_COLOR, "-activeforeground", "activeForeground", "ActiveForeground", + STD_ACTIVE_FOREGROUND, -1, Tk_Offset(AxisOptions, activeFgColor), + 0, NULL, CACHE}, + {TK_OPTION_RELIEF, "-activerelief", "activeRelief", "Relief", + "flat", -1, Tk_Offset(AxisOptions, activeRelief), 0, NULL, CACHE}, + {TK_OPTION_DOUBLE, "-autorange", "autoRange", "AutoRange", + "0", -1, Tk_Offset(AxisOptions, windowSize), 0, NULL, RESET}, + {TK_OPTION_BORDER, "-background", "background", "Background", + NULL, -1, Tk_Offset(AxisOptions, normalBg), TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_SYNONYM, "-bg", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-background", 0}, + {TK_OPTION_CUSTOM, "-bindtags", "bindTags", "BindTags", + "all", -1, Tk_Offset(AxisOptions, tags), + TK_OPTION_NULL_OK, &listObjOption, 0}, + {TK_OPTION_SYNONYM, "-bd", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-borderwidth", 0}, + {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", + STD_BORDERWIDTH, -1, Tk_Offset(AxisOptions, borderWidth), 0, NULL, LAYOUT}, + {TK_OPTION_BOOLEAN, "-checklimits", "checkLimits", "CheckLimits", + "no", -1, Tk_Offset(AxisOptions, checkLimits), 0, NULL, RESET}, + {TK_OPTION_COLOR, "-color", "color", "Color", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(AxisOptions, tickColor), + 0, NULL, CACHE}, + {TK_OPTION_SYNONYM, "-command", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-tickformatcommand", 0}, + {TK_OPTION_BOOLEAN, "-descending", "descending", "Descending", + "no", -1, Tk_Offset(AxisOptions, descending), 0, NULL, RESET}, + {TK_OPTION_BOOLEAN, "-exterior", "exterior", "exterior", + "yes", -1, Tk_Offset(AxisOptions, exterior), 0, NULL, LAYOUT}, + {TK_OPTION_SYNONYM, "-fg", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-color", 0}, + {TK_OPTION_SYNONYM, "-foreground", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-color", 0}, + {TK_OPTION_BOOLEAN, "-grid", "grid", "Grid", + "yes", -1, Tk_Offset(AxisOptions, showGrid), 0, NULL, CACHE}, + {TK_OPTION_COLOR, "-gridcolor", "gridColor", "GridColor", + "gray64", -1, Tk_Offset(AxisOptions, major.color), 0, NULL, CACHE}, + {TK_OPTION_CUSTOM, "-griddashes", "gridDashes", "GridDashes", + "dot", -1, Tk_Offset(AxisOptions, major.dashes), + TK_OPTION_NULL_OK, &dashesObjOption, CACHE}, + {TK_OPTION_PIXELS, "-gridlinewidth", "gridLineWidth", "GridLineWidth", + "1", -1, Tk_Offset(AxisOptions, major.lineWidth), 0, NULL, CACHE}, + {TK_OPTION_BOOLEAN, "-gridminor", "gridMinor", "GridMinor", + "yes", -1, Tk_Offset(AxisOptions, showGridMinor), 0, NULL, CACHE}, + {TK_OPTION_COLOR, "-gridminorcolor", "gridMinorColor", "GridMinorColor", + "gray64", -1, Tk_Offset(AxisOptions, minor.color), 0, NULL, CACHE}, + {TK_OPTION_CUSTOM, "-gridminordashes", "gridMinorDashes", "GridMinorDashes", + "dot", -1, Tk_Offset(AxisOptions, minor.dashes), + TK_OPTION_NULL_OK, &dashesObjOption, CACHE}, + {TK_OPTION_PIXELS, "-gridminorlinewidth", "gridMinorLineWidth", + "GridMinorLineWidth", + "1", -1, Tk_Offset(AxisOptions, minor.lineWidth), 0, NULL, CACHE}, + {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", + "no", -1, Tk_Offset(AxisOptions, hide), 0, NULL, LAYOUT}, + {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify", + "c", -1, Tk_Offset(AxisOptions, titleJustify), 0, NULL, LAYOUT}, + {TK_OPTION_BOOLEAN, "-labeloffset", "labelOffset", "LabelOffset", + "no", -1, Tk_Offset(AxisOptions, labelOffset), 0, NULL, LAYOUT}, + {TK_OPTION_COLOR, "-limitscolor", "limitsColor", "LimitsColor", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(AxisOptions, limitsTextStyle.color), + 0, NULL, CACHE}, + {TK_OPTION_FONT, "-limitsfont", "limitsFont", "LimitsFont", + STD_FONT_SMALL, -1, Tk_Offset(AxisOptions, limitsTextStyle.font), + 0, NULL, LAYOUT}, + {TK_OPTION_STRING, "-limitsformat", "limitsFormat", "LimitsFormat", + NULL, -1, Tk_Offset(AxisOptions, limitsFormat), + TK_OPTION_NULL_OK, NULL, LAYOUT}, + {TK_OPTION_PIXELS, "-linewidth", "lineWidth", "LineWidth", + "1", -1, Tk_Offset(AxisOptions, lineWidth), 0, NULL, LAYOUT}, + {TK_OPTION_BOOLEAN, "-logscale", "logScale", "LogScale", + "no", -1, Tk_Offset(AxisOptions, logScale), 0, NULL, RESET}, + {TK_OPTION_BOOLEAN, "-loosemin", "looseMin", "LooseMin", + "no", -1, Tk_Offset(AxisOptions, looseMin), 0, NULL, RESET}, + {TK_OPTION_BOOLEAN, "-loosemax", "looseMax", "LooseMax", + "no", -1, Tk_Offset(AxisOptions, looseMax), 0, NULL, RESET}, + {TK_OPTION_CUSTOM, "-majorticks", "majorTicks", "MajorTicks", + NULL, -1, Tk_Offset(AxisOptions, t1UPtr), + TK_OPTION_NULL_OK, &ticksObjOption, RESET}, + {TK_OPTION_CUSTOM, "-max", "max", "Max", + NULL, -1, Tk_Offset(AxisOptions, reqMax), + TK_OPTION_NULL_OK, &limitObjOption, RESET}, + {TK_OPTION_CUSTOM, "-min", "min", "Min", + NULL, -1, Tk_Offset(AxisOptions, reqMin), + TK_OPTION_NULL_OK, &limitObjOption, RESET}, + {TK_OPTION_CUSTOM, "-minorticks", "minorTicks", "MinorTicks", + NULL, -1, Tk_Offset(AxisOptions, t2UPtr), + TK_OPTION_NULL_OK, &ticksObjOption, RESET}, + {TK_OPTION_RELIEF, "-relief", "relief", "Relief", + "flat", -1, Tk_Offset(AxisOptions, relief), 0, NULL, CACHE}, + {TK_OPTION_DOUBLE, "-rotate", "rotate", "Rotate", + "0", -1, Tk_Offset(AxisOptions, tickAngle), 0, NULL, LAYOUT}, + {TK_OPTION_CUSTOM, "-scrollcommand", "scrollCommand", "ScrollCommand", + NULL, -1, Tk_Offset(AxisOptions, scrollCmdObjPtr), + TK_OPTION_NULL_OK, &objectObjOption, 0}, + {TK_OPTION_PIXELS, "-scrollincrement", "scrollIncrement", "ScrollIncrement", + "10", -1, Tk_Offset(AxisOptions, scrollUnits), 0, NULL, 0}, + {TK_OPTION_CUSTOM, "-scrollmax", "scrollMax", "ScrollMax", + NULL, -1, Tk_Offset(AxisOptions, reqScrollMax), + TK_OPTION_NULL_OK, &limitObjOption, 0}, + {TK_OPTION_CUSTOM, "-scrollmin", "scrollMin", "ScrollMin", + NULL, -1, Tk_Offset(AxisOptions, reqScrollMin), + TK_OPTION_NULL_OK, &limitObjOption, 0}, + {TK_OPTION_DOUBLE, "-shiftby", "shiftBy", "ShiftBy", + "0", -1, Tk_Offset(AxisOptions, shiftBy), 0, NULL, RESET}, + {TK_OPTION_BOOLEAN, "-showticks", "showTicks", "ShowTicks", + "yes", -1, Tk_Offset(AxisOptions, showTicks), 0, NULL, LAYOUT}, + {TK_OPTION_DOUBLE, "-stepsize", "stepSize", "StepSize", + "0", -1, Tk_Offset(AxisOptions, reqStep), 0, NULL, RESET}, + {TK_OPTION_INT, "-subdivisions", "subdivisions", "Subdivisions", + "2", -1, Tk_Offset(AxisOptions, reqNumMinorTicks), 0, NULL, RESET}, + {TK_OPTION_ANCHOR, "-tickanchor", "tickAnchor", "Anchor", + "c", -1, Tk_Offset(AxisOptions, reqTickAnchor), 0, NULL, LAYOUT}, + {TK_OPTION_FONT, "-tickfont", "tickFont", "Font", + STD_FONT_SMALL, -1, Tk_Offset(AxisOptions, tickFont), 0, NULL, LAYOUT}, + {TK_OPTION_PIXELS, "-ticklength", "tickLength", "TickLength", + "8", -1, Tk_Offset(AxisOptions, tickLength), 0, NULL, LAYOUT}, + {TK_OPTION_INT, "-tickdefault", "tickDefault", "TickDefault", + "4", -1, Tk_Offset(AxisOptions, reqNumMajorTicks), 0, NULL, RESET}, + {TK_OPTION_STRING, "-tickformat", "tickFormat", "TickFormat", + NULL, -1, Tk_Offset(AxisOptions, tickFormat), TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_STRING, "-tickformatcommand", "tickformatcommand", "TickFormatCommand", + NULL, -1, Tk_Offset(AxisOptions, tickFormatCmd), TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_STRING, "-title", "title", "Title", + NULL, -1, Tk_Offset(AxisOptions, title), TK_OPTION_NULL_OK, NULL, LAYOUT}, + {TK_OPTION_BOOLEAN, "-titlealternate", "titleAlternate", "TitleAlternate", + "no", -1, Tk_Offset(AxisOptions, titleAlternate), 0, NULL, LAYOUT}, + {TK_OPTION_COLOR, "-titlecolor", "titleColor", "TitleColor", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(AxisOptions, titleColor), + 0, NULL, CACHE}, + {TK_OPTION_FONT, "-titlefont", "titleFont", "TitleFont", + STD_FONT_NORMAL, -1, Tk_Offset(AxisOptions, titleFont), 0, NULL, LAYOUT}, + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} +}; + +TickLabel::TickLabel(char* str) +{ + anchorPos.x = DBL_MAX; + anchorPos.y = DBL_MAX; + width =0; + height =0; + string = dupstr(str); +} + +TickLabel::~TickLabel() +{ + delete [] string; +} + +Ticks::Ticks(int cnt) +{ + nTicks =cnt; + values = new double[cnt]; +} + +Ticks::~Ticks() +{ + delete [] values; +} + +Axis::Axis(Graph* graphPtr, const char* name, int margin, Tcl_HashEntry* hPtr) +{ + ops_ = (AxisOptions*)calloc(1, sizeof(AxisOptions)); + AxisOptions* ops = (AxisOptions*)ops_; + + graphPtr_ = graphPtr; + classId_ = CID_NONE; + name_ = dupstr(name); + className_ = dupstr("none"); + + hashPtr_ = hPtr; + refCount_ =0; + use_ =0; + active_ =0; + + link =NULL; + chain =NULL; + + titlePos_.x =0; + titlePos_.y =0; + titleWidth_ =0; + titleHeight_ =0; + min_ =0; + max_ =0; + scrollMin_ =0; + scrollMax_ =0; + valueRange_.min =0; + valueRange_.max =0; + valueRange_.range =0; + valueRange_.scale =0; + axisRange_.min =0; + axisRange_.max =0; + axisRange_.range =0; + axisRange_.scale =0; + prevMin_ =0; + prevMax_ =0; + t1Ptr_ =NULL; + t2Ptr_ =NULL; + minorSweep_.initial =0; + minorSweep_.step =0; + minorSweep_.nSteps =0; + majorSweep_.initial =0; + majorSweep_.step =0; + majorSweep_.nSteps =0; + + margin_ = margin; + segments_ =NULL; + nSegments_ =0; + tickLabels_ = new Chain(); + left_ =0; + right_ =0; + top_ =0; + bottom_ =0; + width_ =0; + height_ =0; + maxTickWidth_ =0; + maxTickHeight_ =0; + tickAnchor_ = TK_ANCHOR_N; + tickGC_ =NULL; + activeTickGC_ =NULL; + titleAngle_ =0; + titleAnchor_ = TK_ANCHOR_N; + screenScale_ =0; + screenMin_ =0; + screenRange_ =0; + + ops->reqMin =NAN; + ops->reqMax =NAN; + ops->reqScrollMin =NAN; + ops->reqScrollMax =NAN; + + ops->limitsTextStyle.anchor =TK_ANCHOR_NW; + ops->limitsTextStyle.color =NULL; + ops->limitsTextStyle.font =NULL; + ops->limitsTextStyle.angle =0; + ops->limitsTextStyle.justify =TK_JUSTIFY_LEFT; + + optionTable_ = Tk_CreateOptionTable(graphPtr_->interp_, optionSpecs); +} + +Axis::~Axis() +{ + AxisOptions* ops = (AxisOptions*)ops_; + + graphPtr_->bindTable_->deleteBindings(this); + + if (link) + chain->deleteLink(link); + + if (hashPtr_) + Tcl_DeleteHashEntry(hashPtr_); + + delete [] name_; + delete [] className_; + + if (tickGC_) + Tk_FreeGC(graphPtr_->display_, tickGC_); + + if (activeTickGC_) + Tk_FreeGC(graphPtr_->display_, activeTickGC_); + + delete [] ops->major.segments; + if (ops->major.gc) + graphPtr_->freePrivateGC(ops->major.gc); + + delete [] ops->minor.segments; + if (ops->minor.gc) + graphPtr_->freePrivateGC(ops->minor.gc); + + delete t1Ptr_; + delete t2Ptr_; + + freeTickLabels(); + + delete tickLabels_; + + delete [] segments_; + + Tk_FreeConfigOptions((char*)ops_, optionTable_, graphPtr_->tkwin_); + free(ops_); +} + +int Axis::configure() +{ + AxisOptions* ops = (AxisOptions*)ops_; + + // Check the requested axis limits. Can't allow -min to be greater than + // -max. Do this regardless of -checklimits option. We want to always + // detect when the user has zoomed in beyond the precision of the data + + if (((!isnan(ops->reqMin)) && (!isnan(ops->reqMax))) && + (ops->reqMin >= ops->reqMax)) { + ostringstream str; + str << "impossible axis limits (-min " << ops->reqMin + << " >= -max " << ops->reqMax << ") for \"" + << name_ << "\"" << ends; + Tcl_AppendResult(graphPtr_->interp_, str.str().c_str(), NULL); + return TCL_ERROR; + } + + scrollMin_ = ops->reqScrollMin; + scrollMax_ = ops->reqScrollMax; + if (ops->logScale) { + if (ops->checkLimits) { + // Check that the logscale limits are positive. + if ((!isnan(ops->reqMin)) && (ops->reqMin <= 0.0)) { + ostringstream str; + str << "bad logscale -min limit \"" << ops->reqMin + << "\" for axis \"" << name_ << "\"" << ends; + Tcl_AppendResult(graphPtr_->interp_, str.str().c_str(), NULL); + return TCL_ERROR; + } + } + if ((!isnan(scrollMin_)) && (scrollMin_ <= 0.0)) + scrollMin_ = NAN; + + if ((!isnan(scrollMax_)) && (scrollMax_ <= 0.0)) + scrollMax_ = NAN; + } + + double angle = fmod(ops->tickAngle, 360.0); + if (angle < 0.0) + angle += 360.0; + + ops->tickAngle = angle; + resetTextStyles(); + + titleWidth_ = titleHeight_ = 0; + if (ops->title) { + int w, h; + graphPtr_->getTextExtents(ops->titleFont, ops->title, -1, &w, &h); + titleWidth_ = (unsigned int)w; + titleHeight_ = (unsigned int)h; + } + + return TCL_OK; +} + +void Axis::map(int offset, int margin) +{ + if (isHorizontal()) { + screenMin_ = graphPtr_->hOffset_; + width_ = graphPtr_->right_ - graphPtr_->left_; + screenRange_ = graphPtr_->hRange_; + } + else { + screenMin_ = graphPtr_->vOffset_; + height_ = graphPtr_->bottom_ - graphPtr_->top_; + screenRange_ = graphPtr_->vRange_; + } + screenScale_ = 1.0 / screenRange_; + + AxisInfo info; + offsets(margin, offset, &info); + makeSegments(&info); +} + +void Axis::mapStacked(int count, int margin) +{ + AxisOptions* ops = (AxisOptions*)ops_; + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + + if (Chain_GetLength(gops->margins[margin_].axes) > 1 + || ops->reqNumMajorTicks <= 0) + ops->reqNumMajorTicks = 4; + + unsigned int slice; + if (isHorizontal()) { + slice = graphPtr_->hRange_ / Chain_GetLength(gops->margins[margin].axes); + screenMin_ = graphPtr_->hOffset_; + width_ = slice; + } + else { + slice = graphPtr_->vRange_ / Chain_GetLength(gops->margins[margin].axes); + screenMin_ = graphPtr_->vOffset_; + height_ = slice; + } + + int w, h; + graphPtr_->getTextExtents(ops->tickFont, "0", 1, &w, &h); + screenMin_ += (slice * count) + 2 + h / 2; + screenRange_ = slice - 2 * 2 - h; + screenScale_ = 1.0 / screenRange_; + + AxisInfo info; + offsets(margin, 0, &info); + makeSegments(&info); +} + +void Axis::mapGridlines() +{ + AxisOptions* ops = (AxisOptions*)ops_; + + Ticks* t1Ptr = t1Ptr_; + if (!t1Ptr) + t1Ptr = generateTicks(&majorSweep_); + + Ticks* t2Ptr = t2Ptr_; + if (!t2Ptr) + t2Ptr = generateTicks(&minorSweep_); + + int needed = t1Ptr->nTicks; + if (ops->showGridMinor) + needed += (t1Ptr->nTicks * t2Ptr->nTicks); + + if (needed == 0) { + if (t1Ptr != t1Ptr_) + delete t1Ptr; + if (t2Ptr != t2Ptr_) + delete t2Ptr; + + return; + } + + needed = t1Ptr->nTicks; + if (needed != ops->major.nAllocated) { + delete [] ops->major.segments; + ops->major.segments = new Segment2d[needed]; + ops->major.nAllocated = needed; + } + needed = (t1Ptr->nTicks * t2Ptr->nTicks); + if (needed != ops->minor.nAllocated) { + delete [] ops->minor.segments; + ops->minor.segments = new Segment2d[needed]; + ops->minor.nAllocated = needed; + } + + Segment2d* s1 = ops->major.segments; + Segment2d* s2 = ops->minor.segments; + for (int ii=0; iinTicks; ii++) { + double value = t1Ptr->values[ii]; + if (ops->showGridMinor) { + for (int jj=0; jjnTicks; jj++) { + double subValue = value + (majorSweep_.step * t2Ptr->values[jj]); + if (inRange(subValue, &axisRange_)) { + makeGridLine(subValue, s2); + s2++; + } + } + } + if (inRange(value, &axisRange_)) { + makeGridLine(value, s1); + s1++; + } + } + + if (t1Ptr != t1Ptr_) + delete t1Ptr; + if (t2Ptr != t2Ptr_) + delete t2Ptr; + + ops->major.nUsed = s1 - ops->major.segments; + ops->minor.nUsed = s2 - ops->minor.segments; +} + +void Axis::draw(Drawable drawable) +{ + AxisOptions* ops = (AxisOptions*)ops_; + + if (ops->hide || !use_) + return; + + if (ops->normalBg) { + int relief = active_ ? ops->activeRelief : ops->relief; + Tk_Fill3DRectangle(graphPtr_->tkwin_, drawable, ops->normalBg, + left_, top_, right_ - left_, bottom_ - top_, + ops->borderWidth, relief); + } + + if (ops->title) { + TextStyle ts(graphPtr_); + TextStyleOptions* tops = (TextStyleOptions*)ts.ops(); + + tops->angle = titleAngle_; + tops->font = ops->titleFont; + tops->anchor = titleAnchor_; + tops->color = active_ ? ops->activeFgColor : ops->titleColor; + tops->justify = ops->titleJustify; + + ts.xPad_ = 1; + ts.yPad_ = 0; + ts.drawText(drawable, ops->title, titlePos_.x, titlePos_.y); + } + + if (ops->scrollCmdObjPtr) { + double worldMin = valueRange_.min; + double worldMax = valueRange_.max; + if (!isnan(scrollMin_)) + worldMin = scrollMin_; + if (!isnan(scrollMax_)) + worldMax = scrollMax_; + + double viewMin = min_; + double viewMax = max_; + if (viewMin < worldMin) + viewMin = worldMin; + if (viewMax > worldMax) + viewMax = worldMax; + + if (ops->logScale) { + worldMin = log10(worldMin); + worldMax = log10(worldMax); + viewMin = log10(viewMin); + viewMax = log10(viewMax); + } + + double worldWidth = worldMax - worldMin; + double viewWidth = viewMax - viewMin; + int isHoriz = isHorizontal(); + + double fract; + if (isHoriz != ops->descending) + fract = (viewMin - worldMin) / worldWidth; + else + fract = (worldMax - viewMax) / worldWidth; + + fract = AdjustViewport(fract, viewWidth / worldWidth); + + if (isHoriz != ops->descending) { + viewMin = (fract * worldWidth); + min_ = viewMin + worldMin; + max_ = min_ + viewWidth; + viewMax = viewMin + viewWidth; + if (ops->logScale) { + min_ = EXP10(min_); + max_ = EXP10(max_); + } + updateScrollbar(graphPtr_->interp_, ops->scrollCmdObjPtr, + (int)viewMin, (int)viewMax, (int)worldWidth); + } + else { + viewMax = (fract * worldWidth); + max_ = worldMax - viewMax; + min_ = max_ - viewWidth; + viewMin = viewMax + viewWidth; + if (ops->logScale) { + min_ = EXP10(min_); + max_ = EXP10(max_); + } + updateScrollbar(graphPtr_->interp_, ops->scrollCmdObjPtr, + (int)viewMax, (int)viewMin, (int)worldWidth); + } + } + + if (ops->showTicks) { + TextStyle ts(graphPtr_); + TextStyleOptions* tops = (TextStyleOptions*)ts.ops(); + + tops->angle = ops->tickAngle; + tops->font = ops->tickFont; + tops->anchor = tickAnchor_; + tops->color = active_ ? ops->activeFgColor : ops->tickColor; + + ts.xPad_ = 2; + ts.yPad_ = 0; + + for (ChainLink* link = Chain_FirstLink(tickLabels_); link; + link = Chain_NextLink(link)) { + TickLabel* labelPtr = (TickLabel*)Chain_GetValue(link); + ts.drawText(drawable, labelPtr->string, labelPtr->anchorPos.x, + labelPtr->anchorPos.y); + } + } + + if ((nSegments_ > 0) && (ops->lineWidth > 0)) { + GC gc = active_ ? activeTickGC_ : tickGC_; + graphPtr_->drawSegments(drawable, gc, segments_, nSegments_); + } +} + +void Axis::drawGrids(Drawable drawable) +{ + AxisOptions* ops = (AxisOptions*)ops_; + + if (ops->hide || !ops->showGrid || !use_) + return; + + graphPtr_->drawSegments(drawable, ops->major.gc, + ops->major.segments, ops->major.nUsed); + + if (ops->showGridMinor) + graphPtr_->drawSegments(drawable, ops->minor.gc, + ops->minor.segments, ops->minor.nUsed); +} + +void Axis::drawLimits(Drawable drawable) +{ + AxisOptions* ops = (AxisOptions*)ops_; + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + + if (!ops->limitsFormat) + return; + + int vMin = graphPtr_->left_ + gops->xPad + 2; + int vMax = vMin; + int hMin = graphPtr_->bottom_ - gops->yPad - 2; + int hMax = hMin; + + const int spacing =8; + int isHoriz = isHorizontal(); + char* minPtr =NULL; + char* maxPtr =NULL; + char minString[200]; + char maxString[200]; + const char* fmt = ops->limitsFormat; + if (fmt && *fmt) { + minPtr = minString; + snprintf(minString, 200, fmt, axisRange_.min); + + maxPtr = maxString; + snprintf(maxString, 200, fmt, axisRange_.max); + } + if (ops->descending) { + char *tmp = minPtr; + minPtr = maxPtr; + maxPtr = tmp; + } + + TextStyle ts(graphPtr_, &ops->limitsTextStyle); + if (maxPtr) { + if (isHoriz) { + ops->limitsTextStyle.angle = 90.0; + ops->limitsTextStyle.anchor = TK_ANCHOR_SE; + + int ww, hh; + ts.drawText2(drawable, maxPtr, graphPtr_->right_, hMax, &ww, &hh); + hMax -= (hh + spacing); + } + else { + ops->limitsTextStyle.angle = 0.0; + ops->limitsTextStyle.anchor = TK_ANCHOR_NW; + + int ww, hh; + ts.drawText2(drawable, maxPtr, vMax, graphPtr_->top_, &ww, &hh); + vMax += (ww + spacing); + } + } + if (minPtr) { + ops->limitsTextStyle.anchor = TK_ANCHOR_SW; + + if (isHoriz) { + ops->limitsTextStyle.angle = 90.0; + + int ww, hh; + ts.drawText2(drawable, minPtr, graphPtr_->left_, hMin, &ww, &hh); + hMin -= (hh + spacing); + } + else { + ops->limitsTextStyle.angle = 0.0; + + int ww, hh; + ts.drawText2(drawable, minPtr, vMin, graphPtr_->bottom_, &ww, &hh); + vMin += (ww + spacing); + } + } +} + +void Axis::setClass(ClassId classId) +{ + delete [] className_; + + classId_ = classId; + switch (classId) { + case CID_NONE: + className_ = dupstr("none"); + break; + case CID_AXIS_X: + className_ = dupstr("XAxis"); + break; + case CID_AXIS_Y: + className_ = dupstr("YAxis"); + break; + default: + className_ = NULL; + break; + } +} + +void Axis::logScale(double min, double max) +{ + AxisOptions* ops = (AxisOptions*)ops_; + + double range; + double tickMin, tickMax; + double majorStep, minorStep; + int nMajor, nMinor; + + nMajor = nMinor = 0; + majorStep = minorStep = 0.0; + tickMin = tickMax = NAN; + if (min < max) { + min = (min != 0.0) ? log10(fabs(min)) : 0.0; + max = (max != 0.0) ? log10(fabs(max)) : 1.0; + + tickMin = floor(min); + tickMax = ceil(max); + range = tickMax - tickMin; + + if (range > 10) { + // There are too many decades to display a major tick at every + // decade. Instead, treat the axis as a linear scale + range = niceNum(range, 0); + majorStep = niceNum(range / ops->reqNumMajorTicks, 1); + tickMin = floor(tickMin/majorStep)*majorStep; + tickMax = ceil(tickMax/majorStep)*majorStep; + nMajor = (int)((tickMax - tickMin) / majorStep) + 1; + minorStep = EXP10(floor(log10(majorStep))); + if (minorStep == majorStep) { + nMinor = 4; + minorStep = 0.2; + } + else + nMinor = (int)(majorStep/minorStep) - 1; + } + else { + if (tickMin == tickMax) + tickMax++; + majorStep = 1.0; + nMajor = (int)(tickMax - tickMin + 1); /* FIXME: Check this. */ + + minorStep = 0.0; /* This is a special hack to pass + * information to the GenerateTicks + * routine. An interval of 0.0 tells 1) + * this is a minor sweep and 2) the axis + * is log scale. */ + nMinor = 10; + } + if (!ops->looseMin || (ops->looseMin && !isnan(ops->reqMin))) { + tickMin = min; + nMajor++; + } + if (!ops->looseMax || (ops->looseMax && !isnan(ops->reqMax))) { + tickMax = max; + } + } + majorSweep_.step = majorStep; + majorSweep_.initial = floor(tickMin); + majorSweep_.nSteps = nMajor; + minorSweep_.initial = minorSweep_.step = minorStep; + minorSweep_.nSteps = nMinor; + + setRange(&axisRange_, tickMin, tickMax); +} + +void Axis::linearScale(double min, double max) +{ + AxisOptions* ops = (AxisOptions*)ops_; + + unsigned int nTicks = 0; + double step = 1.0; + double axisMin =NAN; + double axisMax =NAN; + double tickMin =NAN; + double tickMax =NAN; + + if (min < max) { + double range = max - min; + if (ops->reqStep > 0.0) { + step = ops->reqStep; + while ((2 * step) >= range) + step *= 0.5; + } + else { + range = niceNum(range, 0); + step = niceNum(range / ops->reqNumMajorTicks, 1); + } + + axisMin = tickMin = floor(min / step) * step + 0.0; + axisMax = tickMax = ceil(max / step) * step + 0.0; + + nTicks = (int)((tickMax-tickMin) / step) + 1; + } + majorSweep_.step = step; + majorSweep_.initial = tickMin; + majorSweep_.nSteps = nTicks; + + /* + * The limits of the axis are either the range of the data ("tight") or at + * the next outer tick interval ("loose"). The looseness or tightness has + * to do with how the axis fits the range of data values. This option is + * overridden when the user sets an axis limit (by either -min or -max + * option). The axis limit is always at the selected limit (otherwise we + * assume that user would have picked a different number). + */ + if (!ops->looseMin || (ops->looseMin && !isnan(ops->reqMin))) + axisMin = min; + + if (!ops->looseMax || (ops->looseMax && !isnan(ops->reqMax))) + axisMax = max; + + setRange(&axisRange_, axisMin, axisMax); + + if (ops->reqNumMinorTicks > 0) { + nTicks = ops->reqNumMinorTicks - 1; + step = 1.0 / (nTicks + 1); + } + else { + nTicks = 0; + step = 0.5; + } + minorSweep_.initial = minorSweep_.step = step; + minorSweep_.nSteps = nTicks; +} + +void Axis::setRange(AxisRange *rangePtr, double min, double max) +{ + rangePtr->min = min; + rangePtr->max = max; + rangePtr->range = max - min; + if (fabs(rangePtr->range) < DBL_EPSILON) { + rangePtr->range = 1.0; + } + rangePtr->scale = 1.0 / rangePtr->range; +} + +void Axis::fixRange() +{ + AxisOptions* ops = (AxisOptions*)ops_; + + // When auto-scaling, the axis limits are the bounds of the element data. + // If no data exists, set arbitrary limits (wrt to log/linear scale). + double min = valueRange_.min; + double max = valueRange_.max; + + // Check the requested axis limits. Can't allow -min to be greater + // than -max, or have undefined log scale limits. */ + if (((!isnan(ops->reqMin)) && (!isnan(ops->reqMax))) && + (ops->reqMin >= ops->reqMax)) { + ops->reqMin = ops->reqMax = NAN; + } + if (ops->logScale) { + if ((!isnan(ops->reqMin)) && (ops->reqMin <= 0.0)) + ops->reqMin = NAN; + + if ((!isnan(ops->reqMax)) && (ops->reqMax <= 0.0)) + ops->reqMax = NAN; + } + + if (min == DBL_MAX) { + if (!isnan(ops->reqMin)) + min = ops->reqMin; + else + min = (ops->logScale) ? 0.001 : 0.0; + } + if (max == -DBL_MAX) { + if (!isnan(ops->reqMax)) + max = ops->reqMax; + else + max = 1.0; + } + if (min >= max) { + + // There is no range of data (i.e. min is not less than max), so + // manufacture one. + if (min == 0.0) + min = 0.0, max = 1.0; + else + max = min + (fabs(min) * 0.1); + } + setRange(&valueRange_, min, max); + + // The axis limits are either the current data range or overridden by the + // values selected by the user with the -min or -max options. + min_ = min; + max_ = max; + if (!isnan(ops->reqMin)) + min_ = ops->reqMin; + + if (!isnan(ops->reqMax)) + max_ = ops->reqMax; + + if (max_ < min_) { + // If the limits still don't make sense, it's because one limit + // configuration option (-min or -max) was set and the other default + // (based upon the data) is too small or large. Remedy this by making + // up a new min or max from the user-defined limit. + if (isnan(ops->reqMin)) + min_ = max_ - (fabs(max_) * 0.1); + + if (isnan(ops->reqMax)) + max_ = min_ + (fabs(max_) * 0.1); + } + + // If a window size is defined, handle auto ranging by shifting the axis + // limits. + if ((ops->windowSize > 0.0) && + (isnan(ops->reqMin)) && (isnan(ops->reqMax))) { + if (ops->shiftBy < 0.0) + ops->shiftBy = 0.0; + + max = min_ + ops->windowSize; + if (max_ >= max) { + if (ops->shiftBy > 0.0) + max = ceil(max_/ops->shiftBy)*ops->shiftBy; + min_ = max - ops->windowSize; + } + max_ = max; + } + if ((max_ != prevMax_) || + (min_ != prevMin_)) { + /* and save the previous minimum and maximum values */ + prevMin_ = min_; + prevMax_ = max_; + } +} + +// Reference: Paul Heckbert, "Nice Numbers for Graph Labels", +// Graphics Gems, pp 61-63. +double Axis::niceNum(double x, int round) +{ + double expt; /* Exponent of x */ + double frac; /* Fractional part of x */ + double nice; /* Nice, rounded fraction */ + + expt = floor(log10(x)); + frac = x / EXP10(expt); /* between 1 and 10 */ + if (round) { + if (frac < 1.5) { + nice = 1.0; + } else if (frac < 3.0) { + nice = 2.0; + } else if (frac < 7.0) { + nice = 5.0; + } else { + nice = 10.0; + } + } else { + if (frac <= 1.0) { + nice = 1.0; + } else if (frac <= 2.0) { + nice = 2.0; + } else if (frac <= 5.0) { + nice = 5.0; + } else { + nice = 10.0; + } + } + return nice * EXP10(expt); +} + +int Axis::inRange(double x, AxisRange *rangePtr) +{ + if (rangePtr->range < DBL_EPSILON) + return (fabs(rangePtr->max - x) >= DBL_EPSILON); + else { + double norm; + + norm = (x - rangePtr->min) * rangePtr->scale; + return ((norm >= -DBL_EPSILON) && ((norm - 1.0) < DBL_EPSILON)); + } +} + +int Axis::isHorizontal() +{ + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + return ((classId_ == CID_AXIS_Y) == gops->inverted); +} + +void Axis::freeTickLabels() +{ + Chain* chain = tickLabels_; + for (ChainLink* link = Chain_FirstLink(chain); link; + link = Chain_NextLink(link)) { + TickLabel* labelPtr = (TickLabel*)Chain_GetValue(link); + delete labelPtr; + } + chain->reset(); +} + +TickLabel* Axis::makeLabel(double value) +{ +#define TICK_LABEL_SIZE 200 + + AxisOptions* ops = (AxisOptions*)ops_; + + char string[TICK_LABEL_SIZE + 1]; + + if (ops->tickFormat && *ops->tickFormat) { + snprintf(string, TICK_LABEL_SIZE, ops->tickFormat, value); + } else if (ops->logScale) { + snprintf(string, TICK_LABEL_SIZE, "1E%d", int(value)); + } else { + snprintf(string, TICK_LABEL_SIZE, "%.15G", value); + } + + if (ops->tickFormatCmd) { + Tcl_Interp* interp = graphPtr_->interp_; + Tk_Window tkwin = graphPtr_->tkwin_; + + // A TCL proc was designated to format tick labels. Append the path + // name of the widget and the default tick label as arguments when + // invoking it. Copy and save the new label from interp->result. + Tcl_ResetResult(interp); + if (Tcl_VarEval(interp, ops->tickFormatCmd, " ", Tk_PathName(tkwin), + " ", string, NULL) != TCL_OK) { + Tcl_BackgroundError(interp); + } + else { + // The proc could return a string of any length, so arbitrarily + // limit it to what will fit in the return string. + strncpy(string, Tcl_GetStringResult(interp), TICK_LABEL_SIZE); + string[TICK_LABEL_SIZE] = '\0'; + + Tcl_ResetResult(interp); /* Clear the interpreter's result. */ + } + } + + TickLabel* labelPtr = new TickLabel(string); + + return labelPtr; +} + +double Axis::invHMap(double x) +{ + AxisOptions* ops = (AxisOptions*)ops_; + double value; + + x = (double)(x - screenMin_) * screenScale_; + if (ops->descending) { + x = 1.0 - x; + } + value = (x * axisRange_.range) + axisRange_.min; + if (ops->logScale) { + value = EXP10(value); + } + return value; +} + +double Axis::invVMap(double y) +{ + AxisOptions* ops = (AxisOptions*)ops_; + double value; + + y = (double)(y - screenMin_) * screenScale_; + if (ops->descending) { + y = 1.0 - y; + } + value = ((1.0 - y) * axisRange_.range) + axisRange_.min; + if (ops->logScale) { + value = EXP10(value); + } + return value; +} + +double Axis::hMap(double x) +{ + AxisOptions* ops = (AxisOptions*)ops_; + if ((ops->logScale) && (x != 0.0)) { + x = log10(fabs(x)); + } + /* Map graph coordinate to normalized coordinates [0..1] */ + x = (x - axisRange_.min) * axisRange_.scale; + if (ops->descending) { + x = 1.0 - x; + } + return (x * screenRange_ + screenMin_); +} + +double Axis::vMap(double y) +{ + AxisOptions* ops = (AxisOptions*)ops_; + if ((ops->logScale) && (y != 0.0)) { + y = log10(fabs(y)); + } + /* Map graph coordinate to normalized coordinates [0..1] */ + y = (y - axisRange_.min) * axisRange_.scale; + if (ops->descending) { + y = 1.0 - y; + } + return ((1.0 - y) * screenRange_ + screenMin_); +} + +void Axis::getDataLimits(double min, double max) +{ + if (valueRange_.min > min) + valueRange_.min = min; + + if (valueRange_.max < max) + valueRange_.max = max; +} + +void Axis::resetTextStyles() +{ + AxisOptions* ops = (AxisOptions*)ops_; + + XGCValues gcValues; + unsigned long gcMask; + gcMask = (GCForeground | GCLineWidth | GCCapStyle); + gcValues.foreground = ops->tickColor->pixel; + gcValues.font = Tk_FontId(ops->tickFont); + gcValues.line_width = ops->lineWidth; + gcValues.cap_style = CapProjecting; + + GC newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); + if (tickGC_) + Tk_FreeGC(graphPtr_->display_, tickGC_); + tickGC_ = newGC; + + // Assuming settings from above GC + gcValues.foreground = ops->activeFgColor->pixel; + newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); + if (activeTickGC_) + Tk_FreeGC(graphPtr_->display_, activeTickGC_); + activeTickGC_ = newGC; + + gcValues.background = gcValues.foreground = ops->major.color->pixel; + gcValues.line_width = ops->major.lineWidth; + gcMask = (GCForeground | GCBackground | GCLineWidth); + if (LineIsDashed(ops->major.dashes)) { + gcValues.line_style = LineOnOffDash; + gcMask |= GCLineStyle; + } + newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); + if (LineIsDashed(ops->major.dashes)) + graphPtr_->setDashes(newGC, &ops->major.dashes); + + if (ops->major.gc) + graphPtr_->freePrivateGC(ops->major.gc); + + ops->major.gc = newGC; + + gcValues.background = gcValues.foreground = ops->minor.color->pixel; + gcValues.line_width = ops->minor.lineWidth; + gcMask = (GCForeground | GCBackground | GCLineWidth); + if (LineIsDashed(ops->minor.dashes)) { + gcValues.line_style = LineOnOffDash; + gcMask |= GCLineStyle; + } + newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); + if (LineIsDashed(ops->minor.dashes)) + graphPtr_->setDashes(newGC, &ops->minor.dashes); + + if (ops->minor.gc) + graphPtr_->freePrivateGC(ops->minor.gc); + + ops->minor.gc = newGC; +} + +void Axis::makeLine(int line, Segment2d *sp) +{ + AxisOptions* ops = (AxisOptions*)ops_; + + double min = axisRange_.min; + double max = axisRange_.max; + if (ops->logScale) { + min = EXP10(min); + max = EXP10(max); + } + if (isHorizontal()) { + sp->p.x = hMap(min); + sp->q.x = hMap(max); + sp->p.y = sp->q.y = line; + } + else { + sp->q.x = sp->p.x = line; + sp->p.y = vMap(min); + sp->q.y = vMap(max); + } +} + +void Axis::offsets(int margin, int offset, AxisInfo *infoPtr) +{ + AxisOptions* ops = (AxisOptions*)ops_; + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + + int axisLine =0; + int t1 =0; + int t2 =0; + int labelOffset =AXIS_PAD_TITLE; + int tickLabel =0; + + float titleAngle[4] = {0.0, 90.0, 0.0, 270.0}; + titleAngle_ = titleAngle[margin]; + Margin *marginPtr = gops->margins + margin; + + if (ops->lineWidth > 0) { + if (ops->showTicks) { + t1 = ops->tickLength; + t2 = (t1 * 10) / 15; + } + labelOffset = t1 + AXIS_PAD_TITLE; + if (ops->exterior) + labelOffset += ops->lineWidth; + } + + int axisPad =0; + + // Adjust offset for the interior border width and the line width */ + // fixme + int pad = 0; + // int pad = 1; + // if (graphPtr_->plotBW > 0) + // pad += graphPtr_->plotBW + 1; + + // Pre-calculate the x-coordinate positions of the axis, tick labels, and + // the individual major and minor ticks. + int inset = pad + ops->lineWidth / 2; + + switch (margin) { + case MARGIN_TOP: + { + int mark = graphPtr_->top_ - offset - pad; + tickAnchor_ = TK_ANCHOR_S; + left_ = screenMin_ - inset - 2; + right_ = screenMin_ + screenRange_ + inset - 1; + if (gops->stackAxes) + top_ = mark - marginPtr->axesOffset; + else + top_ = mark - height_; + bottom_ = mark; + + axisLine = bottom_; + if (ops->exterior) { + axisLine -= gops->plotBW + axisPad + ops->lineWidth / 2; + tickLabel = axisLine - 2; + if (ops->lineWidth > 0) + tickLabel -= ops->tickLength; + } + else { + if (gops->plotRelief == TK_RELIEF_SOLID) + axisLine--; + + axisLine -= axisPad + ops->lineWidth / 2; + tickLabel = graphPtr_->top_ - gops->plotBW - 2; + } + + int x, y; + if (ops->titleAlternate) { + x = graphPtr_->right_ + AXIS_PAD_TITLE; + y = mark - (height_ / 2); + titleAnchor_ = TK_ANCHOR_W; + } + else { + x = (right_ + left_) / 2; + if (gops->stackAxes) + y = mark - marginPtr->axesOffset + AXIS_PAD_TITLE; + else + y = mark - height_ + AXIS_PAD_TITLE; + + titleAnchor_ = TK_ANCHOR_N; + } + titlePos_.x = x; + titlePos_.y = y; + } + break; + + case MARGIN_BOTTOM: + { + /* + * ----------- bottom + plot borderwidth + * mark -------------------------------------------- + * ===================== axisLine (linewidth) + * tick + * title + * + * ===================== axisLine (linewidth) + * ----------- bottom + plot borderwidth + * mark -------------------------------------------- + * tick + * title + */ + int mark = graphPtr_->bottom_ + offset; + double fangle = fmod(ops->tickAngle, 90.0); + if (fangle == 0.0) + tickAnchor_ = TK_ANCHOR_N; + else { + int quadrant = (int)(ops->tickAngle / 90.0); + if ((quadrant == 0) || (quadrant == 2)) + tickAnchor_ = TK_ANCHOR_NE; + else + tickAnchor_ = TK_ANCHOR_NW; + } + + left_ = screenMin_ - inset - 2; + right_ = screenMin_ + screenRange_ + inset - 1; + top_ = mark + labelOffset - t1; + if (gops->stackAxes) + bottom_ = mark + marginPtr->axesOffset - 1; + else + bottom_ = mark + height_ - 1; + + axisLine = top_; + if (gops->plotRelief == TK_RELIEF_SOLID) + axisLine++; + + if (ops->exterior) { + axisLine += gops->plotBW + axisPad + ops->lineWidth / 2; + tickLabel = axisLine + 2; + if (ops->lineWidth > 0) + tickLabel += ops->tickLength; + } + else { + axisLine -= axisPad + ops->lineWidth / 2; + tickLabel = graphPtr_->bottom_ + gops->plotBW + 2; + } + + int x, y; + if (ops->titleAlternate) { + x = graphPtr_->right_ + AXIS_PAD_TITLE; + y = mark + (height_ / 2); + titleAnchor_ = TK_ANCHOR_W; + } + else { + x = (right_ + left_) / 2; + if (gops->stackAxes) + y = mark + marginPtr->axesOffset - AXIS_PAD_TITLE; + else + y = mark + height_ - AXIS_PAD_TITLE; + titleAnchor_ = TK_ANCHOR_S; + } + titlePos_.x = x; + titlePos_.y = y; + } + break; + + case MARGIN_LEFT: + { + /* + * mark + * | : + * | : + * | : + * | : + * | : + * axisLine + */ + /* + * Exterior axis + * + plotarea right + * |A|B|C|D|E|F|G|H + * |right + * A = plot pad + * B = plot border width + * C = axis pad + * D = axis line + * E = tick length + * F = tick label + * G = graph border width + * H = highlight thickness + */ + /* + * Interior axis + * + plotarea right + * |A|B|C|D|E|F|G|H + * |right + * A = plot pad + * B = tick length + * C = axis line width + * D = axis pad + * E = plot border width + * F = tick label + * G = graph border width + * H = highlight thickness + */ + int mark = graphPtr_->left_ - offset; + tickAnchor_ = TK_ANCHOR_E; + if (gops->stackAxes) + left_ = mark - marginPtr->axesOffset; + else + left_ = mark - width_; + right_ = mark - 3; + top_ = screenMin_ - inset - 2; + bottom_ = screenMin_ + screenRange_ + inset - 1; + + axisLine = right_; + if (ops->exterior) { + axisLine -= gops->plotBW + axisPad + ops->lineWidth / 2; + tickLabel = axisLine - 2; + if (ops->lineWidth > 0) + tickLabel -= ops->tickLength; + } + else { + if (gops->plotRelief == TK_RELIEF_SOLID) + axisLine--; + axisLine += axisPad + ops->lineWidth / 2; + tickLabel = graphPtr_->left_ - gops->plotBW - 2; + } + + int x, y; + if (ops->titleAlternate) { + x = mark - (width_ / 2); + y = graphPtr_->top_ - AXIS_PAD_TITLE; + titleAnchor_ = TK_ANCHOR_SW; + } + else { + if (gops->stackAxes) + x = mark - marginPtr->axesOffset; + else + x = mark - width_ + AXIS_PAD_TITLE; + y = (bottom_ + top_) / 2; + titleAnchor_ = TK_ANCHOR_W; + } + titlePos_.x = x; + titlePos_.y = y; + } + break; + + case MARGIN_RIGHT: + { + int mark = graphPtr_->right_ + offset + pad; + tickAnchor_ = TK_ANCHOR_W; + left_ = mark; + if (gops->stackAxes) + right_ = mark + marginPtr->axesOffset - 1; + else + right_ = mark + width_ - 1; + + top_ = screenMin_ - inset - 2; + bottom_ = screenMin_ + screenRange_ + inset -1; + + axisLine = left_; + if (gops->plotRelief == TK_RELIEF_SOLID) + axisLine++; + + if (ops->exterior) { + axisLine += gops->plotBW + axisPad + ops->lineWidth / 2; + tickLabel = axisLine + 2; + if (ops->lineWidth > 0) + tickLabel += ops->tickLength; + } + else { + axisLine -= axisPad + ops->lineWidth / 2; + tickLabel = graphPtr_->right_ + gops->plotBW + 2; + } + + int x, y; + if (ops->titleAlternate) { + x = mark + (width_ / 2); + y = graphPtr_->top_ - AXIS_PAD_TITLE; + titleAnchor_ = TK_ANCHOR_SE; + } + else { + if (gops->stackAxes) + x = mark + marginPtr->axesOffset - AXIS_PAD_TITLE; + else + x = mark + width_ - AXIS_PAD_TITLE; + + y = (bottom_ + top_) / 2; + titleAnchor_ = TK_ANCHOR_E; + } + titlePos_.x = x; + titlePos_.y = y; + } + break; + + case MARGIN_NONE: + axisLine = 0; + break; + } + + if ((margin == MARGIN_LEFT) || (margin == MARGIN_TOP)) { + t1 = -t1; + t2 = -t2; + labelOffset = -labelOffset; + } + + infoPtr->axis = axisLine; + infoPtr->t1 = axisLine + t1; + infoPtr->t2 = axisLine + t2; + if (tickLabel > 0) + infoPtr->label = tickLabel; + else + infoPtr->label = axisLine + labelOffset; + + if (!ops->exterior) { + infoPtr->t1 = axisLine - t1; + infoPtr->t2 = axisLine - t2; + } +} + +void Axis::makeTick(double value, int tick, int line, Segment2d *sp) +{ + AxisOptions* ops = (AxisOptions*)ops_; + + if (ops->logScale) + value = EXP10(value); + + if (isHorizontal()) { + sp->p.x = hMap(value); + sp->p.y = line; + sp->q.x = sp->p.x; + sp->q.y = tick; + } + else { + sp->p.x = line; + sp->p.y = vMap(value); + sp->q.x = tick; + sp->q.y = sp->p.y; + } +} + +void Axis::makeSegments(AxisInfo *infoPtr) +{ + AxisOptions* ops = (AxisOptions*)ops_; + + delete [] segments_; + segments_ = NULL; + + Ticks* t1Ptr = ops->t1UPtr ? ops->t1UPtr : t1Ptr_; + Ticks* t2Ptr = ops->t2UPtr ? ops->t2UPtr : t2Ptr_; + + int nMajorTicks= t1Ptr ? t1Ptr->nTicks : 0; + int nMinorTicks= t2Ptr ? t2Ptr->nTicks : 0; + + int arraySize = 1 + (nMajorTicks * (nMinorTicks + 1)); + Segment2d* segments = new Segment2d[arraySize]; + Segment2d* sp = segments; + if (ops->lineWidth > 0) { + makeLine(infoPtr->axis, sp); + sp++; + } + + if (ops->showTicks) { + int isHoriz = isHorizontal(); + for (int ii=0; iivalues[ii]; + /* Minor ticks */ + for (int jj=0; jjvalues[jj]); + if (inRange(t2, &axisRange_)) { + makeTick(t2, infoPtr->t2, infoPtr->axis, sp); + sp++; + } + } + if (!inRange(t1, &axisRange_)) + continue; + + /* Major tick */ + makeTick(t1, infoPtr->t1, infoPtr->axis, sp); + sp++; + } + + ChainLink* link = Chain_FirstLink(tickLabels_); + double labelPos = (double)infoPtr->label; + + for (int ii=0; ii< nMajorTicks; ii++) { + double t1 = t1Ptr->values[ii]; + if (ops->labelOffset) + t1 += majorSweep_.step * 0.5; + + if (!inRange(t1, &axisRange_)) + continue; + + TickLabel* labelPtr = (TickLabel*)Chain_GetValue(link); + link = Chain_NextLink(link); + Segment2d seg; + makeTick(t1, infoPtr->t1, infoPtr->axis, &seg); + // Save tick label X-Y position + if (isHoriz) { + labelPtr->anchorPos.x = seg.p.x; + labelPtr->anchorPos.y = labelPos; + } + else { + labelPtr->anchorPos.x = labelPos; + labelPtr->anchorPos.y = seg.p.y; + } + } + } + segments_ = segments; + nSegments_ = sp - segments; +} + +Ticks* Axis::generateTicks(TickSweep *sweepPtr) +{ + Ticks* ticksPtr = new Ticks(sweepPtr->nSteps); + + if (sweepPtr->step == 0.0) { + // Hack: A zero step indicates to use log values + // Precomputed log10 values [1..10] + static double logTable[] = { + 0.0, + 0.301029995663981, + 0.477121254719662, + 0.602059991327962, + 0.698970004336019, + 0.778151250383644, + 0.845098040014257, + 0.903089986991944, + 0.954242509439325, + 1.0 + }; + for (int ii=0; iinSteps; ii++) + ticksPtr->values[ii] = logTable[ii]; + } + else { + double value = sweepPtr->initial; + for (int ii=0; iinSteps; ii++) { + value = (value/sweepPtr->step)*sweepPtr->step; + ticksPtr->values[ii] = value; + value += sweepPtr->step; + } + } + + return ticksPtr; +} + +void Axis::makeGridLine(double value, Segment2d *sp) +{ + AxisOptions* ops = (AxisOptions*)ops_; + + if (ops->logScale) + value = EXP10(value); + + if (isHorizontal()) { + sp->p.x = hMap(value); + sp->p.y = graphPtr_->top_; + sp->q.x = sp->p.x; + sp->q.y = graphPtr_->bottom_; + } + else { + sp->p.x = graphPtr_->left_; + sp->p.y = vMap(value); + sp->q.x = graphPtr_->right_; + sp->q.y = sp->p.y; + } +} + +void Axis::print(PSOutput* psPtr) +{ + AxisOptions* ops = (AxisOptions*)ops_; + PostscriptOptions* pops = (PostscriptOptions*)graphPtr_->postscript_->ops_; + + if (ops->hide || !use_) + return; + + psPtr->format("%% Axis \"%s\"\n", name_); + if (pops->decorations) { + if (ops->normalBg) { + int relief = active_ ? ops->activeRelief : ops->relief; + psPtr->fill3DRectangle(ops->normalBg, left_, top_, + right_-left_, bottom_-top_, + ops->borderWidth, relief); + } + } + else { + psPtr->setClearBackground(); + psPtr->fillRectangle(left_, top_, right_-left_, bottom_-top_); + } + + if (ops->title) { + TextStyle ts(graphPtr_); + TextStyleOptions* tops = (TextStyleOptions*)ts.ops(); + + tops->angle = titleAngle_; + tops->font = ops->titleFont; + tops->anchor = titleAnchor_; + tops->color = active_ ? ops->activeFgColor : ops->titleColor; + tops->justify = ops->titleJustify; + + ts.xPad_ = 1; + ts.yPad_ = 0; + ts.printText(psPtr, ops->title, titlePos_.x, titlePos_.y); + } + + if (ops->showTicks) { + TextStyle ts(graphPtr_); + TextStyleOptions* tops = (TextStyleOptions*)ts.ops(); + + tops->angle = ops->tickAngle; + tops->font = ops->tickFont; + tops->anchor = tickAnchor_; + tops->color = active_ ? ops->activeFgColor : ops->tickColor; + + ts.xPad_ = 2; + ts.yPad_ = 0; + + for (ChainLink* link = Chain_FirstLink(tickLabels_); link; + link = Chain_NextLink(link)) { + TickLabel *labelPtr = (TickLabel*)Chain_GetValue(link); + ts.printText(psPtr, labelPtr->string, labelPtr->anchorPos.x, + labelPtr->anchorPos.y); + } + } + + if ((nSegments_ > 0) && (ops->lineWidth > 0)) { + psPtr->setLineAttributes(active_ ? ops->activeFgColor : ops->tickColor, + ops->lineWidth, (Dashes*)NULL, CapButt, JoinMiter); + psPtr->printSegments(segments_, nSegments_); + } +} + +void Axis::printGrids(PSOutput* psPtr) +{ + AxisOptions* ops = (AxisOptions*)ops_; + + if (ops->hide || !ops->showGrid || !use_) + return; + + psPtr->format("%% Axis %s: grid line attributes\n", name_); + psPtr->setLineAttributes(ops->major.color, ops->major.lineWidth, + &ops->major.dashes, CapButt, JoinMiter); + psPtr->format("%% Axis %s: major grid line segments\n", name_); + psPtr->printSegments(ops->major.segments, ops->major.nUsed); + + if (ops->showGridMinor) { + psPtr->setLineAttributes(ops->minor.color, ops->minor.lineWidth, + &ops->minor.dashes, CapButt, JoinMiter); + psPtr->format("%% Axis %s: minor grid line segments\n", name_); + psPtr->printSegments(ops->minor.segments, ops->minor.nUsed); + } +} + +void Axis::printLimits(PSOutput* psPtr) +{ + AxisOptions* ops = (AxisOptions*)ops_; + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + + if (!ops->limitsFormat) + return; + + double vMin = graphPtr_->left_ + gops->xPad + 2; + double vMax = vMin; + double hMin = graphPtr_->bottom_ - gops->yPad - 2; + double hMax = hMin; + + const int spacing =8; + int isHoriz = isHorizontal(); + char* minPtr =NULL; + char* maxPtr =NULL; + char minString[200]; + char maxString[200]; + const char* fmt = ops->limitsFormat; + if (fmt && *fmt) { + minPtr = minString; + snprintf(minString, 200, fmt, axisRange_.min); + + maxPtr = maxString; + snprintf(maxString, 200, fmt, axisRange_.max); + } + if (ops->descending) { + char *tmp = minPtr; + minPtr = maxPtr; + maxPtr = tmp; + } + + int textWidth, textHeight; + TextStyle ts(graphPtr_, &ops->limitsTextStyle); + if (maxPtr) { + graphPtr_->getTextExtents(ops->tickFont, maxPtr, -1, + &textWidth, &textHeight); + if ((textWidth > 0) && (textHeight > 0)) { + if (isHoriz) { + ops->limitsTextStyle.angle = 90.0; + ops->limitsTextStyle.anchor = TK_ANCHOR_SE; + + ts.printText(psPtr, maxPtr, graphPtr_->right_, (int)hMax); + hMax -= (textWidth + spacing); + } + else { + ops->limitsTextStyle.angle = 0.0; + ops->limitsTextStyle.anchor = TK_ANCHOR_NW; + + ts.printText(psPtr, maxPtr, (int)vMax, graphPtr_->top_); + vMax += (textWidth + spacing); + } + } + } + + if (minPtr) { + graphPtr_->getTextExtents(ops->tickFont, minPtr, -1, + &textWidth, &textHeight); + if ((textWidth > 0) && (textHeight > 0)) { + ops->limitsTextStyle.anchor = TK_ANCHOR_SW; + + if (isHoriz) { + ops->limitsTextStyle.angle = 90.0; + + ts.printText(psPtr, minPtr, graphPtr_->left_, (int)hMin); + hMin -= (textWidth + spacing); + } + else { + ops->limitsTextStyle.angle = 0.0; + + ts.printText(psPtr, minPtr, (int)vMin, graphPtr_->bottom_); + vMin += (textWidth + spacing); + } + } + } +} + +void Axis::updateScrollbar(Tcl_Interp* interp, Tcl_Obj *scrollCmdObjPtr, + int first, int last, int width) +{ + double firstFract =0.0; + double lastFract = 1.0; + if (width > 0) { + firstFract = (double)first / (double)width; + lastFract = (double)last / (double)width; + } + Tcl_Obj *cmdObjPtr = Tcl_DuplicateObj(scrollCmdObjPtr); + Tcl_ListObjAppendElement(interp, cmdObjPtr, Tcl_NewDoubleObj(firstFract)); + Tcl_ListObjAppendElement(interp, cmdObjPtr, Tcl_NewDoubleObj(lastFract)); + Tcl_IncrRefCount(cmdObjPtr); + if (Tcl_EvalObjEx(interp, cmdObjPtr, TCL_EVAL_GLOBAL) != TCL_OK) { + Tcl_BackgroundError(interp); + } + Tcl_DecrRefCount(cmdObjPtr); +} + +void Axis::getGeometry() +{ + AxisOptions* ops = (AxisOptions*)ops_; + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + + freeTickLabels(); + + // Leave room for axis baseline and padding + unsigned int y =0; + if (ops->exterior && (gops->plotRelief != TK_RELIEF_SOLID)) + y += ops->lineWidth + 2; + + maxTickHeight_ = maxTickWidth_ = 0; + + if (t1Ptr_) + delete t1Ptr_; + t1Ptr_ = generateTicks(&majorSweep_); + + if (t2Ptr_) + delete t2Ptr_; + t2Ptr_ = generateTicks(&minorSweep_); + + if (ops->showTicks) { + Ticks* t1Ptr = ops->t1UPtr ? ops->t1UPtr : t1Ptr_; + + int nTicks =0; + if (t1Ptr) + nTicks = t1Ptr->nTicks; + + unsigned int nLabels =0; + for (int ii=0; iivalues[ii]; + double x2 = t1Ptr->values[ii]; + if (ops->labelOffset) + x2 += majorSweep_.step * 0.5; + + if (!inRange(x2, &axisRange_)) + continue; + + TickLabel* labelPtr = makeLabel(x); + tickLabels_->append(labelPtr); + nLabels++; + + // Get the dimensions of each tick label. Remember tick labels + // can be multi-lined and/or rotated. + int lw, lh; + graphPtr_->getTextExtents(ops->tickFont, labelPtr->string, -1, &lw, &lh); + labelPtr->width = lw; + labelPtr->height = lh; + + if (ops->tickAngle != 0.0) { + // Rotated label width and height + double rlw, rlh; + graphPtr_->getBoundingBox(lw, lh, ops->tickAngle, &rlw, &rlh, NULL); + lw = (int)rlw; + lh = (int)rlh; + } + if (maxTickWidth_ < int(lw)) + maxTickWidth_ = lw; + + if (maxTickHeight_ < int(lh)) + maxTickHeight_ = lh; + } + + unsigned int pad =0; + if (ops->exterior) { + // Because the axis cap style is "CapProjecting", we need to + // account for an extra 1.5 linewidth at the end of each line + pad = ((ops->lineWidth * 12) / 8); + } + if (isHorizontal()) + y += maxTickHeight_ + pad; + else { + y += maxTickWidth_ + pad; + if (maxTickWidth_ > 0) + // Pad either size of label. + y += 5; + } + y += 2 * AXIS_PAD_TITLE; + if ((ops->lineWidth > 0) && ops->exterior) + // Distance from axis line to tick label. + y += ops->tickLength; + + } // showTicks + + if (ops->title) { + if (ops->titleAlternate) { + if (y < titleHeight_) + y = titleHeight_; + } + else + y += titleHeight_ + AXIS_PAD_TITLE; + } + + // Correct for orientation of the axis + if (isHorizontal()) + height_ = y; + else + width_ = y; +} + diff --git a/generic/tkbltGrAxis.h b/generic/tkbltGrAxis.h new file mode 100644 index 0000000..d459e8c --- /dev/null +++ b/generic/tkbltGrAxis.h @@ -0,0 +1,266 @@ +/* + * 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 1993-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 IMPIED, 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. + */ + +#ifndef ___BltGrAxis_h__ +#define ___BltGrAxis_h__ + +#include + +#include "tkbltChain.h" + +#include "tkbltGrMisc.h" +#include "tkbltGrText.h" +#include "tkbltGrPSOutput.h" + +namespace Blt { + class Graph; + class Postscript; + + typedef struct { + int axis; + int t1; + int t2; + int label; + } AxisInfo; + + typedef struct { + const char* name; + ClassId classId; + } AxisName; + + extern AxisName axisNames[]; + + typedef struct { + Dashes dashes; + int lineWidth; + XColor* color; + GC gc; + Segment2d *segments; + int nUsed; + int nAllocated; + } Grid; + + typedef struct { + double min; + double max; + double range; + double scale; + } AxisRange; + + class TickLabel { + public: + Point2d anchorPos; + unsigned int width; + unsigned int height; + char* string; + + public: + TickLabel(char*); + virtual ~TickLabel(); + }; + + class Ticks { + public: + int nTicks; + double* values; + + public: + Ticks(int); + virtual ~Ticks(); + }; + + typedef struct { + double initial; + double step; + int nSteps; + } TickSweep; + + typedef struct { + const char** tags; + int checkLimits; + int exterior; + int showGrid; + int showGridMinor; + int hide; + int showTicks; + + double windowSize; + const char *tickFormatCmd; + int descending; + int labelOffset; + TextStyleOptions limitsTextStyle; + const char *limitsFormat; + int lineWidth; + int logScale; + int looseMin; + int looseMax; + Ticks* t1UPtr; + Ticks* t2UPtr; + double reqMin; + double reqMax; + Tcl_Obj *scrollCmdObjPtr; + int scrollUnits; + double reqScrollMin; + double reqScrollMax; + double shiftBy; + double reqStep; + int reqNumMajorTicks; + int reqNumMinorTicks; + int tickLength; + const char *title; + int titleAlternate; + + XColor* activeFgColor; + int activeRelief; + Tk_3DBorder normalBg; + int borderWidth; + XColor* tickColor; + Grid major; + Grid minor; + Tk_Justify titleJustify; + int relief; + double tickAngle; + Tk_Anchor reqTickAnchor; + Tk_Font tickFont; + Tk_Font titleFont; + XColor* titleColor; + + const char *tickFormat; + } AxisOptions; + + class Axis { + protected: + Tk_OptionTable optionTable_; + void* ops_; + + public: + Graph* graphPtr_; + ClassId classId_; + const char* name_; + const char* className_; + + Tcl_HashEntry* hashPtr_; + int refCount_; + int use_; + int active_; + + ChainLink* link; + Chain* chain; + + Point2d titlePos_; + unsigned int titleWidth_; + unsigned int titleHeight_; + double min_; + double max_; + double scrollMin_; + double scrollMax_; + AxisRange valueRange_; + AxisRange axisRange_; + double prevMin_; + double prevMax_; + Ticks* t1Ptr_; + Ticks* t2Ptr_; + TickSweep minorSweep_; + TickSweep majorSweep_; + + int margin_; + Segment2d *segments_; + int nSegments_; + Chain* tickLabels_; + int left_; + int right_; + int top_; + int bottom_; + int width_; + int height_; + int maxTickWidth_; + int maxTickHeight_; + Tk_Anchor tickAnchor_; + GC tickGC_; + GC activeTickGC_; + double titleAngle_; + Tk_Anchor titleAnchor_; + double screenScale_; + int screenMin_; + int screenRange_; + + protected: + double niceNum(double, int); + void setRange(AxisRange*, double, double); + void makeGridLine(double, Segment2d*); + void makeSegments(AxisInfo*); + void resetTextStyles(); + void makeLine(int, Segment2d*); + void makeTick(double, int, int, Segment2d*); + void offsets(int, int, AxisInfo*); + void updateScrollbar(Tcl_Interp*, Tcl_Obj*, int, int, int); + + public: + Axis(Graph*, const char*, int, Tcl_HashEntry*); + virtual ~Axis(); + + Tk_OptionTable optionTable() {return optionTable_;} + void* ops() {return ops_;} + ClassId classId() {return classId_;} + const char* className() {return className_;} + + int configure(); + void map(int, int); + void draw(Drawable); + void drawGrids(Drawable); + void drawLimits(Drawable); + void print(PSOutput*); + void printGrids(PSOutput*); + void printLimits(PSOutput*); + + void mapStacked(int, int); + void mapGridlines(); + void setClass(ClassId); + void logScale(double, double); + void linearScale(double, double); + void fixRange(); + int isHorizontal(); + void freeTickLabels(); + TickLabel* makeLabel(double); + void getDataLimits(double, double); + Ticks* generateTicks(TickSweep*); + int inRange(double, AxisRange*); + void getGeometry(); + + double invHMap(double x); + double invVMap(double y); + double hMap(double x); + double vMap(double y); + }; +}; + +#endif diff --git a/generic/tkbltGrAxisOp.C b/generic/tkbltGrAxisOp.C new file mode 100644 index 0000000..e467d53 --- /dev/null +++ b/generic/tkbltGrAxisOp.C @@ -0,0 +1,648 @@ +/* + * 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 1993-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 + +#include + +#include "tkbltGrBind.h" +#include "tkbltGraph.h" +#include "tkbltGrAxis.h" +#include "tkbltGrAxisOp.h" +#include "tkbltGrMisc.h" +#include "tkbltInt.h" + +using namespace Blt; + +#define EXP10(x) (pow(10.0,(x))) + +static int GetAxisScrollInfo(Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[], + double *offsetPtr, double windowSize, + double scrollUnits, double scale); + +static double Clamp(double x) +{ + return (x < 0.0) ? 0.0 : (x > 1.0) ? 1.0 : x; +} + +int Blt::AxisObjConfigure(Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = axisPtr->graphPtr_; + Tk_SavedOptions savedOptions; + int mask =0; + int error; + Tcl_Obj* errorResult; + + for (error=0; error<=1; error++) { + if (!error) { + if (Tk_SetOptions(interp, (char*)axisPtr->ops(), axisPtr->optionTable(), + objc, objv, graphPtr->tkwin_, &savedOptions, &mask) + != TCL_OK) + continue; + } + else { + errorResult = Tcl_GetObjResult(interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + } + + if (axisPtr->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<4) + return TCL_ERROR; + + Axis* axisPtr; + if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) + return TCL_ERROR; + + return AxisCgetOp(axisPtr, interp, objc-1, objv+1); +} + +static int ConfigureOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Axis* axisPtr; + if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) + return TCL_ERROR; + + return AxisConfigureOp(axisPtr, interp, objc-1, objv+1); +} + +static int ActivateOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Axis* axisPtr; + if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) + return TCL_ERROR; + + return AxisActivateOp(axisPtr, interp, objc, objv); +} + +static int BindOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc == 3) { + Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + Tcl_HashSearch iter; + for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&graphPtr->axes_.tagTable, &iter); hPtr; hPtr = Tcl_NextHashEntry(&iter)) { + char* tagName = (char*)Tcl_GetHashKey(&graphPtr->axes_.tagTable, hPtr); + Tcl_Obj* objPtr = Tcl_NewStringObj(tagName, -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; + } + else + return graphPtr->bindTable_->configure(graphPtr->axisTag(Tcl_GetString(objv[3])), objc-4, objv+4); +} + +static int CreateOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + if (graphPtr->createAxis(objc, objv) != TCL_OK) + return TCL_ERROR; + Tcl_SetObjResult(interp, objv[3]); + + return TCL_OK; +} + +static int DeleteOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Axis* axisPtr; + if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) + return TCL_ERROR; + + if (axisPtr->refCount_ == 0) + delete axisPtr; + + graphPtr->flags |= RESET; + graphPtr->eventuallyRedraw(); + + return TCL_OK; +} + +static int InvTransformOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Axis* axisPtr; + if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) + return TCL_ERROR; + + return AxisInvTransformOp(axisPtr, interp, objc-1, objv+1); +} + +static int LimitsOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Axis* axisPtr; + if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) + return TCL_ERROR; + + return AxisLimitsOp(axisPtr, interp, objc-1, objv+1); +} + +static int MarginOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Axis* axisPtr; + if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) + return TCL_ERROR; + + return AxisMarginOp(axisPtr, interp, objc-1, objv+1); +} + +static int NamesOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + if (objc == 3) { + Tcl_HashSearch cursor; + for (Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&graphPtr->axes_.table, &cursor); hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { + Axis* axisPtr = (Axis*)Tcl_GetHashValue(hPtr); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj(axisPtr->name_, -1)); + } + } + else { + Tcl_HashSearch cursor; + for (Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&graphPtr->axes_.table, &cursor); hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { + Axis* axisPtr = (Axis*)Tcl_GetHashValue(hPtr); + for (int ii=3; iiname_, pattern)) { + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewStringObj(axisPtr->name_, -1)); + break; + } + } + } + } + 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; + if (objc<4) + return TCL_ERROR; + + Axis* axisPtr; + if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) + return TCL_ERROR; + + return AxisTransformOp(axisPtr, interp, objc-1, objv+1); +} + +static int TypeOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Axis* axisPtr; + if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) + return TCL_ERROR; + + return AxisTypeOp(axisPtr, interp, objc-1, objv+1); +} + +static int ViewOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Axis* axisPtr; + if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) + return TCL_ERROR; + + return AxisViewOp(axisPtr, interp, objc-1, objv+1); +} + +const Ensemble Blt::axisEnsemble[] = { + {"activate", ActivateOp, 0}, + {"bind", BindOp, 0}, + {"cget", CgetOp,0 }, + {"configure", ConfigureOp,0 }, + {"create", CreateOp, 0}, + {"deactivate", ActivateOp, 0}, + {"delete", DeleteOp, 0}, + {"invtransform", InvTransformOp, 0}, + {"limits", LimitsOp, 0}, + {"margin", MarginOp, 0}, + {"names", NamesOp, 0}, + {"transform", TransformOp, 0}, + {"type", TypeOp, 0}, + {"view", ViewOp, 0}, + { 0,0,0 } +}; + +// Support + +double AdjustViewport(double offset, double windowSize) +{ + // Canvas-style scrolling allows the world to be scrolled within the window. + if (windowSize > 1.0) { + if (windowSize < (1.0 - offset)) + offset = 1.0 - windowSize; + + if (offset > 0.0) + offset = 0.0; + } + else { + if ((offset + windowSize) > 1.0) + offset = 1.0 - windowSize; + + if (offset < 0.0) + offset = 0.0; + } + return offset; +} + +static int GetAxisScrollInfo(Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[], + double *offsetPtr, double windowSize, + double scrollUnits, double scale) +{ + const char *string; + char c; + double offset; + int length; + + offset = *offsetPtr; + string = Tcl_GetStringFromObj(objv[0], &length); + c = string[0]; + scrollUnits *= scale; + if ((c == 's') && (strncmp(string, "scroll", length) == 0)) { + int count; + double fract; + + /* Scroll number unit/page */ + if (Tcl_GetIntFromObj(interp, objv[1], &count) != TCL_OK) + return TCL_ERROR; + + string = Tcl_GetStringFromObj(objv[2], &length); + c = string[0]; + if ((c == 'u') && (strncmp(string, "units", length) == 0)) + fract = count * scrollUnits; + else if ((c == 'p') && (strncmp(string, "pages", length) == 0)) + /* A page is 90% of the view-able window. */ + fract = (int)(count * windowSize * 0.9 + 0.5); + else if ((c == 'p') && (strncmp(string, "pixels", length) == 0)) + fract = count * scale; + else { + Tcl_AppendResult(interp, "unknown \"scroll\" units \"", string, + "\"", NULL); + return TCL_ERROR; + } + offset += fract; + } + else if ((c == 'm') && (strncmp(string, "moveto", length) == 0)) { + double fract; + + /* moveto fraction */ + if (Tcl_GetDoubleFromObj(interp, objv[1], &fract) != TCL_OK) { + return TCL_ERROR; + } + offset = fract; + } + else { + int count; + double fract; + + /* Treat like "scroll units" */ + if (Tcl_GetIntFromObj(interp, objv[0], &count) != TCL_OK) { + return TCL_ERROR; + } + fract = (double)count * scrollUnits; + offset += fract; + /* CHECK THIS: return TCL_OK; */ + } + *offsetPtr = AdjustViewport(offset, windowSize); + return TCL_OK; +} + +// Common Ops + +int AxisCgetOp(Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = axisPtr->graphPtr_; + + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "cget option"); + return TCL_ERROR; + } + + Tcl_Obj* objPtr = Tk_GetOptionValue(interp, (char*)axisPtr->ops(), + axisPtr->optionTable(), + objv[3], graphPtr->tkwin_); + if (!objPtr) + return TCL_ERROR; + else + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; +} + +int AxisConfigureOp(Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = axisPtr->graphPtr_; + + if (objc <= 4) { + Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)axisPtr->ops(), + axisPtr->optionTable(), + (objc == 4) ? objv[3] : NULL, + graphPtr->tkwin_); + if (!objPtr) + return TCL_ERROR; + else + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; + } + else + return AxisObjConfigure(axisPtr, interp, objc-3, objv+3); +} + +int AxisActivateOp(Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + AxisOptions* ops = (AxisOptions*)axisPtr->ops(); + Graph* graphPtr = axisPtr->graphPtr_; + const char *string; + + string = Tcl_GetString(objv[2]); + axisPtr->active_ = (string[0] == 'a') ? 1 : 0; + + if (!ops->hide && axisPtr->use_) { + graphPtr->flags |= RESET; + graphPtr->eventuallyRedraw(); + } + + return TCL_OK; +} + +int AxisInvTransformOp(Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = axisPtr->graphPtr_; + + if (graphPtr->flags & RESET) + graphPtr->resetAxes(); + + int sy; + if (Tcl_GetIntFromObj(interp, objv[3], &sy) != TCL_OK) + return TCL_ERROR; + + // Is the axis vertical or horizontal? + // Check the site where the axis was positioned. If the axis is + // virtual, all we have to go on is how it was mapped to an + // element (using either -mapx or -mapy options). + double y = axisPtr->isHorizontal() ? + axisPtr->invHMap(sy) : axisPtr->invVMap(sy); + + Tcl_SetDoubleObj(Tcl_GetObjResult(interp), y); + return TCL_OK; +} + +int AxisLimitsOp(Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + AxisOptions* ops = (AxisOptions*)axisPtr->ops(); + Graph* graphPtr = axisPtr->graphPtr_; + + if (graphPtr->flags & RESET) + graphPtr->resetAxes(); + + double min, max; + if (ops->logScale) { + min = EXP10(axisPtr->axisRange_.min); + max = EXP10(axisPtr->axisRange_.max); + } + else { + min = axisPtr->axisRange_.min; + max = axisPtr->axisRange_.max; + } + + Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(min)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(max)); + + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; +} + +int AxisMarginOp(Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + const char *marginName = ""; + if (axisPtr->use_) + marginName = axisNames[axisPtr->margin_].name; + + Tcl_SetStringObj(Tcl_GetObjResult(interp), marginName, -1); + return TCL_OK; +} + +int AxisTransformOp(Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = axisPtr->graphPtr_; + + if (graphPtr->flags & RESET) + graphPtr->resetAxes(); + + double x; + if (Tcl_GetDoubleFromObj(interp, objv[3], &x) != TCL_OK) + return TCL_ERROR; + + if (axisPtr->isHorizontal()) + x = axisPtr->hMap(x); + else + x = axisPtr->vMap(x); + + Tcl_SetIntObj(Tcl_GetObjResult(interp), (int)x); + return TCL_OK; +} + +int AxisTypeOp(Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + const char* typeName = ""; + if (axisPtr->use_) { + switch (axisPtr->classId_) { + case CID_AXIS_X: + typeName = "x"; + break; + case CID_AXIS_Y: + typeName = "y"; + break; + } + } + + Tcl_SetStringObj(Tcl_GetObjResult(interp), typeName, -1); + return TCL_OK; +} + +int AxisViewOp(Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + AxisOptions* ops = (AxisOptions*)axisPtr->ops(); + Graph* graphPtr = axisPtr->graphPtr_; + double worldMin = axisPtr->valueRange_.min; + double worldMax = axisPtr->valueRange_.max; + /* Override data dimensions with user-selected limits. */ + if (!isnan(axisPtr->scrollMin_)) + worldMin = axisPtr->scrollMin_; + + if (!isnan(axisPtr->scrollMax_)) + worldMax = axisPtr->scrollMax_; + + double viewMin = axisPtr->min_; + double viewMax = axisPtr->max_; + /* Bound the view within scroll region. */ + if (viewMin < worldMin) + viewMin = worldMin; + + if (viewMax > worldMax) + viewMax = worldMax; + + if (ops->logScale) { + worldMin = log10(worldMin); + worldMax = log10(worldMax); + viewMin = log10(viewMin); + viewMax = log10(viewMax); + } + double worldWidth = worldMax - worldMin; + double viewWidth = viewMax - viewMin; + + /* Unlike horizontal axes, vertical axis values run opposite of the + * scrollbar first/last values. So instead of pushing the axis minimum + * around, we move the maximum instead. */ + double axisOffset; + double axisScale; + if (axisPtr->isHorizontal() != ops->descending) { + axisOffset = viewMin - worldMin; + axisScale = graphPtr->hScale_; + } else { + axisOffset = worldMax - viewMax; + axisScale = graphPtr->vScale_; + } + if (objc == 4) { + double first = Clamp(axisOffset / worldWidth); + double last = Clamp((axisOffset + viewWidth) / worldWidth); + Tcl_Obj *listObjPtr = Tcl_NewListObj(0, NULL); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(first)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(last)); + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; + } + double fract = axisOffset / worldWidth; + if (GetAxisScrollInfo(interp, objc, objv, &fract, viewWidth / worldWidth, + ops->scrollUnits, axisScale) != TCL_OK) + return TCL_ERROR; + + if (axisPtr->isHorizontal() != ops->descending) { + ops->reqMin = (fract * worldWidth) + worldMin; + ops->reqMax = ops->reqMin + viewWidth; + } + else { + ops->reqMax = worldMax - (fract * worldWidth); + ops->reqMin = ops->reqMax - viewWidth; + } + if (ops->logScale) { + ops->reqMin = EXP10(ops->reqMin); + ops->reqMax = EXP10(ops->reqMax); + } + + graphPtr->flags |= RESET; + graphPtr->eventuallyRedraw(); + + return TCL_OK; +} + diff --git a/generic/tkbltGrAxisOp.h b/generic/tkbltGrAxisOp.h new file mode 100644 index 0000000..777aea7 --- /dev/null +++ b/generic/tkbltGrAxisOp.h @@ -0,0 +1,60 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrAxisOp_h__ +#define __BltGrAxisOp_h__ + +#include "tkbltGraph.h" + +namespace Blt { + extern const Ensemble axisEnsemble[]; + extern int AxisObjConfigure(Blt::Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); +}; + +extern int AxisCgetOp(Blt::Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); +extern int AxisConfigureOp(Blt::Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); +extern int AxisActivateOp(Blt::Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); +extern int AxisInvTransformOp(Blt::Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); +extern int AxisLimitsOp(Blt::Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); +extern int AxisMarginOp(Blt::Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); +extern int AxisTransformOp(Blt::Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); +extern int AxisTypeOp(Blt::Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); +extern int AxisViewOp(Blt::Axis* axisPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); + +#endif diff --git a/generic/tkbltGrAxisOption.C b/generic/tkbltGrAxisOption.C new file mode 100644 index 0000000..6f91d99 --- /dev/null +++ b/generic/tkbltGrAxisOption.C @@ -0,0 +1,264 @@ +/* + * 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 1993-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 + +#include + +#include "tkbltGraph.h" +#include "tkbltGrAxis.h" +#include "tkbltGrAxisOption.h" +#include "tkbltConfig.h" +#include "tkbltInt.h" + +using namespace Blt; + +static Tk_CustomOptionSetProc AxisSetProc; +static Tk_CustomOptionGetProc AxisGetProc; +static Tk_CustomOptionFreeProc AxisFreeProc; +Tk_ObjCustomOption xAxisObjOption = + { + "xaxis", AxisSetProc, AxisGetProc, RestoreProc, AxisFreeProc, + (ClientData)CID_AXIS_X + }; +Tk_ObjCustomOption yAxisObjOption = + { + "yaxis", AxisSetProc, AxisGetProc, RestoreProc, AxisFreeProc, + (ClientData)CID_AXIS_Y + }; + +static int AxisSetProc(ClientData clientData, Tcl_Interp* interp, + Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, + int offset, char* savePtr, int flags) +{ + Axis** axisPtrPtr = (Axis**)(widgRec + offset); + *(double*)savePtr = *(double*)axisPtrPtr; + + if (!axisPtrPtr) + return TCL_OK; + + Graph* graphPtr = getGraphFromWindowData(tkwin); +#ifdef _WIN64 + ClassId classId = (ClassId)((long long)clientData); +#else + ClassId classId = (ClassId)((long)clientData); +#endif + + Axis *axisPtr; + if (graphPtr->getAxis(*objPtr, &axisPtr) != TCL_OK) + return TCL_ERROR; + + if (classId != CID_NONE) { + // Set the axis type on the first use of it. + if ((axisPtr->refCount_ == 0) || (axisPtr->classId_ == CID_NONE)) + axisPtr->setClass(classId); + + else if (axisPtr->classId_ != classId) { + Tcl_AppendResult(interp, "axis \"", Tcl_GetString(*objPtr), + "\" is already in use on an opposite ", + axisPtr->className_, "-axis", + NULL); + return TCL_ERROR; + } + axisPtr->refCount_++; + } + + *axisPtrPtr = axisPtr; + return TCL_OK; +}; + +static Tcl_Obj* AxisGetProc(ClientData clientData, Tk_Window tkwin, + char *widgRec, int offset) +{ + Axis* axisPtr = *(Axis**)(widgRec + offset); + if (!axisPtr) + return Tcl_NewStringObj("", -1); + + return Tcl_NewStringObj(axisPtr->name_, -1); +}; + +static void AxisFreeProc(ClientData clientData, Tk_Window tkwin, char *ptr) +{ + Axis* axisPtr = *(Axis**)ptr; + if (axisPtr) { + axisPtr->refCount_--; + if (axisPtr->refCount_ == 0) + delete axisPtr; + } +} + +static Tk_CustomOptionSetProc LimitSetProc; +static Tk_CustomOptionGetProc LimitGetProc; +Tk_ObjCustomOption limitObjOption = + { + "limit", LimitSetProc, LimitGetProc, NULL, NULL, NULL + }; + +static int LimitSetProc(ClientData clientData, Tcl_Interp* interp, + Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, + int offset, char* save, int flags) +{ + double* limitPtr = (double*)(widgRec + offset); + const char* string = Tcl_GetString(*objPtr); + if (!string || !string[0]) { + *limitPtr = NAN; + return TCL_OK; + } + + if (Tcl_GetDoubleFromObj(interp, *objPtr, limitPtr) != TCL_OK) + return TCL_ERROR; + + return TCL_OK; +} + +static Tcl_Obj* LimitGetProc(ClientData clientData, Tk_Window tkwin, + char *widgRec, int offset) +{ + double limit = *(double*)(widgRec + offset); + Tcl_Obj* objPtr; + + if (!isnan(limit)) + objPtr = Tcl_NewDoubleObj(limit); + else + objPtr = Tcl_NewStringObj("", -1); + + return objPtr; +} + +static Tk_CustomOptionSetProc TicksSetProc; +static Tk_CustomOptionGetProc TicksGetProc; +static Tk_CustomOptionFreeProc TicksFreeProc; +Tk_ObjCustomOption ticksObjOption = + { + "ticks", TicksSetProc, TicksGetProc, RestoreProc, TicksFreeProc, NULL + }; + +static int TicksSetProc(ClientData clientData, Tcl_Interp* interp, + Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, + int offset, char* savePtr, int flags) +{ + Ticks** ticksPtrPtr = (Ticks**)(widgRec + offset); + *(double*)savePtr = *(double*)ticksPtrPtr; + + if (!ticksPtrPtr) + return TCL_OK; + + int objc; + Tcl_Obj** objv; + if (Tcl_ListObjGetElements(interp, *objPtr, &objc, &objv) != TCL_OK) + return TCL_ERROR; + + Ticks* ticksPtr = NULL; + if (objc > 0) { + ticksPtr = new Ticks(objc); + for (int ii=0; iivalues[ii] = value; + } + ticksPtr->nTicks = objc; + } + + *ticksPtrPtr = ticksPtr; + + return TCL_OK; +} + +static Tcl_Obj* TicksGetProc(ClientData clientData, Tk_Window tkwin, + char *widgRec, int offset) +{ + Ticks* ticksPtr = *(Ticks**)(widgRec + offset); + + if (!ticksPtr) + return Tcl_NewListObj(0, NULL); + + int cnt = ticksPtr->nTicks; + Tcl_Obj** ll = new Tcl_Obj*[cnt]; + for (int ii = 0; iivalues[ii]); + + Tcl_Obj* listObjPtr = Tcl_NewListObj(cnt, ll); + delete [] ll; + + return listObjPtr; +} + +static void TicksFreeProc(ClientData clientData, Tk_Window tkwin, + char *ptr) +{ + Ticks* ticksPtr = *(Ticks**)ptr; + delete ticksPtr; +} + +static Tk_CustomOptionSetProc ObjectSetProc; +static Tk_CustomOptionGetProc ObjectGetProc; +static Tk_CustomOptionFreeProc ObjectFreeProc; +Tk_ObjCustomOption objectObjOption = + { + "object", ObjectSetProc, ObjectGetProc, RestoreProc, ObjectFreeProc, NULL, + }; + +static int ObjectSetProc(ClientData clientData, Tcl_Interp* interp, + Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, + int offset, char* savePtr, int flags) +{ + Tcl_Obj** objectPtrPtr = (Tcl_Obj**)(widgRec + offset); + *(double*)savePtr = *(double*)objectPtrPtr; + + if (!objectPtrPtr) + return TCL_OK; + + Tcl_IncrRefCount(*objPtr); + *objectPtrPtr = *objPtr; + + return TCL_OK; +} + +static Tcl_Obj* ObjectGetProc(ClientData clientData, Tk_Window tkwin, + char *widgRec, int offset) +{ + Tcl_Obj** objectPtrPtr = (Tcl_Obj**)(widgRec + offset); + + if (!objectPtrPtr) + return Tcl_NewObj(); + + return *objectPtrPtr; +} + +static void ObjectFreeProc(ClientData clientData, Tk_Window tkwin, + char *ptr) +{ + Tcl_Obj* objectPtr = *(Tcl_Obj**)ptr; + if (objectPtr) + Tcl_DecrRefCount(objectPtr); +} + diff --git a/generic/tkbltGrAxisOption.h b/generic/tkbltGrAxisOption.h new file mode 100644 index 0000000..4efa8ee --- /dev/null +++ b/generic/tkbltGrAxisOption.h @@ -0,0 +1,41 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrAxisOption_h__ +#define __BltGrAxisOption_h__ + +#include + +extern Tk_ObjCustomOption xAxisObjOption; +extern Tk_ObjCustomOption yAxisObjOption; +extern Tk_ObjCustomOption limitObjOption; +extern Tk_ObjCustomOption ticksObjOption; +extern Tk_ObjCustomOption objectObjOption; + +#endif diff --git a/generic/tkbltGrBind.C b/generic/tkbltGrBind.C new file mode 100644 index 0000000..3e7e81e --- /dev/null +++ b/generic/tkbltGrBind.C @@ -0,0 +1,227 @@ +/* + * 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 1998 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 + +#include +#include +#include +using namespace std; + +#include "tkbltGrBind.h" +#include "tkbltGraph.h" +#include "tkbltGrLegd.h" + +using namespace Blt; + +static Tk_EventProc BindProc; + +BindTable::BindTable(Graph* graphPtr, Pick* pickPtr) +{ + graphPtr_ = graphPtr; + pickPtr_ = pickPtr; + grab_ =0; + table_ = Tk_CreateBindingTable(graphPtr->interp_); + currentItem_ =NULL; + currentContext_ =CID_NONE; + newItem_ =NULL; + newContext_ =CID_NONE; + focusItem_ =NULL; + focusContext_ =CID_NONE; + // pickEvent =NULL; + state_ =0; + + unsigned int mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask | + ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | + PointerMotionMask); + Tk_CreateEventHandler(graphPtr->tkwin_, mask, BindProc, this); +} + +BindTable::~BindTable() +{ + Tk_DeleteBindingTable(table_); + unsigned int mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask | + ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | + PointerMotionMask); + Tk_DeleteEventHandler(graphPtr_->tkwin_, mask, BindProc, this); +} + +int BindTable::configure(ClientData item, int objc, Tcl_Obj* const objv[]) +{ + if (objc == 0) { + Tk_GetAllBindings(graphPtr_->interp_, table_, item); + return TCL_OK; + } + + const char *string = Tcl_GetString(objv[0]); + if (objc == 1) { + const char* command = + Tk_GetBinding(graphPtr_->interp_, table_, item, string); + if (!command) { + Tcl_ResetResult(graphPtr_->interp_); + Tcl_AppendResult(graphPtr_->interp_, "invalid binding event \"", + string, "\"", NULL); + return TCL_ERROR; + } + Tcl_SetStringObj(Tcl_GetObjResult(graphPtr_->interp_), command, -1); + return TCL_OK; + } + + const char* seq = string; + const char* command = Tcl_GetString(objv[1]); + if (command[0] == '\0') + return Tk_DeleteBinding(graphPtr_->interp_, table_, item, seq); + + unsigned long mask; + if (command[0] == '+') + mask = Tk_CreateBinding(graphPtr_->interp_, table_, + item, seq, command+1, 1); + else + mask = Tk_CreateBinding(graphPtr_->interp_, table_, + item, seq, command, 0); + if (!mask) + return TCL_ERROR; + + if (mask & (unsigned) ~(ButtonMotionMask|Button1MotionMask + |Button2MotionMask|Button3MotionMask|Button4MotionMask + |Button5MotionMask|ButtonPressMask|ButtonReleaseMask + |EnterWindowMask|LeaveWindowMask|KeyPressMask + |KeyReleaseMask|PointerMotionMask|VirtualEventMask)) { + Tk_DeleteBinding(graphPtr_->interp_, table_, item, seq); + Tcl_ResetResult(graphPtr_->interp_); + Tcl_AppendResult(graphPtr_->interp_, "requested illegal events; ", + "only key, button, motion, enter, leave, and virtual ", + "events may be used", (char *)NULL); + return TCL_ERROR; + } + + return TCL_OK; +} + +void BindTable::deleteBindings(ClientData object) +{ + Tk_DeleteAllBindings(table_, object); + + if (currentItem_ == object) { + currentItem_ =NULL; + currentContext_ =CID_NONE; + } + + if (newItem_ == object) { + newItem_ =NULL; + newContext_ =CID_NONE; + } + + if (focusItem_ == object) { + focusItem_ =NULL; + focusContext_ =CID_NONE; + } +} + +void BindTable::doEvent(XEvent* eventPtr) +{ + ClientData item = currentItem_; + ClassId classId = currentContext_; + + if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) { + item = focusItem_; + classId = focusContext_; + } + if (!item) + return; + + int nTags; + const char** tagArray = graphPtr_->getTags(item, classId, &nTags); + Tk_BindEvent(table_, eventPtr, graphPtr_->tkwin_, nTags, (void**)tagArray); + + delete [] tagArray; +} + +void BindTable::pickItem(XEvent* eventPtr) +{ + int buttonDown = state_ + & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask); + + // A LeaveNotify event automatically means that there's no current item, + if (eventPtr->type != LeaveNotify) { + int x = eventPtr->xcrossing.x; + int y = eventPtr->xcrossing.y; + newItem_ = pickPtr_->pickEntry(x, y, &newContext_); + } + else { + newItem_ =NULL; + newContext_ = CID_NONE; + } + + // Nothing to do: the current item hasn't changed. + if ((newItem_ == currentItem_) && !grab_) + return; + + if (!buttonDown) + grab_ =0; + + if ((newItem_ != currentItem_) && buttonDown) { + grab_ =1; + return; + } + + grab_ =0; + currentItem_ = newItem_; + currentContext_ = newContext_; +} + +static void BindProc(ClientData clientData, XEvent* eventPtr) +{ + BindTable* bindPtr = (BindTable*)clientData; + Tcl_Preserve(bindPtr->graphPtr_); + + switch (eventPtr->type) { + case ButtonPress: + case ButtonRelease: + bindPtr->state_ = eventPtr->xbutton.state; + break; + case EnterNotify: + case LeaveNotify: + bindPtr->state_ = eventPtr->xcrossing.state; + break; + case MotionNotify: + bindPtr->state_ = eventPtr->xmotion.state; + break; + case KeyPress: + case KeyRelease: + bindPtr->state_ = eventPtr->xkey.state; + break; + } + + bindPtr->pickItem(eventPtr); + bindPtr->doEvent(eventPtr); + + Tcl_Release(bindPtr->graphPtr_); +} + diff --git a/generic/tkbltGrBind.h b/generic/tkbltGrBind.h new file mode 100644 index 0000000..7947210 --- /dev/null +++ b/generic/tkbltGrBind.h @@ -0,0 +1,72 @@ +/* + * 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 1998-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. + */ + +#ifndef __BltGrBind_h__ +#define __BltGrBind_h__ + +#include + +#include "tkbltGrMisc.h" + +namespace Blt { + class Graph; + class Pick; + + class BindTable { + protected: + Tk_BindingTable table_; + unsigned int grab_; + ClientData newItem_; + ClassId newContext_; + Pick* pickPtr_; + + public: + Graph* graphPtr_; + ClientData currentItem_; + ClassId currentContext_; + ClientData focusItem_; + ClassId focusContext_; + int state_; + XEvent pickEvent_; + + public: + BindTable(Graph*, Pick*); + virtual ~BindTable(); + + int configure(ClientData, int, Tcl_Obj *const []); + void deleteBindings(ClientData object); + void doEvent(XEvent*); + void pickItem(XEvent*); + + ClientData currentItem() {return currentItem_;} + }; +}; + + +#endif diff --git a/generic/tkbltGrDef.h b/generic/tkbltGrDef.h new file mode 100644 index 0000000..d73836a --- /dev/null +++ b/generic/tkbltGrDef.h @@ -0,0 +1,45 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrDef_h__ +#define __BltGrDef_h__ + +#define STD_NORMAL_BACKGROUND "gray85" +#define STD_NORMAL_FOREGROUND "black" +#define STD_ACTIVE_BACKGROUND "gray90" +#define STD_ACTIVE_FOREGROUND "black" + +#define STD_FONT_LARGE "helvetica 16 normal roman" +#define STD_FONT_MEDIUM "helvetica 14 normal roman" +#define STD_FONT_NORMAL "helvetica 12 normal roman" +#define STD_FONT_SMALL "helvetica 10 normal roman" + +#define STD_BORDERWIDTH "2" + +#endif diff --git a/generic/tkbltGrElem.C b/generic/tkbltGrElem.C new file mode 100644 index 0000000..faf1b72 --- /dev/null +++ b/generic/tkbltGrElem.C @@ -0,0 +1,284 @@ +/* + * 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 1993-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 +#include +#include + +#include + +#include "tkbltGraph.h" +#include "tkbltGrBind.h" +#include "tkbltGrElem.h" +#include "tkbltGrPen.h" +#include "tkbltInt.h" + +using namespace Blt; + +// Class ElemValues + +ElemValues::ElemValues() +{ + values_ =NULL; + nValues_ =0; + min_ =0; + max_ =0; +} + +ElemValues::~ElemValues() +{ + delete [] values_; +} + +void ElemValues::reset() +{ + delete [] values_; + values_ =NULL; + nValues_ =0; + min_ =0; + max_ =0; +} + +ElemValuesSource::ElemValuesSource(int nn) : ElemValues() +{ + nValues_ = nn; + values_ = new double[nn]; +} + +ElemValuesSource::ElemValuesSource(int nn, double* vv) : ElemValues() +{ + nValues_ = nn; + values_ = vv; +} + +ElemValuesSource::~ElemValuesSource() +{ +} + +void ElemValuesSource::findRange() +{ + if (nValues_<1 || !values_) + return; + + min_ = DBL_MAX; + max_ = -DBL_MAX; + for (int ii=0; ii max_) + max_ = values_[ii]; + } + } +} + +ElemValuesVector::ElemValuesVector(Element* ptr, const char* vecName) + : ElemValues() +{ + elemPtr_ = ptr; + Graph* graphPtr = elemPtr_->graphPtr_; + source_ = Blt_AllocVectorId(graphPtr->interp_, vecName); +} + +ElemValuesVector::~ElemValuesVector() +{ + freeSource(); +} + +int ElemValuesVector::getVector() +{ + Graph* graphPtr = elemPtr_->graphPtr_; + + Blt_Vector *vecPtr; + if (Blt_GetVectorById(graphPtr->interp_, source_, &vecPtr) != TCL_OK) + return TCL_ERROR; + + if (fetchValues(vecPtr) != TCL_OK) { + freeSource(); + return TCL_ERROR; + } + + Blt_SetVectorChangedProc(source_, VectorChangedProc, this); + return TCL_OK; +} + +int ElemValuesVector::fetchValues(Blt_Vector* vector) +{ + Graph* graphPtr = elemPtr_->graphPtr_; + + delete [] values_; + values_ = NULL; + nValues_ = 0; + min_ =0; + max_ =0; + + int ss = Blt_VecLength(vector); + if (!ss) + return TCL_OK; + + double* array = new double[ss]; + if (!array) { + Tcl_AppendResult(graphPtr->interp_, "can't allocate new vector", NULL); + return TCL_ERROR; + } + + memcpy(array, Blt_VecData(vector), ss*sizeof(double)); + values_ = array; + nValues_ = Blt_VecLength(vector); + min_ = Blt_VecMin(vector); + max_ = Blt_VecMax(vector); + + return TCL_OK; +} + +void ElemValuesVector::freeSource() +{ + if (source_) { + Blt_SetVectorChangedProc(source_, NULL, NULL); + Blt_FreeVectorId(source_); + source_ = NULL; + } +} + +// Class Element + +Element::Element(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) +{ + graphPtr_ = graphPtr; + name_ = dupstr(name); + optionTable_ =NULL; + ops_ =NULL; + hashPtr_ = hPtr; + + row_ =0; + col_ =0; + activeIndices_ =NULL; + nActiveIndices_ =0; + xRange_ =0; + yRange_ =0; + active_ =0; + labelActive_ =0; + + link =NULL; +} + +Element::~Element() +{ + graphPtr_->bindTable_->deleteBindings(this); + + if (link) + graphPtr_->elements_.displayList->deleteLink(link); + + if (hashPtr_) + Tcl_DeleteHashEntry(hashPtr_); + + delete [] name_; + + delete [] activeIndices_; + + Tk_FreeConfigOptions((char*)ops_, optionTable_, graphPtr_->tkwin_); + free(ops_); +} + +double Element::FindElemValuesMinimum(ElemValues* valuesPtr, double minLimit) +{ + double min = DBL_MAX; + if (!valuesPtr) + return min; + + for (int ii=0; iinValues(); ii++) { + double x = valuesPtr->values_[ii]; + // What do you do about negative values when using log + // scale values seems like a grey area. Mirror. + if (x < 0.0) + x = -x; + if ((x > minLimit) && (min > x)) + min = x; + } + if (min == DBL_MAX) + min = minLimit; + + return min; +} + +PenStyle** Element::StyleMap() +{ + ElementOptions* ops = (ElementOptions*)ops_; + + int nPoints = NUMBEROFPOINTS(ops); + int nWeights = MIN(ops->w ? ops->w->nValues() : 0, nPoints); + double* w = ops->w ? ops->w->values_ : NULL; + ChainLink* link = Chain_FirstLink(ops->stylePalette); + PenStyle* stylePtr = (PenStyle*)Chain_GetValue(link); + + // Create a style mapping array (data point index to style), + // initialized to the default style. + PenStyle** dataToStyle = new PenStyle*[nPoints]; + for (int ii=0; iistylePalette); link; + link=Chain_PrevLink(link)) { + stylePtr = (PenStyle*)Chain_GetValue(link); + + if (stylePtr->weight.range > 0.0) { + double norm = (w[ii] - stylePtr->weight.min) / stylePtr->weight.range; + if (((norm - 1.0) <= DBL_EPSILON) && + (((1.0 - norm) - 1.0) <= DBL_EPSILON)) { + dataToStyle[ii] = stylePtr; + break; + } + } + } + } + + return dataToStyle; +} + +void Element::freeStylePalette(Chain* stylePalette) +{ + // Skip the first slot. It contains the built-in "normal" pen of the element + ChainLink* link = Chain_FirstLink(stylePalette); + if (link) { + ChainLink* next; + for (link=Chain_NextLink(link); link; link=next) { + next = Chain_NextLink(link); + PenStyle *stylePtr = (PenStyle*)Chain_GetValue(link); + Pen* penPtr = stylePtr->penPtr; + if (penPtr) { + penPtr->refCount_--; + if (penPtr->refCount_ == 0) + delete penPtr; + } + stylePalette->deleteLink(link); + } + } +} + diff --git a/generic/tkbltGrElem.h b/generic/tkbltGrElem.h new file mode 100644 index 0000000..8904df0 --- /dev/null +++ b/generic/tkbltGrElem.h @@ -0,0 +1,202 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrElem_h__ +#define __BltGrElem_h__ + +#include + +#include "tkbltVector.h" +#include "tkbltChain.h" + +#include "tkbltGrMisc.h" +#include "tkbltGrPen.h" +#include "tkbltGrPSOutput.h" + +#define SHOW_NONE 0 +#define SHOW_X 1 +#define SHOW_Y 2 +#define SHOW_BOTH 3 + +#define NUMBEROFPOINTS(e) MIN( (e)->coords.x ? (e)->coords.x->nValues() : 0, \ + (e)->coords.y ? (e)->coords.y->nValues() : 0 ) +#define NORMALPEN(e) ((((e)->normalPenPtr == NULL) ? \ + (e)->builtinPenPtr : (e)->normalPenPtr)) + +namespace Blt { + class Axis; + class Element; + class Pen; + class Postscript; + + class ElemValues { + protected: + double min_; + double max_; + int nValues_; + + public: + double* values_; + + public: + ElemValues(); + virtual ~ElemValues(); + + void reset(); + int nValues() {return nValues_;} + double min() {return min_;} + double max() {return max_;} + }; + + class ElemValuesSource : public ElemValues + { + public: + ElemValuesSource(int); + ElemValuesSource(int, double*); + ~ElemValuesSource(); + + void findRange(); + }; + + class ElemValuesVector : public ElemValues + { + public: + Element* elemPtr_; + Blt_VectorId source_; + + public: + ElemValuesVector(Element*, const char*); + ~ElemValuesVector(); + + int getVector(); + int fetchValues(Blt_Vector*); + void freeSource(); + }; + + typedef struct { + Segment2d *segments; + int *map; + int length; + } GraphSegments; + + typedef struct { + ElemValuesSource* x; + ElemValuesSource* y; + } ElemCoords; + + typedef struct { + double min; + double max; + double range; + } Weight; + + typedef struct { + Weight weight; + Pen* penPtr; + } PenStyle; + + typedef struct { + Element* elemPtr; + const char* label; + const char** tags; + Axis* xAxis; + Axis* yAxis; + ElemCoords coords; + ElemValues* w; + ElemValues* xError; + ElemValues* yError; + ElemValues* xHigh; + ElemValues* xLow; + ElemValues* yHigh; + ElemValues* yLow; + int hide; + int legendRelief; + Chain* stylePalette; + Pen* builtinPenPtr; + Pen* activePenPtr; + Pen* normalPenPtr; + PenOptions builtinPen; + } ElementOptions; + + class Element { + protected: + Tk_OptionTable optionTable_; + void* ops_; + + double xRange_; + double yRange_; + + public: + Graph* graphPtr_; + const char* name_; + Tcl_HashEntry* hashPtr_; + unsigned row_; + unsigned col_; + int nActiveIndices_; + int* activeIndices_; + int active_; + int labelActive_; + + ChainLink* link; + + protected: + double FindElemValuesMinimum(ElemValues*, double); + PenStyle** StyleMap(); + + public: + Element(Graph*, const char*, Tcl_HashEntry*); + virtual ~Element(); + + virtual int configure() =0; + virtual void map() =0; + virtual void extents(Region2d*) =0; + virtual void draw(Drawable) =0; + virtual void drawActive(Drawable) =0; + virtual void drawSymbol(Drawable, int, int, int) =0; + virtual void closest() =0; + virtual void print(PSOutput*) =0; + virtual void printActive(PSOutput*) =0; + virtual void printSymbol(PSOutput*, double, double, int) =0; + + virtual ClassId classId() =0; + virtual const char* className() =0; + virtual const char* typeName() =0; + + void freeStylePalette (Chain*); + + Tk_OptionTable optionTable() {return optionTable_;} + void* ops() {return ops_;} + }; +}; + +extern void VectorChangedProc(Tcl_Interp* interp, ClientData clientData, + Blt_VectorNotify notify); + + +#endif diff --git a/generic/tkbltGrElemBar.C b/generic/tkbltGrElemBar.C new file mode 100644 index 0000000..6698760 --- /dev/null +++ b/generic/tkbltGrElemBar.C @@ -0,0 +1,1315 @@ +/* + * 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 1993-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 +#include +#include + +#include + +#include "tkbltGraphBar.h" +#include "tkbltGrElemBar.h" +#include "tkbltGrElemOption.h" +#include "tkbltGrAxis.h" +#include "tkbltGrMisc.h" +#include "tkbltGrDef.h" +#include "tkbltConfig.h" +#include "tkbltGrPSOutput.h" +#include "tkbltInt.h" + +using namespace Blt; + +#define CLAMP(x,l,h) ((x) = (((x)<(l))? (l) : ((x)>(h)) ? (h) : (x))) +#define MIN3(a,b,c) (((a)<(b))?(((a)<(c))?(a):(c)):(((b)<(c))?(b):(c))) + +#define PointInRectangle(r,x0,y0) \ + (((x0) <= (int)((r)->x + (r)->width - 1)) && ((x0) >= (int)(r)->x) && \ + ((y0) <= (int)((r)->y + (r)->height - 1)) && ((y0) >= (int)(r)->y)) + +// OptionSpecs + +static Tk_ObjCustomOption styleObjOption = + { + "style", StyleSetProc, StyleGetProc, StyleRestoreProc, StyleFreeProc, + (ClientData)sizeof(BarStyle) + }; + +extern Tk_ObjCustomOption penObjOption; +extern Tk_ObjCustomOption pairsObjOption; +extern Tk_ObjCustomOption valuesObjOption; +extern Tk_ObjCustomOption xAxisObjOption; +extern Tk_ObjCustomOption yAxisObjOption; + +static Tk_OptionSpec optionSpecs[] = { + {TK_OPTION_CUSTOM, "-activepen", "activePen", "ActivePen", + "active", -1, Tk_Offset(BarElementOptions, activePenPtr), + TK_OPTION_NULL_OK, &penObjOption, LAYOUT}, + {TK_OPTION_SYNONYM, "-background", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-color", 0}, + {TK_OPTION_DOUBLE, "-barwidth", "barWidth", "BarWidth", + "0", -1, Tk_Offset(BarElementOptions, barWidth), 0, NULL, LAYOUT}, + {TK_OPTION_SYNONYM, "-bd", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-borderwidth", 0}, + {TK_OPTION_SYNONYM, "-bg", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-color", 0}, + {TK_OPTION_CUSTOM, "-bindtags", "bindTags", "BindTags", + "all", -1, Tk_Offset(BarElementOptions, tags), + TK_OPTION_NULL_OK, &listObjOption, 0}, + {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", + STD_BORDERWIDTH, -1, Tk_Offset(BarElementOptions, builtinPen.borderWidth), + 0, NULL, CACHE}, + {TK_OPTION_BORDER, "-color", "color", "color", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(BarElementOptions, builtinPen.fill), + 0, NULL, CACHE}, + {TK_OPTION_CUSTOM, "-data", "data", "Data", + NULL, -1, Tk_Offset(BarElementOptions, coords), + TK_OPTION_NULL_OK, &pairsObjOption, RESET}, + {TK_OPTION_COLOR, "-errorbarcolor", "errorBarColor", "ErrorBarColor", + NULL, -1, Tk_Offset(BarElementOptions, builtinPen.errorBarColor), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_PIXELS,"-errorbarwidth", "errorBarWidth", "ErrorBarWidth", + "1", -1, Tk_Offset(BarElementOptions, builtinPen.errorBarLineWidth), + 0, NULL, CACHE}, + {TK_OPTION_PIXELS, "-errorbarcap", "errorBarCap", "ErrorBarCap", + "0", -1, Tk_Offset(BarElementOptions, builtinPen.errorBarCapWidth), + 0, NULL, LAYOUT}, + {TK_OPTION_SYNONYM, "-fg", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-outline", 0}, + {TK_OPTION_SYNONYM, "-fill", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-color", 0}, + {TK_OPTION_SYNONYM, "-foreground", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-outline", 0}, + {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", + "no", -1, Tk_Offset(BarElementOptions, hide), 0, NULL, LAYOUT}, + {TK_OPTION_STRING, "-label", "label", "Label", + NULL, -1, Tk_Offset(BarElementOptions, label), + TK_OPTION_NULL_OK, NULL, LAYOUT}, + {TK_OPTION_RELIEF, "-legendrelief", "legendRelief", "LegendRelief", + "flat", -1, Tk_Offset(BarElementOptions, legendRelief), 0, NULL, LAYOUT}, + {TK_OPTION_CUSTOM, "-mapx", "mapX", "MapX", + "x", -1, Tk_Offset(BarElementOptions, xAxis), 0, &xAxisObjOption, RESET}, + {TK_OPTION_CUSTOM, "-mapy", "mapY", "MapY", + "y", -1, Tk_Offset(BarElementOptions, yAxis), 0, &yAxisObjOption, RESET}, + {TK_OPTION_COLOR, "-outline", "outline", "Outline", + NULL, -1, Tk_Offset(BarElementOptions, builtinPen.outlineColor), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_CUSTOM, "-pen", "pen", "Pen", + NULL, -1, Tk_Offset(BarElementOptions, normalPenPtr), + TK_OPTION_NULL_OK, &penObjOption, LAYOUT}, + {TK_OPTION_RELIEF, "-relief", "relief", "Relief", + "raised", -1, Tk_Offset(BarElementOptions, builtinPen.relief), + 0, NULL, LAYOUT}, + {TK_OPTION_STRING_TABLE, "-showerrorbars", "showErrorBars", "ShowErrorBars", + "both", -1, Tk_Offset(BarElementOptions, builtinPen.errorBarShow), + 0, &fillObjOption, LAYOUT}, + {TK_OPTION_STRING_TABLE, "-showvalues", "showValues", "ShowValues", + "none", -1, Tk_Offset(BarElementOptions, builtinPen.valueShow), + 0, &fillObjOption, CACHE}, + {TK_OPTION_STRING, "-stack", "stack", "Stack", + NULL, -1, Tk_Offset(BarElementOptions, groupName), + TK_OPTION_NULL_OK, NULL, RESET}, + {TK_OPTION_CUSTOM, "-styles", "styles", "Styles", + "", -1, Tk_Offset(BarElementOptions, stylePalette), + 0, &styleObjOption, RESET}, + {TK_OPTION_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor", + "s", -1, Tk_Offset(BarElementOptions, builtinPen.valueStyle.anchor), + 0, NULL, CACHE}, + {TK_OPTION_COLOR, "-valuecolor", "valueColor", "ValueColor", + STD_NORMAL_FOREGROUND, -1, + Tk_Offset(BarElementOptions,builtinPen.valueStyle.color), 0, NULL, CACHE}, + {TK_OPTION_FONT, "-valuefont", "valueFont", "ValueFont", + STD_FONT_SMALL, -1, Tk_Offset(BarElementOptions, builtinPen.valueStyle.font), + 0, NULL, CACHE}, + {TK_OPTION_STRING, "-valueformat", "valueFormat", "ValueFormat", + "%g", -1, Tk_Offset(BarElementOptions, builtinPen.valueFormat), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate", + "0", -1, Tk_Offset(BarElementOptions, builtinPen.valueStyle.angle), + 0, NULL, CACHE}, + {TK_OPTION_CUSTOM, "-weights", "weights", "Weights", + NULL, -1, Tk_Offset(BarElementOptions, w), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_SYNONYM, "-x", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-xdata", 0}, + {TK_OPTION_CUSTOM, "-xdata", "xData", "XData", + NULL, -1, Tk_Offset(BarElementOptions, coords.x), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_CUSTOM, "-xerror", "xError", "XError", + NULL, -1, Tk_Offset(BarElementOptions, xError), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_CUSTOM, "-xhigh", "xHigh", "XHigh", + NULL, -1, Tk_Offset(BarElementOptions, xHigh), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_CUSTOM, "-xlow", "xLow", "XLow", + NULL, -1, Tk_Offset(BarElementOptions, xLow), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_SYNONYM, "-y", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-ydata", 0}, + {TK_OPTION_CUSTOM, "-ydata", "yData", "YData", + NULL, -1, Tk_Offset(BarElementOptions, coords.y), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_CUSTOM, "-yerror", "yError", "YError", + NULL, -1, Tk_Offset(BarElementOptions, yError), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_CUSTOM, "-yhigh", "yHigh", "YHigh", + NULL, -1, Tk_Offset(BarElementOptions, yHigh), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_CUSTOM, "-ylow", "yLow", "YLow", + NULL, -1, Tk_Offset(BarElementOptions, yLow), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} +}; + +BarElement::BarElement(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) + : Element(graphPtr, name, hPtr) +{ + barToData_ =NULL; + bars_ =NULL; + activeToData_ =NULL; + activeRects_ =NULL; + nBars_ =0; + nActive_ =0; + + xeb_.segments =NULL; + xeb_.map =NULL; + xeb_.length =0; + yeb_.segments =NULL; + yeb_.map =NULL; + yeb_.length =0; + + ops_ = (BarElementOptions*)calloc(1, sizeof(BarElementOptions)); + BarElementOptions* ops = (BarElementOptions*)ops_; + ops->elemPtr = (Element*)this; + + builtinPenPtr = new BarPen(graphPtr_, "builtin", &ops->builtinPen); + ops->builtinPenPtr = builtinPenPtr; + + optionTable_ = Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); + + ops->stylePalette = new Chain(); + + // this is an option and will be freed via Tk_FreeConfigOptions + // By default an element's name and label are the same + ops->label = Tcl_Alloc(strlen(name)+1); + if (name) + strcpy((char*)ops->label,(char*)name); + + Tk_InitOptions(graphPtr_->interp_, (char*)&(ops->builtinPen), + builtinPenPtr->optionTable(), graphPtr->tkwin_); +} + +BarElement::~BarElement() +{ + BarElementOptions* ops = (BarElementOptions*)ops_; + + delete builtinPenPtr; + + reset(); + + if (ops->stylePalette) { + freeStylePalette(ops->stylePalette); + delete ops->stylePalette; + } +} + +int BarElement::configure() +{ + BarElementOptions* ops = (BarElementOptions*)ops_; + + if (builtinPenPtr->configure() != TCL_OK) + return TCL_ERROR; + + // Point to the static normal pen if no external pens have been selected. + ChainLink* link = Chain_FirstLink(ops->stylePalette); + if (!link) { + link = new ChainLink(sizeof(BarStyle)); + ops->stylePalette->linkAfter(link, NULL); + } + BarStyle* stylePtr = (BarStyle*)Chain_GetValue(link); + stylePtr->penPtr = NORMALPEN(ops); + + return TCL_OK; +} + +void BarElement::map() +{ + BarGraph* barGraphPtr_ = (BarGraph*)graphPtr_; + BarElementOptions* ops = (BarElementOptions*)ops_; + BarGraphOptions* gops = (BarGraphOptions*)graphPtr_->ops_; + + if (!link) + return; + + reset(); + if (!ops->coords.x || !ops->coords.y || + !ops->coords.x->nValues() || !ops->coords.y->nValues()) + return; + int nPoints = NUMBEROFPOINTS(ops); + + double barWidth = (ops->barWidth > 0.0) ? ops->barWidth : gops->barWidth; + AxisOptions* axisyops = (AxisOptions*)ops->yAxis->ops(); + double baseline = (axisyops->logScale) ? 0.0 : gops->baseline; + double barOffset = barWidth * 0.5; + + // Create an array of bars representing the screen coordinates of all the + // segments in the bar. + Rectangle* bars = new Rectangle[nPoints]; + int* barToData = new int[nPoints]; + + double* x = ops->coords.x->values_; + double* y = ops->coords.y->values_; + int count = 0; + + int ii; + Rectangle* rp; + for (rp=bars, ii=0; ii ops->xAxis->axisRange_.max) || + ((x[ii] + barWidth) < ops->xAxis->axisRange_.min)) + continue; + + c1.x = x[ii] - barOffset; + c1.y = y[ii]; + c2.x = c1.x + barWidth; + c2.y = baseline; + + // If the mode is "aligned" or "stacked" we need to adjust the x or y + // coordinates of the two corners. + if ((barGraphPtr_->nBarGroups_ > 0) && + ((BarGraph::BarMode)gops->barMode != BarGraph::INFRONT) && + (!gops->stackAxes)) { + + BarSetKey key; + key.value =x[ii]; + key.xAxis =ops->xAxis; + key.yAxis =NULL; + Tcl_HashEntry* hPtr = + Tcl_FindHashEntry(&barGraphPtr_->setTable_, (char*)&key); + + if (hPtr) { + Tcl_HashTable *tablePtr = (Tcl_HashTable*)Tcl_GetHashValue(hPtr); + const char *name = (ops->groupName) ? ops->groupName:ops->yAxis->name_; + Tcl_HashEntry* hPtr2 = Tcl_FindHashEntry(tablePtr, name); + if (hPtr2) { + BarGroup* groupPtr = (BarGroup*)Tcl_GetHashValue(hPtr2); + double slice = barWidth / (double)barGraphPtr_->maxBarSetSize_; + double offset = (slice * groupPtr->index); + if (barGraphPtr_->maxBarSetSize_ > 1) { + offset += slice * 0.05; + slice *= 0.90; + } + + switch ((BarGraph::BarMode)gops->barMode) { + case BarGraph::STACKED: + groupPtr->count++; + c2.y = groupPtr->lastY; + c1.y += c2.y; + groupPtr->lastY = c1.y; + c1.x += offset; + c2.x = c1.x + slice; + break; + + case BarGraph::ALIGNED: + slice /= groupPtr->nSegments; + c1.x += offset + (slice * groupPtr->count); + c2.x = c1.x + slice; + groupPtr->count++; + break; + + case BarGraph::OVERLAP: + { + slice /= (groupPtr->nSegments + 1); + double width = slice + slice; + groupPtr->count++; + c1.x += offset + + (slice * (groupPtr->nSegments - groupPtr->count)); + c2.x = c1.x + width; + } + break; + + case BarGraph::INFRONT: + break; + } + } + } + } + + int invertBar = 0; + if (c1.y < c2.y) { + // Handle negative bar values by swapping ordinates + double temp = c1.y; + c1.y = c2.y; + c2.y = temp; + invertBar = 1; + } + + // Get the two corners of the bar segment and compute the rectangle + double ybot = c2.y; + c1 = graphPtr_->map2D(c1.x, c1.y, ops->xAxis, ops->yAxis); + c2 = graphPtr_->map2D(c2.x, c2.y, ops->xAxis, ops->yAxis); + if ((ybot == 0.0) && (axisyops->logScale)) + c2.y = graphPtr_->bottom_; + + if (c2.y < c1.y) { + double t = c1.y; + c1.y = c2.y; + c2.y = t; + } + + if (c2.x < c1.x) { + double t = c1.x; + c1.x = c2.x; + c2.x = t; + } + + if ((c1.x > graphPtr_->right_) || (c2.x < graphPtr_->left_) || + (c1.y > graphPtr_->bottom_) || (c2.y < graphPtr_->top_)) + continue; + + // Bound the bars horizontally by the width of the graph window + // Bound the bars vertically by the position of the axis. + double right =0; + double left =0; + double top =0; + double bottom =0; + if (gops->stackAxes) { + top = ops->yAxis->screenMin_; + bottom = ops->yAxis->screenMin_ + ops->yAxis->screenRange_; + left = graphPtr_->left_; + right = graphPtr_->right_; + } + else { + bottom = right = 10000; + // Shouldn't really have a call to Tk_Width or Tk_Height in + // mapping routine. We only want to clamp the bar segment to the + // size of the window if we're actually mapped onscreen + if (Tk_Height(graphPtr_->tkwin_) > 1) + bottom = Tk_Height(graphPtr_->tkwin_); + if (Tk_Width(graphPtr_->tkwin_) > 1) + right = Tk_Width(graphPtr_->tkwin_); + } + + CLAMP(c1.y, top, bottom); + CLAMP(c2.y, top, bottom); + CLAMP(c1.x, left, right); + CLAMP(c2.x, left, right); + double dx = fabs(c1.x - c2.x); + double dy = fabs(c1.y - c2.y); + if ((dx == 0) || (dy == 0)) + continue; + + int height = (int)dy; + int width = (int)dx; + if (invertBar) + rp->y = (int)MIN(c1.y, c2.y); + else + rp->y = (int)(MAX(c1.y, c2.y)) - height; + + rp->x = (int)MIN(c1.x, c2.x); + + rp->width = width + 1; + rp->width |= 0x1; + if (rp->width < 1) + rp->width = 1; + + rp->height = height + 1; + if (rp->height < 1) + rp->height = 1; + + // Save the data index corresponding to the rectangle + barToData[count] = ii; + count++; + rp++; + } + nBars_ = count; + bars_ = bars; + barToData_ = barToData; + if (nActiveIndices_ > 0) + mapActive(); + + int size = 20; + if (count > 0) + size = bars->width; + + // Set the symbol size of all the pen styles + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + BarStyle *stylePtr = (BarStyle*)Chain_GetValue(link); + BarPen* penPtr = stylePtr->penPtr; + BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); + stylePtr->symbolSize = size; + stylePtr->errorBarCapWidth = pops->errorBarCapWidth; + } + + BarStyle** dataToStyle = (BarStyle**)StyleMap(); + if (((ops->yHigh && ops->yHigh->nValues() > 0) && + (ops->yLow && ops->yLow->nValues() > 0)) || + ((ops->xHigh && ops->xHigh->nValues() > 0) && + (ops->xLow && ops->xLow->nValues() > 0)) || + (ops->xError && ops->xError->nValues() > 0) || + (ops->yError && ops->yError->nValues() > 0)) { + mapErrorBars(dataToStyle); + } + + mergePens(dataToStyle); + delete [] dataToStyle; +} + +void BarElement::extents(Region2d *regPtr) +{ + BarGraph* barGraphPtr_ = (BarGraph*)graphPtr_; + BarElementOptions* ops = (BarElementOptions*)ops_; + BarGraphOptions* gops = (BarGraphOptions*)graphPtr_->ops_; + + regPtr->top = regPtr->left = DBL_MAX; + regPtr->bottom = regPtr->right = -DBL_MAX; + + if (!ops->coords.x || !ops->coords.y || + !ops->coords.x->nValues() || !ops->coords.y->nValues()) + return; + + int nPoints = NUMBEROFPOINTS(ops); + + double middle = 0.5; + regPtr->left = ops->coords.x->min() - middle; + regPtr->right = ops->coords.x->max() + middle; + + regPtr->top = ops->coords.y->min(); + regPtr->bottom = ops->coords.y->max(); + if (regPtr->bottom < gops->baseline) + regPtr->bottom = gops->baseline; + + // Handle stacked bar elements specially. + // If element is stacked, the sum of its ordinates may be outside the + // minimum/maximum limits of the element's data points. + if (((BarGraph::BarMode)gops->barMode == BarGraph::STACKED) && + (barGraphPtr_->nBarGroups_ > 0)) + checkStacks(ops->xAxis, ops->yAxis, ®Ptr->top, ®Ptr->bottom); + + // Warning: You get what you deserve if the x-axis is logScale + AxisOptions* axisxops = (AxisOptions*)ops->xAxis->ops(); + AxisOptions* axisyops = (AxisOptions*)ops->yAxis->ops(); + if (axisxops->logScale) + regPtr->left = FindElemValuesMinimum(ops->coords.x, DBL_MIN) + middle; + + // Fix y-min limits for barchart + if (axisyops->logScale) { + if ((regPtr->top <= 0.0) || (regPtr->top > 1.0)) + regPtr->top = 1.0; + } + else { + if (regPtr->top > 0.0) + regPtr->top = 0.0; + } + + // Correct the extents for error bars if they exist + if (ops->xError && (ops->xError->nValues() > 0)) { + nPoints = MIN(ops->xError->nValues(), nPoints); + for (int ii=0; iicoords.x->values_[ii] + ops->xError->values_[ii]; + if (x > regPtr->right) + regPtr->right = x; + + x = ops->coords.x->values_[ii] - ops->xError->values_[ii]; + if (axisxops->logScale) { + // Mirror negative values, instead of ignoring them + if (x < 0.0) + x = -x; + + if ((x > DBL_MIN) && (x < regPtr->left)) + regPtr->left = x; + + } + else if (x < regPtr->left) + regPtr->left = x; + } + } + else { + if ((ops->xHigh) && + (ops->xHigh->nValues() > 0) && + (ops->xHigh->max() > regPtr->right)) + regPtr->right = ops->xHigh->max(); + + if (ops->xLow && (ops->xLow->nValues() > 0)) { + double left; + if ((ops->xLow->min() <= 0.0) && (axisxops->logScale)) + left = FindElemValuesMinimum(ops->xLow, DBL_MIN); + else + left = ops->xLow->min(); + + if (left < regPtr->left) + regPtr->left = left; + } + } + + if (ops->yError && (ops->yError->nValues() > 0)) { + nPoints = MIN(ops->yError->nValues(), nPoints); + + for (int ii=0; iicoords.y->values_[ii] + ops->yError->values_[ii]; + if (y > regPtr->bottom) + regPtr->bottom = y; + + y = ops->coords.y->values_[ii] - ops->yError->values_[ii]; + if (axisyops->logScale) { + // Mirror negative values, instead of ignoring them + if (y < 0.0) + y = -y; + + if ((y > DBL_MIN) && (y < regPtr->left)) + regPtr->top = y; + + } + else if (y < regPtr->top) + regPtr->top = y; + } + } + else { + if ((ops->yHigh) && + (ops->yHigh->nValues() > 0) && + (ops->yHigh->max() > regPtr->bottom)) + regPtr->bottom = ops->yHigh->max(); + + if (ops->yLow && ops->yLow->nValues() > 0) { + double top; + if ((ops->yLow->min() <= 0.0) && + (axisyops->logScale)) + top = FindElemValuesMinimum(ops->yLow, DBL_MIN); + else + top = ops->yLow->min(); + + if (top < regPtr->top) + regPtr->top = top; + } + } +} + +void BarElement::closest() +{ + BarElementOptions* ops = (BarElementOptions*)ops_; + BarGraphOptions* gops = (BarGraphOptions*)graphPtr_->ops_; + + ClosestSearch* searchPtr = &gops->search; + double minDist = searchPtr->dist; + int imin = 0; + + int ii; + Rectangle* bp; + for (bp=bars_, ii=0; iix, searchPtr->y)) { + imin = barToData_[ii]; + minDist = 0.0; + break; + } + double left = bp->x; + double top = bp->y; + double right = (double)(bp->x + bp->width); + double bottom = (double)(bp->y + bp->height); + + Point2d outline[5]; + outline[4].x = outline[3].x = outline[0].x = left; + outline[4].y = outline[1].y = outline[0].y = top; + outline[2].x = outline[1].x = right; + outline[3].y = outline[2].y = bottom; + + Point2d *pp, *pend; + for (pp=outline, pend=outline+4; ppx, searchPtr->y, pp, pp + 1); + if (t.x > right) + t.x = right; + else if (t.x < left) + t.x = left; + + if (t.y > bottom) + t.y = bottom; + else if (t.y < top) + t.y = top; + + double dist = hypot((t.x - searchPtr->x), (t.y - searchPtr->y)); + if (dist < minDist) { + minDist = dist; + imin = barToData_[ii]; + } + } + } + if (minDist < searchPtr->dist) { + searchPtr->elemPtr = (Element*)this; + searchPtr->dist = minDist; + searchPtr->index = imin; + searchPtr->point.x = + ops->coords.x ? (double)ops->coords.x->values_[imin] : 0; + searchPtr->point.y = + ops->coords.y ? (double)ops->coords.y->values_[imin] : 0; + } +} + +void BarElement::draw(Drawable drawable) +{ + BarElementOptions* ops = (BarElementOptions*)ops_; + + if (ops->hide) + return; + + int count = 0; + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + + BarStyle* stylePtr = (BarStyle*)Chain_GetValue(link); + BarPen* penPtr = (BarPen*)stylePtr->penPtr; + BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); + + if (stylePtr->nBars > 0) + drawSegments(drawable, penPtr, stylePtr->bars, stylePtr->nBars); + + if ((stylePtr->xeb.length > 0) && (pops->errorBarShow & SHOW_X)) + graphPtr_->drawSegments(drawable, penPtr->errorBarGC_, + stylePtr->xeb.segments, stylePtr->xeb.length); + + if ((stylePtr->yeb.length > 0) && (pops->errorBarShow & SHOW_Y)) + graphPtr_->drawSegments(drawable, penPtr->errorBarGC_, + stylePtr->yeb.segments, stylePtr->yeb.length); + + if (pops->valueShow != SHOW_NONE) + drawValues(drawable, penPtr, stylePtr->bars, stylePtr->nBars, + barToData_ + count); + + count += stylePtr->nBars; + } +} + +void BarElement::drawActive(Drawable drawable) +{ + BarElementOptions* ops = (BarElementOptions*)ops_; + + if (ops->hide || !active_) + return; + + BarPen* penPtr = (BarPen*)ops->activePenPtr; + if (!penPtr) + return; + BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); + + if (nActiveIndices_ > 0) { + mapActive(); + + drawSegments(drawable, penPtr, activeRects_, nActive_); + if (pops->valueShow != SHOW_NONE) + drawValues(drawable, penPtr, activeRects_, nActive_, activeToData_); + } + else if (nActiveIndices_ < 0) { + drawSegments(drawable, penPtr, bars_, nBars_); + if (pops->valueShow != SHOW_NONE) + drawValues(drawable, penPtr, bars_, nBars_, barToData_); + } +} + +void BarElement::drawSymbol(Drawable drawable, int x, int y, int size) +{ + BarElementOptions* ops = (BarElementOptions*)ops_; + + BarPen* penPtr = NORMALPEN(ops); + BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); + + int radius = (size / 2); + size--; + + x -= radius; + y -= radius; + + Tk_Fill3DRectangle(graphPtr_->tkwin_, drawable, + pops->fill, x, y, size, size, + pops->borderWidth, pops->relief); + + if (pops->outlineColor) + XDrawRectangle(graphPtr_->display_, drawable, penPtr->outlineGC_, + x, y, size, size); +} + +void BarElement::print(PSOutput* psPtr) +{ + BarElementOptions* ops = (BarElementOptions*)ops_; + + if (ops->hide) + return; + + psPtr->format("\n%% Element \"%s\"\n\n", name_); + + int count = 0; + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + + BarStyle *stylePtr = (BarStyle*)Chain_GetValue(link); + BarPen* penPtr = (BarPen*)stylePtr->penPtr; + BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); + + if (stylePtr->nBars > 0) + printSegments(psPtr, penPtr, stylePtr->bars, stylePtr->nBars); + + XColor* colorPtr = pops->errorBarColor; + if (!colorPtr) + colorPtr = pops->outlineColor; + if (!colorPtr) + colorPtr = Tk_3DBorderColor(pops->fill); + + if ((stylePtr->xeb.length > 0) && (pops->errorBarShow & SHOW_X)) { + psPtr->setLineAttributes(colorPtr, pops->errorBarLineWidth, + NULL, CapButt, JoinMiter); + psPtr->printSegments(stylePtr->xeb.segments, stylePtr->xeb.length); + } + + if ((stylePtr->yeb.length > 0) && (pops->errorBarShow & SHOW_Y)) { + psPtr->setLineAttributes(colorPtr, pops->errorBarLineWidth, + NULL, CapButt, JoinMiter); + psPtr->printSegments(stylePtr->yeb.segments, stylePtr->yeb.length); + } + + if (pops->valueShow != SHOW_NONE) + printValues(psPtr, penPtr, stylePtr->bars, stylePtr->nBars, + barToData_ + count); + + count += stylePtr->nBars; + } +} + +void BarElement::printActive(PSOutput* psPtr) +{ + BarElementOptions* ops = (BarElementOptions*)ops_; + + if (ops->hide || !active_) + return; + + BarPen* penPtr = (BarPen*)ops->activePenPtr; + if (!penPtr) + return; + BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); + + psPtr->format("\n%% Active Element \"%s\"\n\n", name_); + + if (nActiveIndices_ > 0) { + mapActive(); + + printSegments(psPtr, penPtr, activeRects_, nActive_); + if (pops->valueShow != SHOW_NONE) + printValues(psPtr, penPtr, activeRects_, nActive_,activeToData_); + } + else if (nActiveIndices_ < 0) { + printSegments(psPtr, penPtr, bars_, nBars_); + if (pops->valueShow != SHOW_NONE) + printValues(psPtr, penPtr, bars_, nBars_, barToData_); + } +} + +void BarElement::printSymbol(PSOutput* psPtr, double x, double y, int size) +{ + BarElementOptions* ops = (BarElementOptions*)ops_; + + BarPen* penPtr = NORMALPEN(ops); + BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); + + x -= size/2.; + y -= size/2.; + + psPtr->fill3DRectangle(pops->fill, x, y, size, size, + pops->borderWidth, pops->relief); + + if (pops->outlineColor) { + psPtr->setForeground(pops->outlineColor); + psPtr->printRectangle(x, y, size, size); + } +} + +// Support + +void BarElement::ResetStylePalette(Chain* stylePalette) +{ + for (ChainLink* link = Chain_FirstLink(stylePalette); link; + link = Chain_NextLink(link)) { + BarStyle *stylePtr = (BarStyle*)Chain_GetValue(link); + stylePtr->xeb.length = 0; + stylePtr->yeb.length = 0; + stylePtr->nBars = 0; + } +} + +void BarElement::checkStacks(Axis* xAxis, Axis* yAxis, + double* minPtr, double* maxPtr) +{ + BarGraph* barGraphPtr_ = (BarGraph*)graphPtr_; + BarGraphOptions* gops = (BarGraphOptions*)graphPtr_->ops_; + if (((BarGraph::BarMode)gops->barMode != BarGraph::STACKED) || + barGraphPtr_->nBarGroups_ == 0) + return; + + for (BarGroup *gp = barGraphPtr_->barGroups_, + *gend = gp + barGraphPtr_->nBarGroups_; gp < gend; gp++) { + if ((gp->xAxis == xAxis) && (gp->yAxis == yAxis)) { + + // Check if any of the y-values (because of stacking) are greater + // than the current limits of the graph. + if (gp->sum < 0.0) { + if (*minPtr > gp->sum) + *minPtr = gp->sum; + } + else { + if (*maxPtr < gp->sum) + *maxPtr = gp->sum; + } + } + } +} + +void BarElement::mergePens(BarStyle** dataToStyle) +{ + BarElementOptions* ops = (BarElementOptions*)ops_; + + if (Chain_GetLength(ops->stylePalette) < 2) { + ChainLink* link = Chain_FirstLink(ops->stylePalette); + BarStyle *stylePtr = (BarStyle*)Chain_GetValue(link); + stylePtr->nBars = nBars_; + stylePtr->bars = bars_; + stylePtr->symbolSize = bars_->width / 2; + stylePtr->xeb.length = xeb_.length; + stylePtr->xeb.segments = xeb_.segments; + stylePtr->yeb.length = yeb_.length; + stylePtr->yeb.segments = yeb_.segments; + return; + } + + // We have more than one style. Group bar segments of like pen styles together + if (nBars_ > 0) { + Rectangle* bars = new Rectangle[nBars_]; + int* barToData = new int[nBars_]; + Rectangle* bp = bars; + int* ip = barToData; + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + BarStyle *stylePtr = (BarStyle*)Chain_GetValue(link); + stylePtr->symbolSize = bp->width / 2; + stylePtr->bars = bp; + for (int ii=0; iinBars = bp - stylePtr->bars; + } + delete [] bars_; + bars_ = bars; + delete [] barToData_; + barToData_ = barToData; + } + + if (xeb_.length > 0) { + Segment2d* bars = new Segment2d[xeb_.length]; + Segment2d *sp = bars; + int* map = new int[xeb_.length]; + int* ip = map; + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + BarStyle *stylePtr = (BarStyle*)Chain_GetValue(link); + stylePtr->xeb.segments = sp; + for (int ii=0; iixeb.length = sp - stylePtr->xeb.segments; + } + delete [] xeb_.segments; + xeb_.segments = bars; + delete [] xeb_.map; + xeb_.map = map; + } + + if (yeb_.length > 0) { + Segment2d* bars = new Segment2d[yeb_.length]; + Segment2d* sp = bars; + int* map = new int[yeb_.length]; + int* ip = map; + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + BarStyle *stylePtr = (BarStyle*)Chain_GetValue(link); + stylePtr->yeb.segments = sp; + for (int ii=0; iiyeb.length = sp - stylePtr->yeb.segments; + } + delete [] yeb_.segments; + yeb_.segments = bars; + delete [] yeb_.map; + yeb_.map = map; + } +} + +void BarElement::mapActive() +{ + delete [] activeRects_; + activeRects_ = NULL; + + delete [] activeToData_; + activeToData_ = NULL; + + nActive_ = 0; + + if (nActiveIndices_ > 0) { + Rectangle* activeRects = new Rectangle[nActiveIndices_]; + int* activeToData = new int[nActiveIndices_]; + int count = 0; + for (int ii=0; iistylePalette); + + delete [] activeRects_; + activeRects_ = NULL; + delete [] activeToData_; + activeToData_ = NULL; + + delete [] xeb_.segments; + xeb_.segments = NULL; + delete [] xeb_.map; + xeb_.map = NULL; + xeb_.length = 0; + + delete [] yeb_.segments; + yeb_.segments = NULL; + delete [] yeb_.map; + yeb_.map = NULL; + yeb_.length = 0; + + delete [] bars_; + bars_ = NULL; + delete [] barToData_; + barToData_ = NULL; + + nActive_ = 0; + nBars_ = 0; +} + +void BarElement::mapErrorBars(BarStyle **dataToStyle) +{ + BarElementOptions* ops = (BarElementOptions*)ops_; + + Region2d reg; + graphPtr_->extents(®); + + int nPoints = NUMBEROFPOINTS(ops); + int nn =0; + if (ops->coords.x && ops->coords.y) { + if (ops->xError && (ops->xError->nValues() > 0)) + nn = MIN(ops->xError->nValues(), nPoints); + else + if (ops->xHigh && ops->xLow) + nn = MIN3(ops->xHigh->nValues(), ops->xLow->nValues(), nPoints); + } + + if (nn) { + Segment2d* bars = new Segment2d[nn * 3]; + Segment2d* segPtr = bars; + int* map = new int[nn * 3]; + int* indexPtr = map; + + for (int ii=0; iicoords.x->values_[ii]; + double y = ops->coords.y->values_[ii]; + BarStyle* stylePtr = dataToStyle[ii]; + + if ((isfinite(x)) && (isfinite(y))) { + double high, low; + if (ops->xError->nValues() > 0) { + high = x + ops->xError->values_[ii]; + low = x - ops->xError->values_[ii]; + } + else { + high = ops->xHigh ? ops->xHigh->values_[ii] : 0; + low = ops->xLow ? ops->xLow->values_[ii] : 0; + } + if ((isfinite(high)) && (isfinite(low))) { + Point2d p = graphPtr_->map2D(high, y, ops->xAxis, ops->yAxis); + Point2d q = graphPtr_->map2D(low, y, ops->xAxis, ops->yAxis); + segPtr->p = p; + segPtr->q = q; + if (lineRectClip(®, &segPtr->p, &segPtr->q)) { + segPtr++; + *indexPtr++ = ii; + } + // Left cap + segPtr->p.x = p.x; + segPtr->q.x = p.x; + segPtr->p.y = p.y - stylePtr->errorBarCapWidth; + segPtr->q.y = p.y + stylePtr->errorBarCapWidth; + if (lineRectClip(®, &segPtr->p, &segPtr->q)) { + segPtr++; + *indexPtr++ = ii; + } + // Right cap + segPtr->p.x = q.x; + segPtr->q.x = q.x; + segPtr->p.y = q.y - stylePtr->errorBarCapWidth; + segPtr->q.y = q.y + stylePtr->errorBarCapWidth; + if (lineRectClip(®, &segPtr->p, &segPtr->q)) { + segPtr++; + *indexPtr++ = ii; + } + } + } + } + xeb_.segments = bars; + xeb_.length = segPtr - bars; + xeb_.map = map; + } + + nn =0; + if (ops->coords.x && ops->coords.y) { + if (ops->yError && (ops->yError->nValues() > 0)) + nn = MIN(ops->yError->nValues(), nPoints); + else + if (ops->yHigh && ops->yLow) + nn = MIN3(ops->yHigh->nValues(), ops->yLow->nValues(), nPoints); + } + + if (nn) { + Segment2d* bars = new Segment2d[nn * 3]; + Segment2d* segPtr = bars; + int* map = new int[nn * 3]; + int* indexPtr = map; + + for (int ii=0; iicoords.x->values_[ii]; + double y = ops->coords.y->values_[ii]; + BarStyle *stylePtr = dataToStyle[ii]; + + if ((isfinite(x)) && (isfinite(y))) { + double high, low; + if (ops->yError->nValues() > 0) { + high = y + ops->yError->values_[ii]; + low = y - ops->yError->values_[ii]; + } + else { + high = ops->yHigh->values_[ii]; + low = ops->yLow->values_[ii]; + } + if ((isfinite(high)) && (isfinite(low))) { + Point2d p = graphPtr_->map2D(x, high, ops->xAxis, ops->yAxis); + Point2d q = graphPtr_->map2D(x, low, ops->xAxis, ops->yAxis); + segPtr->p = p; + segPtr->q = q; + if (lineRectClip(®, &segPtr->p, &segPtr->q)) { + segPtr++; + *indexPtr++ = ii; + } + // Top cap + segPtr->p.y = p.y; + segPtr->q.y = p.y; + segPtr->p.x = p.x - stylePtr->errorBarCapWidth; + segPtr->q.x = p.x + stylePtr->errorBarCapWidth; + if (lineRectClip(®, &segPtr->p, &segPtr->q)) { + segPtr++; + *indexPtr++ = ii; + } + // Bottom cap + segPtr->p.y = q.y; + segPtr->q.y = q.y; + segPtr->p.x = q.x - stylePtr->errorBarCapWidth; + segPtr->q.x = q.x + stylePtr->errorBarCapWidth; + if (lineRectClip(®, &segPtr->p, &segPtr->q)) { + segPtr++; + *indexPtr++ = ii; + } + } + } + } + yeb_.segments = bars; + yeb_.length = segPtr - bars; + yeb_.map = map; + } +} + +void BarElement::drawSegments(Drawable drawable, BarPen* penPtr, + Rectangle *bars, int nBars) +{ + BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); + for (Rectangle *rp = bars, *rend = rp + nBars; rp < rend; rp++) { + if ((rp->width < 1) || (rp->height < 1)) + continue; + + Tk_Fill3DRectangle(graphPtr_->tkwin_, drawable, + pops->fill, rp->x, rp->y, rp->width, rp->height, + pops->borderWidth, pops->relief); + + if (pops->outlineColor) + XDrawRectangle(graphPtr_->display_, drawable, penPtr->outlineGC_, + rp->x, rp->y, rp->width, rp->height); + } +} + +void BarElement::drawValues(Drawable drawable, BarPen* penPtr, + Rectangle *bars, int nBars, int *barToData) +{ + BarElementOptions* ops = (BarElementOptions*)ops_; + BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); + BarGraphOptions* gops = (BarGraphOptions*)graphPtr_->ops_; + + const char *fmt = pops->valueFormat; + if (!fmt) + fmt = "%g"; + TextStyle ts(graphPtr_, &pops->valueStyle); + + int count = 0; + for (Rectangle *rp = bars, *rend = rp + nBars; rp < rend; rp++) { + Point2d anchorPos; + char string[TCL_DOUBLE_SPACE * 2 + 2]; + + double x = ops->coords.x->values_[barToData[count]]; + double y = ops->coords.y->values_[barToData[count]]; + + count++; + if (pops->valueShow == SHOW_X) + snprintf(string, TCL_DOUBLE_SPACE, fmt, x); + else if (pops->valueShow == SHOW_Y) + snprintf(string, TCL_DOUBLE_SPACE, fmt, y); + else if (pops->valueShow == SHOW_BOTH) { + snprintf(string, TCL_DOUBLE_SPACE, fmt, x); + strcat(string, ","); + snprintf(string + strlen(string), TCL_DOUBLE_SPACE, fmt, y); + } + + if (gops->inverted) { + anchorPos.y = rp->y + rp->height * 0.5; + anchorPos.x = rp->x + rp->width; + if (x < gops->baseline) + anchorPos.x -= rp->width; + } + else { + anchorPos.x = rp->x + rp->width * 0.5; + anchorPos.y = rp->y; + if (y < gops->baseline) + anchorPos.y += rp->height; + } + + ts.drawText(drawable, string, anchorPos.x, anchorPos.y); + } +} + +void BarElement::printSegments(PSOutput* psPtr, BarPen* penPtr, + Rectangle *bars, int nBars) +{ + BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); + for (Rectangle *rp = bars, *rend = rp + nBars; rp < rend; rp++) { + if ((rp->width < 1) || (rp->height < 1)) + continue; + + psPtr->fill3DRectangle(pops->fill, (double)rp->x, (double)rp->y, + (int)rp->width, (int)rp->height, + pops->borderWidth, pops->relief); + + if (pops->outlineColor) { + psPtr->setForeground(pops->outlineColor); + psPtr->printRectangle((double)rp->x, (double)rp->y, + (int)rp->width, (int)rp->height); + } + } +} + +void BarElement::printValues(PSOutput* psPtr, BarPen* penPtr, + Rectangle *bars, int nBars, int *barToData) +{ + BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); + BarElementOptions* ops = (BarElementOptions*)ops_; + BarGraphOptions* gops = (BarGraphOptions*)graphPtr_->ops_; + + int count = 0; + const char* fmt = pops->valueFormat; + if (!fmt) + fmt = "%g"; + TextStyle ts(graphPtr_, &pops->valueStyle); + + for (Rectangle *rp = bars, *rend = rp + nBars; rp < rend; rp++) { + double x = ops->coords.x->values_[barToData[count]]; + double y = ops->coords.y->values_[barToData[count]]; + + count++; + char string[TCL_DOUBLE_SPACE * 2 + 2]; + if (pops->valueShow == SHOW_X) + snprintf(string, TCL_DOUBLE_SPACE, fmt, x); + else if (pops->valueShow == SHOW_Y) + snprintf(string, TCL_DOUBLE_SPACE, fmt, y); + else if (pops->valueShow == SHOW_BOTH) { + snprintf(string, TCL_DOUBLE_SPACE, fmt, x); + strcat(string, ","); + snprintf(string + strlen(string), TCL_DOUBLE_SPACE, fmt, y); + } + + Point2d anchorPos; + if (gops->inverted) { + anchorPos.y = rp->y + rp->height * 0.5; + anchorPos.x = rp->x + rp->width; + if (x < gops->baseline) + anchorPos.x -= rp->width; + } + else { + anchorPos.x = rp->x + rp->width * 0.5; + anchorPos.y = rp->y; + if (y < gops->baseline) + anchorPos.y += rp->height; + } + + ts.printText(psPtr, string, anchorPos.x, anchorPos.y); + } +} + diff --git a/generic/tkbltGrElemBar.h b/generic/tkbltGrElemBar.h new file mode 100644 index 0000000..8b48114 --- /dev/null +++ b/generic/tkbltGrElemBar.h @@ -0,0 +1,132 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrElemBar_h__ +#define __BltGrElemBar_h__ + +#include + +#include + +#include "tkbltGrElem.h" +#include "tkbltGrPenBar.h" + +namespace Blt { + + typedef struct { + float x1; + float y1; + float x2; + float y2; + } BarRegion; + + typedef struct { + Weight weight; + BarPen* penPtr; + Rectangle* bars; + int nBars; + GraphSegments xeb; + GraphSegments yeb; + int symbolSize; + int errorBarCapWidth; + } BarStyle; + + typedef struct { + Element* elemPtr; + const char *label; + char** tags; + Axis* xAxis; + Axis* yAxis; + ElemCoords coords; + ElemValues* w; + ElemValues* xError; + ElemValues* yError; + ElemValues* xHigh; + ElemValues* xLow; + ElemValues* yHigh; + ElemValues* yLow; + int hide; + int legendRelief; + Chain* stylePalette; + BarPen* builtinPenPtr; + BarPen* activePenPtr; + BarPen* normalPenPtr; + BarPenOptions builtinPen; + + // derived + double barWidth; + const char *groupName; + } BarElementOptions; + + class BarElement : public Element { + protected: + BarPen* builtinPenPtr; + int* barToData_; + Rectangle* bars_; + int* activeToData_; + Rectangle* activeRects_; + int nBars_; + int nActive_; + GraphSegments xeb_; + GraphSegments yeb_; + + protected: + void ResetStylePalette(Chain*); + void checkStacks(Axis*, Axis*, double*, double*); + void mergePens(BarStyle**); + void mapActive(); + void reset(); + void mapErrorBars(BarStyle**); + void drawSegments(Drawable, BarPen*, Rectangle*, int); + void drawValues(Drawable, BarPen*, Rectangle*, int, int*); + void printSegments(PSOutput*, BarPen*, Rectangle*, int); + void printValues(PSOutput*, BarPen*, Rectangle*, int, int*); + + public: + BarElement(Graph*, const char*, Tcl_HashEntry*); + virtual ~BarElement(); + + ClassId classId() {return CID_ELEM_BAR;} + const char* className() {return "BarElement";} + const char* typeName() {return "bar";} + + int configure(); + void map(); + void extents(Region2d*); + void closest(); + void draw(Drawable); + void drawActive(Drawable); + void drawSymbol(Drawable, int, int, int); + void print(PSOutput*); + void printActive(PSOutput*); + void printSymbol(PSOutput*, double, double, int); + }; +}; + +#endif diff --git a/generic/tkbltGrElemLine.C b/generic/tkbltGrElemLine.C new file mode 100644 index 0000000..ac2b340 --- /dev/null +++ b/generic/tkbltGrElemLine.C @@ -0,0 +1,2470 @@ +/* + * 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 (c) 1993 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 +#include +#include + +#include + +#include "tkbltGraph.h" +#include "tkbltGrElemLine.h" +#include "tkbltGrElemOption.h" +#include "tkbltGrAxis.h" +#include "tkbltGrMisc.h" +#include "tkbltGrDef.h" +#include "tkbltConfig.h" +#include "tkbltGrPSOutput.h" +#include "tkbltInt.h" + +using namespace Blt; + +#define SEARCH_X 0 +#define SEARCH_Y 1 +#define SEARCH_BOTH 2 + +#define SEARCH_POINTS 0 // closest data point. +#define SEARCH_TRACES 1 // closest point on trace. +#define SEARCH_AUTO 2 // traces if linewidth is > 0 and more than one + +#define MIN3(a,b,c) (((a)<(b))?(((a)<(c))?(a):(c)):(((b)<(c))?(b):(c))) +#define PointInRegion(e,x,y) (((x) <= (e)->right) && ((x) >= (e)->left) && ((y) <= (e)->bottom) && ((y) >= (e)->top)) + +#define BROKEN_TRACE(dir,last,next) (((dir == INCREASING)&&(next < last)) || ((dir == DECREASING)&&(next > last))) +#define DRAW_SYMBOL() (symbolInterval_==0||(symbolCounter_%symbolInterval_)==0) + +static const char* symbolMacros[] = + {"Li", "Sq", "Ci", "Di", "Pl", "Cr", "Sp", "Sc", "Tr", "Ar", "Bm", NULL}; + +// OptionSpecs + +static const char* smoothObjOption[] = + {"linear", "step", "cubic", "quadratic", "catrom", NULL}; + +static const char* penDirObjOption[] = + {"increasing", "decreasing", "both", NULL}; + +static Tk_ObjCustomOption styleObjOption = + { + "styles", StyleSetProc, StyleGetProc, StyleRestoreProc, StyleFreeProc, + (ClientData)sizeof(LineStyle) + }; + +extern Tk_ObjCustomOption penObjOption; +extern Tk_ObjCustomOption pairsObjOption; +extern Tk_ObjCustomOption valuesObjOption; +extern Tk_ObjCustomOption xAxisObjOption; +extern Tk_ObjCustomOption yAxisObjOption; + +static Tk_OptionSpec optionSpecs[] = { + {TK_OPTION_CUSTOM, "-activepen", "activePen", "ActivePen", + "active", -1, Tk_Offset(LineElementOptions, activePenPtr), + TK_OPTION_NULL_OK, &penObjOption, LAYOUT}, + {TK_OPTION_BORDER, "-areabackground", "areaBackground", "AreaBackground", + NULL, -1, Tk_Offset(LineElementOptions, fillBg), + TK_OPTION_NULL_OK, NULL, LAYOUT}, + {TK_OPTION_CUSTOM, "-bindtags", "bindTags", "BindTags", + "all", -1, Tk_Offset(LineElementOptions, tags), + TK_OPTION_NULL_OK, &listObjOption, 0}, + {TK_OPTION_COLOR, "-color", "color", "Color", + STD_NORMAL_FOREGROUND, -1, + Tk_Offset(LineElementOptions, builtinPen.traceColor), 0, NULL, CACHE}, + {TK_OPTION_CUSTOM, "-dashes", "dashes", "Dashes", + NULL, -1, Tk_Offset(LineElementOptions, builtinPen.traceDashes), + TK_OPTION_NULL_OK, &dashesObjOption, CACHE}, + {TK_OPTION_CUSTOM, "-data", "data", "Data", + NULL, -1, Tk_Offset(LineElementOptions, coords), + TK_OPTION_NULL_OK, &pairsObjOption, RESET}, + {TK_OPTION_COLOR, "-errorbarcolor", "errorBarColor", "ErrorBarColor", + NULL, -1, Tk_Offset(LineElementOptions, builtinPen.errorBarColor), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_PIXELS,"-errorbarwidth", "errorBarWidth", "ErrorBarWidth", + "1", -1, Tk_Offset(LineElementOptions, builtinPen.errorBarLineWidth), + 0, NULL, CACHE}, + {TK_OPTION_PIXELS, "-errorbarcap", "errorBarCap", "ErrorBarCap", + "0", -1, Tk_Offset(LineElementOptions, builtinPen.errorBarCapWidth), + 0, NULL, LAYOUT}, + {TK_OPTION_COLOR, "-fill", "fill", "Fill", + NULL, -1, Tk_Offset(LineElementOptions, builtinPen.symbol.fillColor), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", + "no", -1, Tk_Offset(LineElementOptions, hide), 0, NULL, LAYOUT}, + {TK_OPTION_STRING, "-label", "label", "Label", + NULL, -1, Tk_Offset(LineElementOptions, label), + TK_OPTION_NULL_OK | TK_OPTION_DONT_SET_DEFAULT, NULL, LAYOUT}, + {TK_OPTION_RELIEF, "-legendrelief", "legendRelief", "LegendRelief", + "flat", -1, Tk_Offset(LineElementOptions, legendRelief), 0, NULL, LAYOUT}, + {TK_OPTION_PIXELS, "-linewidth", "lineWidth", "LineWidth", + "1", -1, Tk_Offset(LineElementOptions, builtinPen.traceWidth), + 0, NULL, CACHE}, + {TK_OPTION_CUSTOM, "-mapx", "mapX", "MapX", + "x", -1, Tk_Offset(LineElementOptions, xAxis), 0, &xAxisObjOption, RESET}, + {TK_OPTION_CUSTOM, "-mapy", "mapY", "MapY", + "y", -1, Tk_Offset(LineElementOptions, yAxis), 0, &yAxisObjOption, RESET}, + {TK_OPTION_INT, "-maxsymbols", "maxSymbols", "MaxSymbols", + "0", -1, Tk_Offset(LineElementOptions, reqMaxSymbols), 0, NULL, CACHE}, + {TK_OPTION_COLOR, "-offdash", "offDash", "OffDash", + NULL, -1, Tk_Offset(LineElementOptions, builtinPen.traceOffColor), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_COLOR, "-outline", "outline", "Outline", + NULL, -1, Tk_Offset(LineElementOptions, builtinPen.symbol.outlineColor), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_PIXELS, "-outlinewidth", "outlineWidth", "OutlineWidth", + "1", -1, Tk_Offset(LineElementOptions, builtinPen.symbol.outlineWidth), + 0, NULL, CACHE}, + {TK_OPTION_CUSTOM, "-pen", "pen", "Pen", + NULL, -1, Tk_Offset(LineElementOptions, normalPenPtr), + TK_OPTION_NULL_OK, &penObjOption, LAYOUT}, + {TK_OPTION_PIXELS, "-pixels", "pixels", "Pixels", + "0.1i", -1, Tk_Offset(LineElementOptions, builtinPen.symbol.size), + 0, NULL, LAYOUT}, + {TK_OPTION_DOUBLE, "-reduce", "reduce", "Reduce", + "0", -1, Tk_Offset(LineElementOptions, rTolerance), 0, NULL, RESET}, + {TK_OPTION_BOOLEAN, "-scalesymbols", "scaleSymbols", "ScaleSymbols", + "yes", -1, Tk_Offset(LineElementOptions, scaleSymbols), 0, NULL, LAYOUT}, + {TK_OPTION_STRING_TABLE, "-showerrorbars", "showErrorBars", "ShowErrorBars", + "both", -1, Tk_Offset(LineElementOptions, builtinPen.errorBarShow), + 0, &fillObjOption, LAYOUT}, + {TK_OPTION_STRING_TABLE, "-showvalues", "showValues", "ShowValues", + "none", -1, Tk_Offset(LineElementOptions, builtinPen.valueShow), + 0, &fillObjOption, CACHE}, + {TK_OPTION_STRING_TABLE, "-smooth", "smooth", "Smooth", + "linear", -1, Tk_Offset(LineElementOptions, reqSmooth), + 0, &smoothObjOption, LAYOUT}, + {TK_OPTION_CUSTOM, "-styles", "styles", "Styles", + "", -1, Tk_Offset(LineElementOptions, stylePalette), + 0, &styleObjOption, RESET}, + {TK_OPTION_STRING_TABLE, "-symbol", "symbol", "Symbol", + "none", -1, Tk_Offset(LineElementOptions, builtinPen.symbol), + 0, &symbolObjOption, CACHE}, + {TK_OPTION_STRING_TABLE, "-trace", "trace", "Trace", + "both", -1, Tk_Offset(LineElementOptions, penDir), + 0, &penDirObjOption, RESET}, + {TK_OPTION_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor", + "s", -1, Tk_Offset(LineElementOptions, builtinPen.valueStyle.anchor), + 0, NULL, CACHE}, + {TK_OPTION_COLOR, "-valuecolor", "valueColor", "ValueColor", + STD_NORMAL_FOREGROUND,-1, + Tk_Offset(LineElementOptions, builtinPen.valueStyle.color), + 0, NULL, CACHE}, + {TK_OPTION_FONT, "-valuefont", "valueFont", "ValueFont", + STD_FONT_SMALL, -1, + Tk_Offset(LineElementOptions, builtinPen.valueStyle.font), + 0, NULL, CACHE}, + {TK_OPTION_STRING, "-valueformat", "valueFormat", "ValueFormat", + "%g", -1, Tk_Offset(LineElementOptions, builtinPen.valueFormat), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate", + "0", -1, Tk_Offset(LineElementOptions, builtinPen.valueStyle.angle), + 0, NULL, CACHE}, + {TK_OPTION_CUSTOM, "-weights", "weights", "Weights", + NULL, -1, Tk_Offset(LineElementOptions, w), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_SYNONYM, "-x", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-xdata", 0}, + {TK_OPTION_CUSTOM, "-xdata", "xData", "XData", + NULL, -1, Tk_Offset(LineElementOptions, coords.x), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_CUSTOM, "-xerror", "xError", "XError", + NULL, -1, Tk_Offset(LineElementOptions, xError), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_CUSTOM, "-xhigh", "xHigh", "XHigh", + NULL, -1, Tk_Offset(LineElementOptions, xHigh), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_CUSTOM, "-xlow", "xLow", "XLow", + NULL, -1, Tk_Offset(LineElementOptions, xLow), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_SYNONYM, "-y", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-ydata", 0}, + {TK_OPTION_CUSTOM, "-ydata", "yData", "YData", + NULL, -1, Tk_Offset(LineElementOptions, coords.y), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_CUSTOM, "-yerror", "yError", "YError", + NULL, -1, Tk_Offset(LineElementOptions, yError), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_CUSTOM, "-yhigh", "yHigh", "YHigh", + NULL, -1, Tk_Offset(LineElementOptions, yHigh), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_CUSTOM, "-ylow", "yLow", "YLow", + NULL, -1, Tk_Offset(LineElementOptions, yLow), + TK_OPTION_NULL_OK, &valuesObjOption, RESET}, + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} +}; + +LineElement::LineElement(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) + : Element(graphPtr, name, hPtr) +{ + smooth_ = LINEAR; + fillPts_ =NULL; + nFillPts_ = 0; + + symbolPts_.points =NULL; + symbolPts_.length =0; + symbolPts_.map =NULL; + activePts_.points =NULL; + activePts_.length =0; + activePts_.map =NULL; + + xeb_.segments =NULL; + xeb_.map =NULL; + xeb_.length =0; + yeb_.segments =NULL; + yeb_.map =NULL; + yeb_.length =0; + + symbolInterval_ =0; + symbolCounter_ =0; + traces_ =NULL; + + ops_ = (LineElementOptions*)calloc(1, sizeof(LineElementOptions)); + LineElementOptions* ops = (LineElementOptions*)ops_; + ops->elemPtr = (Element*)this; + + builtinPenPtr = new LinePen(graphPtr, "builtin", &ops->builtinPen); + ops->builtinPenPtr = builtinPenPtr; + + optionTable_ = Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); + + ops->stylePalette = new Chain(); + // this is an option and will be freed via Tk_FreeConfigOptions + // By default an element's name and label are the same + ops->label = Tcl_Alloc(strlen(name)+1); + if (name) + strcpy((char*)ops->label,(char*)name); + + Tk_InitOptions(graphPtr->interp_, (char*)&(ops->builtinPen), + builtinPenPtr->optionTable(), graphPtr->tkwin_); +} + +LineElement::~LineElement() +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + + delete builtinPenPtr; + + reset(); + + if (ops->stylePalette) { + freeStylePalette(ops->stylePalette); + delete ops->stylePalette; + } + + delete [] fillPts_; +} + +int LineElement::configure() +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + + if (builtinPenPtr->configure() != TCL_OK) + return TCL_ERROR; + + // Point to the static normal/active pens if no external pens have been + // selected. + ChainLink* link = Chain_FirstLink(ops->stylePalette); + if (!link) { + link = new ChainLink(sizeof(LineStyle)); + ops->stylePalette->linkAfter(link, NULL); + } + LineStyle* stylePtr = (LineStyle*)Chain_GetValue(link); + stylePtr->penPtr = NORMALPEN(ops); + + return TCL_OK; +} + +void LineElement::map() +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + + if (!link) + return; + + reset(); + if (!ops->coords.x || !ops->coords.y || + !ops->coords.x->nValues() || !ops->coords.y->nValues()) + return; + int np = NUMBEROFPOINTS(ops); + + MapInfo mi; + getScreenPoints(&mi); + mapSymbols(&mi); + + if (nActiveIndices_ > 0) + mapActiveSymbols(); + + // Map connecting line segments if they are to be displayed. + smooth_ = (Smoothing)ops->reqSmooth; + if ((np > 1) && (ops->builtinPen.traceWidth > 0)) { + // Do smoothing if necessary. This can extend the coordinate array, + // so both mi.points and mi.nPoints may change. + switch (smooth_) { + case STEP: + generateSteps(&mi); + break; + + case CUBIC: + case QUADRATIC: + // Can't interpolate with less than three points + if (mi.nScreenPts < 3) + smooth_ = LINEAR; + else + generateSpline(&mi); + break; + + case CATROM: + // Can't interpolate with less than three points + if (mi.nScreenPts < 3) + smooth_ = LINEAR; + else + generateParametricSpline(&mi); + break; + + default: + break; + } + if (ops->rTolerance > 0.0) + reducePoints(&mi, ops->rTolerance); + + if (ops->fillBg) + mapFillArea(&mi); + + mapTraces(&mi); + } + delete [] mi.screenPts; + delete [] mi.map; + + // Set the symbol size of all the pen styles + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + LineStyle* stylePtr = (LineStyle*)Chain_GetValue(link); + LinePen* penPtr = (LinePen *)stylePtr->penPtr; + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + int size = scaleSymbol(penOps->symbol.size); + stylePtr->symbolSize = size; + stylePtr->errorBarCapWidth = penOps->errorBarCapWidth; + } + + LineStyle** styleMap = (LineStyle**)StyleMap(); + if (((ops->yHigh && ops->yHigh->nValues() > 0) && + (ops->yLow && ops->yLow->nValues() > 0)) || + ((ops->xHigh && ops->xHigh->nValues() > 0) && + (ops->xLow && ops->xLow->nValues() > 0)) || + (ops->xError && ops->xError->nValues() > 0) || + (ops->yError && ops->yError->nValues() > 0)) { + mapErrorBars(styleMap); + } + + mergePens(styleMap); + delete [] styleMap; +} + +void LineElement::extents(Region2d *extsPtr) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + + extsPtr->top = extsPtr->left = DBL_MAX; + extsPtr->bottom = extsPtr->right = -DBL_MAX; + + if (!ops->coords.x || !ops->coords.y || + !ops->coords.x->nValues() || !ops->coords.y->nValues()) + return; + int np = NUMBEROFPOINTS(ops); + + extsPtr->right = ops->coords.x->max(); + AxisOptions* axisxops = (AxisOptions*)ops->xAxis->ops(); + if ((ops->coords.x->min() <= 0.0) && (axisxops->logScale)) + extsPtr->left = FindElemValuesMinimum(ops->coords.x, DBL_MIN); + else + extsPtr->left = ops->coords.x->min(); + + extsPtr->bottom = ops->coords.y->max(); + AxisOptions* axisyops = (AxisOptions*)ops->yAxis->ops(); + if ((ops->coords.y->min() <= 0.0) && (axisyops->logScale)) + extsPtr->top = FindElemValuesMinimum(ops->coords.y, DBL_MIN); + else + extsPtr->top = ops->coords.y->min(); + + // Correct the data limits for error bars + if (ops->xError && ops->xError->nValues() > 0) { + np = MIN(ops->xError->nValues(), np); + for (int ii=0; iicoords.x->values_[ii] + ops->xError->values_[ii]; + if (x > extsPtr->right) + extsPtr->right = x; + + x = ops->coords.x->values_[ii] - ops->xError->values_[ii]; + AxisOptions* axisxops = (AxisOptions*)ops->xAxis->ops(); + if (axisxops->logScale) { + // Mirror negative values, instead of ignoring them + if (x < 0.0) + x = -x; + if ((x > DBL_MIN) && (x < extsPtr->left)) + extsPtr->left = x; + } + else if (x < extsPtr->left) + extsPtr->left = x; + } + } + else { + if (ops->xHigh && + (ops->xHigh->nValues() > 0) && + (ops->xHigh->max() > extsPtr->right)) { + extsPtr->right = ops->xHigh->max(); + } + if (ops->xLow && ops->xLow->nValues() > 0) { + double left; + if ((ops->xLow->min() <= 0.0) && (axisxops->logScale)) + left = FindElemValuesMinimum(ops->xLow, DBL_MIN); + else + left = ops->xLow->min(); + + if (left < extsPtr->left) + extsPtr->left = left; + } + } + + if (ops->yError && ops->yError->nValues() > 0) { + np = MIN(ops->yError->nValues(), np); + for (int ii=0; iicoords.y->values_[ii] + ops->yError->values_[ii]; + if (y > extsPtr->bottom) + extsPtr->bottom = y; + + y = ops->coords.y->values_[ii] - ops->yError->values_[ii]; + AxisOptions* axisyops = (AxisOptions*)ops->yAxis->ops(); + if (axisyops->logScale) { + // Mirror negative values, instead of ignoring them + if (y < 0.0) + y = -y; + if ((y > DBL_MIN) && (y < extsPtr->left)) + extsPtr->top = y; + } + else if (y < extsPtr->top) + extsPtr->top = y; + } + } + else { + if (ops->yHigh && (ops->yHigh->nValues() > 0) && + (ops->yHigh->max() > extsPtr->bottom)) + extsPtr->bottom = ops->yHigh->max(); + + if (ops->yLow && ops->yLow->nValues() > 0) { + double top; + if ((ops->yLow->min() <= 0.0) && (axisyops->logScale)) + top = FindElemValuesMinimum(ops->yLow, DBL_MIN); + else + top = ops->yLow->min(); + + if (top < extsPtr->top) + extsPtr->top = top; + } + } +} + +void LineElement::closest() +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + + ClosestSearch* searchPtr = &gops->search; + int mode = searchPtr->mode; + if (mode == SEARCH_AUTO) { + LinePen* penPtr = NORMALPEN(ops); + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + mode = SEARCH_POINTS; + if ((NUMBEROFPOINTS(ops) > 1) && (penOps->traceWidth > 0)) + mode = SEARCH_TRACES; + } + if (mode == SEARCH_POINTS) + closestPoint(searchPtr); + else { + int found = closestTrace(); + if ((!found) && (searchPtr->along != SEARCH_BOTH)) + closestPoint(searchPtr); + } +} + +void LineElement::draw(Drawable drawable) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + LinePen* penPtr = NORMALPEN(ops); + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + + if (ops->hide) + return; + + // Fill area under the curve + if (ops->fillBg && fillPts_) { + XPoint*points = new XPoint[nFillPts_]; + + unsigned int count =0; + for (Point2d *pp = fillPts_, *endp = pp + nFillPts_; pp < endp; pp++) { + points[count].x = (short)pp->x; + points[count].y = (short)pp->y; + count++; + } + Tk_Fill3DPolygon(graphPtr_->tkwin_, drawable, ops->fillBg, points, + nFillPts_, 0, TK_RELIEF_FLAT); + delete [] points; + } + + // traces + if ((Chain_GetLength(traces_) > 0) && (penOps->traceWidth > 0)) + drawTraces(drawable, penPtr); + + // Symbols, error bars, values + if (ops->reqMaxSymbols > 0) { + int total = 0; + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); + total += stylePtr->symbolPts.length; + } + symbolInterval_ = total / ops->reqMaxSymbols; + symbolCounter_ = 0; + } + + unsigned int count =0; + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + LineStyle* stylePtr = (LineStyle*)Chain_GetValue(link); + LinePen* penPtr = (LinePen *)stylePtr->penPtr; + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + + if ((stylePtr->xeb.length > 0) && (penOps->errorBarShow & SHOW_X)) + graphPtr_->drawSegments(drawable, penPtr->errorBarGC_, + stylePtr->xeb.segments, stylePtr->xeb.length); + + if ((stylePtr->yeb.length > 0) && (penOps->errorBarShow & SHOW_Y)) + graphPtr_->drawSegments(drawable, penPtr->errorBarGC_, + stylePtr->yeb.segments, stylePtr->yeb.length); + + if ((stylePtr->symbolPts.length > 0) && + (penOps->symbol.type != SYMBOL_NONE)) + drawSymbols(drawable, penPtr, stylePtr->symbolSize, + stylePtr->symbolPts.length, stylePtr->symbolPts.points); + + if (penOps->valueShow != SHOW_NONE) + drawValues(drawable, penPtr, stylePtr->symbolPts.length, + stylePtr->symbolPts.points, symbolPts_.map + count); + + count += stylePtr->symbolPts.length; + } + + symbolInterval_ = 0; + symbolCounter_ = 0; +} + +void LineElement::drawActive(Drawable drawable) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + LinePen* penPtr = (LinePen*)ops->activePenPtr; + if (!penPtr) + return; + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + + if (ops->hide || !active_) + return; + + int symbolSize = scaleSymbol(penOps->symbol.size); + + if (nActiveIndices_ > 0) { + mapActiveSymbols(); + + if (penOps->symbol.type != SYMBOL_NONE) + drawSymbols(drawable, penPtr, symbolSize, activePts_.length, + activePts_.points); + if (penOps->valueShow != SHOW_NONE) + drawValues(drawable, penPtr, activePts_.length, activePts_.points, + activePts_.map); + } + else if (nActiveIndices_ < 0) { + if ((Chain_GetLength(traces_) > 0) && (penOps->traceWidth > 0)) + drawTraces(drawable, penPtr); + + if (penOps->symbol.type != SYMBOL_NONE) + drawSymbols(drawable, penPtr, symbolSize, symbolPts_.length, + symbolPts_.points); + + if (penOps->valueShow != SHOW_NONE) { + drawValues(drawable, penPtr, symbolPts_.length, symbolPts_.points, + symbolPts_.map); + } + } +} + +void LineElement::drawSymbol(Drawable drawable, int x, int y, int size) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + + LinePen* penPtr = NORMALPEN(ops); + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + + if (penOps->traceWidth > 0) { + // Draw an extra line offset by one pixel from the previous to give a + // thicker appearance. This is only for the legend entry. This routine + // is never called for drawing the actual line segments. + XDrawLine(graphPtr_->display_, drawable, penPtr->traceGC_, x - size, y, + x + size, y); + XDrawLine(graphPtr_->display_, drawable, penPtr->traceGC_, x - size, y + 1, + x + size, y + 1); + } + if (penOps->symbol.type != SYMBOL_NONE) { + Point2d point; + point.x = x; + point.y = y; + drawSymbols(drawable, penPtr, size, 1, &point); + } +} + +void LineElement::print(PSOutput* psPtr) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + LinePen* penPtr = NORMALPEN(ops); + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + + if (ops->hide) + return; + + psPtr->format("\n%% Element \"%s\"\n\n", name_); + + // Draw fill area + if (ops->fillBg && fillPts_) { + psPtr->append("% start fill area\n"); + psPtr->setBackground(ops->fillBg); + psPtr->printPolyline(fillPts_, nFillPts_); + psPtr->append("gsave fill grestore\n"); + psPtr->append("% end fill area\n"); + } + + // traces + if ((Chain_GetLength(traces_) > 0) && (penOps->traceWidth > 0)) + printTraces(psPtr, penPtr); + + // Symbols, error bars, values + if (ops->reqMaxSymbols > 0) { + int total = 0; + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); + total += stylePtr->symbolPts.length; + } + symbolInterval_ = total / ops->reqMaxSymbols; + symbolCounter_ = 0; + } + + unsigned int count =0; + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); + LinePen* penPtr = (LinePen *)stylePtr->penPtr; + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + XColor* colorPtr = penOps->errorBarColor; + if (!colorPtr) + colorPtr = penOps->traceColor; + + if ((stylePtr->xeb.length > 0) && (penOps->errorBarShow & SHOW_X)) { + psPtr->setLineAttributes(colorPtr, penOps->errorBarLineWidth, + NULL, CapButt, JoinMiter); + psPtr->printSegments(stylePtr->xeb.segments, stylePtr->xeb.length); + } + + if ((stylePtr->yeb.length > 0) && (penOps->errorBarShow & SHOW_Y)) { + psPtr->setLineAttributes(colorPtr, penOps->errorBarLineWidth, + NULL, CapButt, JoinMiter); + psPtr->printSegments(stylePtr->yeb.segments, stylePtr->yeb.length); + } + + if ((stylePtr->symbolPts.length > 0) && + (penOps->symbol.type != SYMBOL_NONE)) + printSymbols(psPtr, penPtr, stylePtr->symbolSize, + stylePtr->symbolPts.length, stylePtr->symbolPts.points); + + if (penOps->valueShow != SHOW_NONE) + printValues(psPtr, penPtr, stylePtr->symbolPts.length, + stylePtr->symbolPts.points, symbolPts_.map + count); + + count += stylePtr->symbolPts.length; + } + + symbolInterval_ = 0; + symbolCounter_ = 0; +} + +void LineElement::printActive(PSOutput* psPtr) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + LinePen* penPtr = (LinePen *)ops->activePenPtr; + if (!penPtr) + return; + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + + if (ops->hide || !active_) + return; + + psPtr->format("\n%% Active Element \"%s\"\n\n", name_); + + int symbolSize = scaleSymbol(penOps->symbol.size); + if (nActiveIndices_ > 0) { + mapActiveSymbols(); + + if (penOps->symbol.type != SYMBOL_NONE) + printSymbols(psPtr, penPtr, symbolSize, activePts_.length, + activePts_.points); + + if (penOps->valueShow != SHOW_NONE) + printValues(psPtr, penPtr, activePts_.length, activePts_.points, + activePts_.map); + } + else if (nActiveIndices_ < 0) { + if ((Chain_GetLength(traces_) > 0) && (penOps->traceWidth > 0)) + printTraces(psPtr, (LinePen*)penPtr); + + if (penOps->symbol.type != SYMBOL_NONE) + printSymbols(psPtr, penPtr, symbolSize, symbolPts_.length, + symbolPts_.points); + if (penOps->valueShow != SHOW_NONE) { + printValues(psPtr, penPtr, symbolPts_.length, symbolPts_.points, + symbolPts_.map); + } + } +} + +void LineElement::printSymbol(PSOutput* psPtr, double x, double y, int size) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + + LinePen* penPtr = NORMALPEN(ops); + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + + if (penOps->traceWidth > 0) { + // Draw an extra line offset by one pixel from the previous to give a + // thicker appearance. This is only for the legend entry. This routine + // is never called for drawing the actual line segments. + psPtr->setLineAttributes(penOps->traceColor, penOps->traceWidth, + &penOps->traceDashes, CapButt, JoinMiter); + psPtr->format("%g %g %d Li\n", x, y, size + size); + } + + if (penOps->symbol.type != SYMBOL_NONE) { + Point2d point; + point.x = x; + point.y = y; + printSymbols(psPtr, penPtr, size, 1, &point); + } +} + +// Support + +double LineElement::distanceToLine(int x, int y, Point2d *p, Point2d *q, + Point2d *t) +{ + double right, left, top, bottom; + + *t = getProjection(x, y, p, q); + if (p->x > q->x) + right = p->x, left = q->x; + else + left = p->x, right = q->x; + + if (p->y > q->y) + bottom = p->y, top = q->y; + else + top = p->y, bottom = q->y; + + if (t->x > right) + t->x = right; + else if (t->x < left) + t->x = left; + + if (t->y > bottom) + t->y = bottom; + else if (t->y < top) + t->y = top; + + return hypot((t->x - x), (t->y - y)); +} + +double LineElement::distanceToX(int x, int y, Point2d *p, Point2d *q, + Point2d *t) +{ + double dx, dy; + double d; + + if (p->x > q->x) { + if ((x > p->x) || (x < q->x)) { + return DBL_MAX; /* X-coordinate outside line segment. */ + } + } else { + if ((x > q->x) || (x < p->x)) { + return DBL_MAX; /* X-coordinate outside line segment. */ + } + } + dx = p->x - q->x; + dy = p->y - q->y; + t->x = (double)x; + if (fabs(dx) < DBL_EPSILON) { + double d1, d2; + /* + * Same X-coordinate indicates a vertical line. Pick the closest end + * point. + */ + d1 = p->y - y; + d2 = q->y - y; + if (fabs(d1) < fabs(d2)) { + t->y = p->y, d = d1; + } else { + t->y = q->y, d = d2; + } + } + else if (fabs(dy) < DBL_EPSILON) { + /* Horizontal line. */ + t->y = p->y, d = p->y - y; + } + else { + double m = dy / dx; + double b = p->y - (m * p->x); + t->y = (x * m) + b; + d = y - t->y; + } + + return fabs(d); +} + +double LineElement::distanceToY(int x, int y, Point2d *p, Point2d *q, + Point2d *t) +{ + double dx, dy; + double d; + + if (p->y > q->y) { + if ((y > p->y) || (y < q->y)) { + return DBL_MAX; + } + } + else { + if ((y > q->y) || (y < p->y)) { + return DBL_MAX; + } + } + dx = p->x - q->x; + dy = p->y - q->y; + t->y = y; + if (fabs(dy) < DBL_EPSILON) { + double d1, d2; + + /* Save Y-coordinate indicates an horizontal line. Pick the closest end + * point. */ + d1 = p->x - x; + d2 = q->x - x; + if (fabs(d1) < fabs(d2)) { + t->x = p->x, d = d1; + } + else { + t->x = q->x, d = d2; + } + } + else if (fabs(dx) < DBL_EPSILON) { + /* Vertical line. */ + t->x = p->x, d = p->x - x; + } + else { + double m = dy / dx; + double b = p->y - (m * p->x); + t->x = (y - b) / m; + d = x - t->x; + } + + return fabs(d); +} + +int LineElement::scaleSymbol(int normalSize) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + + double scale = 1.0; + if (ops->scaleSymbols) { + double xRange = (ops->xAxis->max_ - ops->xAxis->min_); + double yRange = (ops->yAxis->max_ - ops->yAxis->min_); + // Save the ranges as a baseline for future scaling + if (!xRange_ || !yRange_) { + xRange_ = xRange; + yRange_ = yRange; + } + else { + // Scale the symbol by the smallest change in the X or Y axes + double xScale = xRange_ / xRange; + double yScale = yRange_ / yRange; + scale = MIN(xScale, yScale); + } + } + int newSize = (int)(normalSize * scale); + + int maxSize = MIN(graphPtr_->hRange_, graphPtr_->vRange_); + if (newSize > maxSize) + newSize = maxSize; + + // Make the symbol size odd so that its center is a single pixel. + newSize |= 0x01; + + return newSize; +} + +void LineElement::getScreenPoints(MapInfo* mapPtr) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + + if (!ops->coords.x || !ops->coords.y) { + mapPtr->screenPts = NULL; + mapPtr->nScreenPts = 0; + mapPtr->map = NULL; + } + + int np = NUMBEROFPOINTS(ops); + double* x = ops->coords.x->values_; + double* y = ops->coords.y->values_; + Point2d* points = new Point2d[np]; + int* map = new int[np]; + + int count = 0; + if (gops->inverted) { + for (int ii=0; iiyAxis->hMap(y[ii]); + points[count].y = ops->xAxis->vMap(x[ii]); + map[count] = ii; + count++; + } + } + } + else { + for (int ii=0; ii< np; ii++) { + if ((isfinite(x[ii])) && (isfinite(y[ii]))) { + points[count].x = ops->xAxis->hMap(x[ii]); + points[count].y = ops->yAxis->vMap(y[ii]); + map[count] = ii; + count++; + } + } + } + mapPtr->screenPts = points; + mapPtr->nScreenPts = count; + mapPtr->map = map; +} + +void LineElement::reducePoints(MapInfo *mapPtr, double tolerance) +{ + int* simple = new int[mapPtr->nScreenPts]; + int* map = new int[mapPtr->nScreenPts]; + Point2d* screenPts = new Point2d[mapPtr->nScreenPts]; + int np = simplify(mapPtr->screenPts, 0, mapPtr->nScreenPts - 1, + tolerance, simple); + for (int ii=0; iiscreenPts[kk]; + map[ii] = mapPtr->map[kk]; + } + delete [] simple; + + delete [] mapPtr->screenPts; + mapPtr->screenPts = screenPts; + delete [] mapPtr->map; + mapPtr->map = map; + mapPtr->nScreenPts = np; +} + +// Douglas-Peucker line simplification algorithm +int LineElement::simplify(Point2d *inputPts, int low, int 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 split = -1; + double dist2, tolerance2; + int s = -1; /* Points to top stack item. */ + + int* stack = new int[high - low + 1]; + StackPush(high); + int count = 0; + indices[count++] = 0; + tolerance2 = tolerance * tolerance; + while (!StackEmpty()) { + dist2 = findSplit(inputPts, low, StackTop(), &split); + if (dist2 > tolerance2) + StackPush(split); + else { + indices[count++] = StackTop(); + StackPop(low); + } + } + delete [] stack; + return count; +} + +double LineElement::findSplit(Point2d *points, int i, int j, int *split) +{ + double maxDist2 = -1.0; + if ((i + 1) < j) { + double a = points[i].y - points[j].y; + double b = points[j].x - points[i].x; + double c = (points[i].x * points[j].y) - (points[i].y * points[j].x); + for (int kk = (i + 1); kk < j; kk++) { + double dist2 = (points[kk].x * a) + (points[kk].y * b) + c; + if (dist2 < 0.0) + dist2 = -dist2; + + // Track the maximum. + if (dist2 > maxDist2) { + maxDist2 = dist2; + *split = kk; + } + } + // Correction for segment length---should be redone if can == 0 + maxDist2 *= maxDist2 / (a * a + b * b); + } + return maxDist2; +} + +void LineElement::generateSteps(MapInfo *mapPtr) +{ + int newSize = ((mapPtr->nScreenPts - 1) * 2) + 1; + Point2d* screenPts = new Point2d[newSize]; + int* map = new int[newSize]; + screenPts[0] = mapPtr->screenPts[0]; + map[0] = 0; + + int count = 1; + for (int i = 1; i < mapPtr->nScreenPts; i++) { + screenPts[count + 1] = mapPtr->screenPts[i]; + + // Hold last y-coordinate, use new x-coordinate + screenPts[count].x = screenPts[count + 1].x; + screenPts[count].y = screenPts[count - 1].y; + + // Use the same style for both the hold and the step points + map[count] = map[count + 1] = mapPtr->map[i]; + count += 2; + } + delete [] mapPtr->map; + mapPtr->map = map; + delete [] mapPtr->screenPts; + mapPtr->screenPts = screenPts; + mapPtr->nScreenPts = newSize; +} + +void LineElement::generateSpline(MapInfo *mapPtr) +{ + int nOrigPts = mapPtr->nScreenPts; + Point2d* origPts = mapPtr->screenPts; + + // check points are not monotonically increasing + for (int ii=0, jj=1; jj (double)graphPtr_->right_)) || + ((origPts[mapPtr->nScreenPts - 1].x < (double)graphPtr_->left_))) + return; + + // The spline is computed in screen coordinates instead of data points so + // that we can select the abscissas of the interpolated points from each + // pixel horizontally across the plotting area. + int extra = (graphPtr_->right_ - graphPtr_->left_) + 1; + if (extra < 1) + return; + + int niPts = nOrigPts + extra + 1; + Point2d* iPts = new Point2d[niPts]; + int* map = new int[niPts]; + + // Populate the x2 array with both the original X-coordinates and extra + // X-coordinates for each horizontal pixel that the line segment contains + int count = 0; + for (int ii=0, jj=1; jjmap[ii]; + count++; + + // Is any part of the interval (line segment) in the plotting area? + if ((origPts[jj].x >= (double)graphPtr_->left_) || + (origPts[ii].x <= (double)graphPtr_->right_)) { + double x = origPts[ii].x + 1.0; + + /* + * Since the line segment may be partially clipped on the left or + * right side, the points to interpolate are always interior to + * the plotting area. + * + * left right + * x1----|---------------------------|---x2 + * + * Pick the max of the starting X-coordinate and the left edge and + * the min of the last X-coordinate and the right edge. + */ + x = MAX(x, (double)graphPtr_->left_); + double last = MIN(origPts[jj].x, (double)graphPtr_->right_); + + // Add the extra x-coordinates to the interval + while (x < last) { + map[count] = mapPtr->map[ii]; + iPts[count++].x = x; + x++; + } + } + } + niPts = count; + int result = 0; + if (smooth_ == CUBIC) + result = naturalSpline(origPts, nOrigPts, iPts, niPts); + else if (smooth_ == QUADRATIC) + result = quadraticSpline(origPts, nOrigPts, iPts, niPts); + + // The spline interpolation failed. We will fall back to the current + // coordinates and do no smoothing (standard line segments) + if (!result) { + smooth_ = LINEAR; + delete [] iPts; + delete [] map; + } + else { + delete [] mapPtr->map; + mapPtr->map = map; + delete [] mapPtr->screenPts; + mapPtr->screenPts = iPts; + mapPtr->nScreenPts = niPts; + } +} + +void LineElement::generateParametricSpline(MapInfo *mapPtr) +{ + int nOrigPts = mapPtr->nScreenPts; + Point2d *origPts = mapPtr->screenPts; + + Region2d exts; + graphPtr_->extents(&exts); + + /* + * Populate the x2 array with both the original X-coordinates and extra + * X-coordinates for each horizontal pixel that the line segment contains. + */ + int count = 1; + for (int i = 0, j = 1; j < nOrigPts; i++, j++) { + Point2d p = origPts[i]; + Point2d q = origPts[j]; + count++; + if (lineRectClip(&exts, &p, &q)) + count += (int)(hypot(q.x - p.x, q.y - p.y) * 0.5); + } + int niPts = count; + Point2d *iPts = new Point2d[niPts]; + int* map = new int[niPts]; + + /* + * FIXME: This is just plain wrong. The spline should be computed + * and evaluated in separate steps. This will mean breaking + * up this routine since the catrom coefficients can be + * independently computed for original data point. This + * also handles the problem of allocating enough points + * since evaluation is independent of the number of points + * to be evalualted. The interpolated + * line segments should be clipped, not the original segments. + */ + count = 0; + int i,j; + for (i = 0, j = 1; j < nOrigPts; i++, j++) { + Point2d p = origPts[i]; + Point2d q = origPts[j]; + + double d = hypot(q.x - p.x, q.y - p.y); + /* Add the original x-coordinate */ + iPts[count].x = (double)i; + iPts[count].y = 0.0; + + /* Include the starting offset of the point in the offset array */ + map[count] = mapPtr->map[i]; + count++; + + /* Is any part of the interval (line segment) in the plotting + * area? */ + + if (lineRectClip(&exts, &p, &q)) { + double dp, dq; + + /* Distance of original point to p. */ + dp = hypot(p.x - origPts[i].x, p.y - origPts[i].y); + /* Distance of original point to q. */ + dq = hypot(q.x - origPts[i].x, q.y - origPts[i].y); + dp += 2.0; + while(dp <= dq) { + /* Point is indicated by its interval and parameter t. */ + iPts[count].x = (double)i; + iPts[count].y = dp / d; + map[count] = mapPtr->map[i]; + count++; + dp += 2.0; + } + } + } + iPts[count].x = (double)i; + iPts[count].y = 0.0; + map[count] = mapPtr->map[i]; + count++; + niPts = count; + int result = 0; + if (smooth_ == CUBIC) + result = naturalParametricSpline(origPts, nOrigPts, &exts, 0, iPts, niPts); + else if (smooth_ == CATROM) + result = catromParametricSpline(origPts, nOrigPts, iPts, niPts); + + // The spline interpolation failed. We will fall back to the current + // coordinates and do no smoothing (standard line segments) + if (!result) { + smooth_ = LINEAR; + delete [] iPts; + delete [] map; + } + else { + delete [] mapPtr->map; + mapPtr->map = map; + delete [] mapPtr->screenPts; + mapPtr->screenPts = iPts; + mapPtr->nScreenPts = niPts; + } +} + +void LineElement::mapSymbols(MapInfo *mapPtr) +{ + Point2d* points = new Point2d[mapPtr->nScreenPts]; + int *map = new int[mapPtr->nScreenPts]; + + Region2d exts; + graphPtr_->extents(&exts); + + Point2d *pp; + int count = 0; + int i; + for (pp=mapPtr->screenPts, i=0; inScreenPts; i++, pp++) { + if (PointInRegion(&exts, pp->x, pp->y)) { + points[count].x = pp->x; + points[count].y = pp->y; + map[count] = mapPtr->map[i]; + count++; + } + } + symbolPts_.points = points; + symbolPts_.length = count; + symbolPts_.map = map; +} + +void LineElement::mapActiveSymbols() +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + + delete [] activePts_.points; + activePts_.points = NULL; + delete [] activePts_.map; + activePts_.map = NULL; + + Region2d exts; + graphPtr_->extents(&exts); + + Point2d *points = new Point2d[nActiveIndices_]; + int* map = new int[nActiveIndices_]; + int np = NUMBEROFPOINTS(ops); + int count = 0; + if (ops->coords.x && ops->coords.y) { + for (int ii=0; ii= np) + continue; + + double x = ops->coords.x->values_[iPoint]; + double y = ops->coords.y->values_[iPoint]; + points[count] = graphPtr_->map2D(x, y, ops->xAxis, ops->yAxis); + map[count] = iPoint; + if (PointInRegion(&exts, points[count].x, points[count].y)) { + count++; + } + } + } + + if (count > 0) { + activePts_.points = points; + activePts_.map = map; + } + else { + delete [] points; + delete [] map; + } + activePts_.length = count; +} + +void LineElement::mergePens(LineStyle **styleMap) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + + if (Chain_GetLength(ops->stylePalette) < 2) { + ChainLink* link = Chain_FirstLink(ops->stylePalette); + LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); + stylePtr->symbolPts.length = symbolPts_.length; + stylePtr->symbolPts.points = symbolPts_.points; + stylePtr->xeb.length = xeb_.length; + stylePtr->xeb.segments = xeb_.segments; + stylePtr->yeb.length = yeb_.length; + stylePtr->yeb.segments = yeb_.segments; + return; + } + + if (symbolPts_.length > 0) { + Point2d* points = new Point2d[symbolPts_.length]; + int* map = new int[symbolPts_.length]; + Point2d *pp = points; + int* ip = map; + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); + stylePtr->symbolPts.points = pp; + for (int ii=0; iisymbolPts.length = pp - stylePtr->symbolPts.points; + } + delete [] symbolPts_.points; + symbolPts_.points = points; + delete [] symbolPts_.map; + symbolPts_.map = map; + } + + if (xeb_.length > 0) { + Segment2d* segments = new Segment2d[xeb_.length]; + Segment2d *sp = segments; + int* map = new int[xeb_.length]; + int* ip = map; + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); + stylePtr->xeb.segments = sp; + for (int ii=0; iixeb.length = sp - stylePtr->xeb.segments; + } + delete [] xeb_.segments; + xeb_.segments = segments; + delete [] xeb_.map; + xeb_.map = map; + } + + if (yeb_.length > 0) { + Segment2d* segments = new Segment2d[yeb_.length]; + Segment2d* sp = segments; + int* map = new int [yeb_.length]; + int* ip = map; + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); + stylePtr->yeb.segments = sp; + for (int ii=0; iiyeb.length = sp - stylePtr->yeb.segments; + } + delete [] yeb_.segments; + yeb_.segments = segments; + delete [] yeb_.map; + yeb_.map = map; + } +} + +#define CLIP_TOP (1<<0) +#define CLIP_BOTTOM (1<<1) +#define CLIP_RIGHT (1<<2) +#define CLIP_LEFT (1<<3) + +int LineElement::outCode(Region2d *extsPtr, Point2d *p) +{ + int code =0; + if (p->x > extsPtr->right) + code |= CLIP_RIGHT; + else if (p->x < extsPtr->left) + code |= CLIP_LEFT; + + if (p->y > extsPtr->bottom) + code |= CLIP_BOTTOM; + else if (p->y < extsPtr->top) + code |= CLIP_TOP; + + return code; +} + +int LineElement::clipSegment(Region2d *extsPtr, int code1, int code2, + Point2d *p, Point2d *q) +{ + int inside = ((code1 | code2) == 0); + int outside = ((code1 & code2) != 0); + + /* + * In the worst case, we'll clip the line segment against each of the four + * sides of the bounding rectangle. + */ + while ((!outside) && (!inside)) { + if (code1 == 0) { + Point2d *tmp; + int code; + + /* Swap pointers and out codes */ + tmp = p, p = q, q = tmp; + code = code1, code1 = code2, code2 = code; + } + if (code1 & CLIP_LEFT) { + p->y += (q->y - p->y) * + (extsPtr->left - p->x) / (q->x - p->x); + p->x = extsPtr->left; + } else if (code1 & CLIP_RIGHT) { + p->y += (q->y - p->y) * + (extsPtr->right - p->x) / (q->x - p->x); + p->x = extsPtr->right; + } else if (code1 & CLIP_BOTTOM) { + p->x += (q->x - p->x) * + (extsPtr->bottom - p->y) / (q->y - p->y); + p->y = extsPtr->bottom; + } else if (code1 & CLIP_TOP) { + p->x += (q->x - p->x) * + (extsPtr->top - p->y) / (q->y - p->y); + p->y = extsPtr->top; + } + code1 = outCode(extsPtr, p); + + inside = ((code1 | code2) == 0); + outside = ((code1 & code2) != 0); + } + return (!inside); +} + +void LineElement::saveTrace(int start, int length, MapInfo* mapPtr) +{ + bltTrace* tracePtr = new bltTrace; + Point2d* screenPts = new Point2d[length]; + int* map = new int[length]; + + // Copy the screen coordinates of the trace into the point array + if (mapPtr->map) { + for (int ii=0, jj=start; iiscreenPts[jj].x; + screenPts[ii].y = mapPtr->screenPts[jj].y; + map[ii] = mapPtr->map[jj]; + } + } + else { + for (int ii=0, jj=start; iiscreenPts[jj].x; + screenPts[ii].y = mapPtr->screenPts[jj].y; + map[ii] = jj; + } + } + tracePtr->screenPts.length = length; + tracePtr->screenPts.points = screenPts; + tracePtr->screenPts.map = map; + tracePtr->start = start; + if (traces_ == NULL) + traces_ = new Chain(); + + traces_->append(tracePtr); +} + +void LineElement::freeTraces() +{ + for (ChainLink* link = Chain_FirstLink(traces_); link; + link = Chain_NextLink(link)) { + bltTrace* tracePtr = (bltTrace*)Chain_GetValue(link); + delete [] tracePtr->screenPts.map; + delete [] tracePtr->screenPts.points; + delete tracePtr; + } + delete traces_; + traces_ = NULL; +} + +void LineElement::mapTraces(MapInfo *mapPtr) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + + Region2d exts; + graphPtr_->extents(&exts); + + int count = 1; + int code1 = outCode(&exts, mapPtr->screenPts); + Point2d* p = mapPtr->screenPts; + Point2d* q = p + 1; + + int start; + int ii; + for (ii=1; iinScreenPts; ii++, p++, q++) { + Point2d s; + s.x = 0; + s.y = 0; + int code2 = outCode(&exts, q); + // Save the coordinates of the last point, before clipping + if (code2 != 0) + s = *q; + + int broken = BROKEN_TRACE(ops->penDir, p->x, q->x); + int offscreen = clipSegment(&exts, code1, code2, p, q); + if (broken || offscreen) { + // The last line segment is either totally clipped by the plotting + // area or the x-direction is wrong, breaking the trace. Either + // way, save information about the last trace (if one exists), + // discarding the current line segment + if (count > 1) { + start = ii - count; + saveTrace(start, count, mapPtr); + count = 1; + } + } + else { + // Add the point to the trace + count++; + + // If the last point is clipped, this means that the trace is + // broken after this point. Restore the original coordinate + // (before clipping) after saving the trace. + if (code2 != 0) { + start = ii - (count - 1); + saveTrace(start, count, mapPtr); + mapPtr->screenPts[ii] = s; + count = 1; + } + } + code1 = code2; + } + if (count > 1) { + start = ii - count; + saveTrace(start, count, mapPtr); + } +} + +void LineElement::mapFillArea(MapInfo *mapPtr) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + + if (fillPts_) { + delete [] fillPts_; + fillPts_ = NULL; + nFillPts_ = 0; + } + if (mapPtr->nScreenPts < 3) + return; + + int np = mapPtr->nScreenPts + 3; + Region2d exts; + graphPtr_->extents(&exts); + + Point2d* origPts = new Point2d[np]; + if (gops->inverted) { + int i; + double minX = (double)ops->yAxis->screenMin_; + for (i = 0; i < mapPtr->nScreenPts; i++) { + origPts[i].x = mapPtr->screenPts[i].x + 1; + origPts[i].y = mapPtr->screenPts[i].y; + if (origPts[i].x < minX) { + minX = origPts[i].x; + } + } + // Add edges to make the polygon fill to the bottom of plotting window + origPts[i].x = minX; + origPts[i].y = origPts[i - 1].y; + i++; + origPts[i].x = minX; + origPts[i].y = origPts[0].y; + i++; + origPts[i] = origPts[0]; + } + else { + int i; + double maxY = (double)ops->yAxis->bottom_; + for (i = 0; i < mapPtr->nScreenPts; i++) { + origPts[i].x = mapPtr->screenPts[i].x + 1; + origPts[i].y = mapPtr->screenPts[i].y; + if (origPts[i].y > maxY) { + maxY = origPts[i].y; + } + } + // Add edges to extend the fill polygon to the bottom of plotting window + origPts[i].x = origPts[i - 1].x; + origPts[i].y = maxY; + i++; + origPts[i].x = origPts[0].x; + origPts[i].y = maxY; + i++; + origPts[i] = origPts[0]; + } + + Point2d *clipPts = new Point2d[np * 3]; + np = polyRectClip(&exts, origPts, np - 1, clipPts); + + delete [] origPts; + if (np < 3) + delete [] clipPts; + else { + fillPts_ = clipPts; + nFillPts_ = np; + } +} + +void LineElement::reset() +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + + freeTraces(); + + for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; + link = Chain_NextLink(link)) { + LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); + stylePtr->symbolPts.length = 0; + stylePtr->xeb.length = 0; + stylePtr->yeb.length = 0; + } + + delete [] symbolPts_.points; + symbolPts_.points = NULL; + + delete [] symbolPts_.map; + symbolPts_.map = NULL; + symbolPts_.length = 0; + + delete [] activePts_.points; + activePts_.points = NULL; + activePts_.length = 0; + + delete [] activePts_.map; + activePts_.map = NULL; + + delete [] xeb_.segments; + xeb_.segments = NULL; + delete [] xeb_.map; + xeb_.map = NULL; + xeb_.length = 0; + + delete [] yeb_.segments; + yeb_.segments = NULL; + delete [] yeb_.map; + yeb_.map = NULL; + yeb_.length = 0; +} + +void LineElement::mapErrorBars(LineStyle **styleMap) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + + Region2d exts; + graphPtr_->extents(&exts); + + int nn =0; + int np = NUMBEROFPOINTS(ops); + if (ops->coords.x && ops->coords.y) { + if (ops->xError && (ops->xError->nValues() > 0)) + nn = MIN(ops->xError->nValues(), np); + else + if (ops->xHigh && ops->xLow) + nn = MIN3(ops->xHigh->nValues(), ops->xLow->nValues(), np); + } + + if (nn) { + Segment2d* errorBars = new Segment2d[nn * 3]; + Segment2d* segPtr = errorBars; + int* errorToData = new int[nn * 3]; + int* indexPtr = errorToData; + + for (int ii=0; iicoords.x->values_[ii]; + double y = ops->coords.y->values_[ii]; + LineStyle* stylePtr = styleMap[ii]; + + if ((isfinite(x)) && (isfinite(y))) { + double high; + double low; + if (ops->xError->nValues() > 0) { + high = x + ops->xError->values_[ii]; + low = x - ops->xError->values_[ii]; + } + else { + high = ops->xHigh ? ops->xHigh->values_[ii] : 0; + low = ops->xLow ? ops->xLow->values_[ii] : 0; + } + + if ((isfinite(high)) && (isfinite(low))) { + Point2d p = graphPtr_->map2D(high, y, ops->xAxis, ops->yAxis); + Point2d q = graphPtr_->map2D(low, y, ops->xAxis, ops->yAxis); + segPtr->p = p; + segPtr->q = q; + if (lineRectClip(&exts, &segPtr->p, &segPtr->q)) { + segPtr++; + *indexPtr++ = ii; + } + // Left cap + segPtr->p.x = p.x; + segPtr->q.x = p.x; + segPtr->p.y = p.y - stylePtr->errorBarCapWidth; + segPtr->q.y = p.y + stylePtr->errorBarCapWidth; + if (lineRectClip(&exts, &segPtr->p, &segPtr->q)) { + segPtr++; + *indexPtr++ = ii; + } + // Right cap + segPtr->p.x = q.x; + segPtr->q.x = q.x; + segPtr->p.y = q.y - stylePtr->errorBarCapWidth; + segPtr->q.y = q.y + stylePtr->errorBarCapWidth; + if (lineRectClip(&exts, &segPtr->p, &segPtr->q)) { + segPtr++; + *indexPtr++ = ii; + } + } + } + } + xeb_.segments = errorBars; + xeb_.length = segPtr - errorBars; + xeb_.map = errorToData; + } + + nn =0; + if (ops->coords.x && ops->coords.y) { + if (ops->yError && (ops->yError->nValues() > 0)) + nn = MIN(ops->yError->nValues(), np); + else + if (ops->yHigh && ops->yLow) + nn = MIN3(ops->yHigh->nValues(), ops->yLow->nValues(), np); + } + + if (nn) { + Segment2d* errorBars = new Segment2d[nn * 3]; + Segment2d* segPtr = errorBars; + int* errorToData = new int[nn * 3]; + int* indexPtr = errorToData; + + for (int ii=0; iicoords.x->values_[ii]; + double y = ops->coords.y->values_[ii]; + LineStyle* stylePtr = styleMap[ii]; + + if ((isfinite(x)) && (isfinite(y))) { + double high; + double low; + if (ops->yError->nValues() > 0) { + high = y + ops->yError->values_[ii]; + low = y - ops->yError->values_[ii]; + } + else { + high = ops->yHigh->values_[ii]; + low = ops->yLow->values_[ii]; + } + + if ((isfinite(high)) && (isfinite(low))) { + Point2d p = graphPtr_->map2D(x, high, ops->xAxis, ops->yAxis); + Point2d q = graphPtr_->map2D(x, low, ops->xAxis, ops->yAxis); + segPtr->p = p; + segPtr->q = q; + if (lineRectClip(&exts, &segPtr->p, &segPtr->q)) { + segPtr++; + *indexPtr++ = ii; + } + // Top cap + segPtr->p.y = p.y; + segPtr->q.y = p.y; + segPtr->p.x = p.x - stylePtr->errorBarCapWidth; + segPtr->q.x = p.x + stylePtr->errorBarCapWidth; + if (lineRectClip(&exts, &segPtr->p, &segPtr->q)) { + segPtr++; + *indexPtr++ = ii; + } + // Bottom cap + segPtr->p.y = q.y; + segPtr->q.y = q.y; + segPtr->p.x = q.x - stylePtr->errorBarCapWidth; + segPtr->q.x = q.x + stylePtr->errorBarCapWidth; + if (lineRectClip(&exts, &segPtr->p, &segPtr->q)) { + segPtr++; + *indexPtr++ = ii; + } + } + } + } + yeb_.segments = errorBars; + yeb_.length = segPtr - errorBars; + yeb_.map = errorToData; + } +} + +int LineElement::closestTrace() +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + ClosestSearch* searchPtr = &gops->search; + + Point2d closest; + + int iClose = -1; + double dMin = searchPtr->dist; + closest.x = closest.y = 0; + for (ChainLink *link=Chain_FirstLink(traces_); link; + link = Chain_NextLink(link)) { + bltTrace *tracePtr = (bltTrace*)Chain_GetValue(link); + for (Point2d *p=tracePtr->screenPts.points, + *pend=p + (tracePtr->screenPts.length - 1); palong == SEARCH_X) + d = distanceToX(searchPtr->x, searchPtr->y, p, p + 1, &b); + else if (searchPtr->along == SEARCH_Y) + d = distanceToY(searchPtr->x, searchPtr->y, p, p + 1, &b); + else + d = distanceToLine(searchPtr->x, searchPtr->y, p, p + 1, &b); + + if (d < dMin) { + closest = b; + iClose = tracePtr->screenPts.map[p-tracePtr->screenPts.points]; + dMin = d; + } + } + } + if (dMin < searchPtr->dist) { + searchPtr->dist = dMin; + searchPtr->elemPtr = (Element*)this; + searchPtr->index = iClose; + searchPtr->point = graphPtr_->invMap2D(closest.x, closest.y, + ops->xAxis, ops->yAxis); + return 1; + } + + return 0; +} + +void LineElement::closestPoint(ClosestSearch *searchPtr) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + + double dMin = searchPtr->dist; + int iClose = 0; + + // Instead of testing each data point in graph coordinates, look at the + // array of mapped screen coordinates. The advantages are + // 1) only examine points that are visible (unclipped), and + // 2) the computed distance is already in screen coordinates. + int count =0; + for (Point2d *pp = symbolPts_.points; count < symbolPts_.length; + count++, pp++) { + double dx = (double)(searchPtr->x - pp->x); + double dy = (double)(searchPtr->y - pp->y); + double d; + if (searchPtr->along == SEARCH_BOTH) + d = hypot(dx, dy); + else if (searchPtr->along == SEARCH_X) + d = dx; + else if (searchPtr->along == SEARCH_Y) + d = dy; + else + continue; + + if (d < dMin) { + iClose = symbolPts_.map[count]; + dMin = d; + } + } + if (dMin < searchPtr->dist) { + searchPtr->elemPtr = (Element*)this; + searchPtr->dist = dMin; + searchPtr->index = iClose; + searchPtr->point.x = ops->coords.x->values_[iClose]; + searchPtr->point.y = ops->coords.y->values_[iClose]; + } +} + +void LineElement::drawCircle(Display *display, Drawable drawable, + LinePen* penPtr, + int nSymbolPts, Point2d *symbolPts, int radius) +{ + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + + int count = 0; + int s = radius + radius; + XArc* arcs = new XArc[nSymbolPts]; + XArc *ap = arcs; + for (Point2d *pp=symbolPts, *pend=pp+nSymbolPts; ppx = (short)(pp->x - radius); + ap->y = (short)(pp->y - radius); + ap->width = (short)s; + ap->height = (short)s; + ap->angle1 = 0; + ap->angle2 = 23040; + ap++; + count++; + } + symbolCounter_++; + } + + for (XArc *ap=arcs, *aend=ap+count; apsymbol.fillGC) + XFillArc(display, drawable, penOps->symbol.fillGC, + ap->x, ap->y, ap->width, ap->height, ap->angle1, ap->angle2); + + if (penOps->symbol.outlineWidth > 0) + XDrawArc(display, drawable, penOps->symbol.outlineGC, + ap->x, ap->y, ap->width, ap->height, ap->angle1, ap->angle2); + } + + delete [] arcs; +} + +void LineElement::drawSquare(Display *display, Drawable drawable, + LinePen* penPtr, + int nSymbolPts, Point2d *symbolPts, int r) +{ + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + + int s = r + r; + int count =0; + Rectangle* rectangles = new Rectangle[nSymbolPts]; + Rectangle* rp=rectangles; + for (Point2d *pp=symbolPts, *pend=pp+nSymbolPts; ppx = (int)pp->x - r; + rp->y = (int)pp->y - r; + rp->width = s; + rp->height = s; + rp++; + count++; + } + symbolCounter_++; + } + + for (Rectangle *rp=rectangles, *rend=rp+count; rpsymbol.fillGC) + XFillRectangle(display, drawable, penOps->symbol.fillGC, + rp->x, rp->y, rp->width, rp->height); + + if (penOps->symbol.outlineWidth > 0) + XDrawRectangle(display, drawable, penOps->symbol.outlineGC, + rp->x, rp->y, rp->width, rp->height); + } + + delete [] rectangles; +} + +void LineElement::drawSCross(Display* display, Drawable drawable, + LinePen* penPtr, + int nSymbolPts, Point2d* symbolPts, int r2) +{ + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + + Point pattern[4]; + if (penOps->symbol.type == SYMBOL_SCROSS) { + r2 = (int)(r2 * M_SQRT1_2); + pattern[3].y = pattern[2].x = pattern[0].x = pattern[0].y = -r2; + pattern[3].x = pattern[2].y = pattern[1].y = pattern[1].x = r2; + } + else { + pattern[0].y = pattern[1].y = pattern[2].x = pattern[3].x = 0; + pattern[0].x = pattern[2].y = -r2; + pattern[1].x = pattern[3].y = r2; + } + + for (Point2d *pp=symbolPts, *endp=pp+nSymbolPts; ppx; + int rndy = (int)pp->y; + XDrawLine(graphPtr_->display_, drawable, penOps->symbol.outlineGC, + pattern[0].x + rndx, pattern[0].y + rndy, + pattern[1].x + rndx, pattern[1].y + rndy); + XDrawLine(graphPtr_->display_, drawable, penOps->symbol.outlineGC, + pattern[2].x + rndx, pattern[2].y + rndy, + pattern[3].x + rndx, pattern[3].y + rndy); + } + } +} + +void LineElement::drawCross(Display *display, Drawable drawable, + LinePen* penPtr, + int nSymbolPts, Point2d *symbolPts, int r2) +{ + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + + /* + * 2 3 The plus/cross symbol is a closed polygon + * of 12 points. The diagram to the left + * 0,12 1 4 5 represents the positions of the points + * x,y which are computed below. The extra + * 11 10 7 6 (thirteenth) point connects the first and + * last points. + * 9 8 + */ + int d = (r2 / 3); + Point pattern[13]; + pattern[0].x = pattern[11].x = pattern[12].x = -r2; + pattern[2].x = pattern[1].x = pattern[10].x = pattern[9].x = -d; + pattern[3].x = pattern[4].x = pattern[7].x = pattern[8].x = d; + pattern[5].x = pattern[6].x = r2; + pattern[2].y = pattern[3].y = -r2; + pattern[0].y = pattern[1].y = pattern[4].y = pattern[5].y = + pattern[12].y = -d; + pattern[11].y = pattern[10].y = pattern[7].y = pattern[6].y = d; + pattern[9].y = pattern[8].y = r2; + + if (penOps->symbol.type == SYMBOL_CROSS) { + // For the cross symbol, rotate the points by 45 degrees + for (int ii=0; ii<12; ii++) { + double dx = (double)pattern[ii].x * M_SQRT1_2; + double dy = (double)pattern[ii].y * M_SQRT1_2; + pattern[ii].x = (int)(dx - dy); + pattern[ii].y = (int)(dx + dy); + } + pattern[12] = pattern[0]; + } + + int count = 0; + XPoint* polygon = new XPoint[nSymbolPts*13]; + XPoint* xpp = polygon; + for (Point2d *pp = symbolPts, *endp = pp + nSymbolPts; pp < endp; pp++) { + if (DRAW_SYMBOL()) { + int rndx = (int)pp->x; + int rndy = (int)pp->y; + for (int ii=0; ii<13; ii++) { + xpp->x = (short)(pattern[ii].x + rndx); + xpp->y = (short)(pattern[ii].y + rndy); + xpp++; + } + count++; + } + symbolCounter_++; + } + + if (penOps->symbol.fillGC) { + XPoint* xpp = polygon; + for (int ii=0; iidisplay_, drawable, + penOps->symbol.fillGC, xpp, 13, Complex, + CoordModeOrigin); + } + + if (penOps->symbol.outlineWidth > 0) { + XPoint*xpp = polygon; + for (int ii=0; iidisplay_, drawable, + penOps->symbol.outlineGC, xpp, 13, CoordModeOrigin); + } + + delete [] polygon; +} + +void LineElement::drawDiamond(Display *display, Drawable drawable, + LinePen* penPtr, + int nSymbolPts, Point2d *symbolPts, int r1) +{ + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + + /* + * The plus symbol is a closed polygon + * 1 of 4 points. The diagram to the left + * represents the positions of the points + * 0,4 x,y 2 which are computed below. The extra + * (fifth) point connects the first and + * 3 last points. + */ + Point pattern[5]; + pattern[1].y = pattern[0].x = -r1; + pattern[2].y = pattern[3].x = pattern[0].y = pattern[1].x = 0; + pattern[3].y = pattern[2].x = r1; + pattern[4] = pattern[0]; + + int count = 0; + XPoint* polygon = new XPoint[nSymbolPts*5]; + XPoint* xpp = polygon; + for (Point2d *pp = symbolPts, *endp = pp + nSymbolPts; pp < endp; pp++) { + if (DRAW_SYMBOL()) { + int rndx = (int)pp->x; + int rndy = (int)pp->y; + for (int ii=0; ii<5; ii++) { + xpp->x = (short)(pattern[ii].x + rndx); + xpp->y = (short)(pattern[ii].y + rndy); + xpp++; + } + count++; + } + symbolCounter_++; + } + + if (penOps->symbol.fillGC) { + XPoint* xpp = polygon; + for (int ii=0; iidisplay_, drawable, + penOps->symbol.fillGC, xpp, 5, Convex, CoordModeOrigin); + } + + if (penOps->symbol.outlineWidth > 0) { + XPoint* xpp = polygon; + for (int ii=0; iidisplay_, drawable, + penOps->symbol.outlineGC, xpp, 5, CoordModeOrigin); + } + + delete [] polygon; +} + +#define B_RATIO 1.3467736870885982 +#define TAN30 0.57735026918962573 +#define COS30 0.86602540378443871 +void LineElement::drawArrow(Display *display, Drawable drawable, + LinePen* penPtr, + int nSymbolPts, Point2d *symbolPts, int size) +{ + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + + double b = size * B_RATIO * 0.7 * 0.5; + short b2 = (short)b; + short h2 = (short)(TAN30 * b); + short h1 = (short)(b / COS30); + /* + * The triangle symbol is a closed polygon + * 0,3 of 3 points. The diagram to the left + * represents the positions of the points + * x,y which are computed below. The extra + * (fourth) point connects the first and + * 2 1 last points. + */ + + Point pattern[4]; + if (penOps->symbol.type == SYMBOL_ARROW) { + pattern[3].x = pattern[0].x = 0; + pattern[3].y = pattern[0].y = h1; + pattern[1].x = b2; + pattern[2].y = pattern[1].y = -h2; + pattern[2].x = -b2; + } else { + pattern[3].x = pattern[0].x = 0; + pattern[3].y = pattern[0].y = -h1; + pattern[1].x = b2; + pattern[2].y = pattern[1].y = h2; + pattern[2].x = -b2; + } + + int count = 0; + XPoint* polygon = new XPoint[nSymbolPts*4]; + XPoint* xpp = polygon; + for (Point2d *pp = symbolPts, *endp = pp + nSymbolPts; pp < endp; pp++) { + if (DRAW_SYMBOL()) { + int rndx = (int)pp->x; + int rndy = (int)pp->y; + for (int ii=0; ii<4; ii++) { + xpp->x = (short)(pattern[ii].x + rndx); + xpp->y = (short)(pattern[ii].y + rndy); + xpp++; + } + count++; + } + symbolCounter_++; + } + + if (penOps->symbol.fillGC) { + XPoint* xpp = polygon; + for (int ii=0; iidisplay_, drawable, + penOps->symbol.fillGC, xpp, 4, Convex, CoordModeOrigin); + } + + if (penOps->symbol.outlineWidth > 0) { + XPoint* xpp = polygon; + for (int ii=0; iidisplay_, drawable, + penOps->symbol.outlineGC, xpp, 4, CoordModeOrigin); + } + + delete [] polygon; +} + +#define S_RATIO 0.886226925452758 +void LineElement::drawSymbols(Drawable drawable, LinePen* penPtr, int size, + int nSymbolPts, Point2d* symbolPts) +{ + LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); + + if (size < 3) { + if (penOps->symbol.fillGC) { + for (Point2d *pp = symbolPts, *endp = pp + nSymbolPts; pp < endp; pp++) + XDrawLine(graphPtr_->display_, drawable, penOps->symbol.fillGC, + (int)pp->x, (int)pp->y, (int)pp->x+1, (int)pp->y+1); + } + return; + } + + int r1 = (int)ceil(size * 0.5); + int r2 = (int)ceil(size * S_RATIO * 0.5); + + switch (penOps->symbol.type) { + case SYMBOL_NONE: + break; + case SYMBOL_SQUARE: + drawSquare(graphPtr_->display_, drawable, penPtr, nSymbolPts,symbolPts,r2); + break; + case SYMBOL_CIRCLE: + drawCircle(graphPtr_->display_, drawable, penPtr, nSymbolPts,symbolPts,r1); + break; + case SYMBOL_SPLUS: + case SYMBOL_SCROSS: + drawSCross(graphPtr_->display_, drawable, penPtr, nSymbolPts,symbolPts,r2); + break; + case SYMBOL_PLUS: + case SYMBOL_CROSS: + drawCross(graphPtr_->display_, drawable, penPtr, nSymbolPts,symbolPts,r2); + break; + case SYMBOL_DIAMOND: + drawDiamond(graphPtr_->display_, drawable, penPtr, nSymbolPts,symbolPts,r1); + break; + case SYMBOL_TRIANGLE: + case SYMBOL_ARROW: + drawArrow(graphPtr_->display_, drawable, penPtr, nSymbolPts,symbolPts,size); + break; + } +} + +void LineElement::drawTraces(Drawable drawable, LinePen* penPtr) +{ + for (ChainLink* link = Chain_FirstLink(traces_); link; + link = Chain_NextLink(link)) { + bltTrace* tracePtr = (bltTrace*)Chain_GetValue(link); + + int count = tracePtr->screenPts.length; + XPoint* points = new XPoint[count]; + XPoint*xpp = points; + for (int ii=0; iix = (short)tracePtr->screenPts.points[ii].x; + xpp->y = (short)tracePtr->screenPts.points[ii].y; + } + XDrawLines(graphPtr_->display_, drawable, penPtr->traceGC_, points, + count, CoordModeOrigin); + delete [] points; + } +} + +void LineElement::drawValues(Drawable drawable, LinePen* penPtr, + int length, Point2d *points, int *map) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + LinePenOptions* pops = (LinePenOptions*)penPtr->ops(); + + char string[TCL_DOUBLE_SPACE * 2 + 2]; + const char* fmt = pops->valueFormat; + if (fmt == NULL) + fmt = "%g"; + TextStyle ts(graphPtr_, &pops->valueStyle); + + double* xval = ops->coords.x->values_; + double* yval = ops->coords.y->values_; + int count = 0; + + for (Point2d *pp = points, *endp = points + length; pp < endp; pp++) { + double x = xval[map[count]]; + double y = yval[map[count]]; + count++; + if (pops->valueShow == SHOW_X) + snprintf(string, TCL_DOUBLE_SPACE, fmt, x); + else if (pops->valueShow == SHOW_Y) + snprintf(string, TCL_DOUBLE_SPACE, fmt, y); + else if (pops->valueShow == SHOW_BOTH) { + snprintf(string, TCL_DOUBLE_SPACE, fmt, x); + strcat(string, ","); + snprintf(string + strlen(string), TCL_DOUBLE_SPACE, fmt, y); + } + + ts.drawText(drawable, string, pp->x, pp->y); + } +} + +void LineElement::printSymbols(PSOutput* psPtr, LinePen* penPtr, int size, + int nSymbolPts, Point2d *symbolPts) +{ + LinePenOptions* pops = (LinePenOptions*)penPtr->ops(); + + double symbolSize; + + // Set line and foreground attributes + XColor* fillColor = pops->symbol.fillColor; + if (!fillColor) + fillColor = pops->traceColor; + + XColor* outlineColor = pops->symbol.outlineColor; + if (!outlineColor) + outlineColor = pops->traceColor; + + if (pops->symbol.type == SYMBOL_NONE) + psPtr->setLineAttributes(pops->traceColor, pops->traceWidth + 2, + &pops->traceDashes, CapButt, JoinMiter); + else { + psPtr->setLineWidth(pops->symbol.outlineWidth); + psPtr->setDashes(NULL); + } + + // build DrawSymbolProc + psPtr->append("\n/DrawSymbolProc {\n"); + switch (pops->symbol.type) { + case SYMBOL_NONE: + break; + default: + psPtr->append(" "); + psPtr->setBackground(fillColor); + psPtr->append(" gsave fill grestore\n"); + + if (pops->symbol.outlineWidth > 0) { + psPtr->append(" "); + psPtr->setForeground(outlineColor); + psPtr->append(" stroke\n"); + } + break; + } + psPtr->append("} def\n\n"); + + // set size + symbolSize = (double)size; + switch (pops->symbol.type) { + case SYMBOL_SQUARE: + case SYMBOL_CROSS: + case SYMBOL_PLUS: + case SYMBOL_SCROSS: + case SYMBOL_SPLUS: + symbolSize = (double)size * S_RATIO; + break; + case SYMBOL_TRIANGLE: + case SYMBOL_ARROW: + symbolSize = (double)size * 0.7; + break; + case SYMBOL_DIAMOND: + symbolSize = (double)size * M_SQRT1_2; + break; + + default: + break; + } + + int count =0; + for (Point2d *pp=symbolPts, *endp=symbolPts + nSymbolPts; pp < endp; pp++) { + if (DRAW_SYMBOL()) { + psPtr->format("%g %g %g %s\n", pp->x, pp->y, symbolSize, + symbolMacros[pops->symbol.type]); + count++; + } + symbolCounter_++; + } +} + +void LineElement::setLineAttributes(PSOutput* psPtr, LinePen* penPtr) +{ + LinePenOptions* pops = (LinePenOptions*)penPtr->ops(); + + psPtr->setLineAttributes(pops->traceColor, pops->traceWidth, + &pops->traceDashes, CapButt, JoinMiter); + + if ((LineIsDashed(pops->traceDashes)) && + (pops->traceOffColor)) { + psPtr->append("/DashesProc {\n gsave\n "); + psPtr->setBackground(pops->traceOffColor); + psPtr->append(" "); + psPtr->setDashes(NULL); + psPtr->append("stroke\n grestore\n} def\n"); + } else { + psPtr->append("/DashesProc {} def\n"); + } +} + +void LineElement::printTraces(PSOutput* psPtr, LinePen* penPtr) +{ + setLineAttributes(psPtr, penPtr); + for (ChainLink* link = Chain_FirstLink(traces_); link; + link = Chain_NextLink(link)) { + bltTrace *tracePtr = (bltTrace*)Chain_GetValue(link); + if (tracePtr->screenPts.length > 0) { + psPtr->append("% start trace\n"); + psPtr->printMaxPolyline(tracePtr->screenPts.points, + tracePtr->screenPts.length); + psPtr->append("% end trace\n"); + } + } +} + +void LineElement::printValues(PSOutput* psPtr, LinePen* penPtr, + int nSymbolPts, Point2d *symbolPts, + int *pointToData) +{ + LineElementOptions* ops = (LineElementOptions*)ops_; + LinePenOptions* pops = (LinePenOptions*)penPtr->ops(); + + const char* fmt = pops->valueFormat; + if (fmt == NULL) + fmt = "%g"; + TextStyle ts(graphPtr_, &pops->valueStyle); + + int count = 0; + for (Point2d *pp=symbolPts, *endp=symbolPts + nSymbolPts; pp < endp; pp++) { + double x = ops->coords.x->values_[pointToData[count]]; + double y = ops->coords.y->values_[pointToData[count]]; + count++; + + char string[TCL_DOUBLE_SPACE * 2 + 2]; + if (pops->valueShow == SHOW_X) + snprintf(string, TCL_DOUBLE_SPACE, fmt, x); + else if (pops->valueShow == SHOW_Y) + snprintf(string, TCL_DOUBLE_SPACE, fmt, y); + else if (pops->valueShow == SHOW_BOTH) { + snprintf(string, TCL_DOUBLE_SPACE, fmt, x); + strcat(string, ","); + snprintf(string + strlen(string), TCL_DOUBLE_SPACE, fmt, y); + } + + ts.printText(psPtr, string, pp->x, pp->y); + } +} + + diff --git a/generic/tkbltGrElemLine.h b/generic/tkbltGrElemLine.h new file mode 100644 index 0000000..f937615 --- /dev/null +++ b/generic/tkbltGrElemLine.h @@ -0,0 +1,184 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrElemLine_h__ +#define __BltGrElemLine_h__ + +#include + +#include "tkbltGraph.h" +#include "tkbltGrElem.h" +#include "tkbltGrPenLine.h" + +namespace Blt { + + typedef struct { + Point2d *screenPts; + int nScreenPts; + int *styleMap; + int *map; + } MapInfo; + + typedef struct { + Point2d *points; + int length; + int *map; + } GraphPoints; + + typedef struct { + int start; + GraphPoints screenPts; + } bltTrace; + + typedef struct { + Weight weight; + LinePen* penPtr; + GraphPoints symbolPts; + GraphSegments xeb; + GraphSegments yeb; + int symbolSize; + int errorBarCapWidth; + } LineStyle; + + typedef struct { + Element* elemPtr; + const char* label; + char** tags; + Axis* xAxis; + Axis* yAxis; + ElemCoords coords; + ElemValues* w; + ElemValues* xError; + ElemValues* yError; + ElemValues* xHigh; + ElemValues* xLow; + ElemValues* yHigh; + ElemValues* yLow; + int hide; + int legendRelief; + Chain* stylePalette; + LinePen *builtinPenPtr; + LinePen *activePenPtr; + LinePen *normalPenPtr; + LinePenOptions builtinPen; + + // derived + Tk_3DBorder fillBg; + int reqMaxSymbols; + double rTolerance; + int scaleSymbols; + int reqSmooth; + int penDir; + } LineElementOptions; + + class LineElement : public Element { + public: + enum PenDirection {INCREASING, DECREASING, BOTH_DIRECTIONS}; + enum Smoothing {LINEAR, STEP, CUBIC, QUADRATIC, CATROM}; + + protected: + LinePen* builtinPenPtr; + Smoothing smooth_; + Point2d *fillPts_; + int nFillPts_; + GraphPoints symbolPts_; + GraphPoints activePts_; + GraphSegments xeb_; + GraphSegments yeb_; + int symbolInterval_; + int symbolCounter_; + Chain* traces_; + + void drawCircle(Display*, Drawable, LinePen*, int, Point2d*, int); + void drawSquare(Display*, Drawable, LinePen*, int, Point2d*, int); + void drawSCross(Display*, Drawable, LinePen*, int, Point2d*, int); + void drawCross(Display*, Drawable, LinePen*, int, Point2d*, int); + void drawDiamond(Display*, Drawable, LinePen*, int, Point2d*, int); + void drawArrow(Display*, Drawable, LinePen*, int, Point2d*, int); + + protected: + int scaleSymbol(int); + void getScreenPoints(MapInfo*); + void reducePoints(MapInfo*, double); + void generateSteps(MapInfo*); + void generateSpline(MapInfo*); + void generateParametricSpline(MapInfo*); + void mapSymbols(MapInfo*); + void mapActiveSymbols(); + void mergePens(LineStyle**); + int outCode(Region2d*, Point2d*); + int clipSegment(Region2d*, int, int, Point2d*, Point2d*); + void saveTrace(int, int, MapInfo*); + void freeTraces(); + void mapTraces(MapInfo*); + void mapFillArea(MapInfo*); + void mapErrorBars(LineStyle**); + void reset(); + int closestTrace(); + void closestPoint(ClosestSearch*); + void drawSymbols(Drawable, LinePen*, int, int, Point2d*); + void drawTraces(Drawable, LinePen*); + void drawValues(Drawable, LinePen*, int, Point2d*, int*); + void setLineAttributes(PSOutput*, LinePen*); + void printTraces(PSOutput*, LinePen*); + void printValues(PSOutput*, LinePen*, int, Point2d*, int*); + void printSymbols(PSOutput*, LinePen*, int, int, Point2d*); + double distanceToLine(int, int, Point2d*, Point2d*, Point2d*); + double distanceToX(int, int, Point2d*, Point2d*, Point2d*); + double distanceToY(int, int, Point2d*, Point2d*, Point2d*); + int simplify(Point2d*, int, int, double, int*); + double findSplit(Point2d*, int, int, int*); + + int naturalSpline(Point2d*, int, Point2d*, int); + int quadraticSpline(Point2d*, int, Point2d*, int); + int naturalParametricSpline(Point2d*, int, Region2d*, int, Point2d*, int); + int catromParametricSpline(Point2d*, int, Point2d*, int); + + public: + LineElement(Graph*, const char*, Tcl_HashEntry*); + virtual ~LineElement(); + + ClassId classId() {return CID_ELEM_LINE;} + const char* className() {return "LineElement";} + const char* typeName() {return "line";} + + int configure(); + void map(); + void extents(Region2d*); + void closest(); + void draw(Drawable); + void drawActive(Drawable); + void drawSymbol(Drawable, int, int, int); + void print(PSOutput*); + void printActive(PSOutput*); + void printSymbol(PSOutput*, double, double, int); + }; +}; + +#endif diff --git a/generic/tkbltGrElemLineSpline.C b/generic/tkbltGrElemLineSpline.C new file mode 100644 index 0000000..9224d53 --- /dev/null +++ b/generic/tkbltGrElemLineSpline.C @@ -0,0 +1,1086 @@ +/* + * 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 2009 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 +#include +#include + +#include + +#include "tkbltGrElemLine.h" + +using namespace Blt; + +typedef double TriDiagonalMatrix[3]; +typedef struct { + double b, c, d; +} Cubic2D; + +typedef struct { + double b, c, d, e, f; +} Quint2D; + +// Quadratic spline parameters +#define E1 param[0] +#define E2 param[1] +#define V1 param[2] +#define V2 param[3] +#define W1 param[4] +#define W2 param[5] +#define Z1 param[6] +#define Z2 param[7] +#define Y1 param[8] +#define Y2 param[9] + +/* + *--------------------------------------------------------------------------- + * + * Search -- + * + * Conducts a binary search for a value. This routine is called + * only if key is between x(0) and x(len - 1). + * + * Results: + * Returns the index of the largest value in xtab for which + * x[i] < key. + * + *--------------------------------------------------------------------------- + */ +static int Search(Point2d points[], int nPoints, double key, int *foundPtr) +{ + int low = 0; + int high = nPoints - 1; + + while (high >= low) { + int mid = (high + low) / 2; + if (key > points[mid].x) + low = mid + 1; + else if (key < points[mid].x) + high = mid - 1; + else { + *foundPtr = 1; + return mid; + } + } + *foundPtr = 0; + return low; +} + +/* + *--------------------------------------------------------------------------- + * + * QuadChoose -- + * + * Determines the case needed for the computation of the parame- + * ters of the quadratic spline. + * + * Results: + * Returns a case number (1-4) which controls how the parameters + * of the quadratic spline are evaluated. + * + *--------------------------------------------------------------------------- + */ +static int QuadChoose(Point2d* p, Point2d* q, double m1, double m2, + double epsilon) +{ + // Calculate the slope of the line joining P and Q + double slope = (q->y - p->y) / (q->x - p->x); + + if (slope != 0.0) { + double prod1 = slope * m1; + double prod2 = slope * m2; + + // Find the absolute values of the slopes slope, m1, and m2 + double mref = fabs(slope); + double mref1 = fabs(m1); + double mref2 = fabs(m2); + + // If the relative deviation of m1 or m2 from slope is less than + // epsilon, then choose case 2 or case 3. + double relerr = epsilon * mref; + if ((fabs(slope - m1) > relerr) && (fabs(slope - m2) > relerr) && + (prod1 >= 0.0) && (prod2 >= 0.0)) { + double prod = (mref - mref1) * (mref - mref2); + if (prod < 0.0) { + // l1, the line through (x1,y1) with slope m1, and l2, + // the line through (x2,y2) with slope m2, intersect + // at a point whose abscissa is between x1 and x2. + // The abscissa becomes a knot of the spline. + return 1; + } + if (mref1 > (mref * 2.0)) { + if (mref2 <= ((2.0 - epsilon) * mref)) + return 3; + } + else if (mref2 <= (mref * 2.0)) { + // Both l1 and l2 cross the line through + // (x1+x2)/2.0,y1 and (x1+x2)/2.0,y2, which is the + // midline of the rectangle formed by P and Q or both + // m1 and m2 have signs different than the sign of + // slope, or one of m1 and m2 has opposite sign from + // slope and l1 and l2 intersect to the left of x1 or + // to the right of x2. The point (x1+x2)/2. is a knot + // of the spline. + return 2; + } + else if (mref1 <= ((2.0 - epsilon) * mref)) { + // In cases 3 and 4, sign(m1)=sign(m2)=sign(slope). + // Either l1 or l2 crosses the midline, but not both. + // Choose case 4 if mref1 is greater than + // (2.-epsilon)*mref; otherwise, choose case 3. + return 3; + } + // If neither l1 nor l2 crosses the midline, the spline + // requires two knots between x1 and x2. + return 4; + } + else { + // The sign of at least one of the slopes m1 or m2 does not + // agree with the sign of *slope*. + if ((prod1 < 0.0) && (prod2 < 0.0)) { + return 2; + } + else if (prod1 < 0.0) { + if (mref2 > ((epsilon + 1.0) * mref)) + return 1; + else + return 2; + } + else if (mref1 > ((epsilon + 1.0) * mref)) + return 1; + else + return 2; + } + } + else if ((m1 * m2) >= 0.0) + return 2; + else + return 1; +} + +/* + *--------------------------------------------------------------------------- + * Computes the knots and other parameters of the spline on the + * interval PQ. + * On input-- + * P and Q are the coordinates of the points of interpolation. + * m1 is the slope at P. + * m2 is the slope at Q. + * ncase controls the number and location of the knots. + * On output-- + * + * (v1,v2),(w1,w2),(z1,z2), and (e1,e2) are the coordinates of + * the knots and other parameters of the spline on P. + * (e1,e2) and Q are used only if ncase=4. + *--------------------------------------------------------------------------- + */ +static void QuadCases(Point2d* p, Point2d* q, double m1, double m2, + double param[], int which) +{ + if ((which == 3) || (which == 4)) { + double c1 = p->x + (q->y - p->y) / m1; + double d1 = q->x + (p->y - q->y) / m2; + double h1 = c1 * 2.0 - p->x; + double j1 = d1 * 2.0 - q->x; + double mbar1 = (q->y - p->y) / (h1 - p->x); + double mbar2 = (p->y - q->y) / (j1 - q->x); + + if (which == 4) { + // Case 4 + Y1 = (p->x + c1) / 2.0; + V1 = (p->x + Y1) / 2.0; + V2 = m1 * (V1 - p->x) + p->y; + Z1 = (d1 + q->x) / 2.0; + W1 = (q->x + Z1) / 2.0; + W2 = m2 * (W1 - q->x) + q->y; + double mbar3 = (W2 - V2) / (W1 - V1); + Y2 = mbar3 * (Y1 - V1) + V2; + Z2 = mbar3 * (Z1 - V1) + V2; + E1 = (Y1 + Z1) / 2.0; + E2 = mbar3 * (E1 - V1) + V2; + } + else { + // Case 3 + double k1 = (p->y - q->y + q->x * mbar2 - p->x * mbar1) / (mbar2 - mbar1); + if (fabs(m1) > fabs(m2)) { + Z1 = (k1 + p->x) / 2.0; + } else { + Z1 = (k1 + q->x) / 2.0; + } + V1 = (p->x + Z1) / 2.0; + V2 = p->y + m1 * (V1 - p->x); + W1 = (q->x + Z1) / 2.0; + W2 = q->y + m2 * (W1 - q->x); + Z2 = V2 + (W2 - V2) / (W1 - V1) * (Z1 - V1); + } + } + else if (which == 2) { + // Case 2 + Z1 = (p->x + q->x) / 2.0; + V1 = (p->x + Z1) / 2.0; + V2 = p->y + m1 * (V1 - p->x); + W1 = (Z1 + q->x) / 2.0; + W2 = q->y + m2 * (W1 - q->x); + Z2 = (V2 + W2) / 2.0; + } + else { + // Case 1 + Z1 = (p->y - q->y + m2 * q->x - m1 * p->x) / (m2 - m1); + double ztwo = p->y + m1 * (Z1 - p->x); + V1 = (p->x + Z1) / 2.0; + V2 = (p->y + ztwo) / 2.0; + W1 = (Z1 + q->x) / 2.0; + W2 = (ztwo + q->y) / 2.0; + Z2 = V2 + (W2 - V2) / (W1 - V1) * (Z1 - V1); + } +} + +static int QuadSelect(Point2d* p, Point2d* q, double m1, double m2, + double epsilon, double param[]) +{ + int ncase = QuadChoose(p, q, m1, m2, epsilon); + QuadCases(p, q, m1, m2, param, ncase); + return ncase; +} + +static double QuadGetImage(double p1, double p2, double p3, double x1, + double x2, double x3) +{ + double A = x1 - x2; + double B = x2 - x3; + double C = x1 - x3; + + double y = (p1 * (A * A) + p2 * 2.0 * B * A + p3 * (B * B)) / (C * C); + return y; +} + +/* + *--------------------------------------------------------------------------- + * Finds the image of a point in x. + * On input + * x Contains the value at which the spline is evaluated. + * leftX, leftY + * Coordinates of the left-hand data point used in the + * evaluation of x values. + * rightX, rightY + * Coordinates of the right-hand data point used in the + * evaluation of x values. + * Z1, Z2, Y1, Y2, E2, W2, V2 + * Parameters of the spline. + * ncase Controls the evaluation of the spline by indicating + * whether one or two knots were placed in the interval + * (xtabs,xtabs1). + *--------------------------------------------------------------------------- + */ +static void QuadSpline(Point2d* intp, Point2d* left, Point2d* right, + double param[], int ncase) + +{ + double y; + + if (ncase == 4) { + // Case 4: More than one knot was placed in the interval. + // Determine the location of data point relative to the 1st knot. + if (Y1 > intp->x) + y = QuadGetImage(left->y, V2, Y2, Y1, intp->x, left->x); + else if (Y1 < intp->x) { + // Determine the location of the data point relative to the 2nd knot. + if (Z1 > intp->x) + y = QuadGetImage(Y2, E2, Z2, Z1, intp->x, Y1); + else if (Z1 < intp->x) + y = QuadGetImage(Z2, W2, right->y, right->x, intp->x, Z1); + else + y = Z2; + } + else + y = Y2; + } + else { + // Cases 1, 2, or 3: + // Determine the location of the data point relative to the knot. + if (Z1 < intp->x) + y = QuadGetImage(Z2, W2, right->y, right->x, intp->x, Z1); + else if (Z1 > intp->x) + y = QuadGetImage(left->y, V2, Z2, Z1, intp->x, left->x); + else + y = Z2; + } + + intp->y = y; +} + +/* + *--------------------------------------------------------------------------- + * Calculates the derivative at each of the data points. The + * slopes computed will insure that an osculatory quadratic + * spline will have one additional knot between two adjacent + * points of interpolation. Convexity and monotonicity are + * preserved wherever these conditions are compatible with the + * data. + *--------------------------------------------------------------------------- + */ +static void QuadSlopes(Point2d *points, double *m, int nPoints) +{ + double m1s =0; + double m2s =0; + double m1 =0; + double m2 =0; + int i, n, l; + for (l = 0, i = 1, n = 2; i < (nPoints - 1); l++, i++, n++) { + // Calculate the slopes of the two lines joining three + // consecutive data points. + double ydif1 = points[i].y - points[l].y; + double ydif2 = points[n].y - points[i].y; + m1 = ydif1 / (points[i].x - points[l].x); + m2 = ydif2 / (points[n].x - points[i].x); + if (i == 1) { + // Save slopes of starting point + m1s = m1; + m2s = m2; + } + // If one of the preceding slopes is zero or if they have opposite + // sign, assign the value zero to the derivative at the middle point. + if ((m1 == 0.0) || (m2 == 0.0) || ((m1 * m2) <= 0.0)) + m[i] = 0.0; + else if (fabs(m1) > fabs(m2)) { + // Calculate the slope by extending the line with slope m1. + double xbar = ydif2 / m1 + points[i].x; + double xhat = (xbar + points[n].x) / 2.0; + m[i] = ydif2 / (xhat - points[i].x); + } + else { + // Calculate the slope by extending the line with slope m2. + double xbar = -ydif1 / m2 + points[i].x; + double xhat = (points[l].x + xbar) / 2.0; + m[i] = ydif1 / (points[i].x - xhat); + } + } + + // Calculate the slope at the last point, x(n). + i = nPoints - 2; + n = nPoints - 1; + if ((m1 * m2) < 0.0) + m[n] = m2 * 2.0; + else { + double xmid = (points[i].x + points[n].x) / 2.0; + double yxmid = m[i] * (xmid - points[i].x) + points[i].y; + m[n] = (points[n].y - yxmid) / (points[n].x - xmid); + if ((m[n] * m2) < 0.0) + m[n] = 0.0; + } + + // Calculate the slope at the first point, x(0). + if ((m1s * m2s) < 0.0) + m[0] = m1s * 2.0; + else { + double xmid = (points[0].x + points[1].x) / 2.0; + double yxmid = m[1] * (xmid - points[1].x) + points[1].y; + m[0] = (yxmid - points[0].y) / (xmid - points[0].x); + if ((m[0] * m1s) < 0.0) + m[0] = 0.0; + } +} + +/* + *--------------------------------------------------------------------------- + * + * QuadEval -- + * + * QuadEval controls the evaluation of an osculatory quadratic + * spline. The user may provide his own slopes at the points of + * interpolation or use the subroutine 'QuadSlopes' to calculate + * slopes which are consistent with the shape of the data. + * + * ON INPUT-- + * intpPts must be a nondecreasing vector of points at which the + * spline will be evaluated. + * origPts contains the abscissas of the data points to be + * interpolated. xtab must be increasing. + * y contains the ordinates of the data points to be + * interpolated. + * m contains the slope of the spline at each point of + * interpolation. + * nPoints number of data points (dimension of xtab and y). + * numEval is the number of points of evaluation (dimension of + * xval and yval). + * epsilon is a relative error tolerance used in subroutine + * 'QuadChoose' to distinguish the situation m(i) or + * m(i+1) is relatively close to the slope or twice + * the slope of the linear segment between xtab(i) and + * xtab(i+1). If this situation occurs, roundoff may + * cause a change in convexity or monotonicity of the + * resulting spline and a change in the case number + * provided by 'QuadChoose'. If epsilon is not equal to zero, + * then epsilon should be greater than or equal to machine + * epsilon. + * ON OUTPUT-- + * yval contains the images of the points in xval. + * err is one of the following error codes: + * 0 - QuadEval ran normally. + * 1 - xval(i) is less than xtab(1) for at least one + * i or xval(i) is greater than xtab(num) for at + * least one i. QuadEval will extrapolate to provide + * function values for these abscissas. + * 2 - xval(i+1) < xval(i) for some i. + * + * + * QuadEval calls the following subroutines or functions: + * Search + * QuadCases + * QuadChoose + * QuadSpline + *--------------------------------------------------------------------------- + */ +static int QuadEval(Point2d origPts[], int nOrigPts, Point2d intpPts[], + int nIntpPts, double *m, double epsilon) +{ + double param[10]; + + // Initialize indices and set error result + int error = 0; + int l = nOrigPts - 1; + int p = l - 1; + int ncase = 1; + + // Determine if abscissas of new vector are non-decreasing. + for (int jj=1; jj= origPts[0].x) + break; + } + // Determine if any of the points in xval are GREATER than the + // abscissa of the l data point. + int end; + for (end = nIntpPts - 1; end >= 0; end--) { + if (intpPts[end].x <= origPts[l].x) + break; + } + + if (start > 0) { + // Set error value to indicate that extrapolation has occurred + error = 1; + + // Calculate the images of points of evaluation whose abscissas + // are less than the abscissa of the first data point. + ncase = QuadSelect(origPts, origPts + 1, m[0], m[1], epsilon, param); + for (int jj=0; jj<(start - 1); jj++) + QuadSpline(intpPts + jj, origPts, origPts + 1, param, ncase); + if (nIntpPts == 1) + return error; + } + int ii; + int nn; + if ((nIntpPts == 1) && (end != (nIntpPts - 1))) + goto noExtrapolation; + + // Search locates the interval in which the first in-range + // point of evaluation lies. + int found; + ii = Search(origPts, nOrigPts, intpPts[start].x, &found); + + nn = ii + 1; + if (nn >= nOrigPts) { + nn = nOrigPts - 1; + ii = nOrigPts - 2; + } + /* + * If the first in-range point of evaluation is equal to one + * of the data points, assign the appropriate value from y. + * Continue until a point of evaluation is found which is not + * equal to a data point. + */ + if (found) { + do { + intpPts[start].y = origPts[ii].y; + start++; + if (start >= nIntpPts) { + return error; + } + } while (intpPts[start - 1].x == intpPts[start].x); + + for (;;) { + if (intpPts[start].x < origPts[nn].x) { + break; /* Break out of for-loop */ + } + if (intpPts[start].x == origPts[nn].x) { + do { + intpPts[start].y = origPts[nn].y; + start++; + if (start >= nIntpPts) { + return error; + } + } while (intpPts[start].x == intpPts[start - 1].x); + } + ii++; + nn++; + } + } + /* + * Calculate the images of all the points which lie within + * range of the data. + */ + if ((ii > 0) || (error != 1)) + ncase = QuadSelect(origPts+ii, origPts+nn, m[ii], m[nn], epsilon, param); + + for (int jj=start; jj<=end; jj++) { + // If xx(j) - x(n) is negative, do not recalculate + // the parameters for this section of the spline since + // they are already known. + if (intpPts[jj].x == origPts[nn].x) { + intpPts[jj].y = origPts[nn].y; + continue; + } + else if (intpPts[jj].x > origPts[nn].x) { + double delta; + + // Determine that the routine is in the correct part of the spline + do { + ii++; + nn++; + delta = intpPts[jj].x - origPts[nn].x; + } while (delta > 0.0); + + if (delta < 0.0) + ncase = QuadSelect(origPts+ii, origPts+nn, m[ii], m[nn], + epsilon, param); + else if (delta == 0.0) { + intpPts[jj].y = origPts[nn].y; + continue; + } + } + QuadSpline(intpPts+jj, origPts+ii, origPts+nn, param, ncase); + } + + if (end == (nIntpPts - 1)) + return error; + + if ((nn == l) && (intpPts[end].x != origPts[l].x)) + goto noExtrapolation; + + // Set error value to indicate that extrapolation has occurred + error = 1; + ncase = QuadSelect(origPts + p, origPts + l, m[p], m[l], epsilon, param); + + noExtrapolation: + // Calculate the images of the points of evaluation whose + // abscissas are greater than the abscissa of the last data point. + for (int jj=(end + 1); jj 1) { + return 0; + } + return 1; +} + +/* + *--------------------------------------------------------------------------- + * Reference: + * Numerical Analysis, R. Burden, J. Faires and A. Reynolds. + * Prindle, Weber & Schmidt 1981 pp 112 + *--------------------------------------------------------------------------- + */ +int LineElement::naturalSpline(Point2d *origPts, int nOrigPts, + Point2d *intpPts, int nIntpPts) +{ + Point2d *ip, *iend; + double x, dy, alpha; + int isKnot; + int i, j, n; + + double* dx = new double[nOrigPts]; + /* Calculate vector of differences */ + for (i = 0, j = 1; j < nOrigPts; i++, j++) { + dx[i] = origPts[j].x - origPts[i].x; + if (dx[i] < 0.0) { + return 0; + } + } + n = nOrigPts - 1; /* Number of intervals. */ + TriDiagonalMatrix* A = new TriDiagonalMatrix[nOrigPts]; + if (!A) { + delete [] dx; + return 0; + } + /* Vectors to solve the tridiagonal matrix */ + A[0][0] = A[n][0] = 1.0; + A[0][1] = A[n][1] = 0.0; + A[0][2] = A[n][2] = 0.0; + + /* Calculate the intermediate results */ + for (i = 0, j = 1; j < n; j++, i++) { + alpha = 3.0 * ((origPts[j + 1].y / dx[j]) - (origPts[j].y / dx[i]) - + (origPts[j].y / dx[j]) + (origPts[i].y / dx[i])); + A[j][0] = 2 * (dx[j] + dx[i]) - dx[i] * A[i][1]; + A[j][1] = dx[j] / A[j][0]; + A[j][2] = (alpha - dx[i] * A[i][2]) / A[j][0]; + } + + Cubic2D* eq = new Cubic2D[nOrigPts]; + if (!eq) { + delete [] A; + delete [] dx; + return 0; + } + eq[0].c = eq[n].c = 0.0; + for (j = n, i = n - 1; i >= 0; i--, j--) { + eq[i].c = A[i][2] - A[i][1] * eq[j].c; + dy = origPts[i+1].y - origPts[i].y; + eq[i].b = (dy) / dx[i] - dx[i] * (eq[j].c + 2.0 * eq[i].c) / 3.0; + eq[i].d = (eq[j].c - eq[i].c) / (3.0 * dx[i]); + } + delete [] A; + delete [] dx; + + /* Now calculate the new values */ + for (ip = intpPts, iend = ip + nIntpPts; ip < iend; ip++) { + ip->y = 0.0; + x = ip->x; + + /* Is it outside the interval? */ + if ((x < origPts[0].x) || (x > origPts[n].x)) { + continue; + } + /* Search for the interval containing x in the point array */ + i = Search(origPts, nOrigPts, x, &isKnot); + if (isKnot) { + ip->y = origPts[i].y; + } else { + i--; + x -= origPts[i].x; + ip->y = origPts[i].y + x * (eq[i].b + x * (eq[i].c + x * eq[i].d)); + } + } + delete [] eq; + return 1; +} + +typedef struct { + double t; /* Arc length of interval. */ + double x; /* 2nd derivative of X with respect to T */ + double y; /* 2nd derivative of Y with respect to T */ +} CubicSpline; + +/* + * The following two procedures solve the special linear system which arise + * in cubic spline interpolation. If x is assumed cyclic ( x[i]=x[n+i] ) the + * equations can be written as (i=0,1,...,n-1): + * m[i][0] * x[i-1] + m[i][1] * x[i] + m[i][2] * x[i+1] = b[i] . + * In matrix notation one gets A * x = b, where the matrix A is tridiagonal + * with additional elements in the upper right and lower left position: + * A[i][0] = A_{i,i-1} for i=1,2,...,n-1 and m[0][0] = A_{0,n-1} , + * A[i][1] = A_{i, i } for i=0,1,...,n-1 + * A[i][2] = A_{i,i+1} for i=0,1,...,n-2 and m[n-1][2] = A_{n-1,0}. + * A should be symmetric (A[i+1][0] == A[i][2]) and positive definite. + * The size of the system is given in n (n>=1). + * + * In the first procedure the Cholesky decomposition A = C^T * D * C + * (C is upper triangle with unit diagonal, D is diagonal) is calculated. + * Return TRUE if decomposition exist. + */ +static int SolveCubic1(TriDiagonalMatrix A[], int n) +{ + int i; + double m_ij, m_n, m_nn, d; + + if (n < 1) { + return 0; /* Dimension should be at least 1 */ + } + d = A[0][1]; /* D_{0,0} = A_{0,0} */ + if (d <= 0.0) { + return 0; /* A (or D) should be positive definite */ + } + m_n = A[0][0]; /* A_{0,n-1} */ + m_nn = A[n - 1][1]; /* A_{n-1,n-1} */ + for (i = 0; i < n - 2; i++) { + m_ij = A[i][2]; /* A_{i,1} */ + A[i][2] = m_ij / d; /* C_{i,i+1} */ + A[i][0] = m_n / d; /* C_{i,n-1} */ + m_nn -= A[i][0] * m_n; /* to get C_{n-1,n-1} */ + m_n = -A[i][2] * m_n; /* to get C_{i+1,n-1} */ + d = A[i + 1][1] - A[i][2] * m_ij; /* D_{i+1,i+1} */ + if (d <= 0.0) { + return 0; /* Elements of D should be positive */ + } + A[i + 1][1] = d; + } + if (n >= 2) { /* Complete last column */ + m_n += A[n - 2][2]; /* add A_{n-2,n-1} */ + A[n - 2][0] = m_n / d; /* C_{n-2,n-1} */ + A[n - 1][1] = d = m_nn - A[n - 2][0] * m_n; /* D_{n-1,n-1} */ + if (d <= 0.0) { + return 0; + } + } + return 1; +} + +/* + * The second procedure solves the linear system, with the Cholesky + * decomposition calculated above (in m[][]) and the right side b given + * in x[]. The solution x overwrites the right side in x[]. + */ +static void SolveCubic2(TriDiagonalMatrix A[], CubicSpline spline[], + int nIntervals) +{ + int n = nIntervals - 2; + int m = nIntervals - 1; + + // Division by transpose of C : b = C^{-T} * b + double x = spline[m].x; + double y = spline[m].y; + for (int ii=0; ii= 0) { + // C_{n-2,n-1} * x_{n-1} + spline[m].x = x - A[n][0] * spline[n].x; + spline[m].y = y - A[n][0] * spline[n].y; + } + // Division by D: b = D^{-1} * b + for (int ii=0; ii= 0) { + // C_{n-2,n-1} * x_{n-1} + spline[n].x -= A[n][0] * x; + spline[n].y -= A[n][0] * y; + } + for (int ii=(n - 1); ii>=0; ii--) { + // C_{i,i+1} * x_{i+1} + C_{i,n-1} * x_{n-1} + spline[ii].x -= A[ii][2] * spline[ii + 1].x + A[ii][0] * x; + spline[ii].y -= A[ii][2] * spline[ii + 1].y + A[ii][0] * y; + } +} + +/* + * Find second derivatives (x''(t_i),y''(t_i)) of cubic spline interpolation + * through list of points (x_i,y_i). The parameter t is calculated as the + * length of the linear stroke. The number of points must be at least 3. + * Note: For CLOSED_CONTOURs the first and last point must be equal. + */ +static CubicSpline* CubicSlopes(Point2d points[], int nPoints, + int isClosed, double unitX, double unitY) +{ + CubicSpline *s1, *s2; + int n, i; + double norm, dx, dy; + + CubicSpline* spline = new CubicSpline[nPoints]; + if (!spline) + return NULL; + + TriDiagonalMatrix *A = new TriDiagonalMatrix[nPoints]; + if (!A) { + delete [] spline; + return NULL; + } + /* + * Calculate first differences in (dxdt2[i], y[i]) and interval lengths + * in dist[i]: + */ + s1 = spline; + for (i = 0; i < nPoints - 1; i++) { + s1->x = points[i+1].x - points[i].x; + s1->y = points[i+1].y - points[i].y; + + /* + * The Norm of a linear stroke is calculated in "normal coordinates" + * and used as interval length: + */ + dx = s1->x / unitX; + dy = s1->y / unitY; + s1->t = sqrt(dx * dx + dy * dy); + + s1->x /= s1->t; /* first difference, with unit norm: */ + s1->y /= s1->t; /* || (dxdt2[i], y[i]) || = 1 */ + s1++; + } + + /* + * Setup linear System: Ax = b + */ + n = nPoints - 2; /* Without first and last point */ + if (isClosed) { + /* First and last points must be equal for CLOSED_CONTOURs */ + spline[nPoints - 1].t = spline[0].t; + spline[nPoints - 1].x = spline[0].x; + spline[nPoints - 1].y = spline[0].y; + n++; /* Add last point (= first point) */ + } + s1 = spline, s2 = s1 + 1; + for (i = 0; i < n; i++) { + /* Matrix A, mainly tridiagonal with cyclic second index + ("j = j+n mod n") + */ + A[i][0] = s1->t; /* Off-diagonal element A_{i,i-1} */ + A[i][1] = 2.0 * (s1->t + s2->t); /* A_{i,i} */ + A[i][2] = s2->t; /* Off-diagonal element A_{i,i+1} */ + + /* Right side b_x and b_y */ + s1->x = (s2->x - s1->x) * 6.0; + s1->y = (s2->y - s1->y) * 6.0; + + /* + * If the linear stroke shows a cusp of more than 90 degree, + * the right side is reduced to avoid oscillations in the + * spline: + */ + /* + * The Norm of a linear stroke is calculated in "normal coordinates" + * and used as interval length: + */ + dx = s1->x / unitX; + dy = s1->y / unitY; + norm = sqrt(dx * dx + dy * dy) / 8.5; + if (norm > 1.0) { + /* The first derivative will not be continuous */ + s1->x /= norm; + s1->y /= norm; + } + s1++, s2++; + } + + if (!isClosed) { + /* Third derivative is set to zero at both ends */ + A[0][1] += A[0][0]; /* A_{0,0} */ + A[0][0] = 0.0; /* A_{0,n-1} */ + A[n-1][1] += A[n-1][2]; /* A_{n-1,n-1} */ + A[n-1][2] = 0.0; /* A_{n-1,0} */ + } + /* Solve linear systems for dxdt2[] and y[] */ + + if (SolveCubic1(A, n)) { /* Cholesky decomposition */ + SolveCubic2(A, spline, n); /* A * dxdt2 = b_x */ + } + else { /* Should not happen, but who knows ... */ + delete [] A; + delete [] spline; + return NULL; + } + /* Shift all second derivatives one place right and update the ends. */ + s2 = spline + n, s1 = s2 - 1; + for (/* empty */; s2 > spline; s2--, s1--) { + s2->x = s1->x; + s2->y = s1->y; + } + if (isClosed) { + spline[0].x = spline[n].x; + spline[0].y = spline[n].y; + } else { + /* Third derivative is 0.0 for the first and last interval. */ + spline[0].x = spline[1].x; + spline[0].y = spline[1].y; + spline[n + 1].x = spline[n].x; + spline[n + 1].y = spline[n].y; + } + delete [] A; + return spline; +} + +// Calculate interpolated values of the spline function (defined via p_cntr +// and the second derivatives dxdt2[] and dydt2[]). The number of tabulated +// values is n. On an equidistant grid n_intpol values are calculated. +static int CubicEval(Point2d *origPts, int nOrigPts, Point2d *intpPts, + int nIntpPts, CubicSpline *spline) +{ + double t, tSkip; + Point2d q; + int count; + + /* Sum the lengths of all the segments (intervals). */ + double tMax = 0.0; + for (int ii=0; iiright - extsPtr->left; + double unitY = extsPtr->bottom - extsPtr->top; + if (unitX < FLT_EPSILON) + unitX = FLT_EPSILON; + if (unitY < FLT_EPSILON) + unitY = FLT_EPSILON; + + /* Calculate parameters for cubic spline: + * t = arc length of interval. + * dxdt2 = second derivatives of x with respect to t, + * dydt2 = second derivatives of y with respect to t, + */ + CubicSpline* spline = CubicSlopes(origPts, nOrigPts, isClosed, unitX, unitY); + if (spline == NULL) + return 0; + + int result= CubicEval(origPts, nOrigPts, intpPts, nIntpPts, spline); + + delete [] spline; + return result; +} + +static void CatromCoeffs(Point2d* p, Point2d* a, Point2d* b, + Point2d* c, Point2d* d) +{ + a->x = -p[0].x + 3.0 * p[1].x - 3.0 * p[2].x + p[3].x; + b->x = 2.0 * p[0].x - 5.0 * p[1].x + 4.0 * p[2].x - p[3].x; + c->x = -p[0].x + p[2].x; + d->x = 2.0 * p[1].x; + a->y = -p[0].y + 3.0 * p[1].y - 3.0 * p[2].y + p[3].y; + b->y = 2.0 * p[0].y - 5.0 * p[1].y + 4.0 * p[2].y - p[3].y; + c->y = -p[0].y + p[2].y; + d->y = 2.0 * p[1].y; +} + +int LineElement::catromParametricSpline(Point2d* points, int nPoints, + Point2d* intpPts, int nIntpPts) +{ + // The spline is computed in screen coordinates instead of data points so + // that we can select the abscissas of the interpolated points from each + // pixel horizontally across the plotting area. + + Point2d* origPts = new Point2d[nPoints + 4]; + memcpy(origPts + 1, points, sizeof(Point2d) * nPoints); + + origPts[0] = origPts[1]; + origPts[nPoints + 2] = origPts[nPoints + 1] = origPts[nPoints]; + + for (int ii=0; ii + +#include "tkbltGrBind.h" +#include "tkbltGraph.h" +#include "tkbltGrAxis.h" +#include "tkbltGrElem.h" +#include "tkbltGrElemOp.h" +#include "tkbltGrElemBar.h" +#include "tkbltGrElemLine.h" +#include "tkbltGrLegd.h" + +using namespace Blt; + +static int GetIndex(Tcl_Interp* interp, Element* elemPtr, + Tcl_Obj *objPtr, int *indexPtr); +static Tcl_Obj *DisplayListObj(Graph* graphPtr); + +int Blt::ElementObjConfigure(Element* elemPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = elemPtr->graphPtr_; + Tk_SavedOptions savedOptions; + int mask =0; + int error; + Tcl_Obj* errorResult; + + for (error=0; error<=1; error++) { + if (!error) { + if (Tk_SetOptions(interp, (char*)elemPtr->ops(), elemPtr->optionTable(), + objc, objv, graphPtr->tkwin_, &savedOptions, &mask) + != TCL_OK) + continue; + } + else { + errorResult = Tcl_GetObjResult(interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + } + + if (elemPtr->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 != 5) { + Tcl_WrongNumArgs(interp, 3, objv, "cget option"); + return TCL_ERROR; + } + + Element* elemPtr; + if (graphPtr->getElement(objv[3], &elemPtr) != TCL_OK) + return TCL_ERROR; + + Tcl_Obj* objPtr = Tk_GetOptionValue(interp, + (char*)elemPtr->ops(), + elemPtr->optionTable(), + objv[4], 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<4) + return TCL_ERROR; + + Element* elemPtr; + if (graphPtr->getElement(objv[3], &elemPtr) != TCL_OK) + return TCL_ERROR; + + if (objc <= 5) { + Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)elemPtr->ops(), + elemPtr->optionTable(), + (objc == 5) ? objv[4] : NULL, + graphPtr->tkwin_); + if (objPtr == NULL) + return TCL_ERROR; + else + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; + } + else + return ElementObjConfigure(elemPtr, interp, objc-4, objv+4); +} + +static int ActivateOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + + // List all the currently active elements + if (objc == 3) { + Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + + Tcl_HashSearch iter; + for (Tcl_HashEntry* hPtr = Tcl_FirstHashEntry(&graphPtr->elements_.table, &iter); hPtr; hPtr = Tcl_NextHashEntry(&iter)) { + Element* elemPtr = (Element*)Tcl_GetHashValue(hPtr); + if (elemPtr->active_) + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewStringObj(elemPtr->name_, -1)); + } + + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; + } + + Element* elemPtr; + if (graphPtr->getElement(objv[3], &elemPtr) != TCL_OK) + return TCL_ERROR; + + int* indices = NULL; + int nIndices = -1; + if (objc > 4) { + nIndices = objc - 4; + indices = new int[nIndices]; + + int* activePtr = indices; + for (int ii=4; iiactiveIndices_; + elemPtr->activeIndices_ = indices; + elemPtr->nActiveIndices_ = nIndices; + + elemPtr->active_ = 1; + + graphPtr->flags |= RESET; + graphPtr->eventuallyRedraw(); + + return TCL_OK; +} + +static int BindOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc == 3) { + Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + + Tcl_HashSearch iter; + for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&graphPtr->elements_.tagTable, &iter); hPtr; hPtr = Tcl_NextHashEntry(&iter)) { + char* tagName = + (char*)Tcl_GetHashKey(&graphPtr->elements_.tagTable, hPtr); + Tcl_ListObjAppendElement(interp, listObjPtr,Tcl_NewStringObj(tagName,-1)); + } + + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; + } + + return graphPtr->bindTable_->configure(graphPtr->elementTag(Tcl_GetString(objv[3])), objc - 4, objv + 4); +} + +static int ClosestOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<5) + return TCL_ERROR; + + GraphOptions* gops = (GraphOptions*)graphPtr->ops_; + ClosestSearch* searchPtr = &gops->search; + + if (graphPtr->flags & RESET) + graphPtr->resetAxes(); + + int x; + if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) { + Tcl_AppendResult(interp, ": bad window x-coordinate", NULL); + return TCL_ERROR; + } + int y; + if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) { + Tcl_AppendResult(interp, ": bad window y-coordinate", NULL); + return TCL_ERROR; + } + + searchPtr->x = x; + searchPtr->y = y; + searchPtr->index = -1; + searchPtr->dist = (double)(searchPtr->halo + 1); + + if (objc>5) { + for (int ii=5; iigetElement(objv[ii], &elemPtr) != TCL_OK) + return TCL_ERROR; + + ElementOptions* eops = (ElementOptions*)elemPtr->ops(); + if (!eops->hide) + elemPtr->closest(); + } + } + else { + // 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 (ChainLink* link = Chain_LastLink(graphPtr->elements_.displayList); + link; link = Chain_PrevLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + ElementOptions* eops = (ElementOptions*)elemPtr->ops(); + if (!eops->hide) + elemPtr->closest(); + } + } + + if (searchPtr->dist < (double)searchPtr->halo) { + Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("name", -1)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj(searchPtr->elemPtr->name_, -1)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("index", -1)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(searchPtr->index)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("x", -1)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(searchPtr->point.x)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("y", -1)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(searchPtr->point.y)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("dist", -1)); + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(searchPtr->dist)); + Tcl_SetObjResult(interp, listObjPtr); + } + + return TCL_OK; +} + +static int CreateOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + if (graphPtr->createElement(objc, objv) != TCL_OK) + return TCL_ERROR; + Tcl_SetObjResult(interp, objv[3]); + + graphPtr->flags |= RESET; + graphPtr->eventuallyRedraw(); + + return TCL_OK; +} + +static int DeactivateOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + for (int ii=3; iigetElement(objv[ii], &elemPtr) != TCL_OK) + return TCL_ERROR; + + delete [] elemPtr->activeIndices_; + elemPtr->activeIndices_ = NULL; + elemPtr->nActiveIndices_ = 0; + elemPtr->active_ = 0; + } + + graphPtr->flags |= RESET; + graphPtr->eventuallyRedraw(); + + return TCL_OK; +} + +static int DeleteOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + for (int ii=3; iigetElement(objv[ii], &elemPtr) != TCL_OK) + return TCL_ERROR; + graphPtr->legend_->removeElement(elemPtr); + delete elemPtr; + } + + graphPtr->flags |= RESET; + graphPtr->eventuallyRedraw(); + + return TCL_OK; +} + +static int ExistsOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Tcl_HashEntry *hPtr = + Tcl_FindHashEntry(&graphPtr->elements_.table, Tcl_GetString(objv[3])); + Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (hPtr != NULL)); + return TCL_OK; +} + +static int LowerOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + + // Move the links of lowered elements out of the display list into + // a temporary list + Chain* chain = new Chain(); + + for (int ii=3; iigetElement(objv[ii], &elemPtr) != TCL_OK) + return TCL_ERROR; + + // look for duplicates + int ok=1; + for (ChainLink* link = Chain_FirstLink(chain); + link; link = Chain_NextLink(link)) { + Element* ptr = (Element*)Chain_GetValue(link); + if (ptr == elemPtr) { + ok=0; + break; + } + } + + if (ok && elemPtr->link) { + graphPtr->elements_.displayList->unlinkLink(elemPtr->link); + chain->linkAfter(elemPtr->link, NULL); + } + } + + // Append the links to end of the display list + ChainLink *next; + for (ChainLink *link = Chain_FirstLink(chain); link; link = next) { + next = Chain_NextLink(link); + chain->unlinkLink(link); + graphPtr->elements_.displayList->linkAfter(link, NULL); + } + delete chain; + + graphPtr->flags |= CACHE; + graphPtr->eventuallyRedraw(); + + Tcl_SetObjResult(interp, DisplayListObj(graphPtr)); + return TCL_OK; +} + +static int NamesOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + + Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + if (objc == 3) { + Tcl_HashSearch iter; + for (Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&graphPtr->elements_.table, &iter); hPtr != NULL; hPtr = Tcl_NextHashEntry(&iter)) { + Element* elemPtr = (Element*)Tcl_GetHashValue(hPtr); + Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + } + else { + Tcl_HashSearch iter; + for (Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&graphPtr->elements_.table, &iter); hPtr != NULL; hPtr = Tcl_NextHashEntry(&iter)) { + Element* elemPtr = (Element*)Tcl_GetHashValue(hPtr); + + for (int ii=3; iiname_,Tcl_GetString(objv[ii]))) { + Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + break; + } + } + } + } + + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; +} + +static int RaiseOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + + Chain* chain = new Chain(); + for (int ii=3; iigetElement(objv[ii], &elemPtr) != TCL_OK) + return TCL_ERROR; + + // look for duplicates + int ok=1; + for (ChainLink* link = Chain_FirstLink(chain); + link; link = Chain_NextLink(link)) { + Element* ptr = (Element*)Chain_GetValue(link); + if (ptr == elemPtr) { + ok=0; + break; + } + } + + if (ok && elemPtr->link) { + graphPtr->elements_.displayList->unlinkLink(elemPtr->link); + chain->linkAfter(elemPtr->link, NULL); + } + } + + // Prepend the links to beginning of the display list in reverse order + ChainLink *prev; + for (ChainLink *link = Chain_LastLink(chain); link; link = prev) { + prev = Chain_PrevLink(link); + chain->unlinkLink(link); + graphPtr->elements_.displayList->linkBefore(link, NULL); + } + delete chain; + + graphPtr->flags |= CACHE; + graphPtr->eventuallyRedraw(); + + Tcl_SetObjResult(interp, DisplayListObj(graphPtr)); + return TCL_OK; +} + +static int ShowOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + int elemObjc; + Tcl_Obj** elemObjv; + if (Tcl_ListObjGetElements(interp, objv[3], &elemObjc, &elemObjv) != TCL_OK) + return TCL_ERROR; + + // Collect the named elements into a list + Chain* chain = new Chain(); + for (int ii=0; iigetElement(elemObjv[ii], &elemPtr) != TCL_OK) { + delete chain; + return TCL_ERROR; + } + + // look for duplicates + int ok=1; + for (ChainLink* link = Chain_FirstLink(chain); + link; link = Chain_NextLink(link)) { + Element* ptr = (Element*)Chain_GetValue(link); + if (ptr == elemPtr) { + ok=0; + break; + } + } + + if (ok) + chain->append(elemPtr); + } + + // Clear the links from the currently displayed elements + for (ChainLink* link = Chain_FirstLink(graphPtr->elements_.displayList); + link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + elemPtr->link = NULL; + } + delete graphPtr->elements_.displayList; + graphPtr->elements_.displayList = chain; + + // Set links on all the displayed elements + for (ChainLink* link = Chain_FirstLink(chain); link; + link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + elemPtr->link = link; + } + + graphPtr->flags |= RESET; + graphPtr->eventuallyRedraw(); + + Tcl_SetObjResult(interp, DisplayListObj(graphPtr)); + return TCL_OK; +} + +static int TypeOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Element* elemPtr; + if (graphPtr->getElement(objv[3], &elemPtr) != TCL_OK) + return TCL_ERROR; + + Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->typeName(), -1); + return TCL_OK; +} + +const Ensemble Blt::elementEnsemble[] = { + {"activate", ActivateOp, 0}, + {"bind", BindOp, 0}, + {"cget", CgetOp, 0}, + {"closest", ClosestOp, 0}, + {"configure", ConfigureOp, 0}, + {"create", CreateOp, 0}, + {"deactivate", DeactivateOp, 0}, + {"delete", DeleteOp, 0}, + {"exists", ExistsOp, 0}, + {"lower", LowerOp, 0}, + {"names", NamesOp, 0}, + {"raise", RaiseOp, 0}, + {"show", ShowOp, 0}, + {"type", TypeOp, 0}, + { 0,0,0 } +}; + +// Support + +static Tcl_Obj *DisplayListObj(Graph* graphPtr) +{ + Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + + for (ChainLink* link = Chain_FirstLink(graphPtr->elements_.displayList); + link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); + Tcl_ListObjAppendElement(graphPtr->interp_, listObjPtr, objPtr); + } + + return listObjPtr; +} + +static int GetIndex(Tcl_Interp* interp, Element* elemPtr, + Tcl_Obj *objPtr, int *indexPtr) +{ + ElementOptions* ops = (ElementOptions*)elemPtr->ops(); + + char *string = Tcl_GetString(objPtr); + if ((*string == 'e') && (strcmp("end", string) == 0)) + *indexPtr = NUMBEROFPOINTS(ops); + else if (Tcl_GetIntFromObj(interp, objPtr, indexPtr) != TCL_OK) + return TCL_ERROR; + + return TCL_OK; +} + + diff --git a/generic/tkbltGrElemOp.h b/generic/tkbltGrElemOp.h new file mode 100644 index 0000000..b596b11 --- /dev/null +++ b/generic/tkbltGrElemOp.h @@ -0,0 +1,41 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrElemOp_h__ +#define __BltGrElemOp_h__ + +#include "tkbltGraph.h" + +namespace Blt { + extern const Ensemble elementEnsemble[]; + extern int ElementObjConfigure(Blt::Element* elemPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); +}; + +#endif diff --git a/generic/tkbltGrElemOption.C b/generic/tkbltGrElemOption.C new file mode 100644 index 0000000..a0a67e6 --- /dev/null +++ b/generic/tkbltGrElemOption.C @@ -0,0 +1,396 @@ +/* + * 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 1993-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 +#include +#include + +#include + +#include "tkbltChain.h" + +#include "tkbltGraph.h" +#include "tkbltGrElem.h" +#include "tkbltGrElemOption.h" +#include "tkbltGrPen.h" +#include "tkbltConfig.h" + +using namespace Blt; + +#define SETRANGE(l) ((l).range = ((l).max > (l).min) ? ((l).max - (l).min) : DBL_EPSILON) +#define SETWEIGHT(l, lo, hi) ((l).min = (lo), (l).max = (hi), SETRANGE(l)) + +// Defs + +static int GetPenStyleFromObj(Tcl_Interp* interp, Graph* graphPtr, + Tcl_Obj *objPtr, ClassId classId, + PenStyle *stylePtr); +static int ParseValues(Tcl_Interp* interp, Tcl_Obj *objPtr, int *nValuesPtr, + double **arrayPtr); + +// OptionSpecs + +static Tk_CustomOptionSetProc ValuesSetProc; +static Tk_CustomOptionGetProc ValuesGetProc; +static Tk_CustomOptionFreeProc ValuesFreeProc; +Tk_ObjCustomOption valuesObjOption = + { + "values", ValuesSetProc, ValuesGetProc, RestoreProc, ValuesFreeProc, NULL + }; + +static int ValuesSetProc(ClientData clientData, Tcl_Interp* interp, + Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, + int offset, char* savePtr, int flags) +{ + ElemValues** valuesPtrPtr = (ElemValues**)(widgRec + offset); + *(double*)savePtr = *(double*)valuesPtrPtr; + ElementOptions* ops = (ElementOptions*)widgRec; + Element* elemPtr = ops->elemPtr; + + if (!valuesPtrPtr) + return TCL_OK; + + Tcl_Obj** objv; + int objc; + if (Tcl_ListObjGetElements(interp, *objPtr, &objc, &objv) != TCL_OK) + return TCL_ERROR; + + if (objc == 0) { + *valuesPtrPtr = NULL; + return TCL_OK; + } + + const char *string = Tcl_GetString(objv[0]); + if (objc == 1) { + if (Blt_VectorExists2(interp, string)) { + ElemValuesVector* valuesPtr = new ElemValuesVector(elemPtr, string); + if (valuesPtr->getVector() != TCL_OK) { + delete valuesPtr; + return TCL_ERROR; + } + *valuesPtrPtr = valuesPtr; + } + else + return TCL_ERROR; + } + else { + double* values; + int nValues; + if (ParseValues(interp, *objPtr, &nValues, &values) != TCL_OK) + return TCL_ERROR; + ElemValuesSource* valuesPtr = new ElemValuesSource(nValues, values); + valuesPtr->findRange(); + *valuesPtrPtr = valuesPtr; + } + + return TCL_OK; +} + +static Tcl_Obj* ValuesGetProc(ClientData clientData, Tk_Window tkwin, + char *widgRec, int offset) +{ + ElemValues* valuesPtr = *(ElemValues**)(widgRec + offset); + + if (!valuesPtr) + return Tcl_NewStringObj("", -1); + + int cnt = valuesPtr->nValues(); + if (!cnt) + return Tcl_NewListObj(0, (Tcl_Obj**)NULL); + + Tcl_Obj** ll = new Tcl_Obj*[cnt]; + for (int ii=0; iivalues_[ii]); + Tcl_Obj* listObjPtr = Tcl_NewListObj(cnt, ll); + delete [] ll; + + return listObjPtr; +} + +static void ValuesFreeProc(ClientData clientData, Tk_Window tkwin, char *ptr) +{ + ElemValues* valuesPtr = *(ElemValues**)ptr; + delete valuesPtr; +} + +static Tk_CustomOptionSetProc PairsSetProc; +static Tk_CustomOptionGetProc PairsGetProc; +static Tk_CustomOptionRestoreProc PairsRestoreProc; +static Tk_CustomOptionFreeProc PairsFreeProc; +Tk_ObjCustomOption pairsObjOption = + { + "pairs", PairsSetProc, PairsGetProc, PairsRestoreProc, PairsFreeProc, NULL + }; + +static int PairsSetProc(ClientData clientData, Tcl_Interp* interp, + Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, + int offset, char* savePtr, int flags) +{ + ElemCoords* coordsPtr = (ElemCoords*)(widgRec + offset); + *(double*)savePtr = (double)NULL; + + double* values; + int nValues; + if (ParseValues(interp, *objPtr, &nValues, &values) != TCL_OK) + return TCL_ERROR; + + if (nValues == 0) + return TCL_OK; + + if (nValues & 1) { + Tcl_AppendResult(interp, "odd number of data points", NULL); + delete [] values; + return TCL_ERROR; + } + + nValues /= 2; + delete coordsPtr->x; + coordsPtr->x = new ElemValuesSource(nValues); + + delete coordsPtr->y; + coordsPtr->y = new ElemValuesSource(nValues); + + int ii=0; + for (double* p = values; iix->values_[ii] = *p++; + coordsPtr->y->values_[ii] = *p++; + } + delete [] values; + + coordsPtr->x->findRange(); + coordsPtr->y->findRange(); + + return TCL_OK; +}; + +static Tcl_Obj* PairsGetProc(ClientData clientData, Tk_Window tkwin, + char *widgRec, int offset) +{ + ElemCoords* coordsPtr = (ElemCoords*)(widgRec + offset); + + if (!coordsPtr || + !coordsPtr->x || !coordsPtr->y || + !coordsPtr->x->nValues() || !coordsPtr->y->nValues()) + return Tcl_NewListObj(0, (Tcl_Obj**)NULL); + + int cnt = MIN(coordsPtr->x->nValues(), coordsPtr->y->nValues()); + Tcl_Obj** ll = new Tcl_Obj*[2*cnt]; + for (int ii=0, jj=0; iix->values_[ii]); + ll[jj++] = Tcl_NewDoubleObj(coordsPtr->y->values_[ii]); + } + Tcl_Obj* listObjPtr = Tcl_NewListObj(2*cnt, ll); + delete [] ll; + + return listObjPtr; +}; + +static void PairsRestoreProc(ClientData clientData, Tk_Window tkwin, + char *ptr, char *savePtr) +{ + // do nothing +} + +static void PairsFreeProc(ClientData clientData, Tk_Window tkwin, char *ptr) +{ + // do nothing +} + +int StyleSetProc(ClientData clientData, Tcl_Interp* interp, + Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, + int offset, char* save, int flags) +{ + Chain* stylePalette = *(Chain**)(widgRec + offset); + ElementOptions* ops = (ElementOptions*)(widgRec); + Element* elemPtr = ops->elemPtr; + size_t size = (size_t)clientData; + + int objc; + Tcl_Obj** objv; + if (Tcl_ListObjGetElements(interp, *objPtr, &objc, &objv) != TCL_OK) + return TCL_ERROR; + + // Reserve the first entry for the "normal" pen. We'll set the style later + elemPtr->freeStylePalette(stylePalette); + ChainLink* link = Chain_FirstLink(stylePalette); + if (!link) { + link = new ChainLink(size); + stylePalette->linkAfter(link, NULL); + } + + PenStyle* stylePtr = (PenStyle*)Chain_GetValue(link); + stylePtr->penPtr = NORMALPEN(ops); + for (int ii = 0; iiweight.min = (double)ii; + stylePtr->weight.max = (double)ii + 1.0; + stylePtr->weight.range = 1.0; + if (GetPenStyleFromObj(interp, elemPtr->graphPtr_, objv[ii], + elemPtr->classId(), + (PenStyle*)stylePtr) != TCL_OK) { + elemPtr->freeStylePalette(stylePalette); + return TCL_ERROR; + } + stylePalette->linkAfter(link, NULL); + } + + return TCL_OK; +} + +Tcl_Obj* StyleGetProc(ClientData clientData, Tk_Window tkwin, + char *widgRec, int offset) +{ + Chain* stylePalette = *(Chain**)(widgRec + offset); + + // count how many + int cnt =0; + for (ChainLink* link = Chain_FirstLink(stylePalette); link; + link = Chain_NextLink(link), cnt++) {} + if (!cnt) + return Tcl_NewListObj(0, (Tcl_Obj**)NULL); + + Tcl_Obj** ll = new Tcl_Obj*[3*cnt]; + int ii=0; + for (ChainLink* link = Chain_FirstLink(stylePalette); link; + link = Chain_NextLink(link)) { + PenStyle *stylePtr = (PenStyle*)Chain_GetValue(link); + ll[ii++] = Tcl_NewStringObj(stylePtr->penPtr->name_, -1); + ll[ii++] = Tcl_NewDoubleObj(stylePtr->weight.min); + ll[ii++] = Tcl_NewDoubleObj(stylePtr->weight.max); + } + Tcl_Obj *listObjPtr = Tcl_NewListObj(3*cnt,ll); + delete [] ll; + + return listObjPtr; +} + +void StyleRestoreProc(ClientData clientData, Tk_Window tkwin, + char *ptr, char *savePtr) +{ + // do nothing +} + +void StyleFreeProc(ClientData clientData, Tk_Window tkwin, char *ptr) +{ + // do nothing +} + +// Support + +static int GetPenStyleFromObj(Tcl_Interp* interp, Graph* graphPtr, + Tcl_Obj *objPtr, ClassId classId, + PenStyle *stylePtr) +{ + int objc; + Tcl_Obj **objv; + if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) + return TCL_ERROR; + + if ((objc != 1) && (objc != 3)) { + Tcl_AppendResult(interp, "bad style entry \"", + Tcl_GetString(objPtr), + "\": should be \"penName\" or \"penName min max\"", + NULL); + return TCL_ERROR; + } + + Pen* penPtr; + if (graphPtr->getPen(objv[0], &penPtr) != TCL_OK) + return TCL_ERROR; + + if (objc == 3) { + double min, max; + if ((Tcl_GetDoubleFromObj(interp, objv[1], &min) != TCL_OK) || + (Tcl_GetDoubleFromObj(interp, objv[2], &max) != TCL_OK)) + return TCL_ERROR; + + SETWEIGHT(stylePtr->weight, min, max); + } + + penPtr->refCount_++; + stylePtr->penPtr = penPtr; + return TCL_OK; +} + +void VectorChangedProc(Tcl_Interp* interp, ClientData clientData, + Blt_VectorNotify notify) +{ + ElemValuesVector* valuesPtr = (ElemValuesVector*)clientData; + if (!valuesPtr) + return; + + if (notify == BLT_VECTOR_NOTIFY_DESTROY) { + valuesPtr->freeSource(); + valuesPtr->reset(); + } + else { + Blt_Vector* vector; + Blt_GetVectorById(interp, valuesPtr->source_, &vector); + if (valuesPtr->fetchValues(vector) != TCL_OK) + return; + } + + Element* elemPtr = valuesPtr->elemPtr_; + Graph* graphPtr = elemPtr->graphPtr_; + + graphPtr->flags |= RESET; + graphPtr->eventuallyRedraw(); +} + +static int ParseValues(Tcl_Interp* interp, Tcl_Obj *objPtr, int *nValuesPtr, + double **arrayPtr) +{ + int objc; + Tcl_Obj **objv; + if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) + return TCL_ERROR; + + *arrayPtr = NULL; + *nValuesPtr = 0; + if (objc > 0) { + double* array = new double[objc]; + if (!array) { + Tcl_AppendResult(interp, "can't allocate new vector", NULL); + return TCL_ERROR; + } + + int i=0; + for (double* p = array; i < objc; i++, p++) { + if (Tcl_GetDoubleFromObj(interp, objv[i], p) != TCL_OK) { + delete [] array; + return TCL_ERROR; + } + } + *arrayPtr = array; + *nValuesPtr = objc; + } + + return TCL_OK; +} diff --git a/generic/tkbltGrElemOption.h b/generic/tkbltGrElemOption.h new file mode 100644 index 0000000..4312691 --- /dev/null +++ b/generic/tkbltGrElemOption.h @@ -0,0 +1,41 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrElemOption_h__ +#define __BltGrElemOption_h__ + +#include + +extern const char* fillObjOption[]; +extern Tk_CustomOptionSetProc StyleSetProc; +extern Tk_CustomOptionGetProc StyleGetProc; +extern Tk_CustomOptionRestoreProc StyleRestoreProc; +extern Tk_CustomOptionFreeProc StyleFreeProc; + +#endif diff --git a/generic/tkbltGrHairs.C b/generic/tkbltGrHairs.C new file mode 100644 index 0000000..d7ea3d2 --- /dev/null +++ b/generic/tkbltGrHairs.C @@ -0,0 +1,145 @@ +/* + * 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 1993-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 + +#include "tkbltGraph.h" +#include "tkbltGrHairs.h" +#include "tkbltConfig.h" + +using namespace Blt; + +static Tk_OptionSpec optionSpecs[] = { + {TK_OPTION_COLOR, "-color", "color", "Color", + "green", -1, Tk_Offset(CrosshairsOptions, colorPtr), 0, NULL, 0}, + {TK_OPTION_CUSTOM, "-dashes", "dashes", "Dashes", + NULL, -1, Tk_Offset(CrosshairsOptions, dashes), + TK_OPTION_NULL_OK, &dashesObjOption, 0}, + {TK_OPTION_PIXELS, "-linewidth", "lineWidth", "Linewidth", + "1", -1, Tk_Offset(CrosshairsOptions, lineWidth), 0, NULL, 0}, + {TK_OPTION_PIXELS, "-x", "x", "X", + "0", -1, Tk_Offset(CrosshairsOptions, x), 0, NULL, 0}, + {TK_OPTION_PIXELS, "-y", "y", "Y", + "0", -1, Tk_Offset(CrosshairsOptions, y), 0, NULL, 0}, + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} +}; + +Crosshairs::Crosshairs(Graph* graphPtr) +{ + ops_ = (CrosshairsOptions*)calloc(1, sizeof(CrosshairsOptions)); + + graphPtr_ = graphPtr; + visible_ =0; + gc_ =NULL; + + optionTable_ = Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); + Tk_InitOptions(graphPtr->interp_, (char*)ops_, optionTable_, + graphPtr->tkwin_); +} + +Crosshairs::~Crosshairs() +{ + if (gc_) + graphPtr_->freePrivateGC(gc_); + + Tk_FreeConfigOptions((char*)ops_, optionTable_, graphPtr_->tkwin_); + free(ops_); +} + +// Configure + +int Crosshairs::configure() +{ + CrosshairsOptions* ops = (CrosshairsOptions*)ops_; + + XGCValues gcValues; + gcValues.foreground = ops->colorPtr->pixel; + gcValues.line_width = ops->lineWidth; + unsigned long gcMask = (GCForeground | GCLineWidth); + if (LineIsDashed(ops->dashes)) { + gcValues.line_style = LineOnOffDash; + gcMask |= GCLineStyle; + } + GC newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); + if (LineIsDashed(ops->dashes)) + graphPtr_->setDashes(newGC, &ops->dashes); + + if (gc_) + graphPtr_->freePrivateGC(gc_); + gc_ = newGC; + + // Are the new coordinates on the graph? + map(); + + return TCL_OK; +} + +void Crosshairs::map() +{ + CrosshairsOptions* ops = (CrosshairsOptions*)ops_; + + segArr_[0].x = ops->x; + segArr_[1].x = ops->x; + segArr_[0].y = graphPtr_->bottom_; + segArr_[1].y = graphPtr_->top_; + segArr_[2].y = ops->y; + segArr_[3].y = ops->y; + segArr_[2].x = graphPtr_->left_; + segArr_[3].x = graphPtr_->right_; +} + +void Crosshairs::on() +{ + visible_ =1; +} + +void Crosshairs::off() +{ + visible_ =0; +} + +void Crosshairs::draw(Drawable drawable) +{ + CrosshairsOptions* ops = (CrosshairsOptions*)ops_; + + if (visible_ && Tk_IsMapped(graphPtr_->tkwin_)) { + if (ops->x <= graphPtr_->right_ && + ops->x >= graphPtr_->left_ && + ops->y <= graphPtr_->bottom_ && + ops->y >= graphPtr_->top_) { + XDrawLine(graphPtr_->display_, drawable, gc_, + segArr_[0].x, segArr_[0].y, segArr_[1].x, segArr_[1].y); + XDrawLine(graphPtr_->display_, drawable, gc_, + segArr_[2].x, segArr_[2].y, segArr_[3].x, segArr_[3].y); + } + } +} diff --git a/generic/tkbltGrHairs.h b/generic/tkbltGrHairs.h new file mode 100644 index 0000000..a86d0c6 --- /dev/null +++ b/generic/tkbltGrHairs.h @@ -0,0 +1,78 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrHairs_h__ +#define __BltGrHairs_h__ + +#include + +#include "tkbltGrMisc.h" + +namespace Blt { + class Graph; + + typedef struct { + XColor* colorPtr; + Dashes dashes; + int lineWidth; + int x; + int y; + } CrosshairsOptions; + + class Crosshairs { + protected: + Graph* graphPtr_; + Tk_OptionTable optionTable_; + void* ops_; + + int visible_; + GC gc_; + Point segArr_[4]; + + public: + Crosshairs(Graph*); + virtual ~Crosshairs(); + + int configure(); + void map(); + void draw(Drawable); + + void on(); + void off(); + int isOn() {return visible_;} + + Tk_OptionTable optionTable() {return optionTable_;} + void* ops() {return ops_;} + }; +}; + +#endif diff --git a/generic/tkbltGrHairsOp.C b/generic/tkbltGrHairsOp.C new file mode 100644 index 0000000..57650ce --- /dev/null +++ b/generic/tkbltGrHairsOp.C @@ -0,0 +1,164 @@ +/* + * 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 1993-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 "tkbltGraph.h" +#include "tkbltGrHairs.h" +#include "tkbltGrHairsOp.h" + +using namespace Blt; + +static int CrosshairsObjConfigure(Graph* graphPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Crosshairs* chPtr = graphPtr->crosshairs_; + Tk_SavedOptions savedOptions; + int mask =0; + int error; + Tcl_Obj* errorResult; + + for (error=0; error<=1; error++) { + if (!error) { + if (Tk_SetOptions(interp, (char*)chPtr->ops(), chPtr->optionTable(), + objc, objv, graphPtr->tkwin_, &savedOptions, &mask) + != TCL_OK) + continue; + } + else { + errorResult = Tcl_GetObjResult(interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + } + + if (chPtr->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 != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "cget option"); + return TCL_ERROR; + } + + Crosshairs* chPtr = graphPtr->crosshairs_; + Tcl_Obj* objPtr = Tk_GetOptionValue(interp, + (char*)chPtr->ops(), + chPtr->optionTable(), + objv[3], 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; + Crosshairs* chPtr = graphPtr->crosshairs_; + if (objc <= 4) { + Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)chPtr->ops(), + chPtr->optionTable(), + (objc == 4) ? objv[3] : NULL, + graphPtr->tkwin_); + if (objPtr == NULL) + return TCL_ERROR; + else + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; + } + else + return CrosshairsObjConfigure(graphPtr, interp, objc-3, objv+3); +} + +static int OnOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Crosshairs *chPtr = graphPtr->crosshairs_; + + chPtr->on(); + + return TCL_OK; +} + +static int OffOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Crosshairs *chPtr = graphPtr->crosshairs_; + + chPtr->off(); + + return TCL_OK; +} + +static int ToggleOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Crosshairs *chPtr = graphPtr->crosshairs_; + + if (chPtr->isOn()) + chPtr->off(); + else + chPtr->on(); + + return TCL_OK; +} + +const Ensemble Blt::crosshairsEnsemble[] = { + {"cget", CgetOp, 0}, + {"configure", ConfigureOp, 0}, + {"off", OffOp, 0}, + {"on", OnOp, 0}, + {"toggle", ToggleOp, 0}, + { 0,0,0 } +}; diff --git a/generic/tkbltGrHairsOp.h b/generic/tkbltGrHairsOp.h new file mode 100644 index 0000000..3f3d009 --- /dev/null +++ b/generic/tkbltGrHairsOp.h @@ -0,0 +1,42 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrHairsOp_h__ +#define __BltGrHairsOp_h__ + +#include "tkbltGraph.h" + +namespace Blt { + extern const Ensemble crosshairsEnsemble[]; +}; + +#endif diff --git a/generic/tkbltGrLegd.C b/generic/tkbltGrLegd.C new file mode 100644 index 0000000..b8822f1 --- /dev/null +++ b/generic/tkbltGrLegd.C @@ -0,0 +1,1070 @@ +/* + * 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 1993-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 +#include + +#include "tkbltGrBind.h" +#include "tkbltGraph.h" +#include "tkbltGrLegd.h" +#include "tkbltGrElem.h" +#include "tkbltGrPostscript.h" +#include "tkbltGrMisc.h" +#include "tkbltGrDef.h" +#include "tkbltConfig.h" +#include "tkbltGrPSOutput.h" + +using namespace Blt; + +static void SelectCmdProc(ClientData); +static Tk_SelectionProc SelectionProc; + +// OptionSpecs + +static const char* selectmodeObjOption[] = { + "single", "multiple", NULL +}; +static const char* positionObjOption[] = { + "rightmargin", "leftmargin", "topmargin", "bottommargin", + "plotarea", "xy", NULL +}; + +static Tk_OptionSpec optionSpecs[] = { + {TK_OPTION_BORDER, "-activebackground", "activeBackground", + "ActiveBackground", + STD_ACTIVE_BACKGROUND, -1, Tk_Offset(LegendOptions, activeBg), + 0, NULL, CACHE}, + {TK_OPTION_PIXELS, "-activeborderwidth", "activeBorderWidth", + "ActiveBorderWidth", + STD_BORDERWIDTH, -1, Tk_Offset(LegendOptions, entryBW), 0, NULL, LAYOUT}, + {TK_OPTION_COLOR, "-activeforeground", "activeForeground", "ActiveForeground", + STD_ACTIVE_FOREGROUND, -1, Tk_Offset(LegendOptions, activeFgColor), + 0, NULL, CACHE}, + {TK_OPTION_RELIEF, "-activerelief", "activeRelief", "ActiveRelief", + "flat", -1, Tk_Offset(LegendOptions, activeRelief), 0, NULL, LAYOUT}, + {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor", + "n", -1, Tk_Offset(LegendOptions, anchor), 0, NULL, LAYOUT}, + {TK_OPTION_SYNONYM, "-bg", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-background", 0}, + {TK_OPTION_BORDER, "-background", "background", "Background", + NULL, -1, Tk_Offset(LegendOptions, normalBg), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", + STD_BORDERWIDTH, -1, Tk_Offset(LegendOptions, borderWidth), + 0, NULL, LAYOUT}, + {TK_OPTION_SYNONYM, "-bd", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-borderwidth", 0}, + {TK_OPTION_INT, "-columns", "columns", "columns", + "0", -1, Tk_Offset(LegendOptions, reqColumns), 0, NULL, LAYOUT}, + {TK_OPTION_BOOLEAN, "-exportselection", "exportSelection", "ExportSelection", + "no", -1, Tk_Offset(LegendOptions, exportSelection), 0, NULL, LAYOUT}, + {TK_OPTION_CUSTOM, "-focusdashes", "focusDashes", "FocusDashes", + "dot", -1, Tk_Offset(LegendOptions, focusDashes), + TK_OPTION_NULL_OK, &dashesObjOption, CACHE}, + {TK_OPTION_COLOR, "-focusforeground", "focusForeground", "FocusForeground", + STD_ACTIVE_FOREGROUND, -1, Tk_Offset(LegendOptions, focusColor), + 0, NULL, CACHE}, + {TK_OPTION_FONT, "-font", "font", "Font", + STD_FONT_SMALL, -1, Tk_Offset(LegendOptions, style.font), 0, NULL, LAYOUT}, + {TK_OPTION_SYNONYM, "-fg", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-foreground", 0}, + {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(LegendOptions, fgColor), + 0, NULL, CACHE}, + {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", + "no", -1, Tk_Offset(LegendOptions, hide), 0, NULL, LAYOUT}, + {TK_OPTION_PIXELS, "-ipadx", "iPadX", "Pad", + "1", -1, Tk_Offset(LegendOptions, ixPad), 0, NULL, LAYOUT}, + {TK_OPTION_PIXELS, "-ipady", "iPadY", "Pad", + "1", -1, Tk_Offset(LegendOptions, iyPad), 0, NULL, LAYOUT}, + {TK_OPTION_BORDER, "-nofocusselectbackground", "noFocusSelectBackground", + "NoFocusSelectBackground", + STD_ACTIVE_BACKGROUND, -1, Tk_Offset(LegendOptions, selOutFocusBg), + 0, NULL, CACHE}, + {TK_OPTION_COLOR, "-nofocusselectforeground", "noFocusSelectForeground", + "NoFocusSelectForeground", + STD_ACTIVE_FOREGROUND, -1, Tk_Offset(LegendOptions, selOutFocusFgColor), + 0, NULL, CACHE}, + {TK_OPTION_PIXELS, "-padx", "padX", "Pad", + "1", -1, Tk_Offset(LegendOptions, xPad), 0, NULL, LAYOUT}, + {TK_OPTION_PIXELS, "-pady", "padY", "Pad", + "1", -1, Tk_Offset(LegendOptions, yPad), 0, NULL, LAYOUT}, + {TK_OPTION_STRING_TABLE, "-position", "position", "Position", + "rightmargin", -1, Tk_Offset(LegendOptions, position), + 0, &positionObjOption, LAYOUT}, + {TK_OPTION_BOOLEAN, "-raised", "raised", "Raised", + "no", -1, Tk_Offset(LegendOptions, raised), 0, NULL, LAYOUT}, + {TK_OPTION_RELIEF, "-relief", "relief", "Relief", + "flat", -1, Tk_Offset(LegendOptions, relief), 0, NULL, LAYOUT}, + {TK_OPTION_INT, "-rows", "rows", "rows", + "0", -1, Tk_Offset(LegendOptions, reqRows), 0, NULL, LAYOUT}, + {TK_OPTION_BORDER, "-selectbackground", "selectBackground", + "SelectBackground", + STD_ACTIVE_BACKGROUND, -1, Tk_Offset(LegendOptions, selInFocusBg), + 0, NULL, LAYOUT}, + {TK_OPTION_PIXELS, "-selectborderwidth", "selectBorderWidth", + "SelectBorderWidth", + "1", -1, Tk_Offset(LegendOptions, selBW), 0, NULL, LAYOUT}, + {TK_OPTION_STRING, "-selectcommand", "selectCommand", "SelectCommand", + NULL, -1, Tk_Offset(LegendOptions, selectCmd), TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_COLOR, "-selectforeground", "selectForeground", "SelectForeground", + STD_ACTIVE_FOREGROUND, -1, Tk_Offset(LegendOptions, selInFocusFgColor), + 0, NULL, CACHE}, + {TK_OPTION_STRING_TABLE, "-selectmode", "selectMode", "SelectMode", + "multiple", -1, Tk_Offset(LegendOptions, selectMode), + 0, &selectmodeObjOption, 0}, + {TK_OPTION_RELIEF, "-selectrelief", "selectRelief", "SelectRelief", + "flat", -1, Tk_Offset(LegendOptions, selRelief), 0, NULL, LAYOUT}, + {TK_OPTION_STRING, "-title", "title", "Title", + NULL, -1, Tk_Offset(LegendOptions, title), TK_OPTION_NULL_OK, NULL, LAYOUT}, + {TK_OPTION_COLOR, "-titlecolor", "titleColor", "TitleColor", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(LegendOptions, titleStyle.color), + 0, NULL, CACHE}, + {TK_OPTION_FONT, "-titlefont", "titleFont", "TitleFont", + STD_FONT_SMALL, -1, Tk_Offset(LegendOptions, titleStyle.font), + 0, NULL, LAYOUT}, + {TK_OPTION_PIXELS, "-x", "x", "X", + "0", -1, Tk_Offset(LegendOptions, xReq), 0, NULL, LAYOUT}, + {TK_OPTION_PIXELS, "-y", "y", "Y", + "0", -1, Tk_Offset(LegendOptions, yReq), 0, NULL, LAYOUT}, + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} +}; + +Legend::Legend(Graph* graphPtr) +{ + ops_ = (void*)calloc(1, sizeof(LegendOptions)); + LegendOptions* ops = (LegendOptions*)ops_; + + graphPtr_ = graphPtr; + flags =0; + nEntries_ =0; + nColumns_ =0; + nRows_ =0; + width_ =0; + height_ =0; + entryWidth_ =0; + entryHeight_ =0; + x_ =0; + y_ =0; + bindTable_ =NULL; + focusGC_ =NULL; + focusPtr_ =NULL; + selAnchorPtr_ =NULL; + selMarkPtr_ =NULL; + selected_ = new Chain(); + titleWidth_ =0; + titleHeight_ =0; + + ops->style.anchor =TK_ANCHOR_NW; + ops->style.color =NULL; + ops->style.font =NULL; + ops->style.angle =0; + ops->style.justify =TK_JUSTIFY_LEFT; + + ops->titleStyle.anchor =TK_ANCHOR_NW; + ops->titleStyle.color =NULL; + ops->titleStyle.font =NULL; + ops->titleStyle.angle =0; + ops->titleStyle.justify =TK_JUSTIFY_LEFT; + + bindTable_ = new BindTable(graphPtr, this); + + Tcl_InitHashTable(&selectTable_, TCL_ONE_WORD_KEYS); + + Tk_CreateSelHandler(graphPtr_->tkwin_, XA_PRIMARY, XA_STRING, + SelectionProc, this, XA_STRING); + + optionTable_ =Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); + Tk_InitOptions(graphPtr->interp_, (char*)ops_, optionTable_, graphPtr->tkwin_); +} + +Legend::~Legend() +{ + // LegendOptions* ops = (LegendOptions*)ops_; + + delete bindTable_; + + if (focusGC_) + graphPtr_->freePrivateGC(focusGC_); + + if (graphPtr_->tkwin_) + Tk_DeleteSelHandler(graphPtr_->tkwin_, XA_PRIMARY, XA_STRING); + + delete selected_; + + Tk_FreeConfigOptions((char*)ops_, optionTable_, graphPtr_->tkwin_); + free(ops_); +} + +int Legend::configure() +{ + LegendOptions* ops = (LegendOptions*)ops_; + + // GC for active label, Dashed outline + unsigned long gcMask = GCForeground | GCLineStyle; + XGCValues gcValues; + gcValues.foreground = ops->focusColor->pixel; + gcValues.line_style = (LineIsDashed(ops->focusDashes)) + ? LineOnOffDash : LineSolid; + GC newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); + if (LineIsDashed(ops->focusDashes)) { + ops->focusDashes.offset = 2; + graphPtr_->setDashes(newGC, &ops->focusDashes); + } + if (focusGC_) + graphPtr_->freePrivateGC(focusGC_); + + focusGC_ = newGC; + + return TCL_OK; +} + +void Legend::map(int plotWidth, int plotHeight) +{ + LegendOptions* ops = (LegendOptions*)ops_; + + entryWidth_ =0; + entryHeight_ = 0; + nRows_ =0; + nColumns_ =0; + nEntries_ =0; + height_ =0; + width_ = 0; + + TextStyle tts(graphPtr_, &ops->titleStyle); + tts.getExtents(ops->title, &titleWidth_, &titleHeight_); + + // Count the number of legend entries and determine the widest and tallest + // label. The number of entries would normally be the number of elements, + // but elements can have no legend entry (-label ""). + int nEntries =0; + int maxWidth =0; + int maxHeight =0; + TextStyle ts(graphPtr_, &ops->style); + for (ChainLink* link = Chain_FirstLink(graphPtr_->elements_.displayList); + link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); + + if (!elemOps->label) + continue; + + int w, h; + ts.getExtents(elemOps->label, &w, &h); + if (maxWidth < (int)w) + maxWidth = w; + + if (maxHeight < (int)h) + maxHeight = h; + + nEntries++; + } + if (nEntries == 0) + return; + + Tk_FontMetrics fontMetrics; + Tk_GetFontMetrics(ops->style.font, &fontMetrics); + int symbolWidth = 2 * fontMetrics.ascent; + + maxWidth += 2 * ops->entryBW + 2*ops->ixPad + + + symbolWidth + 3 * 2; + + maxHeight += 2 * ops->entryBW + 2*ops->iyPad; + + maxWidth |= 0x01; + maxHeight |= 0x01; + + int lw = plotWidth - 2 * ops->borderWidth - 2*ops->xPad; + int lh = plotHeight - 2 * ops->borderWidth - 2*ops->yPad; + + /* + * The number of rows and columns is computed as one of the following: + * + * both options set User defined. + * -rows Compute columns from rows. + * -columns Compute rows from columns. + * neither set Compute rows and columns from + * size of plot. + */ + int nRows =0; + int nColumns =0; + if (ops->reqRows > 0) { + nRows = MIN(ops->reqRows, nEntries); + if (ops->reqColumns > 0) + nColumns = MIN(ops->reqColumns, nEntries); + else + nColumns = ((nEntries - 1) / nRows) + 1; /* Only -rows. */ + } + else if (ops->reqColumns > 0) { /* Only -columns. */ + nColumns = MIN(ops->reqColumns, nEntries); + nRows = ((nEntries - 1) / nColumns) + 1; + } + else { + // Compute # of rows and columns from the legend size + nRows = lh / maxHeight; + nColumns = lw / maxWidth; + if (nRows < 1) { + nRows = nEntries; + } + if (nColumns < 1) { + nColumns = nEntries; + } + if (nRows > nEntries) { + nRows = nEntries; + } + switch ((Position)ops->position) { + case TOP: + case BOTTOM: + nRows = ((nEntries - 1) / nColumns) + 1; + break; + case LEFT: + case RIGHT: + default: + nColumns = ((nEntries - 1) / nRows) + 1; + break; + } + } + if (nColumns < 1) + nColumns = 1; + + if (nRows < 1) + nRows = 1; + + lh = (nRows * maxHeight); + if (titleHeight_ > 0) + lh += titleHeight_ + ops->yPad; + + lw = nColumns * maxWidth; + if (lw < (int)(titleWidth_)) + lw = titleWidth_; + + width_ = lw + 2 * ops->borderWidth + 2*ops->xPad; + height_ = lh + 2 * ops->borderWidth + 2*ops->yPad; + nRows_ = nRows; + nColumns_ = nColumns; + nEntries_ = nEntries; + entryHeight_ = maxHeight; + entryWidth_ = maxWidth; + + int row =0; + int col =0; + int count =0; + for (ChainLink* link = Chain_FirstLink(graphPtr_->elements_.displayList); + link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + count++; + elemPtr->row_ = row; + elemPtr->col_ = col; + row++; + if ((count % nRows) == 0) { + col++; + row = 0; + } + } +} + +void Legend::draw(Drawable drawable) +{ + LegendOptions* ops = (LegendOptions*)ops_; + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + + if ((ops->hide) || (nEntries_ == 0)) + return; + + setOrigin(); + Tk_Window tkwin = graphPtr_->tkwin_; + int w = width_; + int h = height_; + + Pixmap pixmap = Tk_GetPixmap(graphPtr_->display_, Tk_WindowId(tkwin), w, h, + Tk_Depth(tkwin)); + + if (ops->normalBg) + Tk_Fill3DRectangle(tkwin, pixmap, ops->normalBg, 0, 0, + w, h, 0, TK_RELIEF_FLAT); + else { + switch ((Position)ops->position) { + case TOP: + case BOTTOM: + case RIGHT: + case LEFT: + Tk_Fill3DRectangle(tkwin, pixmap, gops->normalBg, 0, 0, + w, h, 0, TK_RELIEF_FLAT); + break; + case PLOT: + case XY: + // Legend background is transparent and is positioned over the the + // plot area. Either copy the part of the background from the backing + // store pixmap or (if no backing store exists) just fill it with the + // background color of the plot. + if (graphPtr_->cache_ != None) + XCopyArea(graphPtr_->display_, graphPtr_->cache_, pixmap, + graphPtr_->drawGC_, x_, y_, w, h, 0, 0); + else + Tk_Fill3DRectangle(tkwin, pixmap, gops->plotBg, 0, 0, + w, h, TK_RELIEF_FLAT, 0); + break; + }; + } + + Tk_FontMetrics fontMetrics; + Tk_GetFontMetrics(ops->style.font, &fontMetrics); + + int symbolSize = fontMetrics.ascent; + int xMid = symbolSize + 1 + ops->entryBW; + int yMid = (symbolSize / 2) + 1 + ops->entryBW; + int xLabel = 2 * symbolSize + ops->entryBW + ops->ixPad + 2 * 2; + int ySymbol = yMid + ops->iyPad; + int xSymbol = xMid + 2; + + int x = ops->xPad + ops->borderWidth; + int y = ops->yPad + ops->borderWidth; + + TextStyle tts(graphPtr_, &ops->titleStyle); + tts.drawText(pixmap, ops->title, x, y); + if (titleHeight_ > 0) + y += titleHeight_ + ops->yPad; + + int count = 0; + int yStart = y; + TextStyle ts(graphPtr_, &ops->style); + + for (ChainLink* link = Chain_FirstLink(graphPtr_->elements_.displayList); + link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); + if (!elemOps->label) + continue; + + int isSelected = entryIsSelected(elemPtr); + if (elemPtr->labelActive_) + Tk_Fill3DRectangle(tkwin, pixmap, ops->activeBg, + x, y, entryWidth_, entryHeight_, + ops->entryBW, ops->activeRelief); + else if (isSelected) { + XColor* fg = (flags & FOCUS) ? + ops->selInFocusFgColor : ops->selOutFocusFgColor; + Tk_3DBorder bg = (flags & FOCUS) ? + ops->selInFocusBg : ops->selOutFocusBg; + ops->style.color = fg; + Tk_Fill3DRectangle(tkwin, pixmap, bg, x, y, + entryWidth_, entryHeight_, + ops->selBW, ops->selRelief); + } + else { + ops->style.color = ops->fgColor; + if (elemOps->legendRelief != TK_RELIEF_FLAT) + Tk_Fill3DRectangle(tkwin, pixmap, gops->normalBg, + x, y, entryWidth_, + entryHeight_, ops->entryBW, + elemOps->legendRelief); + } + elemPtr->drawSymbol(pixmap, x + xSymbol, y + ySymbol, symbolSize); + + ts.drawText(pixmap, elemOps->label, x+xLabel, y+ops->entryBW+ops->iyPad); + count++; + + if (focusPtr_ == elemPtr) { + if (isSelected) { + XColor* color = (flags & FOCUS) ? + ops->selInFocusFgColor : ops->selOutFocusFgColor; + XSetForeground(graphPtr_->display_, focusGC_, color->pixel); + } + XDrawRectangle(graphPtr_->display_, pixmap, focusGC_, + x + 1, y + 1, entryWidth_ - 3, + entryHeight_ - 3); + if (isSelected) + XSetForeground(graphPtr_->display_, focusGC_, ops->focusColor->pixel); + } + + // Check when to move to the next column + if ((count % nRows_) > 0) + y += entryHeight_; + else { + x += entryWidth_; + y = yStart; + } + } + + Tk_3DBorder bg = ops->normalBg; + if (!bg) + bg = gops->normalBg; + + Tk_Draw3DRectangle(tkwin, pixmap, bg, 0, 0, w, h, + ops->borderWidth, ops->relief); + XCopyArea(graphPtr_->display_, pixmap, drawable, graphPtr_->drawGC_, + 0, 0, w, h, x_, y_); + + Tk_FreePixmap(graphPtr_->display_, pixmap); +} + +void Legend::print(PSOutput* psPtr) +{ + LegendOptions* ops = (LegendOptions*)ops_; + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + PostscriptOptions* pops = (PostscriptOptions*)graphPtr_->postscript_->ops_; + + if ((ops->hide) || (nEntries_ == 0)) + return; + + setOrigin(); + + double x = x_; + double y = y_; + int width = width_ - 2*ops->xPad; + int height = height_ - 2*ops->yPad; + + psPtr->append("% Legend\n"); + if (pops->decorations) { + if (ops->normalBg) + psPtr->fill3DRectangle(ops->normalBg, x, y, width, height, + ops->borderWidth, ops->relief); + else + psPtr->print3DRectangle(gops->normalBg, x, y, width, height, + ops->borderWidth, ops->relief); + + } + else { + psPtr->setClearBackground(); + psPtr->fillRectangle(x, y, width, height); + } + + Tk_FontMetrics fontMetrics; + Tk_GetFontMetrics(ops->style.font, &fontMetrics); + int symbolSize = fontMetrics.ascent; + int xMid = symbolSize + 1 + ops->entryBW; + int yMid = (symbolSize / 2) + 1 + ops->entryBW; + int xLabel = 2 * symbolSize + ops->entryBW + ops->ixPad + 5; + int xSymbol = xMid + ops->ixPad; + int ySymbol = yMid + ops->iyPad; + + x += ops->borderWidth; + y += ops->borderWidth; + TextStyle tts(graphPtr_, &ops->titleStyle); + tts.printText(psPtr, ops->title, x, y); + if (titleHeight_ > 0) + y += titleHeight_ + ops->yPad; + + int count = 0; + double yStart = y; + TextStyle ts(graphPtr_, &ops->style); + + for (ChainLink* link = Chain_FirstLink(graphPtr_->elements_.displayList); + link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); + + if (!elemOps->label) + continue; + + if (elemPtr->labelActive_) { + ops->style.color = ops->activeFgColor; + psPtr->fill3DRectangle(ops->activeBg, x, y, entryWidth_, + entryHeight_, ops->entryBW, + ops->activeRelief); + } + else { + ops->style.color = ops->fgColor; + if (elemOps->legendRelief != TK_RELIEF_FLAT) + psPtr->print3DRectangle(gops->normalBg, x, y, entryWidth_, entryHeight_, + ops->entryBW, elemOps->legendRelief); + } + elemPtr->printSymbol(psPtr, x + xSymbol, y + ySymbol, symbolSize); + ts.printText(psPtr, elemOps->label, x + xLabel, + y + ops->entryBW + ops->iyPad); + count++; + + if ((count % nRows_) > 0) + y += entryHeight_; + else { + x += entryWidth_; + y = yStart; + } + } +} + +void Legend::removeElement(Element* elemPtr) +{ + bindTable_->deleteBindings(elemPtr); +} + +void Legend::eventuallyInvokeSelectCmd() +{ + if ((flags & SELECT_PENDING) == 0) { + flags |= SELECT_PENDING; + Tcl_DoWhenIdle(SelectCmdProc, this); + } +} + +void Legend::setOrigin() +{ + LegendOptions* ops = (LegendOptions*)ops_; + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + + int x =0; + int y =0; + int w =0; + int h =0; + switch ((Position)ops->position) { + case RIGHT: + w = gops->rightMargin.width - gops->rightMargin.axesOffset; + h = graphPtr_->bottom_ - graphPtr_->top_; + x = graphPtr_->right_ + gops->rightMargin.axesOffset; + y = graphPtr_->top_; + break; + + case LEFT: + w = gops->leftMargin.width - gops->leftMargin.axesOffset; + h = graphPtr_->bottom_ - graphPtr_->top_; + x = graphPtr_->inset_; + y = graphPtr_->top_; + break; + + case TOP: + w = graphPtr_->right_ - graphPtr_->left_; + h = gops->topMargin.height - gops->topMargin.axesOffset; + if (gops->title) + h -= graphPtr_->titleHeight_; + + x = graphPtr_->left_; + y = graphPtr_->inset_; + if (gops->title) + y += graphPtr_->titleHeight_; + break; + + case BOTTOM: + w = graphPtr_->right_ - graphPtr_->left_; + h = gops->bottomMargin.height - gops->bottomMargin.axesOffset; + x = graphPtr_->left_; + y = graphPtr_->bottom_ + gops->bottomMargin.axesOffset; + break; + + case PLOT: + w = graphPtr_->right_ - graphPtr_->left_; + h = graphPtr_->bottom_ - graphPtr_->top_; + x = graphPtr_->left_; + y = graphPtr_->top_; + break; + + case XY: + w = width_; + h = height_; + x = ops->xReq; + y = ops->yReq; + if (x < 0) + x += graphPtr_->width_; + + if (y < 0) + y += graphPtr_->height_; + break; + } + + switch (ops->anchor) { + case TK_ANCHOR_NW: + break; + case TK_ANCHOR_W: + if (h > height_) + y += (h - height_) / 2; + break; + case TK_ANCHOR_SW: + if (h > height_) + y += (h - height_); + break; + case TK_ANCHOR_N: + if (w > width_) + x += (w - width_) / 2; + break; + case TK_ANCHOR_CENTER: + if (h > height_) + y += (h - height_) / 2; + + if (w > width_) + x += (w - width_) / 2; + break; + case TK_ANCHOR_S: + if (w > width_) + x += (w - width_) / 2; + + if (h > height_) + y += (h - height_); + break; + case TK_ANCHOR_NE: + if (w > width_) + x += w - width_; + break; + case TK_ANCHOR_E: + if (w > width_) + x += w - width_; + + if (h > height_) + y += (h - height_) / 2; + break; + case TK_ANCHOR_SE: + if (w > width_) { + x += w - width_; + } + if (h > height_) { + y += (h - height_); + } + break; + } + + x_ = x + ops->xPad; + y_ = y + ops->yPad; +} + +void Legend::selectEntry(Element* elemPtr) +{ + switch (flags & SELECT_TOGGLE) { + case SELECT_CLEAR: + deselectElement(elemPtr); + break; + case SELECT_SET: + selectElement(elemPtr); + break; + case SELECT_TOGGLE: + Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&selectTable_, (char*)elemPtr); + if (hPtr) + deselectElement(elemPtr); + else + selectElement(elemPtr); + break; + } +} + +void Legend::selectElement(Element* elemPtr) +{ + int isNew; + Tcl_HashEntry* hPtr = + Tcl_CreateHashEntry(&selectTable_, (char*)elemPtr, &isNew); + if (isNew) { + ChainLink* link = selected_->append(elemPtr); + Tcl_SetHashValue(hPtr, link); + } +} + +void Legend::deselectElement(Element* elemPtr) +{ + Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&selectTable_, (char*)elemPtr); + if (hPtr) { + ChainLink* link = (ChainLink*)Tcl_GetHashValue(hPtr); + selected_->deleteLink(link); + Tcl_DeleteHashEntry(hPtr); + } +} + + +int Legend::selectRange(Element *fromPtr, Element *toPtr) +{ + int isBefore=0; + for (ChainLink* linkPtr = fromPtr->link; linkPtr; linkPtr = linkPtr->next()) + if (linkPtr == toPtr->link) + isBefore =1; + + if (isBefore) { + for (ChainLink* link = fromPtr->link; link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + selectEntry(elemPtr); + if (link == toPtr->link) + break; + } + } + else { + for (ChainLink* link = fromPtr->link; link; link = Chain_PrevLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + selectEntry(elemPtr); + if (link == toPtr->link) + break; + } + } + + return TCL_OK; +} + +void Legend::clearSelection() +{ + LegendOptions* ops = (LegendOptions*)ops_; + + Tcl_DeleteHashTable(&selectTable_); + Tcl_InitHashTable(&selectTable_, TCL_ONE_WORD_KEYS); + selected_->reset(); + + if (ops->selectCmd) + eventuallyInvokeSelectCmd(); +} + +int Legend::entryIsSelected(Element* elemPtr) +{ + Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&selectTable_, (char*)elemPtr); + return (hPtr != NULL); +} + +int Legend::getElementFromObj(Tcl_Obj* objPtr, Element** elemPtrPtr) +{ + const char *string = Tcl_GetString(objPtr); + Element* elemPtr = NULL; + + if (!strcmp(string, "anchor")) + elemPtr = selAnchorPtr_; + else if (!strcmp(string, "current")) + elemPtr = (Element*)bindTable_->currentItem(); + else if (!strcmp(string, "first")) + elemPtr = getFirstElement(); + else if (!strcmp(string, "focus")) + elemPtr = focusPtr_; + else if (!strcmp(string, "last")) + elemPtr = getLastElement(); + else if (!strcmp(string, "end")) + elemPtr = getLastElement(); + else if (!strcmp(string, "next.row")) + elemPtr = getNextRow(focusPtr_); + else if (!strcmp(string, "next.column")) + elemPtr = getNextColumn(focusPtr_); + else if (!strcmp(string, "previous.row")) + elemPtr = getPreviousRow(focusPtr_); + else if (!strcmp(string, "previous.column")) + elemPtr = getPreviousColumn(focusPtr_); + else if (string[0] == '@') { + int x, y; + if (graphPtr_->getXY(string, &x, &y) != TCL_OK) + return TCL_ERROR; + + ClassId classId; + elemPtr = (Element*)pickEntry(x, y, &classId); + } + else { + if (graphPtr_->getElement(objPtr, &elemPtr) != TCL_OK) + return TCL_ERROR; + + if (!elemPtr->link) { + Tcl_AppendResult(graphPtr_->interp_, "bad legend index \"", string, "\"", + (char *)NULL); + return TCL_ERROR; + } + ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); + if (!elemOps->label) + elemPtr = NULL; + } + + *elemPtrPtr = elemPtr; + return TCL_OK; +} + +Element* Legend::getNextRow(Element* focusPtr) +{ + unsigned col = focusPtr->col_; + unsigned row = focusPtr->row_ + 1; + for (ChainLink* link = focusPtr->link; link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); + + if (!elemOps->label) + continue; + + if ((elemPtr->col_ == col) && (elemPtr->row_ == row)) + return elemPtr; + } + return NULL; +} + +Element* Legend::getNextColumn(Element* focusPtr) +{ + unsigned col = focusPtr->col_ + 1; + unsigned row = focusPtr->row_; + for (ChainLink* link = focusPtr->link; link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); + + if (!elemOps->label) + continue; + + if ((elemPtr->col_ == col) && (elemPtr->row_ == row)) + return elemPtr; + } + return NULL; +} + +Element* Legend::getPreviousRow(Element* focusPtr) +{ + unsigned col = focusPtr->col_; + unsigned row = focusPtr->row_ - 1; + for (ChainLink* link = focusPtr->link; link; link = Chain_PrevLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); + + if (!elemOps->label) + continue; + + if ((elemPtr->col_ == col) && (elemPtr->row_ == row)) + return elemPtr; + } + return NULL; +} + +Element* Legend::getPreviousColumn(Element* focusPtr) +{ + unsigned col = focusPtr->col_ - 1; + unsigned row = focusPtr->row_; + for (ChainLink* link = focusPtr->link; link; link = Chain_PrevLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); + + if (!elemOps->label) + continue; + + if ((elemPtr->col_ == col) && (elemPtr->row_ == row)) + return elemPtr; + } + return NULL; +} + +Element* Legend::getFirstElement() +{ + for (ChainLink* link = Chain_FirstLink(graphPtr_->elements_.displayList); + link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); + if (elemOps->label) + return elemPtr; + } + return NULL; +} + +Element* Legend::getLastElement() +{ + for (ChainLink* link = Chain_LastLink(graphPtr_->elements_.displayList); + link; link = Chain_PrevLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); + if (elemOps->label) + return elemPtr; + } + return NULL; +} + +ClientData Legend::pickEntry(int xx, int yy, ClassId* classIdPtr) +{ + LegendOptions* ops = (LegendOptions*)ops_; + + int ww = width_; + int hh = height_; + + if (titleHeight_ > 0) + yy -= titleHeight_ + ops->yPad; + + xx -= x_ + ops->borderWidth; + yy -= y_ + ops->borderWidth; + ww -= 2 * ops->borderWidth + 2*ops->xPad; + hh -= 2 * ops->borderWidth + 2*ops->yPad; + + // In the bounding box? if so, compute the index + if (xx >= 0 && xx < ww && yy >= 0 && yy < hh) { + int row = yy / entryHeight_; + int column = xx / entryWidth_; + int nn = (column * nRows_) + row; + + // Legend entries are stored in bottom-to-top + if (nn < nEntries_) { + int count = 0; + for (ChainLink* link = Chain_FirstLink(graphPtr_->elements_.displayList); + link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); + if (elemOps->label) { + if (count == nn) { + *classIdPtr = elemPtr->classId(); + return elemPtr; + } + count++; + } + } + } + } + + return NULL; +} + +// Support + +static int SelectionProc(ClientData clientData, int offset, char *buffer, + int maxBytes) +{ + Legend* legendPtr = (Legend*)clientData; + Graph* graphPtr = legendPtr->graphPtr_; + LegendOptions* ops = (LegendOptions*)legendPtr->ops(); + + if ((ops->exportSelection) == 0) + return -1; + + // Retrieve the names of the selected entries + Tcl_DString dString; + Tcl_DStringInit(&dString); + if (legendPtr->flags & SELECT_SORTED) { + for (ChainLink* link=Chain_FirstLink(legendPtr->selected_); + link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + Tcl_DStringAppend(&dString, elemPtr->name_, -1); + Tcl_DStringAppend(&dString, "\n", -1); + } + } + else { + for (ChainLink* link=Chain_FirstLink(graphPtr->elements_.displayList); + link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + if (legendPtr->entryIsSelected(elemPtr)) { + Tcl_DStringAppend(&dString, elemPtr->name_, -1); + Tcl_DStringAppend(&dString, "\n", -1); + } + } + } + + int nBytes = Tcl_DStringLength(&dString) - offset; + strncpy(buffer, Tcl_DStringValue(&dString) + offset, maxBytes); + Tcl_DStringFree(&dString); + buffer[maxBytes] = '\0'; + return MIN(nBytes, maxBytes); +} + +static void SelectCmdProc(ClientData clientData) +{ + Legend* legendPtr = (Legend*)clientData; + LegendOptions* ops = (LegendOptions*)legendPtr->ops(); + + Tcl_Preserve(legendPtr); + legendPtr->flags &= ~SELECT_PENDING; + if (ops->selectCmd) { + Tcl_Interp* interp = legendPtr->graphPtr_->interp_; + if (Tcl_GlobalEval(interp, ops->selectCmd) != TCL_OK) + Tcl_BackgroundError(interp); + } + Tcl_Release(legendPtr); +} + + + diff --git a/generic/tkbltGrLegd.h b/generic/tkbltGrLegd.h new file mode 100644 index 0000000..66ffbc1 --- /dev/null +++ b/generic/tkbltGrLegd.h @@ -0,0 +1,178 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrLegend_h__ +#define __BltGrLegend_h__ + +#include + +#include "tkbltGrMisc.h" +#include "tkbltGrText.h" + +namespace Blt { + class Graph; + class Pick; + class Element; + + /* + * Selection related flags: + * SELECT_PENDING A "selection" command idle task is pending. + * SELECT_CLEAR Clear selection flag of entry. + * SELECT_SET Set selection flag of entry. + * SELECT_TOGGLE Toggle selection flag of entry. + * Mask of selection set/clear/toggle flags. + * SELECT_SORTED Indicates if the entries in the selection + * should be sorted or displayed in the order + * they were selected. + */ + +#define SELECT_CLEAR (1<<24) +#define SELECT_PENDING (1<<25) +#define SELECT_SET (1<<26) +#define SELECT_SORTED (1<<27) +#define SELECT_TOGGLE (SELECT_SET | SELECT_CLEAR) + + typedef enum { + SELECT_MODE_SINGLE, SELECT_MODE_MULTIPLE + } SelectMode; + + typedef struct { + Tk_3DBorder activeBg; + XColor* activeFgColor; + int activeRelief; + Tk_3DBorder normalBg; + XColor* fgColor; + Tk_Anchor anchor; + int borderWidth; + int reqColumns; + int exportSelection; + Dashes focusDashes; + XColor* focusColor; + TextStyleOptions style; + int hide; + int ixPad; + int iyPad; + int xPad; + int yPad; + int raised; + int relief; + int reqRows; + int entryBW; + int selBW; + int xReq; + int yReq; + int position; + const char *selectCmd; + Tk_3DBorder selOutFocusBg; + Tk_3DBorder selInFocusBg; + XColor* selOutFocusFgColor; + XColor* selInFocusFgColor; + SelectMode selectMode; + int selRelief; + const char *title; + TextStyleOptions titleStyle; + } LegendOptions; + + class Legend : public Pick { + public: + enum Position {RIGHT, LEFT, TOP, BOTTOM, PLOT, XY}; + + protected: + Tk_OptionTable optionTable_; + void* ops_; + + GC focusGC_; + Tcl_HashTable selectTable_; + + public: + Graph* graphPtr_; + unsigned int flags; + + int width_; + int height_; + int x_; + int y_; + + int nEntries_; + int nColumns_; + int nRows_; + int entryWidth_; + int entryHeight_; + BindTable* bindTable_; + Element* focusPtr_; + Element* selAnchorPtr_; + Element* selMarkPtr_; + Chain* selected_; + int titleWidth_; + int titleHeight_; + + protected: + void setOrigin(); + Element* getNextRow(Element*); + Element* getNextColumn(Element*); + Element* getPreviousRow(Element*); + Element* getPreviousColumn(Element*); + Element* getFirstElement(); + Element* getLastElement(); + + public: + Legend(Graph*); + virtual ~Legend(); + + int configure(); + void map(int, int); + void draw(Drawable drawable); + void print(PSOutput* ps); + void eventuallyInvokeSelectCmd(); + + void removeElement(Element*); + int getElementFromObj(Tcl_Obj*, Element**); + + void selectEntry(Element*); + void selectElement(Element*); + void deselectElement(Element*); + int selectRange(Element*, Element*); + void clearSelection(); + int entryIsSelected(Element*); + + void* ops() {return ops_;} + Tk_OptionTable optionTable() {return optionTable_;} + + Position position() {return (Position)((LegendOptions*)ops_)->position;} + int isRaised() {return ((LegendOptions*)ops_)->raised;} + int isHidden() {return ((LegendOptions*)ops_)->hide;} + + ClientData pickEntry(int, int, ClassId*); + }; +}; + +#endif diff --git a/generic/tkbltGrLegdOp.C b/generic/tkbltGrLegdOp.C new file mode 100644 index 0000000..139d2f1 --- /dev/null +++ b/generic/tkbltGrLegdOp.C @@ -0,0 +1,496 @@ +/* + * 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 1993-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 +#include + +#include "tkbltGrBind.h" +#include "tkbltGraph.h" +#include "tkbltGrLegd.h" +#include "tkbltGrLegdOp.h" +#include "tkbltGrElem.h" + +using namespace Blt; + +static Tk_LostSelProc LostSelectionProc; + +static int LegendObjConfigure(Graph* graphPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Legend* legendPtr = graphPtr->legend_; + Tk_SavedOptions savedOptions; + int mask =0; + int error; + Tcl_Obj* errorResult; + + for (error=0; error<=1; error++) { + if (!error) { + if (Tk_SetOptions(interp, (char*)legendPtr->ops(), + legendPtr->optionTable(), + objc, objv, graphPtr->tkwin_, &savedOptions, &mask) + != TCL_OK) + continue; + } + else { + errorResult = Tcl_GetObjResult(interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + } + + if (legendPtr->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 != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "cget option"); + return TCL_ERROR; + } + + Legend* legendPtr = graphPtr->legend_; + Tcl_Obj* objPtr = Tk_GetOptionValue(interp, + (char*)legendPtr->ops(), + legendPtr->optionTable(), + objv[3], 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; + Legend* legendPtr = graphPtr->legend_; + if (objc <= 4) { + Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)legendPtr->ops(), + legendPtr->optionTable(), + (objc == 4) ? objv[3] : NULL, + graphPtr->tkwin_); + if (objPtr == NULL) + return TCL_ERROR; + else + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; + } + else + return LegendObjConfigure(graphPtr, interp, objc-3, objv+3); +} + +static int ActivateOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Legend* legendPtr = graphPtr->legend_; + LegendOptions* ops = (LegendOptions*)legendPtr->ops(); + + const char *string = Tcl_GetString(objv[2]); + int active = (string[0] == 'a') ? 1 : 0; + int redraw = 0; + for (int ii=3; iielements_.displayList); + link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + if (Tcl_StringMatch(elemPtr->name_, pattern)) { + if (active) { + if (!elemPtr->labelActive_) { + elemPtr->labelActive_ =1; + redraw = 1; + } + } + else { + if (elemPtr->labelActive_) { + elemPtr->labelActive_ =0; + redraw = 1; + } + } + } + } + } + + if (redraw && !ops->hide) { + graphPtr->flags |= LAYOUT; + graphPtr->eventuallyRedraw(); + } + + // List active elements in stacking order + Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + for (ChainLink* link = Chain_FirstLink(graphPtr->elements_.displayList); + link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + if (elemPtr->labelActive_) { + Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + } + Tcl_SetObjResult(interp, listObjPtr); + + return TCL_OK; +} + +static int BindOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + + if (objc == 3) { + Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + Tcl_HashSearch iter; + for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&graphPtr->elements_.tagTable, &iter); hPtr; hPtr = Tcl_NextHashEntry(&iter)) { + char* tagName = + (char*)Tcl_GetHashKey(&graphPtr->elements_.tagTable, hPtr); + Tcl_Obj *objPtr = Tcl_NewStringObj(tagName, -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; + } + + return graphPtr->legend_->bindTable_->configure(graphPtr->elementTag(Tcl_GetString(objv[3])), objc - 4, objv + 4); +} + +static int CurselectionOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Legend* legendPtr = graphPtr->legend_; + Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + if (legendPtr->flags & SELECT_SORTED) { + for (ChainLink* link = Chain_FirstLink(legendPtr->selected_); link; + link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + } + else { + // List of selected entries is in stacking order + for (ChainLink* link = Chain_FirstLink(graphPtr->elements_.displayList); + link; link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + + if (legendPtr->entryIsSelected(elemPtr)) { + Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + } + } + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; +} + +static int FocusOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Legend* legendPtr = graphPtr->legend_; + + legendPtr->focusPtr_ = NULL; + if (objc == 4) { + Element* elemPtr; + if (legendPtr->getElementFromObj(objv[3], &elemPtr) != TCL_OK) + return TCL_ERROR; + + if (elemPtr) { + legendPtr->focusPtr_ = elemPtr; + + legendPtr->bindTable_->focusItem_ = (ClientData)elemPtr; + legendPtr->bindTable_->focusContext_ = elemPtr->classId(); + } + } + + graphPtr->flags |= CACHE; + graphPtr->eventuallyRedraw(); + + if (legendPtr->focusPtr_) + Tcl_SetStringObj(Tcl_GetObjResult(interp),legendPtr->focusPtr_->name_,-1); + + return TCL_OK; +} + +static int GetOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<3) + return TCL_ERROR; + + Legend* legendPtr = graphPtr->legend_; + LegendOptions* ops = (LegendOptions*)legendPtr->ops(); + + if (((ops->hide) == 0) && (legendPtr->nEntries_ > 0)) { + Element* elemPtr; + + if (legendPtr->getElementFromObj(objv[3], &elemPtr) != TCL_OK) + return TCL_ERROR; + + if (elemPtr) + Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->name_, -1); + } + return TCL_OK; +} + +const Ensemble Blt::legendEnsemble[] = { + {"activate", ActivateOp, 0}, + {"bind", BindOp, 0}, + {"cget", CgetOp, 0}, + {"configure", ConfigureOp, 0}, + {"curselection", CurselectionOp, 0}, + {"deactivate", ActivateOp, 0}, + {"focus", FocusOp, 0}, + {"get", GetOp, 0}, + {"selection", 0, selectionEnsemble}, + { 0,0,0 } +}; + +// Selection Ops + +static int SelectionAnchorOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Legend* legendPtr = graphPtr->legend_; + Element* elemPtr; + + if (legendPtr->getElementFromObj(objv[4], &elemPtr) != TCL_OK) + return TCL_ERROR; + + // Set both the anchor and the mark. Indicates that a single entry + // is selected + legendPtr->selAnchorPtr_ = elemPtr; + legendPtr->selMarkPtr_ = NULL; + if (elemPtr) + Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->name_, -1); + + graphPtr->flags |= CACHE; + graphPtr->eventuallyRedraw(); + + return TCL_OK; +} + +static int SelectionClearallOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Legend* legendPtr = graphPtr->legend_; + legendPtr->clearSelection(); + + graphPtr->flags |= CACHE; + graphPtr->eventuallyRedraw(); + + return TCL_OK; +} + +static int SelectionIncludesOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Legend* legendPtr = graphPtr->legend_; + Element* elemPtr; + if (legendPtr->getElementFromObj(objv[4], &elemPtr) != TCL_OK) + return TCL_ERROR; + + int boo = legendPtr->entryIsSelected(elemPtr); + Tcl_SetBooleanObj(Tcl_GetObjResult(interp), boo); + return TCL_OK; +} + +static int SelectionMarkOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Legend* legendPtr = graphPtr->legend_; + LegendOptions* ops = (LegendOptions*)legendPtr->ops(); + Element* elemPtr; + + if (legendPtr->getElementFromObj(objv[4], &elemPtr) != TCL_OK) + return TCL_ERROR; + + if (legendPtr->selAnchorPtr_ == NULL) { + Tcl_AppendResult(interp, "selection anchor must be set first", NULL); + return TCL_ERROR; + } + + if (legendPtr->selMarkPtr_ != elemPtr) { + // Deselect entry from the list all the way back to the anchor + ChainLink *link, *next; + for (link = Chain_LastLink(legendPtr->selected_); link; link = next) { + next = Chain_PrevLink(link); + Element *selectPtr = (Element*)Chain_GetValue(link); + if (selectPtr == legendPtr->selAnchorPtr_) + break; + + legendPtr->deselectElement(selectPtr); + } + + legendPtr->flags &= ~SELECT_TOGGLE; + legendPtr->flags |= SELECT_SET; + legendPtr->selectRange(legendPtr->selAnchorPtr_, elemPtr); + Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->name_, -1); + legendPtr->selMarkPtr_ = elemPtr; + + if (ops->selectCmd) + legendPtr->eventuallyInvokeSelectCmd(); + + graphPtr->flags |= CACHE; + graphPtr->eventuallyRedraw(); + } + return TCL_OK; +} + +static int SelectionPresentOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Legend* legendPtr = graphPtr->legend_; + int boo = (Chain_GetLength(legendPtr->selected_) > 0); + Tcl_SetBooleanObj(Tcl_GetObjResult(interp), boo); + return TCL_OK; +} + +static int SelectionSetOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Legend* legendPtr = graphPtr->legend_; + LegendOptions* ops = (LegendOptions*)legendPtr->ops(); + + legendPtr->flags &= ~SELECT_TOGGLE; + const char* string = Tcl_GetString(objv[3]); + switch (string[0]) { + case 's': + legendPtr->flags |= SELECT_SET; + break; + case 'c': + legendPtr->flags |= SELECT_CLEAR; + break; + case 't': + legendPtr->flags |= SELECT_TOGGLE; + break; + } + + Element *firstPtr; + if (legendPtr->getElementFromObj(objv[4], &firstPtr) != TCL_OK) + return TCL_ERROR; + ElementOptions* eops = (ElementOptions*)firstPtr->ops(); + + if ((eops->hide) && ((legendPtr->flags & SELECT_CLEAR)==0)) { + Tcl_AppendResult(interp, "can't select hidden node \"", + Tcl_GetString(objv[4]), "\"", (char *)NULL); + return TCL_ERROR; + } + + Element* lastPtr = firstPtr; + if (objc > 5) { + if (legendPtr->getElementFromObj(objv[5], &lastPtr) != TCL_OK) + return TCL_ERROR; + ElementOptions* eops = (ElementOptions*)firstPtr->ops(); + + if (eops->hide && ((legendPtr->flags & SELECT_CLEAR) == 0)) { + Tcl_AppendResult(interp, "can't select hidden node \"", + Tcl_GetString(objv[5]), "\"", (char *)NULL); + return TCL_ERROR; + } + } + + if (firstPtr == lastPtr) + legendPtr->selectEntry(firstPtr); + else + legendPtr->selectRange(firstPtr, lastPtr); + + // Set both the anchor and the mark. Indicates that a single entry is + // selected + if (legendPtr->selAnchorPtr_ == NULL) + legendPtr->selAnchorPtr_ = firstPtr; + + if (ops->exportSelection) + Tk_OwnSelection(graphPtr->tkwin_, XA_PRIMARY, LostSelectionProc, legendPtr); + + if (ops->selectCmd) + legendPtr->eventuallyInvokeSelectCmd(); + + graphPtr->flags |= CACHE; + graphPtr->eventuallyRedraw(); + + return TCL_OK; +} + +const Ensemble Blt::selectionEnsemble[] = { + {"anchor", SelectionAnchorOp, 0}, + {"clear", SelectionSetOp, 0}, + {"clearall", SelectionClearallOp, 0}, + {"includes", SelectionIncludesOp, 0}, + {"mark", SelectionMarkOp, 0}, + {"present", SelectionPresentOp, 0}, + {"set", SelectionSetOp, 0}, + {"toggle", SelectionSetOp, 0}, + { 0,0,0 } +}; + +// Support + +static void LostSelectionProc(ClientData clientData) +{ + Legend* legendPtr = (Legend*)clientData; + LegendOptions* ops = (LegendOptions*)legendPtr->ops(); + Graph* graphPtr = legendPtr->graphPtr_; + + if (ops->exportSelection) + legendPtr->clearSelection(); + + graphPtr->flags |= CACHE; + graphPtr->eventuallyRedraw(); +} + + + + diff --git a/generic/tkbltGrLegdOp.h b/generic/tkbltGrLegdOp.h new file mode 100644 index 0000000..6369a2b --- /dev/null +++ b/generic/tkbltGrLegdOp.h @@ -0,0 +1,40 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrLegdOp_h__ +#define __BltGrLegdOp_h__ + +#include "tkbltGraph.h" + +namespace Blt { + extern const Ensemble legendEnsemble[]; + extern const Ensemble selectionEnsemble[]; +}; + +#endif diff --git a/generic/tkbltGrMarker.C b/generic/tkbltGrMarker.C new file mode 100644 index 0000000..6f701f8 --- /dev/null +++ b/generic/tkbltGrMarker.C @@ -0,0 +1,177 @@ +/* + * 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 1993-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 +#include + +#include + +#include "tkbltGraph.h" +#include "tkbltGrBind.h" +#include "tkbltGrMarker.h" +#include "tkbltGrAxis.h" +#include "tkbltGrMisc.h" + +using namespace Blt; + +Marker::Marker(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) +{ + optionTable_ =NULL; + ops_ =NULL; + + graphPtr_ =graphPtr; + name_ = dupstr(name); + hashPtr_ = hPtr; + link =NULL; + flags =0; + clipped_ =0; +} + +Marker::~Marker() +{ + graphPtr_->bindTable_->deleteBindings(this); + + if (link) + graphPtr_->markers_.displayList->deleteLink(link); + + if (hashPtr_) + Tcl_DeleteHashEntry(hashPtr_); + + delete [] name_; + + Tk_FreeConfigOptions((char*)ops_, optionTable_, graphPtr_->tkwin_); + free(ops_); +} + +double Marker::HMap(Axis *axisPtr, double x) +{ + AxisOptions* ops = (AxisOptions*)axisPtr->ops(); + + if (x == DBL_MAX) + x = 1.0; + else if (x == -DBL_MAX) + x = 0.0; + else { + if (ops->logScale) { + if (x > 0.0) + x = log10(x); + else if (x < 0.0) + x = 0.0; + } + x = (x - axisPtr->axisRange_.min) * axisPtr->axisRange_.scale; + } + if (ops->descending) + x = 1.0 - x; + + // Horizontal transformation + return (x * axisPtr->screenRange_ + axisPtr->screenMin_); +} + +double Marker::VMap(Axis *axisPtr, double y) +{ + AxisOptions* ops = (AxisOptions*)axisPtr->ops(); + + if (y == DBL_MAX) + y = 1.0; + else if (y == -DBL_MAX) + y = 0.0; + else { + if (ops->logScale) { + if (y > 0.0) + y = log10(y); + else if (y < 0.0) + y = 0.0; + } + y = (y - axisPtr->axisRange_.min) * axisPtr->axisRange_.scale; + } + if (ops->descending) + y = 1.0 - y; + + // Vertical transformation + return (((1.0 - y) * axisPtr->screenRange_) + axisPtr->screenMin_); +} + +Point2d Marker::mapPoint(Point2d* pointPtr, Axis* xAxis, Axis* yAxis) +{ + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + Point2d result; + if (gops->inverted) { + result.x = HMap(yAxis, pointPtr->y); + result.y = VMap(xAxis, pointPtr->x); + } + else { + result.x = HMap(xAxis, pointPtr->x); + result.y = VMap(yAxis, pointPtr->y); + } + + return result; +} + +int Marker::boxesDontOverlap(Graph* graphPtr_, Region2d *extsPtr) +{ + return (((double)graphPtr_->right_ < extsPtr->left) || + ((double)graphPtr_->bottom_ < extsPtr->top) || + (extsPtr->right < (double)graphPtr_->left_) || + (extsPtr->bottom < (double)graphPtr_->top_)); +} + +int Marker::regionInPolygon(Region2d *regionPtr, Point2d *points, int nPoints, + int enclosed) +{ + if (enclosed) { + // All points of the polygon must be inside the rectangle. + for (Point2d *pp = points, *pend = pp + nPoints; pp < pend; pp++) { + if ((pp->x < regionPtr->left) || (pp->x > regionPtr->right) || + (pp->y < regionPtr->top) || (pp->y > regionPtr->bottom)) { + return 0; /* One point is exterior. */ + } + } + return 1; + } + else { + // If any segment of the polygon clips the bounding region, the + // polygon overlaps the rectangle. + points[nPoints] = points[0]; + for (Point2d *pp = points, *pend = pp + nPoints; pp < pend; pp++) { + Point2d p = *pp; + Point2d q = *(pp + 1); + if (lineRectClip(regionPtr, &p, &q)) + return 1; + } + + // Otherwise the polygon and rectangle are either disjoint or + // enclosed. Check if one corner of the rectangle is inside the polygon. + Point2d r; + r.x = regionPtr->left; + r.y = regionPtr->top; + + return pointInPolygon(&r, points, nPoints); + } +} + diff --git a/generic/tkbltGrMarker.h b/generic/tkbltGrMarker.h new file mode 100644 index 0000000..573357d --- /dev/null +++ b/generic/tkbltGrMarker.h @@ -0,0 +1,103 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrMarker_h__ +#define __BltGrMarker_h__ + +#include + +#include "tkbltChain.h" + +#include "tkbltGrMisc.h" +#include "tkbltGrPSOutput.h" + +namespace Blt { + class Graph; + class Postscript; + class Axis; + + typedef struct { + Point2d* points; + int num; + } Coords; + + typedef struct { + const char** tags; + Coords* worldPts; + const char* elemName; + Axis* xAxis; + Axis* yAxis; + int hide; + int drawUnder; + int xOffset; + int yOffset; + } MarkerOptions; + + class Marker { + protected: + Tk_OptionTable optionTable_; + void* ops_; + + public: + Graph* graphPtr_; + const char *name_; + Tcl_HashEntry* hashPtr_; + ChainLink* link; + unsigned int flags; + int clipped_; + + protected: + double HMap(Axis*, double); + double VMap(Axis*, double); + Point2d mapPoint(Point2d*, Axis*, Axis*); + int boxesDontOverlap(Graph*, Region2d*); + int regionInPolygon(Region2d *extsPtr, Point2d *points, + int nPoints, int enclosed); + + public: + Marker(Graph*, const char*, Tcl_HashEntry*); + virtual ~Marker(); + + virtual int configure() =0; + virtual void draw(Drawable) =0; + virtual void map() =0; + virtual int pointIn(Point2d*) =0; + virtual int regionIn(Region2d*, int) =0; + virtual void print(PSOutput*) =0; + + virtual ClassId classId() =0; + virtual const char* className() =0; + virtual const char* typeName() =0; + + Tk_OptionTable optionTable() {return optionTable_;} + void* ops() {return ops_;} + }; +}; + +#endif diff --git a/generic/tkbltGrMarkerLine.C b/generic/tkbltGrMarkerLine.C new file mode 100644 index 0000000..82c9ab8 --- /dev/null +++ b/generic/tkbltGrMarkerLine.C @@ -0,0 +1,298 @@ +/* + * 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 1993-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 +#include + +#include + +#include "tkbltGraph.h" +#include "tkbltGrMarkerLine.h" +#include "tkbltGrMarkerOption.h" +#include "tkbltGrMisc.h" +#include "tkbltGrDef.h" +#include "tkbltConfig.h" +#include "tkbltGrPSOutput.h" + +using namespace Blt; + +#define BOUND(x, lo, hi) (((x) > (hi)) ? (hi) : ((x) < (lo)) ? (lo) : (x)) + +static Tk_OptionSpec optionSpecs[] = { + {TK_OPTION_CUSTOM, "-bindtags", "bindTags", "BindTags", + "Line all", -1, Tk_Offset(LineMarkerOptions, tags), + TK_OPTION_NULL_OK, &listObjOption, 0}, + {TK_OPTION_CUSTOM, "-cap", "cap", "Cap", + "butt", -1, Tk_Offset(LineMarkerOptions, capStyle), + 0, &capStyleObjOption, 0}, + {TK_OPTION_CUSTOM, "-coords", "coords", "Coords", + NULL, -1, Tk_Offset(LineMarkerOptions, worldPts), + TK_OPTION_NULL_OK, &coordsObjOption, 0}, + {TK_OPTION_CUSTOM, "-dashes", "dashes", "Dashes", + NULL, -1, Tk_Offset(LineMarkerOptions, dashes), + TK_OPTION_NULL_OK, &dashesObjOption, 0}, + {TK_OPTION_PIXELS, "-dashoffset", "dashOffset", "DashOffset", + "0", -1, Tk_Offset(LineMarkerOptions, dashes.offset), 0, NULL, 0}, + {TK_OPTION_STRING, "-element", "element", "Element", + NULL, -1, Tk_Offset(LineMarkerOptions, elemName), + TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_COLOR, "-fill", "fill", "Fill", + NULL, -1, Tk_Offset(LineMarkerOptions, fillColor), + TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_CUSTOM, "-join", "join", "Join", + "miter", -1, Tk_Offset(LineMarkerOptions, joinStyle), + 0, &joinStyleObjOption, 0}, + {TK_OPTION_PIXELS, "-linewidth", "lineWidth", "LineWidth", + "1", -1, Tk_Offset(LineMarkerOptions, lineWidth), 0, NULL, 0}, + {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", + "no", -1, Tk_Offset(LineMarkerOptions, hide), 0, NULL, 0}, + {TK_OPTION_CUSTOM, "-mapx", "mapX", "MapX", + "x", -1, Tk_Offset(LineMarkerOptions, xAxis), 0, &xAxisObjOption, 0}, + {TK_OPTION_CUSTOM, "-mapy", "mapY", "MapY", + "y", -1, Tk_Offset(LineMarkerOptions, yAxis), 0, &yAxisObjOption, 0}, + {TK_OPTION_COLOR, "-outline", "outline", "Outline", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(LineMarkerOptions, outlineColor), + TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_BOOLEAN, "-under", "under", "Under", + "no", -1, Tk_Offset(LineMarkerOptions, drawUnder), 0, NULL, CACHE}, + {TK_OPTION_PIXELS, "-xoffset", "xOffset", "XOffset", + "0", -1, Tk_Offset(LineMarkerOptions, xOffset), 0, NULL, 0}, + {TK_OPTION_PIXELS, "-yoffset", "yOffset", "YOffset", + "0", -1, Tk_Offset(LineMarkerOptions, yOffset), 0, NULL, 0}, + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} +}; + +LineMarker::LineMarker(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) + : Marker(graphPtr, name, hPtr) +{ + ops_ = (LineMarkerOptions*)calloc(1, sizeof(LineMarkerOptions)); + optionTable_ = Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); + + gc_ =NULL; + segments_ =NULL; + nSegments_ =0; +} + +LineMarker::~LineMarker() +{ + if (gc_) + graphPtr_->freePrivateGC(gc_); + delete [] segments_; +} + +int LineMarker::configure() +{ + LineMarkerOptions* ops = (LineMarkerOptions*)ops_; + + unsigned long gcMask = (GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle); + XGCValues gcValues; + if (ops->outlineColor) { + gcMask |= GCForeground; + gcValues.foreground = ops->outlineColor->pixel; + } + if (ops->fillColor) { + gcMask |= GCBackground; + gcValues.background = ops->fillColor->pixel; + } + gcValues.cap_style = ops->capStyle; + gcValues.join_style = ops->joinStyle; + gcValues.line_width = ops->lineWidth; + gcValues.line_style = LineSolid; + if (LineIsDashed(ops->dashes)) { + gcValues.line_style = + (gcMask & GCBackground) ? LineDoubleDash : LineOnOffDash; + } + + GC newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); + if (gc_) + graphPtr_->freePrivateGC(gc_); + + if (LineIsDashed(ops->dashes)) + graphPtr_->setDashes(newGC, &ops->dashes); + gc_ = newGC; + + return TCL_OK; +} + +void LineMarker::draw(Drawable drawable) +{ + if (nSegments_ > 0) + graphPtr_->drawSegments(drawable, gc_, segments_, nSegments_); +} + +void LineMarker::map() +{ + LineMarkerOptions* ops = (LineMarkerOptions*)ops_; + + delete [] segments_; + segments_ = NULL; + nSegments_ = 0; + + if (!ops->worldPts || (ops->worldPts->num < 2)) + return; + + Region2d extents; + graphPtr_->extents(&extents); + + // Allow twice the number of world coordinates. The line will represented + // as series of line segments, not one continous polyline. This is + // because clipping against the plot area may chop the line into several + // disconnected segments. + + Segment2d* segments = new Segment2d[ops->worldPts->num]; + Point2d* srcPtr = ops->worldPts->points; + Point2d p = mapPoint(srcPtr, ops->xAxis, ops->yAxis); + p.x += ops->xOffset; + p.y += ops->yOffset; + + Segment2d* segPtr = segments; + Point2d* pend; + for (srcPtr++, pend = ops->worldPts->points + ops->worldPts->num; + srcPtr < pend; srcPtr++) { + Point2d next = mapPoint(srcPtr, ops->xAxis, ops->yAxis); + next.x += ops->xOffset; + next.y += ops->yOffset; + Point2d q = next; + + if (lineRectClip(&extents, &p, &q)) { + segPtr->p = p; + segPtr->q = q; + segPtr++; + } + p = next; + } + nSegments_ = segPtr - segments; + segments_ = segments; + clipped_ = (nSegments_ == 0); +} + +int LineMarker::pointIn(Point2d *samplePtr) +{ + GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; + return pointInSegments(samplePtr, segments_, nSegments_, + (double)gops->search.halo); +} + +int LineMarker::pointInSegments(Point2d* samplePtr, Segment2d* segments, + int nSegments, double halo) +{ + double minDist = DBL_MAX; + for (Segment2d *sp = segments, *send = sp + nSegments; sp < send; sp++) { + Point2d t = getProjection((int)samplePtr->x, (int)samplePtr->y, + &sp->p, &sp->q); + double right; + double left; + if (sp->p.x > sp->q.x) { + right = sp->p.x; + left = sp->q.x; + } + else { + right = sp->q.x; + left = sp->p.x; + } + + double top; + double bottom; + if (sp->p.y > sp->q.y) { + bottom = sp->p.y; + top = sp->q.y; + } + else { + bottom = sp->q.y; + top = sp->p.y; + } + + Point2d p; + p.x = BOUND(t.x, left, right); + p.y = BOUND(t.y, top, bottom); + + double dist = hypot(p.x - samplePtr->x, p.y - samplePtr->y); + if (dist < minDist) + minDist = dist; + } + + return (minDist < halo); +} + +int LineMarker::regionIn(Region2d *extsPtr, int enclosed) +{ + LineMarkerOptions* ops = (LineMarkerOptions*)ops_; + + if (!ops->worldPts || ops->worldPts->num < 2) + return 0; + + if (enclosed) { + for (Point2d *pp = ops->worldPts->points, *pend = pp + ops->worldPts->num; + pp < pend; pp++) { + Point2d p = mapPoint(pp, ops->xAxis, ops->yAxis); + if ((p.x < extsPtr->left) && (p.x > extsPtr->right) && + (p.y < extsPtr->top) && (p.y > extsPtr->bottom)) { + return 0; + } + } + return 1; + } + else { + int count = 0; + for (Point2d *pp=ops->worldPts->points, *pend=pp+(ops->worldPts->num - 1); + pp < pend; pp++) { + Point2d p = mapPoint(pp, ops->xAxis, ops->yAxis); + Point2d q = mapPoint(pp + 1, ops->xAxis, ops->yAxis); + if (lineRectClip(extsPtr, &p, &q)) + count++; + } + return (count > 0); /* At least 1 segment passes through + * region. */ + } +} + +void LineMarker::print(PSOutput* psPtr) +{ + LineMarkerOptions* ops = (LineMarkerOptions*)ops_; + + if (nSegments_ > 0) { + psPtr->setLineAttributes(ops->outlineColor, ops->lineWidth, + &ops->dashes, ops->capStyle, ops->joinStyle); + if ((LineIsDashed(ops->dashes)) && (ops->fillColor)) { + psPtr->append("/DashesProc {\n gsave\n "); + psPtr->setBackground(ops->fillColor); + psPtr->append(" "); + psPtr->setDashes(NULL); + psPtr->append("stroke\n"); + psPtr->append("grestore\n"); + psPtr->append("} def\n"); + } + else + psPtr->append("/DashesProc {} def\n"); + + psPtr->printSegments(segments_, nSegments_); + } +} + + diff --git a/generic/tkbltGrMarkerLine.h b/generic/tkbltGrMarkerLine.h new file mode 100644 index 0000000..3191951 --- /dev/null +++ b/generic/tkbltGrMarkerLine.h @@ -0,0 +1,82 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrMarkerLine_h__ +#define __BltGrMarkerLine_h__ + +#include "tkbltGrMarker.h" + +namespace Blt { + + typedef struct { + const char** tags; + Coords* worldPts; + const char* elemName; + Axis* xAxis; + Axis* yAxis; + int hide; + int drawUnder; + int xOffset; + int yOffset; + + int capStyle; + Dashes dashes; + XColor* fillColor; + int joinStyle; + int lineWidth; + XColor* outlineColor; + } LineMarkerOptions; + + class LineMarker : public Marker { + protected: + GC gc_; + Segment2d* segments_; + int nSegments_; + + protected: + int configure(); + void draw(Drawable); + void map(); + int pointIn(Point2d*); + int regionIn(Region2d*, int); + void print(PSOutput*); + int pointInSegments(Point2d *samplePtr, Segment2d *segments, + int nSegments, double halo); + + public: + LineMarker(Graph*, const char*, Tcl_HashEntry*); + virtual ~LineMarker(); + + ClassId classId() {return CID_MARKER_LINE;} + const char* className() {return "LineMarker";} + const char* typeName() {return "line";} + }; +}; + +#endif diff --git a/generic/tkbltGrMarkerOp.C b/generic/tkbltGrMarkerOp.C new file mode 100644 index 0000000..20933ab --- /dev/null +++ b/generic/tkbltGrMarkerOp.C @@ -0,0 +1,458 @@ +/* + * 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 1993-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 + +#include "tkbltGrBind.h" +#include "tkbltGraph.h" +#include "tkbltGrElem.h" +#include "tkbltGrMarkerOp.h" +#include "tkbltGrMarker.h" +#include "tkbltGrMarkerLine.h" +#include "tkbltGrMarkerPolygon.h" +#include "tkbltGrMarkerText.h" + +using namespace Blt; + +static int GetMarkerFromObj(Tcl_Interp* interp, Graph* graphPtr, + Tcl_Obj* objPtr, Marker** markerPtrPtr); + +#define FIND_ENCLOSED (1<<0) +#define FIND_OVERLAPPING (1<<1) + +static int MarkerObjConfigure( Graph* graphPtr,Marker* markerPtr, + 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*)markerPtr->ops(), + markerPtr->optionTable(), + objc, objv, graphPtr->tkwin_, &savedOptions, &mask) + != TCL_OK) + continue; + } + else { + errorResult = Tcl_GetObjResult(interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + } + + markerPtr->flags |= MAP_ITEM; + if (markerPtr->configure() != TCL_OK) + return TCL_ERROR; + + MarkerOptions* ops = (MarkerOptions*)markerPtr->ops(); + if (ops->drawUnder) + graphPtr->flags |= CACHE; + 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 CreateMarker(Graph* graphPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + int offset = 5; + const char* name =NULL; + ostringstream str; + if (objc == 4) { + offset = 4; + str << "marker" << graphPtr->nextMarkerId_++ << ends; + name = dupstr(str.str().c_str()); + } + else { + name = dupstr(Tcl_GetString(objv[4])); + if (name[0] == '-') { + delete [] name; + offset = 4; + str << "marker" << graphPtr->nextMarkerId_++ << ends; + name = dupstr(str.str().c_str()); + } + } + + int isNew; + Tcl_HashEntry* hPtr = + Tcl_CreateHashEntry(&graphPtr->markers_.table, name, &isNew); + if (!isNew) { + Tcl_AppendResult(graphPtr->interp_, "marker \"", name, + "\" already exists in \"", Tcl_GetString(objv[0]), + "\"", NULL); + return TCL_ERROR; + } + + const char* type = Tcl_GetString(objv[3]); + Marker* markerPtr; + if (!strcmp(type, "line")) + markerPtr = new LineMarker(graphPtr, name, hPtr); + else if (!strcmp(type, "polygon")) + markerPtr = new PolygonMarker(graphPtr, name, hPtr); + else if (!strcmp(type, "text")) + markerPtr = new TextMarker(graphPtr, name, hPtr); + else { + Tcl_AppendResult(interp, "unknown marker type ", type, NULL); + return TCL_ERROR; + } + + Tcl_SetHashValue(hPtr, markerPtr); + + if ((Tk_InitOptions(graphPtr->interp_, (char*)markerPtr->ops(), markerPtr->optionTable(), graphPtr->tkwin_) != TCL_OK) || (MarkerObjConfigure(graphPtr, markerPtr, interp, objc-offset, objv+offset) != TCL_OK)) { + delete markerPtr; + return TCL_ERROR; + } + + // Unlike elements, new markers are drawn on top of old markers + markerPtr->link = graphPtr->markers_.displayList->prepend(markerPtr); + + Tcl_SetStringObj(Tcl_GetObjResult(interp), name, -1); + + delete [] name; + return TCL_OK; +} + +static int CgetOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Marker* markerPtr; + if (GetMarkerFromObj(interp, graphPtr, objv[3], &markerPtr) != TCL_OK) + return TCL_ERROR; + + Tcl_Obj* objPtr = Tk_GetOptionValue(interp, + (char*)markerPtr->ops(), + markerPtr->optionTable(), + objv[4], 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<4) + return TCL_ERROR; + + Marker* markerPtr; + if (GetMarkerFromObj(interp, graphPtr, objv[3], &markerPtr) != TCL_OK) + return TCL_ERROR; + + if (objc <= 5) { + Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)markerPtr->ops(), + markerPtr->optionTable(), + (objc == 5) ? objv[4] : NULL, + graphPtr->tkwin_); + if (objPtr == NULL) + return TCL_ERROR; + else + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; + } + else + return MarkerObjConfigure(graphPtr, markerPtr, interp, objc-4, objv+4); +} + +static int BindOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc == 3) { + Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + Tcl_HashSearch iter; + for (Tcl_HashEntry* hp = + Tcl_FirstHashEntry(&graphPtr->markers_.tagTable, &iter); + hp; hp = Tcl_NextHashEntry(&iter)) { + + const char* tag = + (const char*)Tcl_GetHashKey(&graphPtr->markers_.tagTable, hp); + Tcl_Obj* objPtr = Tcl_NewStringObj(tag, -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; + } + + return graphPtr->bindTable_->configure(graphPtr->markerTag(Tcl_GetString(objv[3])), objc - 4, objv + 4); +} + +static int CreateOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (CreateMarker(graphPtr, interp, objc, objv) != TCL_OK) + return TCL_ERROR; + // set in CreateMarker + // Tcl_SetObjResult(interp, objv[3]); + + graphPtr->flags |= CACHE; + graphPtr->eventuallyRedraw(); + + return TCL_OK; +} + +static int DeleteOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + for (int ii=3; iitkwin_), "\"", NULL); + return TCL_ERROR; + } + delete markerPtr; + } + + graphPtr->flags |= CACHE; + graphPtr->eventuallyRedraw(); + + return TCL_OK; +} + +static int ExistsOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Tcl_HashEntry* hPtr = + Tcl_FindHashEntry(&graphPtr->markers_.table, Tcl_GetString(objv[3])); + Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (hPtr != NULL)); + + return TCL_OK; +} + +static int FindOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + const char* string = Tcl_GetString(objv[3]); + int mode; + if (strcmp(string, "enclosed") == 0) + mode = FIND_ENCLOSED; + else if (strcmp(string, "overlapping") == 0) + mode = FIND_OVERLAPPING; + else { + Tcl_AppendResult(interp, "bad search type \"", string, + ": should be \"enclosed\", or \"overlapping\"", + NULL); + return TCL_ERROR; + } + + int left, right, top, bottom; + if ((Tcl_GetIntFromObj(interp, objv[4], &left) != TCL_OK) || + (Tcl_GetIntFromObj(interp, objv[5], &top) != TCL_OK) || + (Tcl_GetIntFromObj(interp, objv[6], &right) != TCL_OK) || + (Tcl_GetIntFromObj(interp, objv[7], &bottom) != TCL_OK)) { + return TCL_ERROR; + } + + Region2d extents; + if (left < right) { + extents.left = (double)left; + extents.right = (double)right; + } + else { + extents.left = (double)right; + extents.right = (double)left; + } + if (top < bottom) { + extents.top = (double)top; + extents.bottom = (double)bottom; + } + else { + extents.top = (double)bottom; + extents.bottom = (double)top; + } + + int enclosed = (mode == FIND_ENCLOSED); + for (ChainLink* link = Chain_FirstLink(graphPtr->markers_.displayList); + link; link = Chain_NextLink(link)) { + Marker* markerPtr = (Marker*)Chain_GetValue(link); + MarkerOptions* ops = (MarkerOptions*)markerPtr->ops(); + if (ops->hide) + continue; + + if (graphPtr->isElementHidden(markerPtr)) + continue; + + if (markerPtr->regionIn(&extents, enclosed)) { + Tcl_Obj* objPtr = Tcl_GetObjResult(interp); + Tcl_SetStringObj(objPtr, markerPtr->name_, -1); + return TCL_OK; + } + } + + Tcl_SetStringObj(Tcl_GetObjResult(interp), "", -1); + return TCL_OK; +} + +static int NamesOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + if (objc == 3) { + for (ChainLink* link=Chain_FirstLink(graphPtr->markers_.displayList); + link; link = Chain_NextLink(link)) { + Marker* markerPtr = (Marker*)Chain_GetValue(link); + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewStringObj(markerPtr->name_, -1)); + } + } + else { + for (ChainLink* link=Chain_FirstLink(graphPtr->markers_.displayList); + link; link = Chain_NextLink(link)) { + Marker* markerPtr = (Marker*)Chain_GetValue(link); + for (int ii = 3; iiname_, pattern)) { + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewStringObj(markerPtr->name_, -1)); + break; + } + } + } + } + + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; +} + +static int RelinkOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Marker* markerPtr; + if (GetMarkerFromObj(interp, graphPtr, objv[3], &markerPtr) != TCL_OK) + return TCL_ERROR; + + Marker* placePtr =NULL; + if (objc == 5) + if (GetMarkerFromObj(interp, graphPtr, objv[4], &placePtr) != TCL_OK) + return TCL_ERROR; + + ChainLink* link = markerPtr->link; + graphPtr->markers_.displayList->unlinkLink(markerPtr->link); + + ChainLink* place = placePtr ? placePtr->link : NULL; + + const char* string = Tcl_GetString(objv[2]); + if (string[0] == 'l') + graphPtr->markers_.displayList->linkAfter(link, place); + else + graphPtr->markers_.displayList->linkBefore(link, place); + + graphPtr->flags |= CACHE; + graphPtr->eventuallyRedraw(); + + return TCL_OK; +} + +static int TypeOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Marker* markerPtr; + if (GetMarkerFromObj(interp, graphPtr, objv[3], &markerPtr) != TCL_OK) + return TCL_ERROR; + + Tcl_SetStringObj(Tcl_GetObjResult(interp), markerPtr->typeName(), -1); + return TCL_OK; +} + +const Ensemble Blt::markerEnsemble[] = { + {"bind", BindOp, 0}, + {"cget", CgetOp, 0}, + {"configure", ConfigureOp, 0}, + {"create", CreateOp, 0}, + {"delete", DeleteOp, 0}, + {"exists", ExistsOp, 0}, + {"find", FindOp, 0}, + {"lower", RelinkOp, 0}, + {"names", NamesOp, 0}, + {"raise", RelinkOp, 0}, + {"type", TypeOp, 0}, + { 0,0,0 } +}; + +// Support + +static int GetMarkerFromObj(Tcl_Interp* interp, Graph* graphPtr, + Tcl_Obj *objPtr, Marker** markerPtrPtr) +{ + const char* string = Tcl_GetString(objPtr); + Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&graphPtr->markers_.table, string); + if (hPtr) { + *markerPtrPtr = (Marker*)Tcl_GetHashValue(hPtr); + return TCL_OK; + } + if (interp) { + Tcl_AppendResult(interp, "can't find marker \"", string, + "\" in \"", Tk_PathName(graphPtr->tkwin_), NULL); + } + + return TCL_ERROR; +} + diff --git a/generic/tkbltGrMarkerOp.h b/generic/tkbltGrMarkerOp.h new file mode 100644 index 0000000..6f7c16f --- /dev/null +++ b/generic/tkbltGrMarkerOp.h @@ -0,0 +1,39 @@ +/* + * 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 1993-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. + */ + +#ifndef __Blt_GrMarkerOp_h__ +#define __Blt_GrMarkerOp_h__ + +#include "tkbltGraph.h" + +namespace Blt { + extern const Ensemble markerEnsemble[]; +}; + +#endif diff --git a/generic/tkbltGrMarkerOption.C b/generic/tkbltGrMarkerOption.C new file mode 100644 index 0000000..6b13fd9 --- /dev/null +++ b/generic/tkbltGrMarkerOption.C @@ -0,0 +1,209 @@ +/* + * 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 1993-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 +#include +#include + +#include "tkbltGrMarker.h" +#include "tkbltGrMarkerOption.h" +#include "tkbltConfig.h" + +using namespace Blt; + +static Tcl_Obj* PrintCoordinate(double x); +static int GetCoordinate(Tcl_Interp* interp, Tcl_Obj *objPtr, double *valuePtr); + +static Tk_CustomOptionSetProc CoordsSetProc; +static Tk_CustomOptionGetProc CoordsGetProc; +static Tk_CustomOptionFreeProc CoordsFreeProc; +Tk_ObjCustomOption coordsObjOption = + { + "coords", CoordsSetProc, CoordsGetProc, RestoreProc, CoordsFreeProc, NULL + }; + +static int CoordsSetProc(ClientData clientData, Tcl_Interp* interp, + Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, + int offset, char* savePtr, int flags) +{ + Coords** coordsPtrPtr = (Coords**)(widgRec + offset); + *(double*)savePtr = *(double*)coordsPtrPtr; + + if (!coordsPtrPtr) + return TCL_OK; + + int objc; + Tcl_Obj** objv; + if (Tcl_ListObjGetElements(interp, *objPtr, &objc, &objv) != TCL_OK) + return TCL_ERROR; + + if (objc == 0) { + *coordsPtrPtr = NULL; + return TCL_OK; + } + + if (objc & 1) { + Tcl_AppendResult(interp, "odd number of marker coordinates specified",NULL); + return TCL_ERROR; + } + + Coords* coordsPtr = new Coords; + coordsPtr->num = objc/2; + coordsPtr->points = new Point2d[coordsPtr->num]; + + Point2d* pp = coordsPtr->points; + for (int ii=0; iix = x; + pp->y = y; + pp++; + } + + *coordsPtrPtr = coordsPtr; + return TCL_OK; +} + +static Tcl_Obj* CoordsGetProc(ClientData clientData, Tk_Window tkwin, + char *widgRec, int offset) +{ + Coords* coordsPtr = *(Coords**)(widgRec + offset); + + if (!coordsPtr) + return Tcl_NewListObj(0, NULL); + + int cnt = coordsPtr->num*2; + Tcl_Obj** ll = new Tcl_Obj*[cnt]; + + Point2d* pp = coordsPtr->points; + for (int ii=0; iix); + ll[ii++] = PrintCoordinate(pp->y); + } + + Tcl_Obj* listObjPtr = Tcl_NewListObj(cnt, ll); + delete [] ll; + return listObjPtr; +} + +static void CoordsFreeProc(ClientData clientData, Tk_Window tkwin, + char *ptr) +{ + Coords* coordsPtr = *(Coords**)ptr; + if (coordsPtr) { + delete [] coordsPtr->points; + delete coordsPtr; + } +} + +static Tk_CustomOptionSetProc CapStyleSetProc; +static Tk_CustomOptionGetProc CapStyleGetProc; +Tk_ObjCustomOption capStyleObjOption = + { + "capStyle", CapStyleSetProc, CapStyleGetProc, NULL, NULL, NULL + }; + +static int CapStyleSetProc(ClientData clientData, Tcl_Interp* interp, + Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, + int offset, char* save, int flags) +{ + int* ptr = (int*)(widgRec + offset); + + Tk_Uid uid = Tk_GetUid(Tcl_GetString(*objPtr)); + int cap; + if (Tk_GetCapStyle(interp, uid, &cap) != TCL_OK) + return TCL_ERROR; + *ptr = cap; + + return TCL_OK; +} + +static Tcl_Obj* CapStyleGetProc(ClientData clientData, Tk_Window tkwin, + char *widgRec, int offset) +{ + int* ptr = (int*)(widgRec + offset); + return Tcl_NewStringObj(Tk_NameOfCapStyle(*ptr), -1); +} + +static Tk_CustomOptionSetProc JoinStyleSetProc; +static Tk_CustomOptionGetProc JoinStyleGetProc; +Tk_ObjCustomOption joinStyleObjOption = + { + "joinStyle", JoinStyleSetProc, JoinStyleGetProc, NULL, NULL, NULL + }; + +static int JoinStyleSetProc(ClientData clientData, Tcl_Interp* interp, + Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, + int offset, char* save, int flags) +{ + int* ptr = (int*)(widgRec + offset); + + Tk_Uid uid = Tk_GetUid(Tcl_GetString(*objPtr)); + int join; + if (Tk_GetJoinStyle(interp, uid, &join) != TCL_OK) + return TCL_ERROR; + *ptr = join; + + return TCL_OK; +} + +static Tcl_Obj* JoinStyleGetProc(ClientData clientData, Tk_Window tkwin, + char *widgRec, int offset) +{ + int* ptr = (int*)(widgRec + offset); + return Tcl_NewStringObj(Tk_NameOfJoinStyle(*ptr), -1); +} + +static Tcl_Obj* PrintCoordinate(double x) +{ + if (x == DBL_MAX) + return Tcl_NewStringObj("+Inf", -1); + else if (x == -DBL_MAX) + return Tcl_NewStringObj("-Inf", -1); + else + return Tcl_NewDoubleObj(x); +} + +static int GetCoordinate(Tcl_Interp* interp, Tcl_Obj *objPtr, double *valuePtr) +{ + const char* expr = Tcl_GetString(objPtr); + char c = expr[0]; + if ((c == 'I') && (strcmp(expr, "Inf") == 0)) + *valuePtr = DBL_MAX; /* Elastic upper bound */ + else if ((c == '-') && (expr[1] == 'I') && (strcmp(expr, "-Inf") == 0)) + *valuePtr = -DBL_MAX; /* Elastic lower bound */ + else if ((c == '+') && (expr[1] == 'I') && (strcmp(expr, "+Inf") == 0)) + *valuePtr = DBL_MAX; /* Elastic upper bound */ + else if (Tcl_GetDoubleFromObj(interp, objPtr, valuePtr) != TCL_OK) + return TCL_ERROR; + + return TCL_OK; +} diff --git a/generic/tkbltGrMarkerOption.h b/generic/tkbltGrMarkerOption.h new file mode 100644 index 0000000..143810e --- /dev/null +++ b/generic/tkbltGrMarkerOption.h @@ -0,0 +1,39 @@ +/* + * 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 1993-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. + */ + +#ifndef __Blt_GrMarkerOption_h__ +#define __Blt_GrMarkerOption_h__ + +extern Tk_ObjCustomOption coordsObjOption; +extern Tk_ObjCustomOption capStyleObjOption; +extern Tk_ObjCustomOption joinStyleObjOption; +extern Tk_ObjCustomOption xAxisObjOption; +extern Tk_ObjCustomOption yAxisObjOption; + +#endif diff --git a/generic/tkbltGrMarkerPolygon.C b/generic/tkbltGrMarkerPolygon.C new file mode 100644 index 0000000..383d0e8 --- /dev/null +++ b/generic/tkbltGrMarkerPolygon.C @@ -0,0 +1,298 @@ +/* + * 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 1993-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 + +#include "tkbltGraph.h" +#include "tkbltGrMarkerPolygon.h" +#include "tkbltGrMarkerOption.h" +#include "tkbltGrMisc.h" +#include "tkbltGrDef.h" +#include "tkbltConfig.h" +#include "tkbltGrPSOutput.h" + +using namespace Blt; + +static Tk_OptionSpec optionSpecs[] = { + {TK_OPTION_CUSTOM, "-bindtags", "bindTags", "BindTags", + "Polygon all", -1, Tk_Offset(PolygonMarkerOptions, tags), + TK_OPTION_NULL_OK, &listObjOption, 0}, + {TK_OPTION_CUSTOM, "-cap", "cap", "Cap", + "butt", -1, Tk_Offset(PolygonMarkerOptions, capStyle), + 0, &capStyleObjOption, 0}, + {TK_OPTION_CUSTOM, "-coords", "coords", "Coords", + NULL, -1, Tk_Offset(PolygonMarkerOptions, worldPts), + TK_OPTION_NULL_OK, &coordsObjOption, 0}, + {TK_OPTION_CUSTOM, "-dashes", "dashes", "Dashes", + NULL, -1, Tk_Offset(PolygonMarkerOptions, dashes), + TK_OPTION_NULL_OK, &dashesObjOption, 0}, + {TK_OPTION_STRING, "-element", "element", "Element", + NULL, -1, Tk_Offset(PolygonMarkerOptions, elemName), + TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_COLOR, "-fill", "fill", "Fill", + NULL, -1, Tk_Offset(PolygonMarkerOptions, fill), + TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_CUSTOM, "-join", "join", "Join", + "miter", -1, Tk_Offset(PolygonMarkerOptions, joinStyle), + 0, &joinStyleObjOption, 0}, + {TK_OPTION_PIXELS, "-linewidth", "lineWidth", "LineWidth", + "1", -1, Tk_Offset(PolygonMarkerOptions, lineWidth), 0, NULL, 0}, + {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", + "no", -1, Tk_Offset(PolygonMarkerOptions, hide), 0, NULL, 0}, + {TK_OPTION_CUSTOM, "-mapx", "mapX", "MapX", + "x", -1, Tk_Offset(PolygonMarkerOptions, xAxis), 0, &xAxisObjOption, 0}, + {TK_OPTION_CUSTOM, "-mapy", "mapY", "MapY", + "y", -1, Tk_Offset(PolygonMarkerOptions, yAxis), 0, &yAxisObjOption, 0}, + {TK_OPTION_COLOR, "-outline", "outline", "Outline", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(PolygonMarkerOptions, outline), + TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_BOOLEAN, "-under", "under", "Under", + "no", -1, Tk_Offset(PolygonMarkerOptions, drawUnder), 0, NULL, CACHE}, + {TK_OPTION_PIXELS, "-xoffset", "xOffset", "XOffset", + "0", -1, Tk_Offset(PolygonMarkerOptions, xOffset), 0, NULL, 0}, + {TK_OPTION_PIXELS, "-yoffset", "yOffset", "YOffset", + "0", -1, Tk_Offset(PolygonMarkerOptions, yOffset), 0, NULL, 0}, + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} +}; + +PolygonMarker::PolygonMarker(Graph* graphPtr, const char* name, + Tcl_HashEntry* hPtr) + : Marker(graphPtr, name, hPtr) +{ + ops_ = (PolygonMarkerOptions*)calloc(1, sizeof(PolygonMarkerOptions)); + optionTable_ = Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); + + screenPts_ =NULL; + outlineGC_ =NULL; + fillGC_ =NULL; + fillPts_ =NULL; + nFillPts_ =0; + outlinePts_ =NULL; + nOutlinePts_ =0; +} + +PolygonMarker::~PolygonMarker() +{ + if (fillGC_) + Tk_FreeGC(graphPtr_->display_, fillGC_); + if (outlineGC_) + graphPtr_->freePrivateGC(outlineGC_); + delete [] fillPts_; + delete [] outlinePts_; + delete [] screenPts_; +} + +int PolygonMarker::configure() +{ + PolygonMarkerOptions* ops = (PolygonMarkerOptions*)ops_; + + // outlineGC + unsigned long gcMask = (GCLineWidth | GCLineStyle); + XGCValues gcValues; + if (ops->outline) { + gcMask |= GCForeground; + gcValues.foreground = ops->outline->pixel; + } + gcMask |= (GCCapStyle | GCJoinStyle); + gcValues.cap_style = ops->capStyle; + gcValues.join_style = ops->joinStyle; + gcValues.line_style = LineSolid; + gcValues.dash_offset = 0; + gcValues.line_width = ops->lineWidth; + if (LineIsDashed(ops->dashes)) + gcValues.line_style = LineOnOffDash; + + GC newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); + if (LineIsDashed(ops->dashes)) + graphPtr_->setDashes(newGC, &ops->dashes); + if (outlineGC_) + graphPtr_->freePrivateGC(outlineGC_); + outlineGC_ = newGC; + + // fillGC + gcMask = 0; + if (ops->fill) { + gcMask |= GCForeground; + gcValues.foreground = ops->fill->pixel; + } + newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); + if (fillGC_) + Tk_FreeGC(graphPtr_->display_, fillGC_); + fillGC_ = newGC; + + return TCL_OK; +} + +void PolygonMarker::draw(Drawable drawable) +{ + PolygonMarkerOptions* ops = (PolygonMarkerOptions*)ops_; + + // fill region + if ((nFillPts_ > 0) && (ops->fill)) { + XPoint* points = new XPoint[nFillPts_]; + if (!points) + return; + + XPoint* dp = points; + for (Point2d *sp = fillPts_, *send = sp + nFillPts_; sp < send; sp++) { + dp->x = (short)sp->x; + dp->y = (short)sp->y; + dp++; + } + + XFillPolygon(graphPtr_->display_, drawable, fillGC_, points, + nFillPts_, Complex, CoordModeOrigin); + delete [] points; + } + + // outline + if ((nOutlinePts_ > 0) && (ops->lineWidth > 0) && (ops->outline)) + graphPtr_->drawSegments(drawable, outlineGC_, outlinePts_, nOutlinePts_); +} + +void PolygonMarker::map() +{ + PolygonMarkerOptions* ops = (PolygonMarkerOptions*)ops_; + + if (outlinePts_) { + delete [] outlinePts_; + outlinePts_ = NULL; + nOutlinePts_ = 0; + } + + if (fillPts_) { + delete [] fillPts_; + fillPts_ = NULL; + nFillPts_ = 0; + } + + if (screenPts_) { + delete [] screenPts_; + screenPts_ = NULL; + } + + if (!ops->worldPts || ops->worldPts->num < 3) + return; + + // Allocate and fill a temporary array to hold the screen coordinates of + // the polygon. + + int nScreenPts = ops->worldPts->num + 1; + Point2d* screenPts = new Point2d[nScreenPts + 1]; + { + Point2d* dp = screenPts; + for (Point2d *sp = ops->worldPts->points, *send = sp + ops->worldPts->num; + sp < send; sp++) { + *dp = mapPoint(sp, ops->xAxis, ops->yAxis); + dp->x += ops->xOffset; + dp->y += ops->yOffset; + dp++; + } + *dp = screenPts[0]; + } + Region2d extents; + graphPtr_->extents(&extents); + + clipped_ = 1; + if (ops->fill) { + Point2d* lfillPts = new Point2d[nScreenPts * 3]; + int n = polyRectClip(&extents, screenPts, ops->worldPts->num,lfillPts); + if (n < 3) + delete [] lfillPts; + else { + nFillPts_ = n; + fillPts_ = lfillPts; + clipped_ = 0; + } + } + if ((ops->outline) && (ops->lineWidth > 0)) { + // Generate line segments representing the polygon outline. The + // resulting outline may or may not be closed from viewport clipping. + Segment2d* outlinePts = new Segment2d[nScreenPts]; + if (!outlinePts) + return; + + // Note that this assumes that the point array contains an extra point + // that closes the polygon. + Segment2d* segPtr = outlinePts; + for (Point2d *sp=screenPts, *send=sp+(nScreenPts - 1); sp < send; sp++) { + segPtr->p = sp[0]; + segPtr->q = sp[1]; + if (lineRectClip(&extents, &segPtr->p, &segPtr->q)) { + segPtr++; + } + } + nOutlinePts_ = segPtr - outlinePts; + outlinePts_ = outlinePts; + if (nOutlinePts_ > 0) + clipped_ = 0; + } + + screenPts_ = screenPts; +} + +int PolygonMarker::pointIn(Point2d *samplePtr) +{ + PolygonMarkerOptions* ops = (PolygonMarkerOptions*)ops_; + + if (ops->worldPts && (ops->worldPts->num >= 3) && screenPts_) + return pointInPolygon(samplePtr, screenPts_, ops->worldPts->num + 1); + + return 0; +} + +int PolygonMarker::regionIn(Region2d *extsPtr, int enclosed) +{ + PolygonMarkerOptions* ops = (PolygonMarkerOptions*)ops_; + + if (ops->worldPts && (ops->worldPts->num >= 3) && screenPts_) + return regionInPolygon(extsPtr, screenPts_, ops->worldPts->num, enclosed); + + return 0; +} + +void PolygonMarker::print(PSOutput* psPtr) +{ + PolygonMarkerOptions* ops = (PolygonMarkerOptions*)ops_; + + if (ops->fill) { + psPtr->printPolyline(fillPts_, nFillPts_); + psPtr->setForeground(ops->fill); + psPtr->append("fill\n"); + } + + if ((ops->lineWidth > 0) && (ops->outline)) { + psPtr->setLineAttributes(ops->outline, ops->lineWidth, &ops->dashes, + ops->capStyle, ops->joinStyle); + psPtr->append("/DashesProc {} def\n"); + + psPtr->printSegments(outlinePts_, nOutlinePts_); + } +} + diff --git a/generic/tkbltGrMarkerPolygon.h b/generic/tkbltGrMarkerPolygon.h new file mode 100644 index 0000000..8eb2216 --- /dev/null +++ b/generic/tkbltGrMarkerPolygon.h @@ -0,0 +1,84 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrMarkerPolygon_h__ +#define __BltGrMarkerPolygon_h__ + +#include "tkbltGrMarker.h" + +namespace Blt { + + typedef struct { + const char** tags; + Coords* worldPts; + const char* elemName; + Axis* xAxis; + Axis* yAxis; + int hide; + int drawUnder; + int xOffset; + int yOffset; + + int capStyle; + Dashes dashes; + XColor* fill; + int joinStyle; + int lineWidth; + XColor* outline; + } PolygonMarkerOptions; + + class PolygonMarker : public Marker { + protected: + Point2d *screenPts_; + GC outlineGC_; + GC fillGC_; + Point2d *fillPts_; + int nFillPts_; + Segment2d *outlinePts_; + int nOutlinePts_; + + protected: + int configure(); + void draw(Drawable); + void map(); + int pointIn(Point2d*); + int regionIn(Region2d*, int); + void print(PSOutput*); + + public: + PolygonMarker(Graph*, const char*, Tcl_HashEntry*); + virtual ~PolygonMarker(); + + ClassId classId() {return CID_MARKER_POLYGON;} + const char* className() {return "PolygonMarker";} + const char* typeName() {return "polygon";} + }; +}; + +#endif diff --git a/generic/tkbltGrMarkerText.C b/generic/tkbltGrMarkerText.C new file mode 100644 index 0000000..9c4da2a --- /dev/null +++ b/generic/tkbltGrMarkerText.C @@ -0,0 +1,276 @@ +/* + * 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 1993-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 + +#include + +#include "tkbltGraph.h" +#include "tkbltGrMarkerText.h" +#include "tkbltGrMarkerOption.h" +#include "tkbltGrMisc.h" +#include "tkbltGrDef.h" +#include "tkbltConfig.h" +#include "tkbltGrPSOutput.h" + +using namespace Blt; + +static Tk_OptionSpec optionSpecs[] = { + {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor", + "center", -1, Tk_Offset(TextMarkerOptions, anchor), 0, NULL, 0}, + {TK_OPTION_COLOR, "-background", "background", "Background", + NULL, -1, Tk_Offset(TextMarkerOptions, fillColor), + TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_SYNONYM, "-bg", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-background", 0}, + {TK_OPTION_CUSTOM, "-bindtags", "bindTags", "BindTags", + "Text all", -1, Tk_Offset(TextMarkerOptions, tags), + TK_OPTION_NULL_OK, &listObjOption, 0}, + {TK_OPTION_CUSTOM, "-coords", "coords", "Coords", + NULL, -1, Tk_Offset(TextMarkerOptions, worldPts), + TK_OPTION_NULL_OK, &coordsObjOption, 0}, + {TK_OPTION_STRING, "-element", "element", "Element", + NULL, -1, Tk_Offset(TextMarkerOptions, elemName), + TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_SYNONYM, "-fg", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-foreground", 0}, + {TK_OPTION_SYNONYM, "-fill", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-background", 0}, + {TK_OPTION_FONT, "-font", "font", "Font", + STD_FONT_NORMAL, -1, Tk_Offset(TextMarkerOptions, style.font), 0, NULL, 0}, + {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(TextMarkerOptions, style.color), + 0, NULL, 0}, + {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify", + "left", -1, Tk_Offset(TextMarkerOptions, style.justify), 0, NULL, 0}, + {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", + "no", -1, Tk_Offset(TextMarkerOptions, hide), 0, NULL, 0}, + {TK_OPTION_CUSTOM, "-mapx", "mapX", "MapX", + "x", -1, Tk_Offset(TextMarkerOptions, xAxis), 0, &xAxisObjOption, 0}, + {TK_OPTION_CUSTOM, "-mapy", "mapY", "MapY", + "y", -1, Tk_Offset(TextMarkerOptions, yAxis), 0, &yAxisObjOption, 0}, + {TK_OPTION_SYNONYM, "-outline", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-foreground", 0}, + {TK_OPTION_DOUBLE, "-rotate", "rotate", "Rotate", + "0", -1, Tk_Offset(TextMarkerOptions, style.angle), 0, NULL, 0}, + {TK_OPTION_STRING, "-text", "text", "Text", + NULL, -1, Tk_Offset(TextMarkerOptions, string), TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_BOOLEAN, "-under", "under", "Under", + "no", -1, Tk_Offset(TextMarkerOptions, drawUnder), 0, NULL, CACHE}, + {TK_OPTION_PIXELS, "-xoffset", "xOffset", "XOffset", + "0", -1, Tk_Offset(TextMarkerOptions, xOffset), 0, NULL, 0}, + {TK_OPTION_PIXELS, "-yoffset", "yOffset", "YOffset", + "0", -1, Tk_Offset(TextMarkerOptions, yOffset), 0, NULL, 0}, + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} +}; + +TextMarker::TextMarker(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) + : Marker(graphPtr, name, hPtr) +{ + ops_ = (TextMarkerOptions*)calloc(1, sizeof(TextMarkerOptions)); + TextMarkerOptions* ops = (TextMarkerOptions*)ops_; + + ops->style.anchor =TK_ANCHOR_NW; + ops->style.color =NULL; + ops->style.font =NULL; + ops->style.angle =0; + ops->style.justify =TK_JUSTIFY_LEFT; + + anchorPt_.x =0; + anchorPt_.y =0; + width_ =0; + height_ =0; + fillGC_ =NULL; + + optionTable_ = Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); +} + +TextMarker::~TextMarker() +{ +} + +int TextMarker::configure() +{ + TextMarkerOptions* ops = (TextMarkerOptions*)ops_; + + ops->style.angle = (float)fmod(ops->style.angle, 360.0); + if (ops->style.angle < 0.0) + ops->style.angle += 360.0; + + GC newGC = NULL; + XGCValues gcValues; + unsigned long gcMask; + if (ops->fillColor) { + gcMask = GCForeground; + gcValues.foreground = ops->fillColor->pixel; + newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); + } + if (fillGC_) + Tk_FreeGC(graphPtr_->display_, fillGC_); + fillGC_ = newGC; + + return TCL_OK; +} + +void TextMarker::draw(Drawable drawable) +{ + TextMarkerOptions* ops = (TextMarkerOptions*)ops_; + + if (!ops->string) + return; + + if (fillGC_) { + XPoint points[4]; + for (int ii=0; ii<4; ii++) { + points[ii].x = (short)(outline_[ii].x + anchorPt_.x); + points[ii].y = (short)(outline_[ii].y + anchorPt_.y); + } + XFillPolygon(graphPtr_->display_, drawable, fillGC_, points, 4, + Convex, CoordModeOrigin); + } + + TextStyle ts(graphPtr_, &ops->style); + ts.drawText(drawable, ops->string, anchorPt_.x, anchorPt_.y); +} + +void TextMarker::map() +{ + TextMarkerOptions* ops = (TextMarkerOptions*)ops_; + + if (!ops->string) + return; + + if (!ops->worldPts || (ops->worldPts->num < 1)) + return; + + width_ =0; + height_ =0; + + int w, h; + TextStyle ts(graphPtr_, &ops->style); + ts.getExtents(ops->string, &w, &h); + + double rw; + double rh; + graphPtr_->getBoundingBox(w, h, ops->style.angle, &rw, &rh, outline_); + width_ = (int)rw; + height_ = (int)rh; + for (int ii=0; ii<4; ii++) { + outline_[ii].x += rw * 0.5; + outline_[ii].y += rh * 0.5; + } + outline_[4].x = outline_[0].x; + outline_[4].y = outline_[0].y; + + Point2d anchorPtr = mapPoint(ops->worldPts->points, ops->xAxis, ops->yAxis); + anchorPtr = graphPtr_->anchorPoint(anchorPtr.x, anchorPtr.y, + width_, height_, ops->anchor); + anchorPtr.x += ops->xOffset; + anchorPtr.y += ops->yOffset; + + Region2d extents; + extents.left = anchorPtr.x; + extents.top = anchorPtr.y; + extents.right = anchorPtr.x + width_ - 1; + extents.bottom = anchorPtr.y + height_ - 1; + clipped_ = boxesDontOverlap(graphPtr_, &extents); + + anchorPt_ = anchorPtr; +} + +int TextMarker::pointIn(Point2d *samplePtr) +{ + TextMarkerOptions* ops = (TextMarkerOptions*)ops_; + + if (!ops->string) + return 0; + + if (ops->style.angle != 0.0) { + Point2d points[5]; + + // Figure out the bounding polygon (isolateral) for the text and see + // if the point is inside of it. + for (int ii=0; ii<5; ii++) { + points[ii].x = outline_[ii].x + anchorPt_.x; + points[ii].y = outline_[ii].y + anchorPt_.y; + } + return pointInPolygon(samplePtr, points, 5); + } + + return ((samplePtr->x >= anchorPt_.x) && + (samplePtr->x < (anchorPt_.x + width_)) && + (samplePtr->y >= anchorPt_.y) && + (samplePtr->y < (anchorPt_.y + height_))); +} + +int TextMarker::regionIn(Region2d *extsPtr, int enclosed) +{ + TextMarkerOptions* ops = (TextMarkerOptions*)ops_; + + if (ops->style.angle != 0.0) { + Point2d points[5]; + for (int ii=0; ii<4; ii++) { + points[ii].x = outline_[ii].x + anchorPt_.x; + points[ii].y = outline_[ii].y + anchorPt_.y; + } + return regionInPolygon(extsPtr, points, 4, enclosed); + } + + if (enclosed) + return ((anchorPt_.x >= extsPtr->left) && + (anchorPt_.y >= extsPtr->top) && + ((anchorPt_.x + width_) <= extsPtr->right) && + ((anchorPt_.y + height_) <= extsPtr->bottom)); + + return !((anchorPt_.x >= extsPtr->right) || + (anchorPt_.y >= extsPtr->bottom) || + ((anchorPt_.x + width_) <= extsPtr->left) || + ((anchorPt_.y + height_) <= extsPtr->top)); +} + +void TextMarker::print(PSOutput* psPtr) +{ + TextMarkerOptions* ops = (TextMarkerOptions*)ops_; + + if (!ops->string) + return; + + if (fillGC_) { + Point2d points[4]; + for (int ii=0; ii<4; ii++) { + points[ii].x = outline_[ii].x + anchorPt_.x; + points[ii].y = outline_[ii].y + anchorPt_.y; + } + psPtr->setBackground(ops->fillColor); + psPtr->fillPolygon(points, 4); + } + + TextStyle ts(graphPtr_, &ops->style); + ts.printText(psPtr, ops->string, anchorPt_.x, anchorPt_.y); +} diff --git a/generic/tkbltGrMarkerText.h b/generic/tkbltGrMarkerText.h new file mode 100644 index 0000000..99dc814 --- /dev/null +++ b/generic/tkbltGrMarkerText.h @@ -0,0 +1,82 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrMarkerText_h__ +#define __BltGrMarkerText_h__ + +#include + +#include "tkbltGrMarker.h" + +namespace Blt { + + typedef struct { + const char** tags; + Coords* worldPts; + const char* elemName; + Axis* xAxis; + Axis* yAxis; + int hide; + int drawUnder; + int xOffset; + int yOffset; + + Tk_Anchor anchor; + XColor* fillColor; + TextStyleOptions style; + const char* string; + } TextMarkerOptions; + + class TextMarker : public Marker { + protected: + Point2d anchorPt_; + int width_; + int height_; + GC fillGC_; + Point2d outline_[5]; + + protected: + int configure(); + void draw(Drawable); + void map(); + int pointIn(Point2d*); + int regionIn(Region2d*, int); + void print(PSOutput*); + + public: + TextMarker(Graph*, const char*, Tcl_HashEntry*); + virtual ~TextMarker(); + + ClassId classId() {return CID_MARKER_TEXT;} + const char* className() {return "TextMarker";} + const char* typeName() {return "text";} + }; +}; + +#endif diff --git a/generic/tkbltGrMisc.C b/generic/tkbltGrMisc.C new file mode 100644 index 0000000..d951494 --- /dev/null +++ b/generic/tkbltGrMisc.C @@ -0,0 +1,335 @@ +/* + * 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 1993-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 +#include +#include +#include + +#include + +#include +#include + +#include "tkbltGraph.h" +#include "tkbltGrMisc.h" + +using namespace Blt; + +char* Blt::dupstr(const char* str) +{ + char* copy =NULL; + if (str) { + copy=new char[strlen(str)+1]; + strcpy(copy,str); + } + + return copy; +} + +int Blt::pointInPolygon(Point2d *s, Point2d *points, int nPoints) +{ + int count = 0; + for (Point2d *p=points, *q=p+1, *qend=p + nPoints; q < qend; p++, q++) { + if (((p->y <= s->y) && (s->y < q->y)) || + ((q->y <= s->y) && (s->y < p->y))) { + double b; + + b = (q->x - p->x) * (s->y - p->y) / (q->y - p->y) + p->x; + if (s->x < b) { + count++; /* Count the number of intersections. */ + } + } + } + return (count & 0x01); +} + +static int ClipTest (double ds, double dr, double *t1, double *t2) +{ + double t; + + if (ds < 0.0) { + t = dr / ds; + if (t > *t2) { + return 0; + } + if (t > *t1) { + *t1 = t; + } + } else if (ds > 0.0) { + t = dr / ds; + if (t < *t1) { + return 0; + } + 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 0; + } + } + return 1; +} + +/* + *--------------------------------------------------------------------------- + * 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, Y-D., and B. Barsky, A new concept and method for + * Line Clipping, ACM, TOG,3(1), 1984, pp.1-22. + *--------------------------------------------------------------------------- + */ +int Blt::lineRectClip(Region2d* regionPtr, Point2d *p, Point2d *q) +{ + double t1, t2; + double dx, dy; + + t1 = 0.0, t2 = 1.0; + dx = q->x - p->x; + if ((ClipTest (-dx, p->x - regionPtr->left, &t1, &t2)) && + (ClipTest (dx, regionPtr->right - p->x, &t1, &t2))) { + dy = q->y - p->y; + if ((ClipTest (-dy, p->y - regionPtr->top, &t1, &t2)) && + (ClipTest (dy, regionPtr->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 1; + } + } + return 0; +} + +/* + *--------------------------------------------------------------------------- + * 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 Y. D. and Brian A. Barsky, "Analysis and Algorithm for + * Polygon Clipping", Communications of ACM, Vol. 26, + * p.868-877, 1983 + *--------------------------------------------------------------------------- + */ +#define AddVertex(vx, vy) r->x=(vx), r->y=(vy), r++, count++ +#define LastVertex(vx, vy) r->x=(vx), r->y=(vy), count++ + +int Blt::polyRectClip(Region2d *regionPtr, Point2d *points, int nPoints, + Point2d *clipPts) +{ + Point2d* r = clipPts; + // Counts # of vertices in output polygon. + int count = 0; + + points[nPoints] = points[0]; + for (Point2d *p=points, *q=p+1, *pend=p+nPoints; px - p->x; /* X-direction */ + dy = q->y - p->y; /* Y-direction */ + + if (fabs(dx) < FLT_EPSILON) + dx = (p->x > regionPtr->left) ? -FLT_EPSILON : FLT_EPSILON ; + + if (fabs(dy) < FLT_EPSILON) + dy = (p->y > regionPtr->top) ? -FLT_EPSILON : FLT_EPSILON ; + + if (dx > 0.0) { /* Left */ + xin = regionPtr->left; + xout = regionPtr->right + 1.0; + } + else { /* Right */ + xin = regionPtr->right + 1.0; + xout = regionPtr->left; + } + if (dy > 0.0) { /* Top */ + yin = regionPtr->top; + yout = regionPtr->bottom + 1.0; + } + else { /* Bottom */ + yin = regionPtr->bottom + 1.0; + yout = regionPtr->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 = (xout - p->x) / dx; + double touty = (yout - p->y) / dy; + double 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) { + LastVertex(clipPts[0].x, clipPts[0].y); + } + return count; +} + +/* + *--------------------------------------------------------------------------- + * 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. + *--------------------------------------------------------------------------- + */ +Point2d Blt::getProjection(int x, int y, Point2d *p, Point2d *q) +{ + double dx = p->x - q->x; + double dy = p->y - q->y; + + /* Test for horizontal and vertical lines */ + Point2d t; + 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 { + /* Compute the slope and intercept of PQ. */ + double m1 = (dy / dx); + double 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 PQ. */ + double midX = (p->x + q->x) * 0.5; + double midY = (p->y + q->y) * 0.5; + + /* Rotate the line 90 degrees */ + double ax = midX - (0.5 * dy); + double ay = midY - (0.5 * -dx); + double bx = midX + (0.5 * dy); + double by = midY + (0.5 * -dx); + + double m2 = (ay - by) / (ax - bx); + double 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; +} + +Graph* Blt::getGraphFromWindowData(Tk_Window tkwin) +{ + while (tkwin) { + TkWindow* winPtr = (TkWindow*)tkwin; + if (winPtr->instanceData != NULL) { + Graph* graphPtr = (Graph*)winPtr->instanceData; + if (graphPtr) + return graphPtr; + } + tkwin = Tk_Parent(tkwin); + } + return NULL; +} + diff --git a/generic/tkbltGrMisc.h b/generic/tkbltGrMisc.h new file mode 100644 index 0000000..ba86b75 --- /dev/null +++ b/generic/tkbltGrMisc.h @@ -0,0 +1,118 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrMisc_h__ +#define __BltGrMisc_h__ + +#include +#include +#include +using namespace std; + +#include + +#ifndef MIN +# define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +#ifndef MAX +# define MAX(x,y) ((x)>(y)?(x):(y)) +#endif + +#define GRAPH_DELETED (1<<1) +#define REDRAW_PENDING (1<<2) +#define FOCUS (1<<3) + +#define MAP_ITEM (1<<4) + +#define RESET (1<<5) +#define LAYOUT (1<<6) +#define MAP_MARKERS (1<<7) +#define CACHE (1<<8) + +#define MARGIN_NONE -1 +#define MARGIN_BOTTOM 0 /* x */ +#define MARGIN_LEFT 1 /* y */ +#define MARGIN_TOP 2 /* x2 */ +#define MARGIN_RIGHT 3 /* y2 */ + +#define LineIsDashed(d) ((d).values[0] != 0) + +namespace Blt { + class Graph; + + typedef struct { + int x, y; + } Point; + + typedef struct { + int x, y; + unsigned width, height; + } Rectangle; + + typedef struct { + double x; + double y; + } Point2d; + + typedef struct { + Point2d p; + Point2d q; + } Segment2d; + + typedef struct { + double left; + double right; + double top; + double bottom; + } Region2d; + + typedef enum { + CID_NONE, CID_AXIS_X, CID_AXIS_Y, CID_ELEM_BAR, CID_ELEM_LINE, + CID_MARKER_BITMAP, CID_MARKER_IMAGE, CID_MARKER_LINE, CID_MARKER_POLYGON, + CID_MARKER_TEXT + } ClassId; + + typedef struct { + unsigned char values[12]; + int offset; + } Dashes; + + extern char* dupstr(const char*); + extern Graph* getGraphFromWindowData(Tk_Window tkwin); + + extern int pointInPolygon(Point2d *samplePtr, Point2d *screenPts, + int nScreenPts); + extern int polyRectClip(Region2d *extsPtr, Point2d *inputPts, + int nInputPts, Point2d *outputPts); + extern int lineRectClip(Region2d *regionPtr, Point2d *p, Point2d *q); + extern Point2d getProjection (int x, int y, Point2d *p, Point2d *q); +}; + +#endif diff --git a/generic/tkbltGrPSOutput.C b/generic/tkbltGrPSOutput.C new file mode 100644 index 0000000..07d4864 --- /dev/null +++ b/generic/tkbltGrPSOutput.C @@ -0,0 +1,931 @@ +/* + * 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 +#include +#include + +#include + +#include "tk.h" + +// copied from tk3d.h + +typedef struct TkBorder { + Screen *screen; /* Screen on which the border will be used. */ + Visual *visual; /* Visual for all windows and pixmaps using + * the border. */ + int depth; /* Number of bits per pixel of drawables where + * the border will be used. */ + Colormap colormap; /* Colormap out of which pixels are + * allocated. */ + int resourceRefCount; /* Number of active uses of this color (each + * active use corresponds to a call to + * Tk_Alloc3DBorderFromObj or Tk_Get3DBorder). + * If this count is 0, then this structure is + * no longer valid and it isn't present in + * borderTable: it is being kept around only + * because there are objects referring to it. + * The structure is freed when objRefCount and + * resourceRefCount are both 0. */ + int objRefCount; /* The number of Tcl objects that reference + * this structure. */ + XColor *bgColorPtr; /* Background color (intensity between + * lightColorPtr and darkColorPtr). */ + XColor *darkColorPtr; /* Color for darker areas (must free when + * deleting structure). NULL means shadows + * haven't been allocated yet.*/ + XColor *lightColorPtr; /* Color used for lighter areas of border + * (must free this when deleting structure). + * NULL means shadows haven't been allocated + * yet. */ + Pixmap shadow; /* Stipple pattern to use for drawing shadows + * areas. Used for displays with <= 64 colors + * or where colormap has filled up. */ + GC bgGC; /* Used (if necessary) to draw areas in the + * background color. */ + GC darkGC; /* Used to draw darker parts of the border. + * None means the shadow colors haven't been + * allocated yet.*/ + GC lightGC; /* Used to draw lighter parts of the border. + * None means the shadow colors haven't been + * allocated yet. */ + Tcl_HashEntry *hashPtr; /* Entry in borderTable (needed in order to + * delete structure). */ + struct TkBorder *nextPtr; /* Points to the next TkBorder structure with + * the same color name. Borders with the same + * name but different screens or colormaps are + * chained together off a single entry in + * borderTable. */ +} TkBorder; + +#include "tkbltGraph.h" +#include "tkbltGrPostscript.h" +#include "tkbltGrPSOutput.h" + +using namespace Blt; + +PSOutput::PSOutput(Graph* graphPtr) +{ + graphPtr_ = graphPtr; + + Tcl_DStringInit(&dString_); +} + +PSOutput::~PSOutput() +{ + Tcl_DStringFree(&dString_); +} + +void PSOutput::printPolyline(Point2d* screenPts, int nScreenPts) +{ + Point2d* pp = screenPts; + append("newpath\n"); + format(" %g %g moveto\n", pp->x, pp->y); + + Point2d* pend; + for (pp++, pend = screenPts + nScreenPts; pp < pend; pp++) + format(" %g %g lineto\n", pp->x, pp->y); +} + +void PSOutput::printMaxPolyline(Point2d* points, int nPoints) +{ + if (nPoints <= 0) + return; + + for (int nLeft = nPoints; nLeft > 0; nLeft -= 1500) { + int length = MIN(1500, nLeft); + printPolyline(points, length); + append("DashesProc stroke\n"); + points += length; + } +} + +void PSOutput::printSegments(Segment2d* segments, int nSegments) +{ + append("newpath\n"); + + for (Segment2d *sp = segments, *send = sp + nSegments; sp < send; sp++) { + format(" %g %g moveto %g %g lineto\n", sp->p.x, sp->p.y, sp->q.x, sp->q.y); + append("DashesProc stroke\n"); + } +} + +void PSOutput::computeBBox(int width, int height) +{ + Postscript* setupPtr = graphPtr_->postscript_; + PostscriptOptions* pops = (PostscriptOptions*)setupPtr->ops_; + + // scale from points to pica + double pica = 25.4 / 72 * + WidthOfScreen(Tk_Screen(graphPtr_->tkwin_)) / + WidthMMOfScreen(Tk_Screen(graphPtr_->tkwin_)); + + double hBorder = 2*pops->xPad/pica; + double vBorder = 2*pops->yPad/pica; + int hSize = !pops->landscape ? width : height; + int vSize = !pops->landscape ? height : width; + + // If the paper size wasn't specified, set it to the graph size plus the + // paper border. + double paperWidth = pops->reqPaperWidth > 0 ? pops->reqPaperWidth/pica : + hSize + hBorder; + double paperHeight = pops->reqPaperHeight > 0 ? pops->reqPaperHeight/pica : + vSize + vBorder; + + // Scale the plot size if it's bigger than the paper + double hScale = (hSize+hBorder) > paperWidth ? 1.0 : + paperWidth - hBorder / hSize; + double vScale = (vSize + vBorder) > paperHeight ? 1.0 : + paperHeight - vBorder / vSize; + + double scale = MIN(hScale, vScale); + if (scale != 1.0) { + hSize = (int)(hSize*scale + 0.5); + vSize = (int)(vSize*scale + 0.5); + } + + int x = (int)((paperWidth > hSize) && pops->center ? + (paperWidth - hSize) / 2 : pops->xPad/pica); + int y = (int)((paperHeight > vSize) && pops->center ? + (paperHeight - vSize) / 2 : pops->yPad/pica); + + setupPtr->left = x; + setupPtr->bottom = y; + setupPtr->right = x + hSize - 1; + setupPtr->top = y + vSize - 1; + setupPtr->scale = scale; + setupPtr->paperHeight = (int)paperHeight; + setupPtr->paperWidth = (int)paperWidth; +} + +const char* PSOutput::getValue(int* lengthPtr) +{ + *lengthPtr = strlen(Tcl_DStringValue(&dString_)); + return Tcl_DStringValue(&dString_); +} + +void PSOutput::append(const char* string) +{ + Tcl_DStringAppend(&dString_, string, -1); +} + +void PSOutput::format(const char* fmt, ...) +{ + va_list argList; + + va_start(argList, fmt); + vsnprintf(scratchArr_, POSTSCRIPT_BUFSIZ, fmt, argList); + va_end(argList); + Tcl_DStringAppend(&dString_, scratchArr_, -1); +} + +void PSOutput::setLineWidth(int lineWidth) +{ + if (lineWidth < 1) + lineWidth = 1; + format("%d setlinewidth\n", lineWidth); +} + +void PSOutput::printRectangle(double x, double y, int width, int height) +{ + append("newpath\n"); + format(" %g %g moveto\n", x, y); + format(" %d %d rlineto\n", width, 0); + format(" %d %d rlineto\n", 0, height); + format(" %d %d rlineto\n", -width, 0); + append("closepath\n"); + append("stroke\n"); +} + +void PSOutput::fillRectangle(double x, double y, int width, int height) +{ + append("newpath\n"); + format(" %g %g moveto\n", x, y); + format(" %d %d rlineto\n", width, 0); + format(" %d %d rlineto\n", 0, height); + format(" %d %d rlineto\n", -width, 0); + append("closepath\n"); + append("fill\n"); +} + +void PSOutput::fillRectangles(Rectangle* rectangles, int nRectangles) +{ + for (Rectangle *rp = rectangles, *rend = rp + nRectangles; rp < rend; rp++) + fillRectangle((double)rp->x, (double)rp->y, (int)rp->width,(int)rp->height); +} + +void PSOutput::setBackground(XColor* colorPtr) +{ + PostscriptOptions* pops = (PostscriptOptions*)graphPtr_->postscript_->ops_; + printXColor(colorPtr); + append(" setrgbcolor\n"); + if (pops->greyscale) + append(" currentgray setgray\n"); +} + +void PSOutput::setForeground(XColor* colorPtr) +{ + PostscriptOptions* pops = (PostscriptOptions*)graphPtr_->postscript_->ops_; + printXColor(colorPtr); + append(" setrgbcolor\n"); + if (pops->greyscale) + append(" currentgray setgray\n"); +} + +void PSOutput::setBackground(Tk_3DBorder border) +{ + TkBorder* borderPtr = (TkBorder*)border; + setBackground(borderPtr->bgColorPtr); +} + +void PSOutput::setFont(Tk_Font font) +{ + Tcl_DString psdstr; + Tcl_DStringInit(&psdstr); + int psSize = Tk_PostscriptFontName(font, &psdstr); + format("%d /%s SetFont\n", psSize, Tcl_DStringValue(&psdstr)); + Tcl_DStringFree(&psdstr); +} + +void PSOutput::setLineAttributes(XColor* colorPtr,int lineWidth, + Dashes* dashesPtr, int capStyle, + int joinStyle) +{ + setJoinStyle(joinStyle); + setCapStyle(capStyle); + setForeground(colorPtr); + setLineWidth(lineWidth); + setDashes(dashesPtr); + append("/DashesProc {} def\n"); +} + +void PSOutput::fill3DRectangle(Tk_3DBorder border, double x, double y, + int width, int height, int borderWidth, + int relief) +{ + TkBorder* borderPtr = (TkBorder*)border; + + setBackground(borderPtr->bgColorPtr); + fillRectangle(x, y, width, height); + print3DRectangle(border, x, y, width, height, borderWidth, relief); +} + +void PSOutput::setClearBackground() +{ + append("1 1 1 setrgbcolor\n"); +} + +void PSOutput::setDashes(Dashes* dashesPtr) +{ + + append("[ "); + if (dashesPtr) { + for (unsigned char* vp = dashesPtr->values; *vp != 0; vp++) + format(" %d", *vp); + } + append("] 0 setdash\n"); +} + +void PSOutput::fillPolygon(Point2d *screenPts, int nScreenPts) +{ + printPolygon(screenPts, nScreenPts); + append("fill\n"); +} + +void PSOutput::setJoinStyle(int joinStyle) +{ + // miter = 0, round = 1, bevel = 2 + format("%d setlinejoin\n", joinStyle); +} + +void PSOutput::setCapStyle(int capStyle) +{ + // X11:not last = 0, butt = 1, round = 2, projecting = 3 + // PS: butt = 0, round = 1, projecting = 2 + if (capStyle > 0) + capStyle--; + + format("%d setlinecap\n", capStyle); +} + +void PSOutput::printPolygon(Point2d *screenPts, int nScreenPts) +{ + Point2d* pp = screenPts; + append("newpath\n"); + format(" %g %g moveto\n", pp->x, pp->y); + + Point2d* pend; + for (pp++, pend = screenPts + nScreenPts; pp < pend; pp++) + format(" %g %g lineto\n", pp->x, pp->y); + + format(" %g %g lineto\n", screenPts[0].x, screenPts[0].y); + append("closepath\n"); +} + +void PSOutput::print3DRectangle(Tk_3DBorder border, double x, double y, + int width, int height, int borderWidth, + int relief) +{ + int twiceWidth = (borderWidth * 2); + if ((width < twiceWidth) || (height < twiceWidth)) + return; + + TkBorder* borderPtr = (TkBorder*)border; + + // Handle grooves and ridges with recursive calls + if ((relief == TK_RELIEF_GROOVE) || (relief == TK_RELIEF_RIDGE)) { + int halfWidth = borderWidth / 2; + int insideOffset = borderWidth - halfWidth; + print3DRectangle(border, (double)x, (double)y, width, height, halfWidth, + (relief == TK_RELIEF_GROOVE) ? + TK_RELIEF_SUNKEN : TK_RELIEF_RAISED); + print3DRectangle(border, (double)(x + insideOffset), + (double)(y + insideOffset), width - insideOffset * 2, + height - insideOffset * 2, halfWidth, + (relief == TK_RELIEF_GROOVE) ? + TK_RELIEF_RAISED : TK_RELIEF_SUNKEN); + return; + } + + XColor* lightPtr = borderPtr->lightColorPtr; + XColor* darkPtr = borderPtr->darkColorPtr; + XColor light; + if (!lightPtr) { + light.red = 0x00; + light.blue = 0x00; + light.green = 0x00; + lightPtr = &light; + } + XColor dark; + if (!darkPtr) { + dark.red = 0x00; + dark.blue = 0x00; + dark.green = 0x00; + darkPtr = &dark; + } + + XColor* topPtr, *bottomPtr; + if (relief == TK_RELIEF_RAISED) { + topPtr = lightPtr; + bottomPtr = darkPtr; + } + else if (relief == TK_RELIEF_SUNKEN) { + topPtr = darkPtr; + bottomPtr = lightPtr; + } + else if (relief == TK_RELIEF_SOLID) { + topPtr = lightPtr; + bottomPtr = lightPtr; + } + else { + topPtr = borderPtr->bgColorPtr; + bottomPtr = borderPtr->bgColorPtr; + } + + setBackground(bottomPtr); + fillRectangle(x, y + height - borderWidth, width, borderWidth); + fillRectangle(x + width - borderWidth, y, borderWidth, height); + + Point2d points[7]; + points[0].x = points[1].x = points[6].x = x; + points[0].y = points[6].y = y + height; + points[1].y = points[2].y = y; + points[2].x = x + width; + points[3].x = x + width - borderWidth; + points[3].y = points[4].y = y + borderWidth; + points[4].x = points[5].x = x + borderWidth; + points[5].y = y + height - borderWidth; + if (relief != TK_RELIEF_FLAT) + setBackground(topPtr); + + fillPolygon(points, 7); +} + +void PSOutput::printXColor(XColor* colorPtr) +{ + format("%g %g %g", + ((double)(colorPtr->red >> 8) / 255.0), + ((double)(colorPtr->green >> 8) / 255.0), + ((double)(colorPtr->blue >> 8) / 255.0)); +} + +int PSOutput::preamble(const char* fileName) +{ + Postscript* setupPtr = graphPtr_->postscript_; + PostscriptOptions* ops = (PostscriptOptions*)setupPtr->ops_; + + if (!fileName) + fileName = Tk_PathName(graphPtr_->tkwin_); + + // Comments + append("%!PS-Adobe-3.0 EPSF-3.0\n"); + + // The "BoundingBox" comment is required for EPS files. The box + // coordinates are integers, so we need round away from the center of the + // box. + format("%%%%BoundingBox: %d %d %d %d\n", + setupPtr->left, setupPtr->paperHeight - setupPtr->top, + setupPtr->right, setupPtr->paperHeight - setupPtr->bottom); + + append("%%Pages: 0\n"); + + format("%%%%Creator: (%s %s %s)\n", PACKAGE_NAME, PACKAGE_VERSION, + Tk_Class(graphPtr_->tkwin_)); + + time_t ticks = time((time_t *) NULL); + char date[200]; + strcpy(date, ctime(&ticks)); + char* newline = date + strlen(date) - 1; + if (*newline == '\n') + *newline = '\0'; + + format("%%%%CreationDate: (%s)\n", date); + format("%%%%Title: (%s)\n", fileName); + append("%%DocumentData: Clean7Bit\n"); + if (ops->landscape) + append("%%Orientation: Landscape\n"); + else + append("%%Orientation: Portrait\n"); + + append("%%DocumentNeededResources: font Helvetica Courier\n"); + addComments(ops->comments); + append("%%EndComments\n\n"); + + // Prolog + prolog(); + + // Setup + append("%%BeginSetup\n"); + append("gsave\n"); + append("1 setlinewidth\n"); + append("1 setlinejoin\n"); + append("0 setlinecap\n"); + append("[] 0 setdash\n"); + append("0 0 0 setrgbcolor\n"); + + if (ops->footer) { + const char* who = getenv("LOGNAME"); + if (!who) + who = "???"; + + append("8 /Helvetica SetFont\n"); + append("10 30 moveto\n"); + format("(Date: %s) show\n", date); + append("10 20 moveto\n"); + format("(File: %s) show\n", fileName); + append("10 10 moveto\n"); + format("(Created by: %s@%s) show\n", who, Tcl_GetHostName()); + append("0 0 moveto\n"); + } + + // Set the conversion from postscript to X11 coordinates. Scale pica to + // pixels and flip the y-axis (the origin is the upperleft corner). + // Papersize is in pixels. Translate the new origin *after* changing the scale + append("% Transform coordinate system to use X11 coordinates\n"); + append("% 1. Flip y-axis over by reversing the scale,\n"); + append("% 2. Translate the origin to the other side of the page,\n"); + append("% making the origin the upper left corner\n"); + append("1 -1 scale\n"); + format("0 %d translate\n", -setupPtr->paperHeight); + + // Set Origin + format("%% Set origin\n%d %d translate\n\n", setupPtr->left,setupPtr->bottom); + if (ops->landscape) + format("%% Landscape orientation\n0 %g translate\n-90 rotate\n", + ((double)graphPtr_->width_ * setupPtr->scale)); + + append("\n%%EndSetup\n\n"); + + return TCL_OK; +} + +void PSOutput::addComments(const char** comments) +{ + if (!comments) + return; + + for (const char** pp = comments; *pp; pp+=2) { + if (*(pp+1) == NULL) + break; + format("%% %s: %s\n", *pp, *(pp+1)); + } +} + +unsigned char PSOutput::reverseBits(unsigned char byte) +{ + byte = ((byte >> 1) & 0x55) | ((byte << 1) & 0xaa); + byte = ((byte >> 2) & 0x33) | ((byte << 2) & 0xcc); + byte = ((byte >> 4) & 0x0f) | ((byte << 4) & 0xf0); + return byte; +} + +void PSOutput::byteToHex(unsigned char byte, char* string) +{ + static char hexDigits[] = "0123456789ABCDEF"; + + string[0] = hexDigits[byte >> 4]; + string[1] = hexDigits[byte & 0x0F]; +} + +void PSOutput::prolog() +{ + append( +"%%BeginProlog\n" +"%\n" +"% PostScript prolog file of the BLT graph widget.\n" +"%\n" +"% Copyright 1989-1992 Regents of the University of California.\n" +"% Permission to use, copy, modify, and distribute this\n" +"% software and its documentation for any purpose and without\n" +"% fee is hereby granted, provided that the above copyright\n" +"% notice appear in all copies. The University of California\n" +"% makes no representations about the suitability of this\n" +"% software for any purpose. It is provided 'as is' without\n" +"% express or implied warranty.\n" +"%\n" +"% Copyright 1991-1997 Bell Labs Innovations for Lucent Technologies.\n" +"%\n" +"% Permission to use, copy, modify, and distribute this software and its\n" +"% documentation for any purpose and without fee is hereby granted, provided\n" +"% that the above copyright notice appear in all copies and that both that the\n" +"% copyright notice and warranty disclaimer appear in supporting documentation,\n" +"% and that the names of Lucent Technologies any of their entities not be used\n" +"% in advertising or publicity pertaining to distribution of the software\n" +"% without specific, written prior permission.\n" +"%\n" +"% Lucent Technologies disclaims all warranties with regard to this software,\n" +"% including all implied warranties of merchantability and fitness. In no event\n" +"% shall Lucent Technologies be liable for any special, indirect or\n" +"% consequential damages or any damages whatsoever resulting from loss of use,\n" +"% data or profits, whether in an action of contract, negligence or other\n" +"% tortuous action, arising out of or in connection with the use or performance\n" +"% of this software.\n" +"%\n" +"\n" +"200 dict begin\n" +"\n" +"/BaseRatio 1.3467736870885982 def % Ratio triangle base / symbol size\n" +"/DrawSymbolProc 0 def % Routine to draw symbol outline/fill\n" +"/DashesProc 0 def % Dashes routine (line segments)\n" +"\n" +"% Define the array ISOLatin1Encoding (which specifies how characters are \n" +"% encoded for ISO-8859-1 fonts), if it isn't already present (Postscript \n" +"% level 2 is supposed to define it, but level 1 doesn't). \n" +"\n" +"systemdict /ISOLatin1Encoding known not { \n" +" /ISOLatin1Encoding [ \n" +" /space /space /space /space /space /space /space /space \n" +" /space /space /space /space /space /space /space /space \n" +" /space /space /space /space /space /space /space /space \n" +" /space /space /space /space /space /space /space /space \n" +" /space /exclam /quotedbl /numbersign /dollar /percent /ampersand \n" +" /quoteright \n" +" /parenleft /parenright /asterisk /plus /comma /minus /period /slash \n" +" /zero /one /two /three /four /five /six /seven \n" +" /eight /nine /colon /semicolon /less /equal /greater /question \n" +" /at /A /B /C /D /E /F /G \n" +" /H /I /J /K /L /M /N /O \n" +" /P /Q /R /S /T /U /V /W \n" +" /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore \n" +" /quoteleft /a /b /c /d /e /f /g \n" +" /h /i /j /k /l /m /n /o \n" +" /p /q /r /s /t /u /v /w \n" +" /x /y /z /braceleft /bar /braceright /asciitilde /space \n" +" /space /space /space /space /space /space /space /space \n" +" /space /space /space /space /space /space /space /space \n" +" /dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent \n" +" /dieresis /space /ring /cedilla /space /hungarumlaut /ogonek /caron \n" +" /space /exclamdown /cent /sterling /currency /yen /brokenbar /section \n" +" /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen \n" +" /registered /macron \n" +" /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph \n" +" /periodcentered \n" +" /cedillar /onesuperior /ordmasculine /guillemotright /onequarter \n" +" /onehalf /threequarters /questiondown \n" +" /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla \n" +" /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex \n" +" /Idieresis \n" +" /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply \n" +" /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn \n" +" /germandbls \n" +" /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla \n" +" /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex \n" +" /idieresis \n" +" /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide \n" +" /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn \n" +" /ydieresis \n" +" ] def \n" +"} if \n" +"\n" +"% font ISOEncode font \n" +"% This procedure changes the encoding of a font from the default \n" +"% Postscript encoding to ISOLatin1. It is typically invoked just \n" +"% before invoking 'setfont'. The body of this procedure comes from \n" +"% Section 5.6.1 of the Postscript book. \n" +"\n" +"/ISOEncode { \n" +" dup length dict\n" +" begin \n" +" {1 index /FID ne {def} {pop pop} ifelse} forall \n" +" /Encoding ISOLatin1Encoding def \n" +" currentdict \n" +" end \n" +"\n" +" % I'm not sure why it's necessary to use 'definefont' on this new \n" +" % font, but it seems to be important; just use the name 'Temporary' \n" +" % for the font. \n" +"\n" +" /Temporary exch definefont \n" +"} bind def \n" +"\n" +"/Stroke {\n" +" gsave\n" +" stroke\n" +" grestore\n" +"} def\n" +"\n" +"/Fill {\n" +" gsave\n" +" fill\n" +" grestore\n" +"} def\n" +"\n" +"/SetFont { \n" +" % Stack: pointSize fontName\n" +" findfont exch scalefont ISOEncode setfont\n" +"} def\n" +"\n" +"/Box {\n" +" % Stack: x y width height\n" +" newpath\n" +" exch 4 2 roll moveto\n" +" dup 0 rlineto\n" +" exch 0 exch rlineto\n" +" neg 0 rlineto\n" +" closepath\n" +"} def\n" +"\n" +"/LS { % Stack: x1 y1 x2 y2\n" +" newpath \n" +" 4 2 roll moveto \n" +" lineto \n" +" closepath\n" +" stroke\n" +"} def\n" +"\n" +"/baselineSampler ( TXygqPZ) def\n" +"% Put an extra-tall character in; done this way to avoid encoding trouble\n" +"baselineSampler 0 196 put\n" +"\n" +"/cstringshow {\n" +" {\n" +" dup type /stringtype eq\n" +" { show } { glyphshow }\n" +" ifelse\n" +" } forall\n" +"} bind def\n" +"\n" +"/cstringwidth {\n" +" 0 exch 0 exch\n" +" {\n" +" dup type /stringtype eq\n" +" { stringwidth } {\n" +" currentfont /Encoding get exch 1 exch put (\001)\n" +" stringwidth\n" +" }\n" +" ifelse\n" +" exch 3 1 roll add 3 1 roll add exch\n" +" } forall\n" +"} bind def\n" +"\n" +"/DrawText {\n" +" gsave\n" +" /justify exch def\n" +" /yoffset exch def\n" +" /xoffset exch def\n" +" /strings exch def\n" +" % Compute the baseline offset and the actual font height.\n" +" 0 0 moveto baselineSampler false charpath\n" +" pathbbox dup /baseline exch def\n" +" exch pop exch sub /height exch def pop\n" +" newpath\n" +" % overall width\n" +" /ww 0 def\n" +" strings {\n" +" cstringwidth pop\n" +" dup ww gt {/ww exch def} {pop} ifelse\n" +" newpath\n" +" } forall\n" +" % overall height\n" +" /hh 0 def\n" +" strings length height mul /hh exch def\n" +" newpath\n" +" % Translate to x,y\n" +" translate\n" +" % Translate to offset\n" +" ww xoffset mul hh yoffset mul translate\n" +" % rotate\n" +" ww 2 div hh 2 div translate\n" +" neg rotate\n" +" ww -2 div hh -2 div translate\n" +" % Translate to justify and baseline\n" +" justify ww mul baseline translate\n" +" % For each line, justify and display\n" +" strings {\n" +" dup cstringwidth pop\n" +" justify neg mul 0 moveto\n" +" gsave\n" +" 1 -1 scale\n" +" cstringshow\n" +" grestore\n" +" 0 height translate\n" +" } forall\n" +" grestore\n" +"} bind def \n" +"\n" +"% Symbols:\n" +"\n" +"% Skinny-cross\n" +"/Sc {\n" +" % Stack: x y symbolSize\n" +" gsave\n" +" 3 -2 roll translate 45 rotate\n" +" 0 0 3 -1 roll Sp\n" +" grestore\n" +"} def\n" +"\n" +"% Skinny-plus\n" +"/Sp {\n" +" % Stack: x y symbolSize\n" +" gsave\n" +" 3 -2 roll translate\n" +" 2 div\n" +" dup 2 copy\n" +" newpath \n" +" neg 0 \n" +" moveto 0 \n" +" lineto\n" +" DrawSymbolProc\n" +" newpath \n" +" neg 0 \n" +" exch moveto 0 \n" +" exch lineto\n" +" DrawSymbolProc\n" +" grestore\n" +"} def\n" +"\n" +"% Cross\n" +"/Cr {\n" +" % Stack: x y symbolSize\n" +" gsave\n" +" 3 -2 roll translate 45 rotate\n" +" 0 0 3 -1 roll Pl\n" +" grestore\n" +"} def\n" +"\n" +"% Plus\n" +"/Pl {\n" +" % Stack: x y symbolSize\n" +" gsave\n" +" 3 -2 roll translate\n" +" dup 2 div\n" +" exch 6 div\n" +"\n" +" %\n" +" % 2 3 The plus/cross symbol is a\n" +" % closed polygon of 12 points.\n" +" % 0 1 4 5 The diagram to the left\n" +" % x,y represents the positions of\n" +" % 11 10 7 6 the points which are computed\n" +" % below.\n" +" % 9 8\n" +" %\n" +"\n" +" newpath\n" +" 2 copy exch neg exch neg moveto \n" +" dup neg dup lineto\n" +" 2 copy neg exch neg lineto\n" +" 2 copy exch neg lineto\n" +" dup dup neg lineto \n" +" 2 copy neg lineto 2 copy lineto\n" +" dup dup lineto \n" +" 2 copy exch lineto \n" +" 2 copy neg exch lineto\n" +" dup dup neg exch lineto \n" +" exch neg exch lineto\n" +" closepath\n" +" DrawSymbolProc\n" +" grestore\n" +"} def\n" +"\n" +"% Circle\n" +"/Ci {\n" +" % Stack: x y symbolSize\n" +" gsave\n" +" 3 copy pop moveto \n" +" newpath\n" +" 2 div 0 360 arc\n" +" closepath \n" +" DrawSymbolProc\n" +" grestore\n" +"} def\n" +"\n" +"% Square\n" +"/Sq {\n" +" % Stack: x y symbolSize\n" +" gsave\n" +" dup dup 2 div dup\n" +" 6 -1 roll exch sub exch\n" +" 5 -1 roll exch sub 4 -2 roll Box\n" +" DrawSymbolProc\n" +" grestore\n" +"} def\n" +"\n" +"% Line\n" +"/Li {\n" +" % Stack: x y symbolSize\n" +" gsave\n" +" 3 1 roll exch 3 -1 roll 2 div 3 copy\n" +" newpath\n" +" sub exch moveto \n" +" add exch lineto\n" +" closepath\n" +" stroke\n" +" grestore\n" +"} def\n" +"\n" +"% Diamond\n" +"/Di {\n" +" % Stack: x y symbolSize\n" +" gsave\n" +" 3 1 roll translate 45 rotate 0 0 3 -1 roll Sq\n" +" grestore\n" +"} def\n" +" \n" +"% Triangle\n" +"/Tr {\n" +" % Stack: x y symbolSize\n" +" gsave\n" +" 3 -2 roll translate\n" +" BaseRatio mul 0.5 mul % Calculate 1/2 base\n" +" dup 0 exch 30 cos mul % h1 = height above center point\n" +" neg % b2 0 -h1\n" +" newpath \n" +" moveto % point 1; b2\n" +" dup 30 sin 30 cos div mul % h2 = height below center point\n" +" 2 copy lineto % point 2; b2 h2\n" +" exch neg exch lineto % \n" +" closepath\n" +" DrawSymbolProc\n" +" grestore\n" +"} def\n" +"\n" +"% Arrow\n" +"/Ar {\n" +" % Stack: x y symbolSize\n" +" gsave\n" +" 3 -2 roll translate\n" +" BaseRatio mul 0.5 mul % Calculate 1/2 base\n" +" dup 0 exch 30 cos mul % h1 = height above center point\n" +" % b2 0 h1\n" +" newpath moveto % point 1; b2\n" +" dup 30 sin 30 cos div mul % h2 = height below center point\n" +" neg % -h2 b2\n" +" 2 copy lineto % point 2; b2 h2\n" +" exch neg exch lineto % \n" +" closepath\n" +" DrawSymbolProc\n" +" grestore\n" +"} def\n" +"\n" +"%%EndProlog\n" +); +} diff --git a/generic/tkbltGrPSOutput.h b/generic/tkbltGrPSOutput.h new file mode 100644 index 0000000..04bb2fd --- /dev/null +++ b/generic/tkbltGrPSOutput.h @@ -0,0 +1,90 @@ +/* + * 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 1993-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. + */ + +#ifndef __Blt_GrPSOutput_h__ +#define __Blt_GrPSOutput_h__ + +#include + +#define POSTSCRIPT_BUFSIZ ((BUFSIZ*2)-1) + +namespace Blt { + class Graph; + class Postscript; + + class PSOutput { + protected: + Graph* graphPtr_; + Tcl_DString dString_; + char scratchArr_[POSTSCRIPT_BUFSIZ+1]; + + protected: + void addComments(const char**); + void printXColor(XColor*); + unsigned char reverseBits(unsigned char); + void byteToHex(unsigned char, char*); + void setJoinStyle(int); + void setCapStyle(int); + void prolog(); + + public: + PSOutput(Graph*); + virtual ~PSOutput(); + + void printPolyline(Point2d*, int); + void printMaxPolyline(Point2d*, int); + void printSegments(Segment2d*, int); + void printRectangle(double, double, int, int); + void printPolygon(Point2d*, int); + void print3DRectangle(Tk_3DBorder, double, double, int, int, int, int); + + void fillRectangle(double, double, int, int); + void fillRectangles(Rectangle*, int); + void fill3DRectangle(Tk_3DBorder, double, double, int, int, int, int); + void fillPolygon(Point2d*, int); + + void setFont(Tk_Font); + void setLineWidth(int); + void setBackground(XColor*); + void setForeground(XColor*); + void setBackground(Tk_3DBorder); + void setLineAttributes(XColor*,int, Dashes*, int, int); + void setClearBackground(); + void setDashes(Dashes*); + + int preamble(const char*); + void computeBBox(int, int); + const char* getValue(int*); + void append(const char*); + void format(const char*, ...); + void varAppend(const char*, ...); + }; +}; + +#endif diff --git a/generic/tkbltGrPen.C b/generic/tkbltGrPen.C new file mode 100644 index 0000000..71e5fe9 --- /dev/null +++ b/generic/tkbltGrPen.C @@ -0,0 +1,62 @@ +/* + * 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 1993-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 + +#include "tkbltGrPen.h" +#include "tkbltGraph.h" + +using namespace Blt; + +Pen::Pen(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) +{ + optionTable_ = NULL; + ops_ = NULL; + graphPtr_ = graphPtr; + name_ = dupstr(name); + hashPtr_ = hPtr; + refCount_ =0; + flags =0; + manageOptions_ =0; +} + +Pen::~Pen() +{ + delete [] name_; + + if (hashPtr_) + Tcl_DeleteHashEntry(hashPtr_); + + // PenOptions* ops = (PenOptions*)ops_; + + Tk_FreeConfigOptions((char*)ops_, optionTable_, graphPtr_->tkwin_); + + if (manageOptions_) + free(ops_); +} diff --git a/generic/tkbltGrPen.h b/generic/tkbltGrPen.h new file mode 100644 index 0000000..003e8dd --- /dev/null +++ b/generic/tkbltGrPen.h @@ -0,0 +1,79 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrPen_h__ +#define __BltGrPen_h__ + +#include + +#include "tkbltGrText.h" + +namespace Blt { + class Graph; + + typedef struct { + int errorBarShow; + int errorBarLineWidth; + int errorBarCapWidth; + XColor* errorBarColor; + int valueShow; + const char* valueFormat; + TextStyleOptions valueStyle; + } PenOptions; + + class Pen { + protected: + Tk_OptionTable optionTable_; + void* ops_; + + public: + Graph* graphPtr_; + const char *name_; + Tcl_HashEntry *hashPtr_; + int refCount_; + unsigned int flags; + int manageOptions_; + + public: + Pen(); + Pen(Graph*, const char*, Tcl_HashEntry*); + virtual ~Pen(); + + virtual ClassId classId() =0; + virtual const char* className() =0; + virtual const char* typeName() =0; + + Tk_OptionTable optionTable() {return optionTable_;} + void* ops() {return ops_;} + + virtual int configure() =0; + }; +}; + +#endif diff --git a/generic/tkbltGrPenBar.C b/generic/tkbltGrPenBar.C new file mode 100644 index 0000000..8f6e59a --- /dev/null +++ b/generic/tkbltGrPenBar.C @@ -0,0 +1,174 @@ +/* + * 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 1993-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 + +#include "tkbltGrPenBar.h" +#include "tkbltGraph.h" +#include "tkbltGrDef.h" +#include "tkbltConfig.h" + +using namespace Blt; + +static Tk_OptionSpec barPenOptionSpecs[] = { + {TK_OPTION_SYNONYM, "-background", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-color", 0}, + {TK_OPTION_SYNONYM, "-bd", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-borderwidth", 0}, + {TK_OPTION_SYNONYM, "-bg", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-color", 0}, + {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", + STD_BORDERWIDTH, -1, Tk_Offset(BarPenOptions, borderWidth), 0, NULL, CACHE}, + {TK_OPTION_BORDER, "-color", "color", "Color", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(BarPenOptions, fill), 0, NULL, CACHE}, + {TK_OPTION_COLOR, "-errorbarcolor", "errorBarColor", "ErrorBarColor", + NULL, -1, Tk_Offset(BarPenOptions, errorBarColor), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_PIXELS, "-errorbarwidth", "errorBarWidth","ErrorBarWidth", + "1", -1, Tk_Offset(BarPenOptions, errorBarLineWidth), 0, NULL, CACHE}, + {TK_OPTION_PIXELS, "-errorbarcap", "errorBarCap", "ErrorBarCap", + "0", -1, Tk_Offset(BarPenOptions, errorBarCapWidth), 0, NULL, LAYOUT}, + {TK_OPTION_SYNONYM, "-fg", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-outline", 0}, + {TK_OPTION_SYNONYM, "-fill", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-color", 0}, + {TK_OPTION_SYNONYM, "-foreground", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-outline", 0}, + {TK_OPTION_COLOR, "-outline", "outline", "Outline", + NULL, -1, Tk_Offset(BarPenOptions, outlineColor), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_RELIEF, "-relief", "relief", "Relief", + "raised", -1, Tk_Offset(BarPenOptions, relief), 0, NULL, LAYOUT}, + {TK_OPTION_STRING_TABLE, "-showerrorbars", "showErrorBars", "ShowErrorBars", + "both", -1, Tk_Offset(BarPenOptions, errorBarShow), + 0, &fillObjOption, LAYOUT}, + {TK_OPTION_STRING_TABLE, "-showvalues", "showValues", "ShowValues", + "none", -1, Tk_Offset(BarPenOptions, valueShow), 0, &fillObjOption, CACHE}, + {TK_OPTION_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor", + "s", -1, Tk_Offset(BarPenOptions, valueStyle.anchor), 0, NULL, CACHE}, + {TK_OPTION_COLOR, "-valuecolor", "valueColor", "ValueColor", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(BarPenOptions, valueStyle.color), + 0, NULL, CACHE}, + {TK_OPTION_FONT, "-valuefont", "valueFont", "ValueFont", + STD_FONT_SMALL, -1, Tk_Offset(BarPenOptions, valueStyle.font), + 0, NULL, CACHE}, + {TK_OPTION_STRING, "-valueformat", "valueFormat", "ValueFormat", + "%g", -1, Tk_Offset(BarPenOptions, valueFormat), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate", + "0", -1, Tk_Offset(BarPenOptions, valueStyle.angle), 0, NULL, CACHE}, + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} +}; + +BarPen::BarPen(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) + : Pen(graphPtr, name, hPtr) +{ + ops_ = calloc(1, sizeof(BarPenOptions)); + BarPenOptions* ops = (BarPenOptions*)ops_; + manageOptions_ =1; + + outlineGC_ =NULL; + errorBarGC_ =NULL; + + ops->valueStyle.anchor =TK_ANCHOR_NW; + ops->valueStyle.color =NULL; + ops->valueStyle.font =NULL; + ops->valueStyle.angle =0; + ops->valueStyle.justify =TK_JUSTIFY_LEFT; + + optionTable_ = Tk_CreateOptionTable(graphPtr_->interp_, barPenOptionSpecs); +} + +BarPen::BarPen(Graph* graphPtr, const char* name, void* options) + : Pen(graphPtr, name, NULL) +{ + ops_ = options; + BarPenOptions* ops = (BarPenOptions*)ops_; + manageOptions_ =0; + + outlineGC_ =NULL; + errorBarGC_ =NULL; + + ops->valueStyle.anchor =TK_ANCHOR_NW; + ops->valueStyle.color =NULL; + ops->valueStyle.font =NULL; + ops->valueStyle.angle =0; + ops->valueStyle.justify =TK_JUSTIFY_LEFT; + + optionTable_ = Tk_CreateOptionTable(graphPtr_->interp_, barPenOptionSpecs); +} + +BarPen::~BarPen() +{ + if (outlineGC_) + Tk_FreeGC(graphPtr_->display_, outlineGC_); + if (errorBarGC_) + Tk_FreeGC(graphPtr_->display_, errorBarGC_); +} + +int BarPen::configure() +{ + BarPenOptions* ops = (BarPenOptions*)ops_; + + // outlineGC + { + unsigned long gcMask = GCForeground | GCLineWidth; + XGCValues gcValues; + gcValues.line_width = ops->borderWidth; + if (ops->outlineColor) + gcValues.foreground = ops->outlineColor->pixel; + else if (ops->fill) + gcValues.foreground = Tk_3DBorderColor(ops->fill)->pixel; + GC newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); + if (outlineGC_) + Tk_FreeGC(graphPtr_->display_, outlineGC_); + outlineGC_ = newGC; + } + + // errorBarGC + { + unsigned long gcMask = GCForeground | GCLineWidth; + XGCValues gcValues; + if (ops->errorBarColor) + gcValues.foreground = ops->errorBarColor->pixel; + else if (ops->outlineColor) + gcValues.foreground = ops->outlineColor->pixel; + else if (ops->fill) + gcValues.foreground = Tk_3DBorderColor(ops->fill)->pixel; + + gcValues.line_width = ops->errorBarLineWidth; + GC newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); + if (errorBarGC_) + Tk_FreeGC(graphPtr_->display_, errorBarGC_); + errorBarGC_ = newGC; + } + + return TCL_OK; +} + diff --git a/generic/tkbltGrPenBar.h b/generic/tkbltGrPenBar.h new file mode 100644 index 0000000..f39db94 --- /dev/null +++ b/generic/tkbltGrPenBar.h @@ -0,0 +1,73 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrPenBar_h__ +#define __BltGrPenBar_h__ + +#include + +#include "tkbltGrPen.h" + +namespace Blt { + + typedef struct { + int errorBarShow; + int errorBarLineWidth; + int errorBarCapWidth; + XColor* errorBarColor; + int valueShow; + const char *valueFormat; + TextStyleOptions valueStyle; + + XColor* outlineColor; + Tk_3DBorder fill; + int borderWidth; + int relief; + } BarPenOptions; + + class BarPen : public Pen { + public: + GC fillGC_; + GC outlineGC_; + GC errorBarGC_; + + public: + BarPen(Graph*, const char*, Tcl_HashEntry*); + BarPen(Graph*, const char*, void*); + virtual ~BarPen(); + + ClassId classId() {return CID_ELEM_BAR;} + const char* className() {return "BarElement";} + const char* typeName() {return "bar";} + + int configure(); + }; +}; + +#endif diff --git a/generic/tkbltGrPenLine.C b/generic/tkbltGrPenLine.C new file mode 100644 index 0000000..5f15ce8 --- /dev/null +++ b/generic/tkbltGrPenLine.C @@ -0,0 +1,243 @@ +/* + * 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 1993-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 +#include + +#include "tkbltGrPenLine.h" +#include "tkbltGraph.h" +#include "tkbltGrMisc.h" +#include "tkbltGrDef.h" +#include "tkbltConfig.h" + +using namespace Blt; + +const char* symbolObjOption[] = + {"none", "square", "circle", "diamond", "plus", "cross", "splus", "scross", "triangle", "arrow", NULL}; + +// Defs + +static Tk_OptionSpec linePenOptionSpecs[] = { + {TK_OPTION_COLOR, "-color", "color", "Color", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(LinePenOptions, traceColor), + 0, NULL, CACHE}, + {TK_OPTION_CUSTOM, "-dashes", "dashes", "Dashes", + NULL, -1, Tk_Offset(LinePenOptions, traceDashes), + TK_OPTION_NULL_OK, &dashesObjOption, CACHE}, + {TK_OPTION_COLOR, "-errorbarcolor", "errorBarColor", "ErrorBarColor", + NULL, -1, Tk_Offset(LinePenOptions, errorBarColor), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_PIXELS, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth", + "1", -1, Tk_Offset(LinePenOptions, errorBarLineWidth), 0, NULL, CACHE}, + {TK_OPTION_PIXELS, "-errorbarcap", "errorBarCap", "ErrorBarCap", + "0", -1, Tk_Offset(LinePenOptions, errorBarCapWidth), 0, NULL, LAYOUT}, + {TK_OPTION_COLOR, "-fill", "fill", "Fill", + NULL, -1, Tk_Offset(LinePenOptions, symbol.fillColor), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_PIXELS, "-linewidth", "lineWidth", "LineWidth", + "1", -1, Tk_Offset(LinePenOptions, traceWidth), 0, NULL, CACHE}, + {TK_OPTION_COLOR, "-offdash", "offDash", "OffDash", + NULL, -1, Tk_Offset(LinePenOptions, traceOffColor), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_COLOR, "-outline", "outline", "Outline", + NULL, -1, Tk_Offset(LinePenOptions, symbol.outlineColor), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_PIXELS, "-outlinewidth", "outlineWidth", "OutlineWidth", + "1", -1, Tk_Offset(LinePenOptions, symbol.outlineWidth), 0, NULL, CACHE}, + {TK_OPTION_PIXELS, "-pixels", "pixels", "Pixels", + "0.1i", -1, Tk_Offset(LinePenOptions, symbol.size), 0, NULL, LAYOUT}, + {TK_OPTION_STRING_TABLE, "-showerrorbars", "showErrorBars", "ShowErrorBars", + "both", -1, Tk_Offset(LinePenOptions, errorBarShow), + 0, &fillObjOption, LAYOUT}, + {TK_OPTION_STRING_TABLE, "-showvalues", "showValues", "ShowValues", + "none", -1, Tk_Offset(LinePenOptions, valueShow), 0, &fillObjOption, CACHE}, + {TK_OPTION_STRING_TABLE, "-symbol", "symbol", "Symbol", + "none", -1, Tk_Offset(LinePenOptions, symbol), 0, &symbolObjOption, CACHE}, + {TK_OPTION_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor", + "s", -1, Tk_Offset(LinePenOptions, valueStyle.anchor), 0, NULL, CACHE}, + {TK_OPTION_COLOR, "-valuecolor", "valueColor", "ValueColor", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(LinePenOptions, valueStyle.color), + 0, NULL, CACHE}, + {TK_OPTION_FONT, "-valuefont", "valueFont", "ValueFont", + STD_FONT_SMALL, -1, Tk_Offset(LinePenOptions, valueStyle.font), + 0, NULL, CACHE}, + {TK_OPTION_STRING, "-valueformat", "valueFormat", "ValueFormat", + "%g", -1, Tk_Offset(LinePenOptions, valueFormat), + TK_OPTION_NULL_OK, NULL, CACHE}, + {TK_OPTION_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate", + "0", -1, Tk_Offset(LinePenOptions, valueStyle.angle), 0, NULL, CACHE}, + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} +}; + +LinePen::LinePen(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) + : Pen(graphPtr, name, hPtr) +{ + ops_ = calloc(1, sizeof(LinePenOptions)); + LinePenOptions* ops = (LinePenOptions*)ops_; + manageOptions_ =1; + + traceGC_ =NULL; + errorBarGC_ =NULL; + + ops->symbol.type = SYMBOL_NONE; + + ops->valueStyle.anchor =TK_ANCHOR_NW; + ops->valueStyle.color =NULL; + ops->valueStyle.font =NULL; + ops->valueStyle.angle =0; + ops->valueStyle.justify =TK_JUSTIFY_LEFT; + + optionTable_ = Tk_CreateOptionTable(graphPtr_->interp_, linePenOptionSpecs); +} + +LinePen::LinePen(Graph* graphPtr, const char* name, void* options) + : Pen(graphPtr, name, NULL) +{ + ops_ = options; + LinePenOptions* ops = (LinePenOptions*)ops_; + manageOptions_ =0; + + traceGC_ =NULL; + errorBarGC_ =NULL; + + ops->symbol.type = SYMBOL_NONE; + + ops->valueStyle.anchor =TK_ANCHOR_NW; + ops->valueStyle.color =NULL; + ops->valueStyle.font =NULL; + ops->valueStyle.angle =0; + ops->valueStyle.justify =TK_JUSTIFY_LEFT; + + optionTable_ = Tk_CreateOptionTable(graphPtr_->interp_, linePenOptionSpecs); +} + +LinePen::~LinePen() +{ + LinePenOptions* ops = (LinePenOptions*)ops_; + + if (errorBarGC_) + Tk_FreeGC(graphPtr_->display_, errorBarGC_); + + if (traceGC_) + graphPtr_->freePrivateGC(traceGC_); + + if (ops->symbol.outlineGC) + Tk_FreeGC(graphPtr_->display_, ops->symbol.outlineGC); + + if (ops->symbol.fillGC) + Tk_FreeGC(graphPtr_->display_, ops->symbol.fillGC); +} + +int LinePen::configure() +{ + LinePenOptions* ops = (LinePenOptions*)ops_; + + // symbol outline + { + unsigned long gcMask = (GCLineWidth | GCForeground); + XColor* colorPtr = ops->symbol.outlineColor; + if (!colorPtr) + colorPtr = ops->traceColor; + XGCValues gcValues; + gcValues.foreground = colorPtr->pixel; + gcValues.line_width = ops->symbol.outlineWidth; + GC newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); + if (ops->symbol.outlineGC) + Tk_FreeGC(graphPtr_->display_, ops->symbol.outlineGC); + ops->symbol.outlineGC = newGC; + } + + // symbol fill + { + unsigned long gcMask = (GCLineWidth | GCForeground); + XColor* colorPtr = ops->symbol.fillColor; + if (!colorPtr) + colorPtr = ops->traceColor; + GC newGC = NULL; + XGCValues gcValues; + if (colorPtr) { + gcValues.foreground = colorPtr->pixel; + newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); + } + if (ops->symbol.fillGC) + Tk_FreeGC(graphPtr_->display_, ops->symbol.fillGC); + ops->symbol.fillGC = newGC; + } + + // trace + { + unsigned long gcMask = + (GCLineWidth | GCForeground | GCLineStyle | GCCapStyle | GCJoinStyle); + XGCValues gcValues; + gcValues.cap_style = CapButt; + gcValues.join_style = JoinRound; + gcValues.line_style = LineSolid; + gcValues.line_width = ops->traceWidth; + + gcValues.foreground = ops->traceColor->pixel; + XColor* colorPtr = ops->traceOffColor; + if (colorPtr) { + gcMask |= GCBackground; + gcValues.background = colorPtr->pixel; + } + if (LineIsDashed(ops->traceDashes)) { + gcValues.line_width = ops->traceWidth; + gcValues.line_style = !colorPtr ? LineOnOffDash : LineDoubleDash; + } + GC newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); + if (traceGC_) + graphPtr_->freePrivateGC(traceGC_); + + if (LineIsDashed(ops->traceDashes)) { + ops->traceDashes.offset = ops->traceDashes.values[0] / 2; + graphPtr_->setDashes(newGC, &ops->traceDashes); + } + traceGC_ = newGC; + } + + // errorbar + { + unsigned long gcMask = (GCLineWidth | GCForeground); + XColor* colorPtr = ops->errorBarColor; + if (!colorPtr) + colorPtr = ops->traceColor; + XGCValues gcValues; + gcValues.line_width = ops->errorBarLineWidth; + gcValues.foreground = colorPtr->pixel; + GC newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); + if (errorBarGC_) { + Tk_FreeGC(graphPtr_->display_, errorBarGC_); + } + errorBarGC_ = newGC; + } + + return TCL_OK; +} + + diff --git a/generic/tkbltGrPenLine.h b/generic/tkbltGrPenLine.h new file mode 100644 index 0000000..63eeeb8 --- /dev/null +++ b/generic/tkbltGrPenLine.h @@ -0,0 +1,88 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrPenLine_h__ +#define __BltGrPenLine_h__ + +#include "tkbltGrPen.h" + +namespace Blt { + + typedef enum { + SYMBOL_NONE, SYMBOL_SQUARE, SYMBOL_CIRCLE, SYMBOL_DIAMOND, SYMBOL_PLUS, + SYMBOL_CROSS, SYMBOL_SPLUS, SYMBOL_SCROSS, SYMBOL_TRIANGLE, SYMBOL_ARROW + } SymbolType; + + typedef struct { + SymbolType type; + int size; + XColor* outlineColor; + int outlineWidth; + GC outlineGC; + XColor* fillColor; + GC fillGC; + } Symbol; + + typedef struct { + int errorBarShow; + int errorBarLineWidth; + int errorBarCapWidth; + XColor* errorBarColor; + int valueShow; + const char* valueFormat; + TextStyleOptions valueStyle; + + Symbol symbol; + int traceWidth; + Dashes traceDashes; + XColor* traceColor; + XColor* traceOffColor; + } LinePenOptions; + + class LinePen : public Pen { + public: + GC traceGC_; + GC errorBarGC_; + + public: + LinePen(Graph*, const char*, Tcl_HashEntry*); + LinePen(Graph*, const char*, void*); + virtual ~LinePen(); + + ClassId classId() {return CID_ELEM_LINE;} + const char* className() {return "LineElement";} + const char* typeName() {return "line";} + + int configure(); + }; +}; + +extern const char* symbolObjOption[]; + +#endif diff --git a/generic/tkbltGrPenOp.C b/generic/tkbltGrPenOp.C new file mode 100644 index 0000000..8c5669d --- /dev/null +++ b/generic/tkbltGrPenOp.C @@ -0,0 +1,217 @@ +/* + * 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 1996-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 "tkbltGraph.h" +#include "tkbltGrPen.h" +#include "tkbltGrPenOp.h" +#include "tkbltGrPenLine.h" +#include "tkbltGrPenBar.h" + +using namespace Blt; + +int Blt::PenObjConfigure(Graph* graphPtr, Pen* penPtr, + 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*)penPtr->ops(), penPtr->optionTable(), + objc, objv, graphPtr->tkwin_, &savedOptions, &mask) + != TCL_OK) + continue; + } + else { + errorResult = Tcl_GetObjResult(interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + } + + if (penPtr->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 != 5) { + Tcl_WrongNumArgs(interp, 3, objv, "cget option"); + return TCL_ERROR; + } + + Pen* penPtr; + if (graphPtr->getPen(objv[3], &penPtr) != TCL_OK) + return TCL_ERROR; + + Tcl_Obj* objPtr = Tk_GetOptionValue(interp, + (char*)penPtr->ops(), + penPtr->optionTable(), + objv[4], 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<4) + return TCL_ERROR; + + Pen* penPtr; + if (graphPtr->getPen(objv[3], &penPtr) != TCL_OK) + return TCL_ERROR; + + if (objc <= 5) { + Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)penPtr->ops(), + penPtr->optionTable(), + (objc == 5) ? objv[4] : NULL, + graphPtr->tkwin_); + if (objPtr == NULL) + return TCL_ERROR; + else + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; + } + else + return PenObjConfigure(graphPtr, penPtr, interp, objc-4, objv+4); +} + +static int CreateOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + if (graphPtr->createPen(Tcl_GetString(objv[3]), objc, objv) != TCL_OK) + return TCL_ERROR; + Tcl_SetObjResult(interp, objv[3]); + + return TCL_OK; +} + +static int DeleteOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Pen* penPtr; + if (graphPtr->getPen(objv[3], &penPtr) != TCL_OK) + return TCL_ERROR; + + if (penPtr->refCount_ == 0) + delete penPtr; + + return TCL_OK; +} + +static int NamesOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + if (objc == 3) { + Tcl_HashSearch iter; + for (Tcl_HashEntry *hPtr=Tcl_FirstHashEntry(&graphPtr->penTable_, &iter); + hPtr; hPtr=Tcl_NextHashEntry(&iter)) { + Pen* penPtr = (Pen*)Tcl_GetHashValue(hPtr); + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewStringObj(penPtr->name_, -1)); + } + } + else { + Tcl_HashSearch iter; + for (Tcl_HashEntry *hPtr=Tcl_FirstHashEntry(&graphPtr->penTable_, &iter); + hPtr; hPtr=Tcl_NextHashEntry(&iter)) { + Pen* penPtr = (Pen*)Tcl_GetHashValue(hPtr); + for (int ii=3; iiname_, pattern)) { + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewStringObj(penPtr->name_, -1)); + break; + } + } + } + } + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; +} + +static int TypeOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + if (objc<4) + return TCL_ERROR; + + Pen* penPtr; + if (graphPtr->getPen(objv[3], &penPtr) != TCL_OK) + return TCL_ERROR; + + Tcl_SetStringObj(Tcl_GetObjResult(interp), penPtr->typeName(), -1); + return TCL_OK; +} + +const Ensemble Blt::penEnsemble[] = { + {"cget", CgetOp, 0}, + {"configure", ConfigureOp, 0}, + {"create", CreateOp, 0}, + {"delete", DeleteOp, 0}, + {"names", NamesOp, 0}, + {"type", TypeOp, 0}, + { 0,0,0 } +}; + diff --git a/generic/tkbltGrPenOp.h b/generic/tkbltGrPenOp.h new file mode 100644 index 0000000..5dab592 --- /dev/null +++ b/generic/tkbltGrPenOp.h @@ -0,0 +1,42 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrPenOp_h__ +#define __BltGrPenOp_h__ + +#include "tkbltGraph.h" + +namespace Blt { + extern const Ensemble penEnsemble[]; + extern int PenObjConfigure(Blt::Graph* graphPtr, Blt::Pen* penPtr, + Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); +}; + +#endif diff --git a/generic/tkbltGrPenOption.C b/generic/tkbltGrPenOption.C new file mode 100644 index 0000000..b1da1b6 --- /dev/null +++ b/generic/tkbltGrPenOption.C @@ -0,0 +1,89 @@ +/* + * 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 1996-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 "tkbltGraph.h" +#include "tkbltGrPen.h" +#include "tkbltConfig.h" + +using namespace Blt; + +static Tk_CustomOptionSetProc PenSetProc; +static Tk_CustomOptionGetProc PenGetProc; +static Tk_CustomOptionFreeProc PenFreeProc; +Tk_ObjCustomOption penObjOption = + { + "pen", PenSetProc, PenGetProc, RestoreProc, PenFreeProc, NULL + }; + +static int PenSetProc(ClientData clientData, Tcl_Interp* interp, + Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, + int offset, char* savePtr, int flags) +{ + Pen** penPtrPtr = (Pen**)(widgRec + offset); + *(double*)savePtr = *(double*)penPtrPtr; + + if (!penPtrPtr) + return TCL_OK; + + const char* string = Tcl_GetString(*objPtr); + if (!string || !string[0]) { + *penPtrPtr = NULL; + return TCL_OK; + } + + Graph* graphPtr = getGraphFromWindowData(tkwin); + Pen* penPtr; + if (graphPtr->getPen(*objPtr, &penPtr) != TCL_OK) + return TCL_ERROR; + + penPtr->refCount_++; + *penPtrPtr = penPtr; + + return TCL_OK; +}; + +static Tcl_Obj* PenGetProc(ClientData clientData, Tk_Window tkwin, + char *widgRec, int offset) +{ + Pen* penPtr = *(Pen**)(widgRec + offset); + if (!penPtr) + return Tcl_NewStringObj("", -1); + + return Tcl_NewStringObj(penPtr->name_, -1); +}; + +static void PenFreeProc(ClientData clientData, Tk_Window tkwin, char *ptr) +{ + Pen* penPtr = *(Pen**)ptr; + if (penPtr) + if (penPtr->refCount_ > 0) + penPtr->refCount_--; +} + + diff --git a/generic/tkbltGrPostscript.C b/generic/tkbltGrPostscript.C new file mode 100644 index 0000000..4bbf504 --- /dev/null +++ b/generic/tkbltGrPostscript.C @@ -0,0 +1,84 @@ +/* + * 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 + +#include "tkbltGraph.h" +#include "tkbltGrPostscript.h" +#include "tkbltConfig.h" + +using namespace Blt; + +static Tk_OptionSpec optionSpecs[] = { + {TK_OPTION_BOOLEAN, "-center", "center", "Center", + "yes", -1, Tk_Offset(PostscriptOptions, center), 0, NULL, 0}, + {TK_OPTION_CUSTOM, "-comments", "comments", "Comments", + NULL, -1, Tk_Offset(PostscriptOptions, comments), + TK_OPTION_NULL_OK, &listObjOption, 0}, + {TK_OPTION_BOOLEAN, "-decorations", "decorations", "Decorations", + "yes", -1, Tk_Offset(PostscriptOptions, decorations), 0, NULL, 0}, + {TK_OPTION_BOOLEAN, "-footer", "footer", "Footer", + "no", -1, Tk_Offset(PostscriptOptions, footer), 0, NULL, 0}, + {TK_OPTION_BOOLEAN, "-greyscale", "greyscale", "Greyscale", + "no", -1, Tk_Offset(PostscriptOptions, greyscale), 0, NULL, 0}, + {TK_OPTION_PIXELS, "-height", "height", "Height", + "0", -1, Tk_Offset(PostscriptOptions, reqHeight), 0, NULL, 0}, + {TK_OPTION_BOOLEAN, "-landscape", "landscape", "Landscape", + "no", -1, Tk_Offset(PostscriptOptions, landscape), 0, NULL, 0}, + {TK_OPTION_INT, "-level", "level", "Level", + "2", -1, Tk_Offset(PostscriptOptions, level), 0, NULL, 0}, + {TK_OPTION_PIXELS, "-padx", "padX", "PadX", + "1.0i", -1, Tk_Offset(PostscriptOptions, xPad), 0, NULL, 0}, + {TK_OPTION_PIXELS, "-pady", "padY", "PadY", + "1.0i", -1, Tk_Offset(PostscriptOptions, yPad), 0, NULL, 0}, + {TK_OPTION_PIXELS, "-paperheight", "paperHeight", "PaperHeight", + "11.0i", -1, Tk_Offset(PostscriptOptions, reqPaperHeight), 0, NULL, 0}, + {TK_OPTION_PIXELS, "-paperwidth", "paperWidth", "PaperWidth", + "8.5i", -1, Tk_Offset(PostscriptOptions, reqPaperWidth), 0, NULL, 0}, + {TK_OPTION_PIXELS, "-width", "width", "Width", + "0", -1, Tk_Offset(PostscriptOptions, reqWidth), 0, NULL, 0}, + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} +}; + +Postscript::Postscript(Graph* graphPtr) +{ + ops_ = (PostscriptOptions*)calloc(1, sizeof(PostscriptOptions)); + graphPtr_ = graphPtr; + + optionTable_ =Tk_CreateOptionTable(graphPtr_->interp_, optionSpecs); + Tk_InitOptions(graphPtr_->interp_, (char*)ops_, optionTable_, + graphPtr_->tkwin_); +} + +Postscript::~Postscript() +{ + Tk_FreeConfigOptions((char*)ops_, optionTable_, graphPtr_->tkwin_); + free(ops_); +} + diff --git a/generic/tkbltGrPostscript.h b/generic/tkbltGrPostscript.h new file mode 100644 index 0000000..a0c35a1 --- /dev/null +++ b/generic/tkbltGrPostscript.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#ifndef __BltGrPostscript_h__ +#define __BltGrPostscript_h__ + +#include + +namespace Blt { + + typedef struct { + int center; + const char **comments; + int decorations; + int footer; + int greyscale; + int landscape; + int level; + int xPad; + int yPad; + int reqPaperWidth; + int reqPaperHeight; + int reqWidth; + int reqHeight; + } PostscriptOptions; + + class Postscript { + public: + Tk_OptionTable optionTable_; + void* ops_; + Graph* graphPtr_; + + int left; + int bottom; + int right; + int top; + double scale; + int paperHeight; + int paperWidth; + + public: + Postscript(Graph*); + virtual ~Postscript(); + }; +}; + +#endif diff --git a/generic/tkbltGrPostscriptOp.C b/generic/tkbltGrPostscriptOp.C new file mode 100644 index 0000000..931feb9 --- /dev/null +++ b/generic/tkbltGrPostscriptOp.C @@ -0,0 +1,183 @@ +/* + * 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 + +#include "tkbltGraph.h" +#include "tkbltGrPostscript.h" +#include "tkbltGrPostscriptOp.h" +#include "tkbltGrPSOutput.h" + +using namespace Blt; + +int Blt::PostscriptObjConfigure(Graph* graphPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Postscript* setupPtr = graphPtr->postscript_; + Tk_SavedOptions savedOptions; + int mask =0; + int error; + Tcl_Obj* errorResult; + + for (error=0; error<=1; error++) { + if (!error) { + if (Tk_SetOptions(interp, (char*)setupPtr->ops_, setupPtr->optionTable_, + objc, objv, graphPtr->tkwin_, &savedOptions, &mask) + != TCL_OK) + continue; + } + else { + errorResult = Tcl_GetObjResult(interp); + Tcl_IncrRefCount(errorResult); + Tk_RestoreSavedOptions(&savedOptions); + } + + 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 != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "cget option"); + return TCL_ERROR; + } + + Postscript *setupPtr = graphPtr->postscript_; + Tcl_Obj* objPtr = Tk_GetOptionValue(interp, + (char*)setupPtr->ops_, + setupPtr->optionTable_, + objv[3], 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; + Postscript* setupPtr = graphPtr->postscript_; + if (objc <= 4) { + Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)setupPtr->ops_, + setupPtr->optionTable_, + (objc == 4) ? objv[3] : NULL, + graphPtr->tkwin_); + if (objPtr == NULL) + return TCL_ERROR; + else + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; + } + else + return PostscriptObjConfigure(graphPtr, interp, objc-3, objv+3); +} + +static int OutputOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + + const char *fileName = NULL; + Tcl_Channel channel = NULL; + if (objc > 3) { + fileName = Tcl_GetString(objv[3]); + if (fileName[0] != '-') { + // First argument is the file name + objv++, objc--; + + channel = Tcl_OpenFileChannel(interp, fileName, "w", 0666); + if (!channel) + return TCL_ERROR; + + if (Tcl_SetChannelOption(interp, channel, "-translation", "binary") + != TCL_OK) + return TCL_ERROR; + } + } + + PSOutput* psPtr = new PSOutput(graphPtr); + + if (PostscriptObjConfigure(graphPtr, interp, objc-3, objv+3) != TCL_OK) { + if (channel) + Tcl_Close(interp, channel); + delete psPtr; + return TCL_ERROR; + } + + if (graphPtr->print(fileName, psPtr) != TCL_OK) { + if (channel) + Tcl_Close(interp, channel); + delete psPtr; + return TCL_ERROR; + } + + int length; + const char* buffer = psPtr->getValue(&length); + if (channel) { + int nBytes = Tcl_Write(channel, buffer, length); + if (nBytes < 0) { + Tcl_AppendResult(interp, "error writing file \"", fileName, "\": ", + Tcl_PosixError(interp), (char *)NULL); + if (channel) + Tcl_Close(interp, channel); + delete psPtr; + return TCL_ERROR; + } + Tcl_Close(interp, channel); + } + else + Tcl_SetStringObj(Tcl_GetObjResult(interp), buffer, length); + + delete psPtr; + + return TCL_OK; +} + +const Ensemble Blt::postscriptEnsemble[] = { + {"cget", CgetOp, 0}, + {"configure", ConfigureOp, 0}, + {"output", OutputOp, 0}, + { 0,0,0 } +}; diff --git a/generic/tkbltGrPostscriptOp.h b/generic/tkbltGrPostscriptOp.h new file mode 100644 index 0000000..9a81266 --- /dev/null +++ b/generic/tkbltGrPostscriptOp.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef __BltGrPostscriptOp_h__ +#define __BltGrPostscriptOp_h__ + +#include "tkbltGraph.h" + +namespace Blt { + extern const Ensemble postscriptEnsemble[]; + extern int PostscriptObjConfigure(Blt::Graph* graphPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); +}; + +#endif diff --git a/generic/tkbltGrText.C b/generic/tkbltGrText.C new file mode 100644 index 0000000..1bed069 --- /dev/null +++ b/generic/tkbltGrText.C @@ -0,0 +1,249 @@ +/* + * 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 1993-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 + +#include +#include + +#include "tkbltGrText.h" +#include "tkbltGraph.h" +#include "tkbltGrPSOutput.h" + +using namespace Blt; + +TextStyle::TextStyle(Graph* graphPtr) +{ + ops_ = (TextStyleOptions*)calloc(1, sizeof(TextStyleOptions)); + TextStyleOptions* ops = (TextStyleOptions*)ops_; + graphPtr_ = graphPtr; + manageOptions_ = 1; + + ops->anchor =TK_ANCHOR_NW; + ops->color =NULL; + ops->font =NULL; + ops->angle =0; + ops->justify =TK_JUSTIFY_LEFT; + + xPad_ = 0; + yPad_ = 0; + gc_ = NULL; +} + +TextStyle::TextStyle(Graph* graphPtr, TextStyleOptions* ops) +{ + ops_ = (TextStyleOptions*)ops; + graphPtr_ = graphPtr; + manageOptions_ = 0; + + xPad_ = 0; + yPad_ = 0; + gc_ = NULL; +} + +TextStyle::~TextStyle() +{ + // TextStyleOptions* ops = (TextStyleOptions*)ops_; + + if (gc_) + Tk_FreeGC(graphPtr_->display_, gc_); + + if (manageOptions_) + free(ops_); +} + +void TextStyle::drawText(Drawable drawable, const char *text, int x, int y) +{ + TextStyleOptions* ops = (TextStyleOptions*)ops_; + + if (!text || !(*text)) + return; + + if (!gc_) + resetStyle(); + + int w1, h1; + Tk_TextLayout layout = Tk_ComputeTextLayout(ops->font, text, -1, -1, + ops->justify, 0, &w1, &h1); + Point2d rr = rotateText(x, y, w1, h1); +#if (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 6) + TkDrawAngledTextLayout(graphPtr_->display_, drawable, gc_, layout, + (int)rr.x, (int)rr.y, + ops->angle, 0, -1); +#else + Tk_DrawTextLayout(graphPtr_->display_, drawable, gc_, layout, + rr.x, rr.y, 0, -1); +#endif +} + +void TextStyle::drawText(Drawable drawable, const char *text, double x, double y) { + return drawText(drawable, text, (int)x, (int)y); +} + +void TextStyle::drawText2(Drawable drawable, const char *text, + int x, int y, int* ww, int* hh) +{ + TextStyleOptions* ops = (TextStyleOptions*)ops_; + + if (!text || !(*text)) + return; + + if (!gc_) + resetStyle(); + + int w1, h1; + Tk_TextLayout layout = Tk_ComputeTextLayout(ops->font, text, -1, -1, + ops->justify, 0, &w1, &h1); + Point2d rr = rotateText(x, y, w1, h1); +#if (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 6) + TkDrawAngledTextLayout(graphPtr_->display_, drawable, gc_, layout, + (int)rr.x, (int)rr.y, ops->angle, 0, -1); +#else + Tk_DrawTextLayout(graphPtr_->display_, drawable, gc_, layout, + (int)rr.x, (int)rr.y, 0, -1); +#endif + + double angle = fmod(ops->angle, 360.0); + if (angle < 0.0) + angle += 360.0; + + if (angle != 0.0) { + double rotWidth, rotHeight; + graphPtr_->getBoundingBox(w1, h1, angle, &rotWidth, &rotHeight, NULL); + w1 = (int)rotWidth; + h1 = (int)rotHeight; + } + + *ww = w1; + *hh = h1; +} + +void TextStyle::printText(PSOutput* psPtr, const char *text, int x, int y) +{ + TextStyleOptions* ops = (TextStyleOptions*)ops_; + + if (!text || !(*text)) + return; + + int w1, h1; + Tk_TextLayout layout = Tk_ComputeTextLayout(ops->font, text, -1, -1, + ops->justify, 0, &w1, &h1); + + int xx =0; + int yy =0; + switch (ops->anchor) { + case TK_ANCHOR_NW: xx = 0; yy = 0; break; + case TK_ANCHOR_N: xx = 1; yy = 0; break; + case TK_ANCHOR_NE: xx = 2; yy = 0; break; + case TK_ANCHOR_E: xx = 2; yy = 1; break; + case TK_ANCHOR_SE: xx = 2; yy = 2; break; + case TK_ANCHOR_S: xx = 1; yy = 2; break; + case TK_ANCHOR_SW: xx = 0; yy = 2; break; + case TK_ANCHOR_W: xx = 0; yy = 1; break; + case TK_ANCHOR_CENTER: xx = 1; yy = 1; break; + } + + const char* justify =NULL; + switch (ops->justify) { + case TK_JUSTIFY_LEFT: justify = "0"; break; + case TK_JUSTIFY_CENTER: justify = "0.5"; break; + case TK_JUSTIFY_RIGHT: justify = "1"; break; + } + + psPtr->setFont(ops->font); + psPtr->setForeground(ops->color); + + psPtr->format("%g %d %d [\n", ops->angle, x, y); + Tcl_ResetResult(graphPtr_->interp_); + Tk_TextLayoutToPostscript(graphPtr_->interp_, layout); + psPtr->append(Tcl_GetStringResult(graphPtr_->interp_)); + Tcl_ResetResult(graphPtr_->interp_); + psPtr->format("] %g %g %s DrawText\n", xx/-2.0, yy/-2.0, justify); +} + +void TextStyle::printText(PSOutput* psPtr, const char *text, double x, double y) { + return printText(psPtr, text, (int)x, (int)y); +} + +void TextStyle::resetStyle() +{ + TextStyleOptions* ops = (TextStyleOptions*)ops_; + + unsigned long gcMask; + gcMask = GCFont; + + XGCValues gcValues; + gcValues.font = Tk_FontId(ops->font); + if (ops->color) { + gcMask |= GCForeground; + gcValues.foreground = ops->color->pixel; + } + GC newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); + if (gc_) + Tk_FreeGC(graphPtr_->display_, gc_); + + gc_ = newGC; +} + +Point2d TextStyle::rotateText(int x, int y, int w1, int h1) +{ + TextStyleOptions* ops = (TextStyleOptions*)ops_; + + // Matrix t0 = Translate(-x,-y); + // Matrix t1 = Translate(-w1/2,-h1/2); + // Matrix rr = Rotate(angle); + // Matrix t2 = Translate(w2/2,h2/2); + // Matrix t3 = Translate(x,y); + + double angle = ops->angle; + double ccos = cos(M_PI*angle/180.); + double ssin = sin(M_PI*angle/180.); + double w2, h2; + graphPtr_->getBoundingBox(w1, h1, angle, &w2, &h2, NULL); + + double x1 = x+w1/2.; + double y1 = y+h1/2.; + double x2 = w2/2.+x; + double y2 = h2/2.+y; + + double rx = x*ccos + y*ssin + (-x1*ccos -y1*ssin +x2); + double ry = -x*ssin + y*ccos + ( x1*ssin -y1*ccos +y2); + + return graphPtr_->anchorPoint(rx, ry, w2, h2, ops->anchor); +} + +void TextStyle::getExtents(const char *text, int* ww, int* hh) +{ + TextStyleOptions* ops = (TextStyleOptions*)ops_; + + int w, h; + graphPtr_->getTextExtents(ops->font, text, -1, &w, &h); + *ww = w + 2*xPad_; + *hh = h + 2*yPad_; +} diff --git a/generic/tkbltGrText.h b/generic/tkbltGrText.h new file mode 100644 index 0000000..4337e81 --- /dev/null +++ b/generic/tkbltGrText.h @@ -0,0 +1,79 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltText_h__ +#define __BltText_h__ + +#include + +#include "tkbltGrMisc.h" + +namespace Blt { + class Graph; + class PSOutput; + + typedef struct { + Tk_Anchor anchor; + XColor* color; + Tk_Font font; + double angle; + Tk_Justify justify; + } TextStyleOptions; + + class TextStyle { + protected: + Graph* graphPtr_; + void* ops_; + GC gc_; + int manageOptions_; + + public: + int xPad_; + int yPad_; + + protected: + void resetStyle(); + Point2d rotateText(int, int, int, int); + + public: + TextStyle(Graph*); + TextStyle(Graph*, TextStyleOptions*); + virtual ~TextStyle(); + + void* ops() {return ops_;} + void drawText(Drawable, const char*, int, int); + void drawText(Drawable, const char*, double, double); + void drawText2(Drawable, const char*, int, int, int*, int*); + void printText(PSOutput*, const char*, int, int); + void printText(PSOutput*, const char*, double, double); + void getExtents(const char*, int*, int*); + }; +}; + +#endif diff --git a/generic/tkbltGrXAxisOp.C b/generic/tkbltGrXAxisOp.C new file mode 100644 index 0000000..ac788ff --- /dev/null +++ b/generic/tkbltGrXAxisOp.C @@ -0,0 +1,222 @@ +/* + * 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 1993-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 + +#include "tkbltGraph.h" +#include "tkbltGrBind.h" +#include "tkbltGrXAxisOp.h" +#include "tkbltGrAxis.h" +#include "tkbltGrAxisOp.h" + +using namespace Blt; + +static Axis* GetAxisFromCmd(ClientData clientData, Tcl_Obj* obj) +{ + Graph* graphPtr = (Graph*)clientData; + GraphOptions* ops = (GraphOptions*)graphPtr->ops_; + + int margin; + const char* name = Tcl_GetString(obj); + if (!strcmp(name,"xaxis")) + margin = (ops->inverted) ? MARGIN_LEFT : MARGIN_BOTTOM; + else if (!strcmp(name,"yaxis")) + margin = (ops->inverted) ? MARGIN_BOTTOM : MARGIN_LEFT; + else if (!strcmp(name,"x2axis")) + margin = (ops->inverted) ? MARGIN_RIGHT : MARGIN_TOP; + else if (!strcmp(name,"y2axis")) + margin = (ops->inverted) ? MARGIN_TOP : MARGIN_RIGHT; + else + return NULL; + + ChainLink* link = Chain_FirstLink(ops->margins[margin].axes); + return (Axis*)Chain_GetValue(link); +} + +static int CgetOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); + return AxisCgetOp(axisPtr, interp, objc, objv); +} + +static int ConfigureOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); + return AxisConfigureOp(axisPtr, interp, objc, objv); +} + +static int ActivateOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); + return AxisActivateOp(axisPtr, interp, objc, objv); +} + +static int BindOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); + return graphPtr->bindTable_->configure(graphPtr->axisTag(axisPtr->name_), objc-3, objv+3); +} + +static int InvTransformOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); + return AxisInvTransformOp(axisPtr, interp, objc, objv); +} + +static int LimitsOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); + return AxisLimitsOp(axisPtr, interp, objc, objv); +} + +static int TransformOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); + return AxisTransformOp(axisPtr, interp, objc, objv); +} + +static int UseOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Graph* graphPtr = (Graph*)clientData; + GraphOptions* ops = (GraphOptions*)graphPtr->ops_; + + int margin; + ClassId classId; + const char* name = Tcl_GetString(objv[1]); + if (!strcmp(name,"xaxis")) { + classId = CID_AXIS_X; + margin = (ops->inverted) ? MARGIN_LEFT : MARGIN_BOTTOM; + } + else if (!strcmp(name,"yaxis")) { + classId = CID_AXIS_Y; + margin = (ops->inverted) ? MARGIN_BOTTOM : MARGIN_LEFT; + } + else if (!strcmp(name,"x2axis")) { + classId = CID_AXIS_X; + margin = (ops->inverted) ? MARGIN_RIGHT : MARGIN_TOP; + } + else if (!strcmp(name,"y2axis")) { + classId = CID_AXIS_Y; + margin = (ops->inverted) ? MARGIN_TOP : MARGIN_RIGHT; + } + else + return TCL_ERROR; + + Chain* chain = ops->margins[margin].axes; + + if (objc == 3) { + Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + for (ChainLink* link = Chain_FirstLink(chain); link; + link = Chain_NextLink(link)) { + Axis* axisPtr = (Axis*)Chain_GetValue(link); + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewStringObj(axisPtr->name_, -1)); + } + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; + } + + int axisObjc; + Tcl_Obj **axisObjv; + if (Tcl_ListObjGetElements(interp, objv[3], &axisObjc, &axisObjv) != TCL_OK) + return TCL_ERROR; + + for (ChainLink* link = Chain_FirstLink(chain); link; + link = Chain_NextLink(link)) { + Axis* axisPtr = (Axis*)Chain_GetValue(link); + axisPtr->link = NULL; + axisPtr->use_ =0; + axisPtr->margin_ = MARGIN_NONE; + // Clear the axis type if it's not currently used + if (axisPtr->refCount_ == 0) + axisPtr->setClass(CID_NONE); + } + + chain->reset(); + for (int ii=0; iigetAxis(axisObjv[ii], &axisPtr) != TCL_OK) + return TCL_ERROR; + + if (axisPtr->classId_ == CID_NONE) + axisPtr->setClass(classId); + else if (axisPtr->classId_ != classId) { + Tcl_AppendResult(interp, "wrong type axis \"", + axisPtr->name_, "\": can't use ", + axisPtr->className_, " type axis.", NULL); + return TCL_ERROR; + } + if (axisPtr->link) { + // Move the axis from the old margin's "use" list to the new + axisPtr->chain->unlinkLink(axisPtr->link); + chain->linkAfter(axisPtr->link, NULL); + } + else + axisPtr->link = chain->append(axisPtr); + + axisPtr->chain = chain; + axisPtr->use_ =1; + axisPtr->margin_ = margin; + } + + graphPtr->flags |= RESET; + graphPtr->eventuallyRedraw(); + + return TCL_OK; +} + +static int ViewOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); + return AxisViewOp(axisPtr, interp, objc, objv); +} + +const Ensemble Blt::xaxisEnsemble[] = { + {"activate", ActivateOp, 0}, + {"bind", BindOp, 0}, + {"cget", CgetOp, 0}, + {"configure", ConfigureOp, 0}, + {"deactivate", ActivateOp, 0}, + {"invtransform", InvTransformOp, 0}, + {"limits", LimitsOp, 0}, + {"transform", TransformOp, 0}, + {"use", UseOp, 0}, + {"view", ViewOp, 0}, + { 0,0,0 } +}; diff --git a/generic/tkbltGrXAxisOp.h b/generic/tkbltGrXAxisOp.h new file mode 100644 index 0000000..b813c83 --- /dev/null +++ b/generic/tkbltGrXAxisOp.h @@ -0,0 +1,39 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGrXAxisOp_h__ +#define __BltGrXAxisOp_h__ + +#include "tkbltGraph.h" + +namespace Blt { + extern const Ensemble xaxisEnsemble[]; +}; + +#endif diff --git a/generic/tkbltGraph.C b/generic/tkbltGraph.C new file mode 100644 index 0000000..f1453dc --- /dev/null +++ b/generic/tkbltGraph.C @@ -0,0 +1,1458 @@ +/* + * 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 +#include + +#include + +#include "tkbltGraph.h" +#include "tkbltGraphOp.h" + +#include "tkbltGrBind.h" +#include "tkbltGrAxis.h" +#include "tkbltGrAxisOp.h" +#include "tkbltGrXAxisOp.h" +#include "tkbltGrPen.h" +#include "tkbltGrPenBar.h" +#include "tkbltGrPenLine.h" +#include "tkbltGrElem.h" +#include "tkbltGrElemBar.h" +#include "tkbltGrElemLine.h" +#include "tkbltGrMarker.h" +#include "tkbltGrLegd.h" +#include "tkbltGrHairs.h" +#include "tkbltGrDef.h" +#include "tkbltGrPostscript.h" +#include "tkbltGrPSOutput.h" +#include "tkbltInt.h" + +using namespace Blt; + +#define MARKER_ABOVE 0 +#define MARKER_UNDER 1 + +// OptionSpecs + +Graph::Graph(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + valid_ =1; + interp_ = interp; + tkwin_ = Tk_CreateWindowFromPath(interp_, Tk_MainWindow(interp_), + Tcl_GetString(objv[1]), NULL); + if (!tkwin_) { + valid_ =0; + return; + } + display_ = Tk_Display(tkwin_); + ((TkWindow*)tkwin_)->instanceData = this; + + cmdToken_ = Tcl_CreateObjCommand(interp_, Tk_PathName(tkwin_), + GraphInstCmdProc, this, + GraphInstCmdDeleteProc); + + flags = RESET; + nextMarkerId_ = 1; + + inset_ =0; + titleX_ =0; + titleY_ =0; + titleWidth_ =0; + titleHeight_ =0; + width_ =0; + height_ =0; + left_ =0; + right_ =0; + top_ =0; + bottom_ =0; + focusPtr_ =NULL; + halo_ =0; + drawGC_ =NULL; + vRange_ =0; + hRange_ =0; + vOffset_ =0; + hOffset_ =0; + vScale_ =0; + hScale_ =0; + cache_ =None; + cacheWidth_ =0; + cacheHeight_ =0; + + Tcl_InitHashTable(&axes_.table, TCL_STRING_KEYS); + Tcl_InitHashTable(&axes_.tagTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&elements_.table, TCL_STRING_KEYS); + Tcl_InitHashTable(&elements_.tagTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&markers_.table, TCL_STRING_KEYS); + Tcl_InitHashTable(&markers_.tagTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&penTable_, TCL_STRING_KEYS); + + axes_.displayList = new Chain(); + elements_.displayList = new Chain(); + markers_.displayList = new Chain(); + bindTable_ = new BindTable(this, this); + + // Keep a hold of the associated tkwin until we destroy the graph, + // otherwise Tk might free it while we still need it. + Tcl_Preserve(tkwin_); + + Tk_CreateEventHandler(tkwin_, + ExposureMask|StructureNotifyMask|FocusChangeMask, + GraphEventProc, this); +} + +Graph::~Graph() +{ + // GraphOptions* ops = (GraphOptions*)ops_; + + destroyMarkers(); + destroyElements(); // must come before legend and others + + delete crosshairs_; + delete legend_; + delete postscript_; + + destroyAxes(); + destroyPens(); + + delete bindTable_; + + if (drawGC_) + Tk_FreeGC(display_, drawGC_); + + if (cache_ != None) + Tk_FreePixmap(display_, cache_); + + Tk_FreeConfigOptions((char*)ops_, optionTable_, tkwin_); + Tcl_Release(tkwin_); + tkwin_ = NULL; + + free (ops_); +} + +int Graph::configure() +{ + GraphOptions* ops = (GraphOptions*)ops_; + + inset_ = ops->borderWidth + ops->highlightWidth; + if ((ops->reqHeight != Tk_ReqHeight(tkwin_)) || + (ops->reqWidth != Tk_ReqWidth(tkwin_))) + Tk_GeometryRequest(tkwin_, ops->reqWidth, ops->reqHeight); + + Tk_SetInternalBorder(tkwin_, ops->borderWidth); + XColor* colorPtr = Tk_3DBorderColor(ops->normalBg); + + titleWidth_ =0; + titleHeight_ =0; + if (ops->title != NULL) { + int w, h; + TextStyle ts(this, &ops->titleTextStyle); + ts.getExtents(ops->title, &w, &h); + titleHeight_ = h; + } + + // Create GCs for interior and exterior regions, and a background GC for + // clearing the margins with XFillRectangle + // Margin + XGCValues gcValues; + gcValues.foreground = ops->titleTextStyle.color->pixel; + gcValues.background = colorPtr->pixel; + unsigned long gcMask = (GCForeground | GCBackground); + GC newGC = Tk_GetGC(tkwin_, gcMask, &gcValues); + if (drawGC_ != NULL) + Tk_FreeGC(display_, drawGC_); + drawGC_ = newGC; + + // If the -inverted option changed, we need to readjust the pointers + // to the axes and recompute the their scales. + adjustAxes(); + + // Free the pixmap if we're not buffering the display of elements anymore. + if (cache_ != None) { + Tk_FreePixmap(display_, cache_); + cache_ = None; + } + + return TCL_OK; +} + +void Graph::map() +{ + if (flags & RESET) { + resetAxes(); + flags &= ~RESET; + flags |= LAYOUT; + } + + if (flags & LAYOUT) { + layoutGraph(); + crosshairs_->map(); + mapAxes(); + mapElements(); + flags &= ~LAYOUT; + flags |= MAP_MARKERS | CACHE; + } + + mapMarkers(); +} + +void Graph::draw() +{ + GraphOptions* ops = (GraphOptions*)ops_; + + flags &= ~REDRAW_PENDING; + if ((flags & GRAPH_DELETED) || !Tk_IsMapped(tkwin_)) + return; + + // Don't bother computing the layout until the size of the window is + // something reasonable. + if ((Tk_Width(tkwin_) <= 1) || (Tk_Height(tkwin_) <= 1)) + return; + + width_ = Tk_Width(tkwin_); + height_ = Tk_Height(tkwin_); + + map(); + + // Create a pixmap the size of the window for double buffering + Pixmap drawable = Tk_GetPixmap(display_, Tk_WindowId(tkwin_), + width_, height_, Tk_Depth(tkwin_)); + + if (cache_ == None || cacheWidth_ != width_ || cacheHeight_ != height_) { + if (cache_ != None) + Tk_FreePixmap(display_, cache_); + cache_ = Tk_GetPixmap(display_, Tk_WindowId(tkwin_), width_, height_, + Tk_Depth(tkwin_)); + cacheWidth_ = width_; + cacheHeight_ = height_; + flags |= CACHE; + } + + // Update cache if needed + if (flags & CACHE) { + drawMargins(cache_); + + switch (legend_->position()) { + case Legend::TOP: + case Legend::BOTTOM: + case Legend::RIGHT: + case Legend::LEFT: + legend_->draw(cache_); + break; + default: + break; + } + + // Draw the background of the plotting area with 3D border + Tk_Fill3DRectangle(tkwin_, cache_, ops->plotBg, + left_-ops->plotBW, + top_-ops->plotBW, + right_-left_+1+2*ops->plotBW, + bottom_-top_+1+2*ops->plotBW, + ops->plotBW, ops->plotRelief); + + drawAxesGrids(cache_); + drawAxes(cache_); + drawAxesLimits(cache_); + + if (!legend_->isRaised()) { + switch (legend_->position()) { + case Legend::PLOT: + case Legend::XY: + legend_->draw(cache_); + break; + default: + break; + } + } + + drawMarkers(cache_, MARKER_UNDER); + drawElements(cache_); + drawActiveElements(cache_); + + if (legend_->isRaised()) { + switch (legend_->position()) { + case Legend::PLOT: + case Legend::XY: + legend_->draw(cache_); + break; + default: + break; + } + } + + flags &= ~CACHE; + } + + XCopyArea(display_, cache_, drawable, drawGC_, 0, 0, Tk_Width(tkwin_), + Tk_Height(tkwin_), 0, 0); + + drawMarkers(drawable, MARKER_ABOVE); + + // Draw 3D border just inside of the focus highlight ring + if ((ops->borderWidth > 0) && (ops->relief != TK_RELIEF_FLAT)) + Tk_Draw3DRectangle(tkwin_, drawable, ops->normalBg, + ops->highlightWidth, ops->highlightWidth, + width_ - 2*ops->highlightWidth, + height_ - 2*ops->highlightWidth, + ops->borderWidth, ops->relief); + + // Draw focus highlight ring + if ((ops->highlightWidth > 0) && (flags & FOCUS)) { + GC gc = Tk_GCForColor(ops->highlightColor, drawable); + Tk_DrawFocusHighlight(tkwin_, gc, ops->highlightWidth, drawable); + } + + // crosshairs + crosshairs_->draw(drawable); + + XCopyArea(display_, drawable, Tk_WindowId(tkwin_), drawGC_, + 0, 0, width_, height_, 0, 0); + + Tk_FreePixmap(display_, drawable); +} + +int Graph::print(const char* ident, PSOutput* psPtr) +{ + GraphOptions* ops = (GraphOptions*)ops_; + PostscriptOptions* pops = (PostscriptOptions*)postscript_->ops_; + + // be sure the window is realized so that relief colors are available + if (flags & REDRAW_PENDING) { + flags |= REDRAW_PENDING; + DisplayGraph(this); + } + + // We need to know how big a graph to print. If the graph hasn't been drawn + // yet, the width and height will be 1. Instead use the requested size of + // the widget. The user can still override this with the -width and -height + // postscript options. + if (pops->reqWidth > 0) + width_ = pops->reqWidth; + else if (width_ < 2) + width_ = Tk_ReqWidth(tkwin_); + + if (pops->reqHeight > 0) + height_ = pops->reqHeight; + else if (height_ < 2) + height_ = Tk_ReqHeight(tkwin_); + + psPtr->computeBBox(width_, height_); + flags |= RESET; + + // Turn on PostScript measurements when computing the graph's layout. + reconfigure(); + + map(); + + int x = left_ - ops->plotBW; + int y = top_ - ops->plotBW; + + int w = (right_ - left_ + 1) + (2*ops->plotBW); + int h = (bottom_ - top_ + 1) + (2*ops->plotBW); + + int result = psPtr->preamble(ident); + if (result != TCL_OK) + goto error; + + psPtr->setFont(ops->titleTextStyle.font); + if (pops->decorations) + psPtr->setBackground(Tk_3DBorderColor(ops->plotBg)); + else + psPtr->setClearBackground(); + + psPtr->fillRectangle(x, y, w, h); + psPtr->append("gsave\n\n"); + + // Start + printMargins(psPtr); + + switch (legend_->position()) { + case Legend::TOP: + case Legend::BOTTOM: + case Legend::RIGHT: + case Legend::LEFT: + legend_->print(psPtr); + break; + default: + break; + } + + printAxesGrids(psPtr); + printAxes(psPtr); + printAxesLimits(psPtr); + + if (!legend_->isRaised()) { + switch (legend_->position()) { + case Legend::PLOT: + case Legend::XY: + legend_->print(psPtr); + break; + default: + break; + } + } + + printMarkers(psPtr, MARKER_UNDER); + printElements(psPtr); + printActiveElements(psPtr); + + if (legend_->isRaised()) { + switch (legend_->position()) { + case Legend::PLOT: + case Legend::XY: + legend_->print(psPtr); + break; + default: + break; + } + } + printMarkers(psPtr, MARKER_ABOVE); + + psPtr->append("\n"); + psPtr->append("% Unset clipping\n"); + psPtr->append("grestore\n\n"); + psPtr->append("showpage\n"); + psPtr->append("%Trailer\n"); + psPtr->append("grestore\n"); + psPtr->append("end\n"); + psPtr->append("%EOF\n"); + + error: + width_ = Tk_Width(tkwin_); + height_ = Tk_Height(tkwin_); + reconfigure(); + + // Redraw the graph in order to re-calculate the layout as soon as + // possible. This is in the case the crosshairs are active. + flags |= LAYOUT; + eventuallyRedraw(); + + return result; +} + +void Graph::eventuallyRedraw() +{ + if (flags & GRAPH_DELETED) + return; + + if (!(flags & REDRAW_PENDING)) { + flags |= REDRAW_PENDING; + Tcl_DoWhenIdle(DisplayGraph, this); + } +} + +void Graph::extents(Region2d* regionPtr) +{ + GraphOptions* ops = (GraphOptions*)ops_; + + regionPtr->left = (double)(hOffset_ - ops->xPad); + regionPtr->top = (double)(vOffset_ - ops->yPad); + regionPtr->right = (double)(hOffset_ + hRange_ + ops->xPad); + regionPtr->bottom = (double)(vOffset_ + vRange_ + ops->yPad); +} + +int Graph::invoke(const Ensemble* ensemble, int cmdIndex, + int objc, Tcl_Obj* const objv[]) +{ + while (cmdIndex < objc) { + int index; + if (Tcl_GetIndexFromObjStruct(interp_, objv[cmdIndex], ensemble, sizeof(ensemble[0]), "command", 0, &index) != TCL_OK) + return TCL_ERROR; + + if (ensemble[index].proc) + return ensemble[index].proc(this, interp_, objc, objv); + + ensemble = ensemble[index].subensemble; + ++cmdIndex; + } + + Tcl_WrongNumArgs(interp_, cmdIndex, objv, "option ?arg ...?"); + return TCL_ERROR; +} + +void Graph::reconfigure() +{ + configure(); + legend_->configure(); + configureElements(); + configureAxes(); + configureMarkers(); +} + +// Margins + +void Graph::drawMargins(Drawable drawable) +{ + GraphOptions* ops = (GraphOptions*)ops_; + Rectangle rects[4]; + + // Draw the four outer rectangles which encompass the plotting + // surface. This clears the surrounding area and clips the plot. + rects[0].x = rects[0].y = rects[3].x = rects[1].x = 0; + rects[0].width = rects[3].width = width_; + rects[0].height = top_; + rects[3].y = bottom_; + rects[3].height = height_ - bottom_; + rects[2].y = rects[1].y = top_; + rects[1].width = left_; + rects[2].height = rects[1].height = bottom_ - top_; + rects[2].x = right_; + rects[2].width = width_ - right_; + + Tk_Fill3DRectangle(tkwin_, drawable, ops->normalBg, + rects[0].x, rects[0].y, rects[0].width, rects[0].height, + 0, TK_RELIEF_FLAT); + Tk_Fill3DRectangle(tkwin_, drawable, ops->normalBg, + rects[1].x, rects[1].y, rects[1].width, rects[1].height, + 0, TK_RELIEF_FLAT); + Tk_Fill3DRectangle(tkwin_, drawable, ops->normalBg, + rects[2].x, rects[2].y, rects[2].width, rects[2].height, + 0, TK_RELIEF_FLAT); + Tk_Fill3DRectangle(tkwin_, drawable, ops->normalBg, + rects[3].x, rects[3].y, rects[3].width, rects[3].height, + 0, TK_RELIEF_FLAT); + + // Draw 3D border around the plotting area + if (ops->plotBW > 0) { + int x = left_ - ops->plotBW; + int y = top_ - ops->plotBW; + int w = (right_ - left_) + (2*ops->plotBW); + int h = (bottom_ - top_) + (2*ops->plotBW); + Tk_Draw3DRectangle(tkwin_, drawable, ops->normalBg, + x, y, w, h, ops->plotBW, ops->plotRelief); + } + + if (ops->title) { + TextStyle ts(this, &ops->titleTextStyle); + ts.drawText(drawable, ops->title, titleX_, titleY_); + } +} + +void Graph::printMargins(PSOutput* psPtr) +{ + GraphOptions* ops = (GraphOptions*)ops_; + PostscriptOptions* pops = (PostscriptOptions*)postscript_->ops_; + Rectangle margin[4]; + + margin[0].x = margin[0].y = margin[3].x = margin[1].x = 0; + margin[0].width = margin[3].width = width_; + margin[0].height = top_; + margin[3].y = bottom_; + margin[3].height = height_ - bottom_; + margin[2].y = margin[1].y = top_; + margin[1].width = left_; + margin[2].height = margin[1].height = bottom_ - top_; + margin[2].x = right_; + margin[2].width = width_ - right_; + + // Clear the surrounding margins and clip the plotting surface + if (pops->decorations) + psPtr->setBackground(Tk_3DBorderColor(ops->normalBg)); + else + psPtr->setClearBackground(); + + psPtr->append("% Margins\n"); + psPtr->fillRectangles(margin, 4); + + if (pops->decorations) { + psPtr->append("% Interior 3D border\n"); + if (ops->plotBW > 0) { + int x = left_ - ops->plotBW; + int y = top_ - ops->plotBW; + int w = (right_ - left_) + (2*ops->plotBW); + int h = (bottom_ - top_) + (2*ops->plotBW); + psPtr->print3DRectangle(ops->normalBg, (double)x, (double)y, w, h, + ops->plotBW, ops->plotRelief); + } + } + + if (ops->title) { + psPtr->append("% Graph title\n"); + TextStyle ts(this, &ops->titleTextStyle); + ts.printText(psPtr, ops->title, titleX_, titleY_); + } +} + +// Pens + +void Graph::destroyPens() +{ + Tcl_HashSearch iter; + for (Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&penTable_, &iter); + hPtr; hPtr = Tcl_NextHashEntry(&iter)) { + Pen* penPtr = (Pen*)Tcl_GetHashValue(hPtr); + delete penPtr; + } + Tcl_DeleteHashTable(&penTable_); +} + +int Graph::getPen(Tcl_Obj* objPtr, Pen** penPtrPtr) +{ + *penPtrPtr = NULL; + const char *name = Tcl_GetString(objPtr); + if (!name || !name[0]) + return TCL_ERROR; + + Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&penTable_, name); + if (!hPtr) { + Tcl_AppendResult(interp_, "can't find pen \"", name, "\" in \"", + Tk_PathName(tkwin_), "\"", NULL); + return TCL_ERROR; + } + + *penPtrPtr = (Pen*)Tcl_GetHashValue(hPtr); + + return TCL_OK; +} + +// Elements + +void Graph::destroyElements() +{ + Tcl_HashSearch iter; + for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&elements_.table, &iter); + hPtr; hPtr = Tcl_NextHashEntry(&iter)) { + Element* elemPtr = (Element*)Tcl_GetHashValue(hPtr); + legend_->removeElement(elemPtr); + delete elemPtr; + } + + Tcl_DeleteHashTable(&elements_.table); + Tcl_DeleteHashTable(&elements_.tagTable); + delete elements_.displayList; +} + +void Graph::configureElements() +{ + for (ChainLink* link = Chain_FirstLink(elements_.displayList); link; + link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + elemPtr->configure(); + } +} + +void Graph::mapElements() +{ + for (ChainLink* link = Chain_FirstLink(elements_.displayList); link; + link = Chain_NextLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + elemPtr->map(); + } +} + +void Graph::drawElements(Drawable drawable) +{ + // Draw with respect to the stacking order + for (ChainLink* link=Chain_LastLink(elements_.displayList); link; + link = Chain_PrevLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + elemPtr->draw(drawable); + } +} + +void Graph::drawActiveElements(Drawable drawable) +{ + for (ChainLink* link = Chain_LastLink(elements_.displayList); link; + link = Chain_PrevLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + elemPtr->drawActive(drawable); + } +} + +void Graph::printElements(PSOutput* psPtr) +{ + for (ChainLink* link = Chain_LastLink(elements_.displayList); link; + link = Chain_PrevLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + elemPtr->print(psPtr); + } +} + +void Graph::printActiveElements(PSOutput* psPtr) +{ + for (ChainLink* link = Chain_LastLink(elements_.displayList); link; + link = Chain_PrevLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + elemPtr->printActive(psPtr); + } +} + +int Graph::getElement(Tcl_Obj *objPtr, Element **elemPtrPtr) +{ + *elemPtrPtr =NULL; + const char* name = Tcl_GetString(objPtr); + if (!name || !name[0]) + return TCL_ERROR; + + Tcl_HashEntry*hPtr = Tcl_FindHashEntry(&elements_.table, name); + if (!hPtr) { + Tcl_AppendResult(interp_, "can't find element \"", name, "\" in \"", + Tk_PathName(tkwin_), "\"", NULL); + return TCL_ERROR; + } + + *elemPtrPtr = (Element*)Tcl_GetHashValue(hPtr); + return TCL_OK; +} + +ClientData Graph::elementTag(const char *tagName) +{ + int isNew; + Tcl_HashEntry* hPtr = + Tcl_CreateHashEntry(&elements_.tagTable, tagName, &isNew); + return Tcl_GetHashKey(&elements_.tagTable, hPtr); +} + +// Markers + +void Graph::destroyMarkers() +{ + Tcl_HashSearch iter; + for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&markers_.table, &iter); + hPtr; hPtr=Tcl_NextHashEntry(&iter)) { + Marker* markerPtr = (Marker*)Tcl_GetHashValue(hPtr); + delete markerPtr; + } + Tcl_DeleteHashTable(&markers_.table); + Tcl_DeleteHashTable(&markers_.tagTable); + delete markers_.displayList; +} + + +void Graph::configureMarkers() +{ + for (ChainLink* link = Chain_FirstLink(markers_.displayList); link; + link = Chain_NextLink(link)) { + Marker* markerPtr = (Marker*)Chain_GetValue(link); + markerPtr->configure(); + } +} + +void Graph::mapMarkers() +{ + for (ChainLink* link = Chain_FirstLink(markers_.displayList); link; + link = Chain_NextLink(link)) { + Marker* markerPtr = (Marker*)Chain_GetValue(link); + MarkerOptions* mops = (MarkerOptions*)markerPtr->ops(); + + if (mops->hide) + continue; + + if ((flags & MAP_MARKERS) || (markerPtr->flags & MAP_ITEM)) { + markerPtr->map(); + markerPtr->flags &= ~MAP_ITEM; + } + } + + flags &= ~MAP_MARKERS; +} + +void Graph::drawMarkers(Drawable drawable, int under) +{ + for (ChainLink* link = Chain_LastLink(markers_.displayList); link; + link = Chain_PrevLink(link)) { + Marker* markerPtr = (Marker*)Chain_GetValue(link); + MarkerOptions* mops = (MarkerOptions*)markerPtr->ops(); + + if ((mops->drawUnder != under) || markerPtr->clipped_ || mops->hide) + continue; + + if (isElementHidden(markerPtr)) + continue; + + markerPtr->draw(drawable); + } +} + +void Graph::printMarkers(PSOutput* psPtr, int under) +{ + for (ChainLink* link = Chain_LastLink(markers_.displayList); link; + link = Chain_PrevLink(link)) { + Marker* markerPtr = (Marker*)Chain_GetValue(link); + MarkerOptions* mops = (MarkerOptions*)markerPtr->ops(); + if (mops->drawUnder != under) + continue; + + if (mops->hide) + continue; + + if (isElementHidden(markerPtr)) + continue; + + psPtr->format("%% Marker \"%s\" is a %s.\n", + markerPtr->name_, markerPtr->className()); + markerPtr->print(psPtr); + } +} + +ClientData Graph::markerTag(const char* tagName) +{ + int isNew; + Tcl_HashEntry* hPtr = Tcl_CreateHashEntry(&markers_.tagTable, tagName,&isNew); + return Tcl_GetHashKey(&markers_.tagTable, hPtr); +} + +Marker* Graph::nearestMarker(int x, int y, int under) +{ + Point2d point; + point.x = (double)x; + point.y = (double)y; + for (ChainLink* link = Chain_FirstLink(markers_.displayList); link; + link = Chain_NextLink(link)) { + Marker* markerPtr = (Marker*)Chain_GetValue(link); + MarkerOptions* mops = (MarkerOptions*)markerPtr->ops(); + + if ((markerPtr->flags & MAP_ITEM) || mops->hide) + continue; + + if (isElementHidden(markerPtr)) + continue; + + if (mops->drawUnder == under) + if (markerPtr->pointIn(&point)) + return markerPtr; + } + return NULL; +} + +int Graph::isElementHidden(Marker* markerPtr) +{ + MarkerOptions* mops = (MarkerOptions*)markerPtr->ops(); + + if (mops->elemName) { + Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&elements_.table, mops->elemName); + if (hPtr) { + Element* elemPtr = (Element*)Tcl_GetHashValue(hPtr); + ElementOptions* eops = (ElementOptions*)elemPtr->ops(); + if (!elemPtr->link || eops->hide) + return 1; + } + } + return 0; +} + +// Axis + +int Graph::createAxes() +{ + for (int ii=0; ii<4; ii++) { + int isNew; + Tcl_HashEntry* hPtr = + Tcl_CreateHashEntry(&axes_.table, axisNames[ii].name, &isNew); + Chain* chain = new Chain(); + + Axis* axisPtr = new Axis(this, axisNames[ii].name, ii, hPtr); + if (!axisPtr) + return TCL_ERROR; + AxisOptions* ops = (AxisOptions*)axisPtr->ops(); + + Tcl_SetHashValue(hPtr, axisPtr); + + axisPtr->refCount_ = 1; + axisPtr->use_ =1; + + axisPtr->setClass(!(ii&1) ? CID_AXIS_X : CID_AXIS_Y); + + if (Tk_InitOptions(interp_, (char*)axisPtr->ops(), + axisPtr->optionTable(), tkwin_) != TCL_OK) + return TCL_ERROR; + + if (axisPtr->configure() != TCL_OK) + return TCL_ERROR; + + if ((axisPtr->margin_ == MARGIN_RIGHT) || (axisPtr->margin_ == MARGIN_TOP)) + ops->hide = 1; + + axisChain_[ii] = chain; + axisPtr->link = chain->append(axisPtr); + axisPtr->chain = chain; + } + return TCL_OK; +} + +int Graph::createAxis(int objc, Tcl_Obj* const objv[]) +{ + char *string = Tcl_GetString(objv[3]); + if (string[0] == '-') { + Tcl_AppendResult(interp_, "name of axis \"", string, + "\" can't start with a '-'", NULL); + return TCL_ERROR; + } + + int isNew; + Tcl_HashEntry* hPtr = Tcl_CreateHashEntry(&axes_.table, string, &isNew); + if (!isNew) { + Tcl_AppendResult(interp_, "axis \"", string, "\" already exists in \"", + Tcl_GetString(objv[0]), "\"", NULL); + return TCL_ERROR; + } + + Axis* axisPtr = new Axis(this, Tcl_GetString(objv[3]), MARGIN_NONE, hPtr); + if (!axisPtr) + return TCL_ERROR; + + Tcl_SetHashValue(hPtr, axisPtr); + + if ((Tk_InitOptions(interp_, (char*)axisPtr->ops(), axisPtr->optionTable(), tkwin_) != TCL_OK) || (AxisObjConfigure(axisPtr, interp_, objc-4, objv+4) != TCL_OK)) { + delete axisPtr; + return TCL_ERROR; + } + + return TCL_OK; +} + +void Graph::destroyAxes() +{ + Tcl_HashSearch cursor; + for (Tcl_HashEntry *hPtr=Tcl_FirstHashEntry(&axes_.table, &cursor); + hPtr; hPtr=Tcl_NextHashEntry(&cursor)) { + Axis *axisPtr = (Axis*)Tcl_GetHashValue(hPtr); + delete axisPtr; + } + Tcl_DeleteHashTable(&axes_.table); + + for (int ii=0; ii<4; ii++) + delete axisChain_[ii]; + + Tcl_DeleteHashTable(&axes_.tagTable); + delete axes_.displayList; +} + +void Graph::configureAxes() +{ + Tcl_HashSearch cursor; + for (Tcl_HashEntry *hPtr=Tcl_FirstHashEntry(&axes_.table, &cursor); + hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { + Axis *axisPtr = (Axis*)Tcl_GetHashValue(hPtr); + axisPtr->configure(); + } +} + +void Graph::mapAxes() +{ + GraphOptions* ops = (GraphOptions*)ops_; + + for (int ii=0; ii<4; ii++) { + int count =0; + int offset =0; + + Chain* chain = ops->margins[ii].axes; + for (ChainLink* link=Chain_FirstLink(chain); link; + link = Chain_NextLink(link)) { + Axis *axisPtr = (Axis*)Chain_GetValue(link); + AxisOptions* aops = (AxisOptions*)axisPtr->ops(); + if (!axisPtr->use_) + continue; + + if (aops->reqNumMajorTicks <= 0) + aops->reqNumMajorTicks = 4; + + if (ops->stackAxes) + axisPtr->mapStacked(count, ii); + else + axisPtr->map(offset, ii); + + if (aops->showGrid) + axisPtr->mapGridlines(); + + offset += axisPtr->isHorizontal() ? axisPtr->height_ : axisPtr->width_; + count++; + } + } +} + +void Graph::drawAxes(Drawable drawable) +{ + GraphOptions* ops = (GraphOptions*)ops_; + + for (int ii=0; ii<4; ii++) { + for (ChainLink* link = Chain_LastLink(ops->margins[ii].axes); link; + link = Chain_PrevLink(link)) { + Axis *axisPtr = (Axis*)Chain_GetValue(link); + axisPtr->draw(drawable); + } + } +} + +void Graph::drawAxesLimits(Drawable drawable) +{ + Tcl_HashSearch cursor; + for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&axes_.table, &cursor); + hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { + Axis *axisPtr = (Axis*)Tcl_GetHashValue(hPtr); + axisPtr->drawLimits(drawable); + } +} + +void Graph::drawAxesGrids(Drawable drawable) +{ + GraphOptions* ops = (GraphOptions*)ops_; + + for (int ii=0; ii<4; ii++) { + for (ChainLink* link = Chain_FirstLink(ops->margins[ii].axes); link; + link = Chain_NextLink(link)) { + Axis *axisPtr = (Axis*)Chain_GetValue(link); + axisPtr->drawGrids(drawable); + } + } +} + +void Graph::printAxes(PSOutput* psPtr) +{ + GraphOptions* ops = (GraphOptions*)ops_; + + for (Margin *mp = ops->margins, *mend = mp + 4; mp < mend; mp++) { + for (ChainLink* link = Chain_FirstLink(mp->axes); link; + link = Chain_NextLink(link)) { + Axis *axisPtr = (Axis*)Chain_GetValue(link); + axisPtr->print(psPtr); + } + } +} + +void Graph::printAxesGrids(PSOutput* psPtr) +{ + GraphOptions* ops = (GraphOptions*)ops_; + + for (int ii=0; ii<4; ii++) { + for (ChainLink* link = Chain_FirstLink(ops->margins[ii].axes); link; + link = Chain_NextLink(link)) { + Axis *axisPtr = (Axis*)Chain_GetValue(link); + axisPtr->printGrids(psPtr); + } + } +} + +void Graph::printAxesLimits(PSOutput* psPtr) +{ + Tcl_HashSearch cursor; + for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&axes_.table, &cursor); + hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { + Axis *axisPtr = (Axis*)Tcl_GetHashValue(hPtr); + axisPtr->printLimits(psPtr); + } +} + +int Graph::getAxis(Tcl_Obj *objPtr, Axis **axisPtrPtr) +{ + *axisPtrPtr = NULL; + const char* name = Tcl_GetString(objPtr); + if (!name || !name[0]) + return TCL_ERROR; + + Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&axes_.table, name); + if (!hPtr) { + Tcl_AppendResult(interp_, "can't find axis \"", name, "\" in \"", + Tk_PathName(tkwin_), "\"", NULL); + return TCL_ERROR; + } + + *axisPtrPtr = (Axis*)Tcl_GetHashValue(hPtr); + return TCL_OK; +} + +ClientData Graph::axisTag(const char *tagName) +{ + int isNew; + Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&axes_.tagTable, tagName, &isNew); + return Tcl_GetHashKey(&axes_.tagTable, hPtr); +} + +void Graph::adjustAxes() +{ + GraphOptions* ops = (GraphOptions*)ops_; + + if (ops->inverted) { + ops->leftMargin.axes = axisChain_[0]; + ops->bottomMargin.axes = axisChain_[1]; + ops->rightMargin.axes = axisChain_[2]; + ops->topMargin.axes = axisChain_[3]; + } + else { + ops->leftMargin.axes = axisChain_[1]; + ops->bottomMargin.axes = axisChain_[0]; + ops->rightMargin.axes = axisChain_[3]; + ops->topMargin.axes = axisChain_[2]; + } +} + +Point2d Graph::map2D(double x, double y, Axis* xAxis, Axis* yAxis) +{ + GraphOptions* ops = (GraphOptions*)ops_; + + Point2d point; + if (ops->inverted) { + point.x = yAxis->hMap(y); + point.y = xAxis->vMap(x); + } + else { + point.x = xAxis->hMap(x); + point.y = yAxis->vMap(y); + } + return point; +} + +Point2d Graph::invMap2D(double x, double y, Axis* xAxis, Axis* yAxis) +{ + GraphOptions* ops = (GraphOptions*)ops_; + + Point2d point; + if (ops->inverted) { + point.x = xAxis->invVMap(y); + point.y = yAxis->invHMap(x); + } + else { + point.x = xAxis->invHMap(x); + point.y = yAxis->invVMap(y); + } + return point; +} + +void Graph::resetAxes() +{ + // Step 1: Reset all axes. Initialize the data limits of the axis to + // impossible values. + Tcl_HashSearch cursor; + for (Tcl_HashEntry* hPtr = Tcl_FirstHashEntry(&axes_.table, &cursor); + hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { + Axis *axisPtr = (Axis*)Tcl_GetHashValue(hPtr); + axisPtr->min_ = axisPtr->valueRange_.min = DBL_MAX; + axisPtr->max_ = axisPtr->valueRange_.max = -DBL_MAX; + } + + // Step 2: For each element that's to be displayed, get the smallest + // and largest data values mapped to each X and Y-axis. This + // will be the axis limits if the user doesn't override them + // with -min and -max options. + for (ChainLink* link = Chain_FirstLink(elements_.displayList); link; + link = Chain_NextLink(link)) { + Region2d exts; + + Element* elemPtr = (Element*)Chain_GetValue(link); + ElementOptions* elemops = (ElementOptions*)elemPtr->ops(); + elemPtr->extents(&exts); + elemops->xAxis->getDataLimits(exts.left, exts.right); + elemops->yAxis->getDataLimits(exts.top, exts.bottom); + } + + // Step 3: Now that we know the range of data values for each axis, + // set axis limits and compute a sweep to generate tick values. + for (Tcl_HashEntry* hPtr = Tcl_FirstHashEntry(&axes_.table, &cursor); + hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { + Axis *axisPtr = (Axis*)Tcl_GetHashValue(hPtr); + AxisOptions* ops = (AxisOptions*)axisPtr->ops(); + axisPtr->fixRange(); + + double min = axisPtr->min_; + double max = axisPtr->max_; + if ((!isnan(axisPtr->scrollMin_)) && (min < axisPtr->scrollMin_)) + min = axisPtr->scrollMin_; + + if ((!isnan(axisPtr->scrollMax_)) && (max > axisPtr->scrollMax_)) + max = axisPtr->scrollMax_; + + if (ops->logScale) + axisPtr->logScale(min, max); + else + axisPtr->linearScale(min, max); + } +} + +Axis* Graph::nearestAxis(int x, int y) +{ + Tcl_HashSearch cursor; + for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&axes_.table, &cursor); + hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { + Axis *axisPtr = (Axis*)Tcl_GetHashValue(hPtr); + AxisOptions* ops = (AxisOptions*)axisPtr->ops(); + if (ops->hide || !axisPtr->use_) + continue; + + if (ops->showTicks) { + for (ChainLink* link = Chain_FirstLink(axisPtr->tickLabels_); link; + link = Chain_NextLink(link)) { + TickLabel *labelPtr = (TickLabel*)Chain_GetValue(link); + double rw, rh; + Point2d bbox[5]; + getBoundingBox(labelPtr->width, labelPtr->height, ops->tickAngle, + &rw, &rh, bbox); + Point2d t; + t = anchorPoint(labelPtr->anchorPos.x, labelPtr->anchorPos.y, + rw, rh, axisPtr->tickAnchor_); + t.x = x - t.x - (rw * 0.5); + t.y = y - t.y - (rh * 0.5); + + bbox[4] = bbox[0]; + if (pointInPolygon(&t, bbox, 5)) { + return axisPtr; + } + } + } + + if (ops->title) { + int w, h; + double rw, rh; + Point2d bbox[5]; + getTextExtents(ops->titleFont, ops->title, -1, &w, &h); + getBoundingBox(w, h, axisPtr->titleAngle_, &rw, &rh, bbox); + Point2d t = anchorPoint(axisPtr->titlePos_.x, axisPtr->titlePos_.y, + rw, rh, axisPtr->titleAnchor_); + // Translate the point so that the 0,0 is the upper left + // corner of the bounding box + t.x = x - t.x - (rw * 0.5); + t.y = y - t.y - (rh * 0.5); + + bbox[4] = bbox[0]; + if (pointInPolygon(&t, bbox, 5)) { + return axisPtr; + } + } + if (ops->lineWidth > 0) { + if ((x <= axisPtr->right_) && (x >= axisPtr->left_) && + (y <= axisPtr->bottom_) && (y >= axisPtr->top_)) { + return axisPtr; + } + } + } + + return NULL; +} + +// Bind + +const char** Graph::getTags(ClientData object, ClassId classId, int* num) +{ + const char** tags =NULL; + + switch (classId) { + case CID_ELEM_BAR: + case CID_ELEM_LINE: + { + Element* ptr = (Element*)object; + ElementOptions* ops = (ElementOptions*)ptr->ops(); + int cnt =0; + for (const char** pp=ops->tags; *pp; pp++) + cnt++; + cnt +=2; + + tags = new const char*[cnt]; + tags[0] = (const char*)elementTag(ptr->name_); + tags[1] = (const char*)elementTag(ptr->className()); + int ii=2; + for (const char** pp = ops->tags; *pp; pp++, ii++) + tags[ii] = (const char*)elementTag(*pp); + + *num = cnt; + return tags; + } + break; + case CID_AXIS_X: + case CID_AXIS_Y: + { + Axis* ptr = (Axis*)object; + AxisOptions* ops = (AxisOptions*)ptr->ops(); + int cnt =0; + for (const char** pp=ops->tags; *pp; pp++) + cnt++; + cnt +=2; + + tags = new const char*[cnt]; + tags[0] = (const char*)axisTag(ptr->name_); + tags[1] = (const char*)axisTag(ptr->className()); + int ii=2; + for (const char** pp = ops->tags; *pp; pp++, ii++) + tags[ii] = (const char*)axisTag(*pp); + + *num = cnt; + return tags; + } + break; + case CID_MARKER_BITMAP: + case CID_MARKER_LINE: + case CID_MARKER_POLYGON: + case CID_MARKER_TEXT: + { + Marker* ptr = (Marker*)object; + MarkerOptions* ops = (MarkerOptions*)ptr->ops(); + int cnt =0; + for (const char** pp=ops->tags; *pp; pp++) + cnt++; + cnt +=2; + + tags = new const char*[cnt]; + tags[0] = (const char*)markerTag(ptr->name_); + tags[1] = (const char*)markerTag(ptr->className()); + int ii=2; + for (const char** pp = ops->tags; *pp; pp++, ii++) + tags[ii] = (const char*)markerTag(*pp); + + *num = cnt; + return tags; + } + break; + default: + break; + } + + return NULL; +} + +ClientData Graph::pickEntry(int xx, int yy, ClassId* classIdPtr) +{ + if (flags & (LAYOUT | MAP_MARKERS)) { + *classIdPtr = CID_NONE; + return NULL; + } + + // Sample coordinate is in one of the graph margins. Can only pick an axis. + Region2d exts; + extents(&exts); + if (xx>=exts.right || xx=exts.bottom || yyclassId(); + return axisPtr; + } + } + + // From top-to-bottom check: + // 1. markers drawn on top (-under false). + // 2. elements using its display list back to front. + // 3. markers drawn under element (-under true). + Marker* markerPtr = nearestMarker(xx, yy, 0); + if (markerPtr) { + *classIdPtr = markerPtr->classId(); + return markerPtr; + } + + GraphOptions* ops = (GraphOptions*)ops_; + ClosestSearch* searchPtr = &ops->search; + searchPtr->index = -1; + searchPtr->x = xx; + searchPtr->y = yy; + searchPtr->dist = (double)(searchPtr->halo + 1); + + for (ChainLink* link = Chain_LastLink(elements_.displayList); link; + link = Chain_PrevLink(link)) { + Element* elemPtr = (Element*)Chain_GetValue(link); + ElementOptions* eops = (ElementOptions*)elemPtr->ops(); + if (eops->hide) + continue; + elemPtr->closest(); + } + + // Found an element within the minimum halo distance. + if (searchPtr->dist <= (double)searchPtr->halo) { + *classIdPtr = searchPtr->elemPtr->classId(); + return searchPtr->elemPtr; + } + + markerPtr = nearestMarker(xx, yy, 1); + if (markerPtr) { + *classIdPtr = markerPtr->classId(); + return markerPtr; + } + + *classIdPtr = CID_NONE; + return NULL; +} + +int Graph::getXY(const char* string, int* xPtr, int* yPtr) +{ + if (!string || !*string) { + *xPtr = -SHRT_MAX; + *yPtr = -SHRT_MAX; + return TCL_OK; + } + + if (*string != '@') { + Tcl_AppendResult(interp_, "bad position \"", string, + "\": should be \"@x,y\"", (char *)NULL); + return TCL_ERROR; + } + + char* comma = (char*)strchr(string + 1, ','); + if (!comma) { + Tcl_AppendResult(interp_, "bad position \"", string, + "\": should be \"@x,y\"", (char *)NULL); + return TCL_ERROR; + } + + *comma = '\0'; + int x, y; + int 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; +} + +// Graphics + +void Graph::drawSegments(Drawable drawable, GC gc, + Segment2d* segments, int nSegments) +{ + for (Segment2d *sp = segments, *send = sp + nSegments; sp < send; sp++) + XDrawLine(display_, drawable, gc, (int)sp->p.x, (int)sp->p.y, (int)sp->q.x, (int)sp->q.y); +} + +GC Graph::getPrivateGC(unsigned long gcMask, XGCValues *valuePtr) +{ + Pixmap pixmap = None; + Drawable drawable = Tk_WindowId(tkwin_); + Display* display = Tk_Display(tkwin_); + if (drawable == None) + drawable = RootWindow(Tk_Display(tkwin_),Tk_ScreenNumber(tkwin_)); + + GC gc = XCreateGC(display, drawable, gcMask, valuePtr); + if (pixmap != None) + Tk_FreePixmap(display, pixmap); + + return gc; +} + +void Graph::freePrivateGC(GC gc) +{ + Tk_FreeXId(display_, (XID)XGContextFromGC(gc)); + XFreeGC(display_, gc); +} + +void Graph::setDashes(GC gc, Dashes* dashesPtr) +{ + XSetDashes(display_, gc, dashesPtr->offset, (const char*)dashesPtr->values, + (int)strlen((char*)dashesPtr->values)); +} diff --git a/generic/tkbltGraph.h b/generic/tkbltGraph.h new file mode 100644 index 0000000..6f8df01 --- /dev/null +++ b/generic/tkbltGraph.h @@ -0,0 +1,256 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGraph_h__ +#define __BltGraph_h__ + +#include + +#include "tkbltChain.h" +#include "tkbltGrMisc.h" +#include "tkbltGrText.h" + +typedef struct Ensemble { + const char *name; + Tcl_ObjCmdProc *proc; + const struct Ensemble *subensemble; +} Ensemble; + +namespace Blt { + class Axis; + class BindTable; + class Crosshairs; + class Element; + class Marker; + class Legend; + class Pen; + class Postscript; + class PSOutput; + + class Pick { + public: + virtual ClientData pickEntry(int, int, ClassId*) =0; + }; + + typedef struct { + int halo; + int mode; + int x; + int y; + int along; + + Element* elemPtr; + Point2d point; + int index; + double dist; + } ClosestSearch; + + typedef struct { + int width; + int height; + int axesOffset; + int axesTitleLength; + int maxTickWidth; + int maxTickHeight; + unsigned int nAxes; + Chain* axes; + int reqSize; + int site; + } Margin; + + typedef struct { + Tcl_HashTable table; + Chain* displayList; + Tcl_HashTable tagTable; + } Component; + +#define rightMargin margins[MARGIN_RIGHT] +#define leftMargin margins[MARGIN_LEFT] +#define topMargin margins[MARGIN_TOP] +#define bottomMargin margins[MARGIN_BOTTOM] + + typedef struct { + double aspect; + Tk_3DBorder normalBg; + int borderWidth; + Margin margins[4]; + Tk_Cursor cursor; + TextStyleOptions titleTextStyle; + int reqHeight; + XColor* highlightBgColor; + XColor* highlightColor; + int highlightWidth; + int inverted; + Tk_3DBorder plotBg; + int plotBW; + int xPad; + int yPad; + int plotRelief; + int relief; + ClosestSearch search; + int stackAxes; + const char *takeFocus; // nor used in C code + const char *title; + int reqWidth; + int reqPlotWidth; + int reqPlotHeight; + } GraphOptions; + + class Graph : public Pick { + public: + Tcl_Interp* interp_; + Tk_Window tkwin_; + Display *display_; + Tcl_Command cmdToken_; + Tk_OptionTable optionTable_; + void* ops_; + int valid_; + + unsigned int flags; + int nextMarkerId_; + + Component axes_; + Component elements_; + Component markers_; + Tcl_HashTable penTable_; + BindTable* bindTable_; + Chain* axisChain_[4]; + + Legend* legend_; + Crosshairs* crosshairs_; + Postscript* postscript_; + + int inset_; + int titleX_; + int titleY_; + int titleWidth_; + int titleHeight_; + int width_; + int height_; + int left_; + int right_; + int top_; + int bottom_; + Axis* focusPtr_; + int halo_; + GC drawGC_; + int vRange_; + int hRange_; + int vOffset_; + int hOffset_; + double vScale_; + double hScale_; + Pixmap cache_; + int cacheWidth_; + int cacheHeight_; + + protected: + void layoutGraph(); + + void drawMargins(Drawable); + void printMargins(PSOutput*); + int getMarginGeometry(Margin*); + + void destroyPens(); + + void destroyElements(); + void configureElements(); + virtual void mapElements(); + void drawElements(Drawable); + void drawActiveElements(Drawable); + void printElements(PSOutput*); + void printActiveElements(PSOutput*); + + void destroyMarkers(); + void configureMarkers(); + void mapMarkers(); + void drawMarkers(Drawable, int); + void printMarkers(PSOutput*, int); + + int createAxes(); + void destroyAxes(); + void configureAxes(); + void mapAxes(); + void drawAxes(Drawable); + void drawAxesLimits(Drawable); + void drawAxesGrids(Drawable); + void adjustAxes(); + + public: + Graph(ClientData, Tcl_Interp*, int, Tcl_Obj* const []); + virtual ~Graph(); + + virtual int configure(); + void map(); + void draw(); + void eventuallyRedraw(); + int print(const char*, PSOutput*); + void extents(Region2d*); + int invoke(const Ensemble*, int, int, Tcl_Obj* const []); + void reconfigure(); + + int createAxis(int, Tcl_Obj* const []); + void printAxes(PSOutput*); + void printAxesGrids(PSOutput*); + void printAxesLimits(PSOutput*); + int getAxis(Tcl_Obj*, Axis**); + ClientData axisTag(const char*); + Point2d map2D(double, double, Axis*, Axis*); + Point2d invMap2D(double, double, Axis*, Axis*); + virtual void resetAxes(); + Axis* nearestAxis(int, int); + + ClientData markerTag(const char*); + Marker* nearestMarker(int, int, int); + int isElementHidden(Marker*); + + virtual int createElement(int, Tcl_Obj* const []) =0; + int getElement(Tcl_Obj*, Element**); + ClientData elementTag(const char*); + + virtual int createPen(const char*, int, Tcl_Obj* const []) =0; + int getPen(Tcl_Obj*, Pen**); + + int getXY(const char*, int*, int*); + void getTextExtents(Tk_Font, const char*, int, int*, int*); + void getBoundingBox(int, int, double, double*, double*, Point2d*); + Point2d anchorPoint(double, double, double, double, Tk_Anchor); + + const char** getTags(ClientData, ClassId, int*); + ClientData pickEntry(int, int, ClassId*); + + void drawSegments(Drawable, GC, Segment2d*, int); + void setDashes(GC, Dashes*); + + GC getPrivateGC(unsigned long, XGCValues*); + void freePrivateGC(GC); + }; +}; + +#endif diff --git a/generic/tkbltGraphBar.C b/generic/tkbltGraphBar.C new file mode 100644 index 0000000..861c12a --- /dev/null +++ b/generic/tkbltGraphBar.C @@ -0,0 +1,516 @@ +/* + * 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 + +#include "tkbltGraphBar.h" +#include "tkbltGraphOp.h" + +#include "tkbltGrAxis.h" +#include "tkbltGrXAxisOp.h" +#include "tkbltGrPen.h" +#include "tkbltGrPenOp.h" +#include "tkbltGrPenBar.h" +#include "tkbltGrPenLine.h" +#include "tkbltGrElem.h" +#include "tkbltGrElemOp.h" +#include "tkbltGrElemBar.h" +#include "tkbltGrElemLine.h" +#include "tkbltGrMarker.h" +#include "tkbltGrLegd.h" +#include "tkbltGrHairs.h" +#include "tkbltGrPostscript.h" +#include "tkbltGrDef.h" + +using namespace Blt; + +// BarGroup + +BarGroup::BarGroup() +{ + nSegments =0; + xAxis =NULL; + yAxis =NULL; + sum =0; + count =0; + lastY =0; + index =0; +} + +// BarGraph + +static const char* barmodeObjOption[] = + {"normal", "stacked", "aligned", "overlap", NULL}; +static const char* searchModeObjOption[] = {"points", "traces", "auto", NULL}; +static const char* searchAlongObjOption[] = {"x", "y", "both", NULL}; + +static Tk_OptionSpec optionSpecs[] = { + {TK_OPTION_DOUBLE, "-aspect", "aspect", "Aspect", + "0", -1, Tk_Offset(BarGraphOptions, aspect), 0, NULL, RESET}, + {TK_OPTION_BORDER, "-background", "background", "Background", + STD_NORMAL_BACKGROUND, -1, Tk_Offset(BarGraphOptions, normalBg), + 0, NULL, CACHE}, + {TK_OPTION_STRING_TABLE, "-barmode", "barMode", "BarMode", + "normal", -1, Tk_Offset(BarGraphOptions, barMode), + 0, &barmodeObjOption, RESET}, + {TK_OPTION_DOUBLE, "-barwidth", "barWidth", "BarWidth", + ".9", -1, Tk_Offset(BarGraphOptions, barWidth), 0, NULL, RESET}, + {TK_OPTION_DOUBLE, "-baseline", "baseline", "Baseline", + "0", -1, Tk_Offset(BarGraphOptions, baseline), 0, NULL, RESET}, + {TK_OPTION_SYNONYM, "-bd", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-borderwidth", 0}, + {TK_OPTION_SYNONYM, "-bg", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-background", 0}, + {TK_OPTION_SYNONYM, "-bm", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-bottommargin", 0}, + {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", + STD_BORDERWIDTH, -1, Tk_Offset(BarGraphOptions, borderWidth), + 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-bottommargin", "bottomMargin", "BottomMargin", + "0", -1, Tk_Offset(BarGraphOptions, bottomMargin.reqSize), 0, NULL, RESET}, + {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", + "crosshair", -1, Tk_Offset(BarGraphOptions, cursor), + TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_SYNONYM, "-fg", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-foreground", 0}, + {TK_OPTION_FONT, "-font", "font", "Font", + STD_FONT_MEDIUM, -1, Tk_Offset(BarGraphOptions, titleTextStyle.font), + 0, NULL, RESET}, + {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(BarGraphOptions, titleTextStyle.color), + 0, NULL, CACHE}, + {TK_OPTION_SYNONYM, "-halo", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-searchhalo", 0}, + {TK_OPTION_PIXELS, "-height", "height", "Height", + "4i", -1, Tk_Offset(BarGraphOptions, reqHeight), 0, NULL, RESET}, + {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground", + "HighlightBackground", + STD_NORMAL_BACKGROUND, -1, Tk_Offset(BarGraphOptions, highlightBgColor), + 0, NULL, CACHE}, + {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(BarGraphOptions, highlightColor), + 0, NULL, CACHE}, + {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness", + "HighlightThickness", + "2", -1, Tk_Offset(BarGraphOptions, highlightWidth), 0, NULL, RESET}, + {TK_OPTION_BOOLEAN, "-invertxy", "invertXY", "InvertXY", + "no", -1, Tk_Offset(BarGraphOptions, inverted), 0, NULL, RESET}, + {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify", + "center", -1, Tk_Offset(BarGraphOptions, titleTextStyle.justify), + 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-leftmargin", "leftMargin", "Margin", + "0", -1, Tk_Offset(BarGraphOptions, leftMargin.reqSize), 0, NULL, RESET}, + {TK_OPTION_SYNONYM, "-lm", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-leftmargin", 0}, + {TK_OPTION_BORDER, "-plotbackground", "plotbackground", "PlotBackground", + STD_NORMAL_BACKGROUND, -1, Tk_Offset(BarGraphOptions, plotBg), + 0, NULL, CACHE}, + {TK_OPTION_PIXELS, "-plotborderwidth", "plotBorderWidth", "PlotBorderWidth", + STD_BORDERWIDTH, -1, Tk_Offset(BarGraphOptions, plotBW), 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-plotpadx", "plotPadX", "PlotPad", + "0", -1, Tk_Offset(BarGraphOptions, xPad), 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-plotpady", "plotPadY", "PlotPad", + "0", -1, Tk_Offset(BarGraphOptions, yPad), 0, NULL, RESET}, + {TK_OPTION_RELIEF, "-plotrelief", "plotRelief", "Relief", + "flat", -1, Tk_Offset(BarGraphOptions, plotRelief), 0, NULL, RESET}, + {TK_OPTION_RELIEF, "-relief", "relief", "Relief", + "flat", -1, Tk_Offset(BarGraphOptions, relief), 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-rightmargin", "rightMargin", "Margin", + "0", -1, Tk_Offset(BarGraphOptions, rightMargin.reqSize), 0, NULL, RESET}, + {TK_OPTION_SYNONYM, "-rm", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-rightmargin", 0}, + {TK_OPTION_PIXELS, "-searchhalo", "searchhalo", "SearchHalo", + "2m", -1, Tk_Offset(BarGraphOptions, search.halo), 0, NULL, 0}, + {TK_OPTION_STRING_TABLE, "-searchmode", "searchMode", "SearchMode", + "points", -1, Tk_Offset(BarGraphOptions, search.mode), + 0, &searchModeObjOption, 0}, + {TK_OPTION_STRING_TABLE, "-searchalong", "searchAlong", "SearchAlong", + "both", -1, Tk_Offset(BarGraphOptions, search.along), + 0, &searchAlongObjOption, 0}, + {TK_OPTION_BOOLEAN, "-stackaxes", "stackAxes", "StackAxes", + "no", -1, Tk_Offset(BarGraphOptions, stackAxes), 0, NULL, RESET}, + {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus", + NULL, -1, Tk_Offset(BarGraphOptions, takeFocus), + TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_STRING, "-title", "title", "Title", + NULL, -1, Tk_Offset(BarGraphOptions, title), TK_OPTION_NULL_OK, NULL, RESET}, + {TK_OPTION_SYNONYM, "-tm", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-topmargin", 0}, + {TK_OPTION_PIXELS, "-topmargin", "topMargin", "TopMargin", + "0", -1, Tk_Offset(BarGraphOptions, topMargin.reqSize), 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-width", "width", "Width", + "5i", -1, Tk_Offset(BarGraphOptions, reqWidth), 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-plotwidth", "plotWidth", "PlotWidth", + "0", -1, Tk_Offset(BarGraphOptions, reqPlotWidth), 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-plotheight", "plotHeight", "PlotHeight", + "0", -1, Tk_Offset(BarGraphOptions, reqPlotHeight), 0, NULL, RESET}, + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} +}; + +// Create + +BarGraph::BarGraph(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) + : Graph(clientData, interp, objc, objv) +{ + // problems so far? + if (!valid_) + return; + + ops_ = (BarGraphOptions*)calloc(1, sizeof(BarGraphOptions)); + BarGraphOptions* ops = (BarGraphOptions*)ops_; + + Tk_SetClass(tkwin_, "Barchart"); + + barGroups_ =NULL; + nBarGroups_ =0; + maxBarSetSize_ =0; + Tcl_InitHashTable(&setTable_, sizeof(BarSetKey)/sizeof(int)); + + ops->bottomMargin.site = MARGIN_BOTTOM; + ops->leftMargin.site = MARGIN_LEFT; + ops->topMargin.site = MARGIN_TOP; + ops->rightMargin.site = MARGIN_RIGHT; + + ops->titleTextStyle.anchor = TK_ANCHOR_N; + ops->titleTextStyle.color =NULL; + ops->titleTextStyle.font =NULL; + ops->titleTextStyle.angle =0; + ops->titleTextStyle.justify =TK_JUSTIFY_LEFT; + + optionTable_ = Tk_CreateOptionTable(interp_, optionSpecs); + if ((Tk_InitOptions(interp_, (char*)ops_, optionTable_, tkwin_) != TCL_OK) || (GraphObjConfigure(this, interp_, objc-2, objv+2) != TCL_OK)) { + valid_ =0; + return; + } + + // do this last after Tk_SetClass set + legend_ = new Legend(this); + crosshairs_ = new Crosshairs(this); + postscript_ = new Postscript(this); + + if (createPen("active", 0, NULL) != TCL_OK) { + valid_ =0; + return; + } + + if (createAxes() != TCL_OK) { + valid_ =0; + return; + } + + adjustAxes(); + + Tcl_SetStringObj(Tcl_GetObjResult(interp_), Tk_PathName(tkwin_), -1); +} + +BarGraph::~BarGraph() +{ + destroyBarSets(); +} + +int BarGraph::configure() +{ + BarGraphOptions* ops = (BarGraphOptions*)ops_; + // Don't allow negative bar widths. Reset to an arbitrary value (0.1) + if (ops->barWidth <= 0.0) + ops->barWidth = 0.9; + + return Graph::configure(); +} + +int BarGraph::createPen(const char* penName, int objc, Tcl_Obj* const objv[]) +{ + int isNew; + Tcl_HashEntry *hPtr = + Tcl_CreateHashEntry(&penTable_, penName, &isNew); + if (!isNew) { + Tcl_AppendResult(interp_, "pen \"", penName, "\" already exists in \"", + Tk_PathName(tkwin_), "\"", (char *)NULL); + return TCL_ERROR; + } + + Pen* penPtr = new BarPen(this, penName, hPtr); + if (!penPtr) + return TCL_ERROR; + + Tcl_SetHashValue(hPtr, penPtr); + + if ((Tk_InitOptions(interp_, (char*)penPtr->ops(), penPtr->optionTable(), tkwin_) != TCL_OK) || (PenObjConfigure(this, penPtr, interp_, objc-4, objv+4) != TCL_OK)) { + delete penPtr; + return TCL_ERROR; + } + + flags |= RESET; + eventuallyRedraw(); + + return TCL_OK; +} + +int BarGraph::createElement(int objc, Tcl_Obj* const objv[]) +{ + char *name = Tcl_GetString(objv[3]); + if (name[0] == '-') { + Tcl_AppendResult(interp_, "name of element \"", name, + "\" can't start with a '-'", NULL); + return TCL_ERROR; + } + + int isNew; + Tcl_HashEntry* hPtr = + Tcl_CreateHashEntry(&elements_.table, name, &isNew); + if (!isNew) { + Tcl_AppendResult(interp_, "element \"", name, + "\" already exists in \"", Tcl_GetString(objv[0]), + "\"", NULL); + return TCL_ERROR; + } + + Element* elemPtr = new BarElement(this, name, hPtr); + if (!elemPtr) + return TCL_ERROR; + + Tcl_SetHashValue(hPtr, elemPtr); + + if ((Tk_InitOptions(interp_, (char*)elemPtr->ops(), elemPtr->optionTable(), tkwin_) != TCL_OK) || (ElementObjConfigure(elemPtr, interp_, objc-4, objv+4) != TCL_OK)) { + delete elemPtr; + return TCL_ERROR; + } + + elemPtr->link = elements_.displayList->append(elemPtr); + + return TCL_OK; +} + +void BarGraph::mapElements() +{ + BarGraphOptions* ops = (BarGraphOptions*)ops_; + if ((BarMode)ops->barMode != INFRONT) + resetBarSets(); + + Graph::mapElements(); +} + +void BarGraph::resetAxes() +{ + BarGraphOptions* ops = (BarGraphOptions*)ops_; + + /* FIXME: This should be called whenever the display list of + * elements change. Maybe yet another flag INIT_STACKS to + * indicate that the element display list has changed. + * Needs to be done before the axis limits are set. + */ + initBarSets(); + if (((BarMode)ops->barMode == STACKED) && (nBarGroups_ > 0)) + computeBarStacks(); + + Graph::resetAxes(); +} + +void BarGraph::initBarSets() +{ + BarGraphOptions* ops = (BarGraphOptions*)ops_; + + // Free resources associated with a previous frequency table. This includes + // the array of frequency information and the table itself + destroyBarSets(); + if ((BarMode)ops->barMode == INFRONT) + return; + + // Initialize a hash table and fill it with unique abscissas. Keep track + // of the frequency of each x-coordinate and how many abscissas have + // duplicate mappings. + Tcl_HashTable setTable; + Tcl_InitHashTable(&setTable, sizeof(BarSetKey)/sizeof(int)); + int nSegs =0; + + for (ChainLink* link = Chain_FirstLink(elements_.displayList); + link; link = Chain_NextLink(link)) { + BarElement* bePtr = (BarElement*)Chain_GetValue(link); + BarElementOptions* ops = (BarElementOptions*)bePtr->ops(); + if (ops->hide) + continue; + + nSegs++; + if (ops->coords.x) { + int nPoints = ops->coords.x->nValues(); + for (double *x=ops->coords.x->values_, *xend=x+nPoints; xxAxis; + key.yAxis =NULL; + + int isNew; + Tcl_HashEntry* hhPtr = + Tcl_CreateHashEntry(&setTable, (char*)&key, &isNew); + Tcl_HashTable* tablePtr; + if (isNew) { + tablePtr = (Tcl_HashTable*)malloc(sizeof(Tcl_HashTable)); + Tcl_InitHashTable(tablePtr, TCL_STRING_KEYS); + Tcl_SetHashValue(hhPtr, tablePtr); + } + else + tablePtr = (Tcl_HashTable*)Tcl_GetHashValue(hhPtr); + + const char* name = ops->groupName ? ops->groupName : ops->yAxis->name_; + Tcl_HashEntry* hhPtr2 = Tcl_CreateHashEntry(tablePtr, name, &isNew); + size_t count =1; + if (!isNew) { + count = (size_t)Tcl_GetHashValue(hhPtr2); + count++; + } + Tcl_SetHashValue(hhPtr2, count); + } + } + } + + // no bar elements to be displayed + if (setTable.numEntries == 0) + return; + + int sum =0; + int max =0; + Tcl_HashSearch iter; + for (Tcl_HashEntry *hhPtr = Tcl_FirstHashEntry(&setTable, &iter); hhPtr; + hhPtr = Tcl_NextHashEntry(&iter)) { + BarSetKey* keyPtr = (BarSetKey*)Tcl_GetHashKey(&setTable, hhPtr); + Tcl_HashTable* tablePtr = (Tcl_HashTable*)Tcl_GetHashValue(hhPtr); + + int isNew; + Tcl_HashEntry* hPtr = + Tcl_CreateHashEntry(&setTable_, (char*)keyPtr, &isNew); + Tcl_SetHashValue(hPtr, tablePtr); + + if (max < tablePtr->numEntries) + max = tablePtr->numEntries; // # of stacks in group + sum += tablePtr->numEntries; + } + + Tcl_DeleteHashTable(&setTable); + + if (sum > 0) { + barGroups_ = new BarGroup[sum]; + BarGroup* groupPtr = barGroups_; + Tcl_HashSearch iter; + for (Tcl_HashEntry* hPtr = Tcl_FirstHashEntry(&setTable_, &iter); + hPtr; hPtr = Tcl_NextHashEntry(&iter)) { + BarSetKey* keyPtr = (BarSetKey*)Tcl_GetHashKey(&setTable_, hPtr); + Tcl_HashTable* tablePtr = (Tcl_HashTable*)Tcl_GetHashValue(hPtr); + + size_t xcount = 0; + Tcl_HashSearch iter2; + for (Tcl_HashEntry *hPtr2 = Tcl_FirstHashEntry(tablePtr, &iter2); + hPtr2; hPtr2 = Tcl_NextHashEntry(&iter2)) { + size_t count = (size_t)Tcl_GetHashValue(hPtr2); + groupPtr->nSegments = count; + groupPtr->xAxis = keyPtr->xAxis; + groupPtr->yAxis = keyPtr->yAxis; + groupPtr->index = xcount++; + Tcl_SetHashValue(hPtr2, groupPtr); + + groupPtr++; + } + } + } + + maxBarSetSize_ = max; + nBarGroups_ = sum; +} + +void BarGraph::destroyBarSets() +{ + delete [] barGroups_; + barGroups_ = NULL; + + nBarGroups_ = 0; + Tcl_HashSearch iter; + for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&setTable_, &iter); hPtr; + hPtr=Tcl_NextHashEntry(&iter)) { + Tcl_HashTable* tablePtr = (Tcl_HashTable*)Tcl_GetHashValue(hPtr); + Tcl_DeleteHashTable(tablePtr); + free(tablePtr); + } + + Tcl_DeleteHashTable(&setTable_); + Tcl_InitHashTable(&setTable_, sizeof(BarSetKey)/sizeof(int)); +} + +void BarGraph::resetBarSets() +{ + for (BarGroup *gp = barGroups_, *gend = gp + nBarGroups_; gp < gend; gp++) { + gp->lastY = 0.0; + gp->count = 0; + } +} + +void BarGraph::computeBarStacks() +{ + BarGraphOptions* ops = (BarGraphOptions*)ops_; + + if (((BarMode)ops->barMode != STACKED) || (nBarGroups_ == 0)) + return; + + // Initialize the stack sums to zero + for (BarGroup *gp = barGroups_, *gend = gp + nBarGroups_; gp < gend; gp++) + gp->sum = 0.0; + + // Consider each bar x-y coordinate. Add the ordinates of duplicate + // abscissas + + for (ChainLink* link = Chain_FirstLink(elements_.displayList); link; + link = Chain_NextLink(link)) { + BarElement* bePtr = (BarElement*)Chain_GetValue(link); + BarElementOptions* ops = (BarElementOptions*)bePtr->ops(); + if (ops->hide) + continue; + + if (ops->coords.x && ops->coords.y) { + for (double *x=ops->coords.x->values_, *y=ops->coords.y->values_, + *xend=x+ops->coords.x->nValues(); xxAxis; + key.yAxis =NULL; + Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&setTable_, (char*)&key); + if (!hPtr) + continue; + + Tcl_HashTable *tablePtr = (Tcl_HashTable*)Tcl_GetHashValue(hPtr); + const char *name = ops->groupName ? ops->groupName : ops->yAxis->name_; + hPtr = Tcl_FindHashEntry(tablePtr, name); + if (!hPtr) + continue; + + BarGroup *groupPtr = (BarGroup*)Tcl_GetHashValue(hPtr); + groupPtr->sum += *y; + } + } + } +} + diff --git a/generic/tkbltGraphBar.h b/generic/tkbltGraphBar.h new file mode 100644 index 0000000..5ee1ab8 --- /dev/null +++ b/generic/tkbltGraphBar.h @@ -0,0 +1,119 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGraphBar_h__ +#define __BltGraphBar_h__ + +#include + +#include "tkbltGraph.h" + +namespace Blt { + + typedef struct { + double value; + Axis* xAxis; + Axis* yAxis; + } BarSetKey; + + class BarGroup { + public: + int nSegments; + Axis* xAxis; + Axis* yAxis; + double sum; + int count; + double lastY; + size_t index; + + public: + BarGroup(); + }; + + typedef struct { + double aspect; + Tk_3DBorder normalBg; + int borderWidth; + Margin margins[4]; + Tk_Cursor cursor; + TextStyleOptions titleTextStyle; + int reqHeight; + XColor* highlightBgColor; + XColor* highlightColor; + int highlightWidth; + int inverted; + Tk_3DBorder plotBg; + int plotBW; + int xPad; + int yPad; + int plotRelief; + int relief; + ClosestSearch search; + int stackAxes; + const char *takeFocus; // nor used in C code + const char *title; + int reqWidth; + int reqPlotWidth; + int reqPlotHeight; + + // bar graph + int barMode; + double barWidth; + double baseline; + } BarGraphOptions; + + class BarGraph : public Graph { + public: + enum BarMode {INFRONT, STACKED, ALIGNED, OVERLAP}; + + public: + BarGroup* barGroups_; + int nBarGroups_; + Tcl_HashTable setTable_; + int maxBarSetSize_; + + protected: + void resetAxes(); + void mapElements(); + void initBarSets(); + void destroyBarSets(); + void resetBarSets(); + void computeBarStacks(); + + public: + BarGraph(ClientData, Tcl_Interp*, int, Tcl_Obj* const []); + virtual ~BarGraph(); + + int configure(); + int createPen(const char*, int, Tcl_Obj* const []); + int createElement(int, Tcl_Obj* const []); + }; +}; + +#endif diff --git a/generic/tkbltGraphLine.C b/generic/tkbltGraphLine.C new file mode 100644 index 0000000..fe3f5d0 --- /dev/null +++ b/generic/tkbltGraphLine.C @@ -0,0 +1,267 @@ +/* + * 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 + +#include "tkbltGraphLine.h" +#include "tkbltGraphOp.h" + +#include "tkbltGrAxis.h" +#include "tkbltGrXAxisOp.h" +#include "tkbltGrPen.h" +#include "tkbltGrPenOp.h" +#include "tkbltGrPenBar.h" +#include "tkbltGrPenLine.h" +#include "tkbltGrElem.h" +#include "tkbltGrElemOp.h" +#include "tkbltGrElemBar.h" +#include "tkbltGrElemLine.h" +#include "tkbltGrMarker.h" +#include "tkbltGrLegd.h" +#include "tkbltGrHairs.h" +#include "tkbltGrPostscript.h" +#include "tkbltGrDef.h" + +using namespace Blt; + +static const char* searchModeObjOption[] = {"points", "traces", "auto", NULL}; +static const char* searchAlongObjOption[] = {"x", "y", "both", NULL}; + +static Tk_OptionSpec optionSpecs[] = { + {TK_OPTION_DOUBLE, "-aspect", "aspect", "Aspect", + "0", -1, Tk_Offset(LineGraphOptions, aspect), 0, NULL, RESET}, + {TK_OPTION_BORDER, "-background", "background", "Background", + STD_NORMAL_BACKGROUND, -1, Tk_Offset(LineGraphOptions, normalBg), + 0, NULL, CACHE}, + {TK_OPTION_SYNONYM, "-bd", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-borderwidth", 0}, + {TK_OPTION_SYNONYM, "-bg", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-background", 0}, + {TK_OPTION_SYNONYM, "-bm", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-bottommargin", 0}, + {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", + STD_BORDERWIDTH, -1, Tk_Offset(LineGraphOptions, borderWidth), + 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-bottommargin", "bottomMargin", "BottomMargin", + "0", -1, Tk_Offset(LineGraphOptions, bottomMargin.reqSize), 0, NULL, RESET}, + {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", + "crosshair", -1, Tk_Offset(LineGraphOptions, cursor), + TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_SYNONYM, "-fg", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-foreground", 0}, + {TK_OPTION_FONT, "-font", "font", "Font", + STD_FONT_MEDIUM, -1, Tk_Offset(LineGraphOptions, titleTextStyle.font), + 0, NULL, RESET}, + {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", + STD_NORMAL_FOREGROUND, -1, + Tk_Offset(LineGraphOptions, titleTextStyle.color), 0, NULL, CACHE}, + {TK_OPTION_SYNONYM, "-halo", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-searchhalo", 0}, + {TK_OPTION_PIXELS, "-height", "height", "Height", + "4i", -1, Tk_Offset(LineGraphOptions, reqHeight), 0, NULL, RESET}, + {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground", + "HighlightBackground", + STD_NORMAL_BACKGROUND, -1, Tk_Offset(LineGraphOptions, highlightBgColor), + 0, NULL, CACHE}, + {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", + STD_NORMAL_FOREGROUND, -1, Tk_Offset(LineGraphOptions, highlightColor), + 0, NULL, CACHE}, + {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness", + "HighlightThickness", + "2", -1, Tk_Offset(LineGraphOptions, highlightWidth), 0, NULL, RESET}, + {TK_OPTION_BOOLEAN, "-invertxy", "invertXY", "InvertXY", + "no", -1, Tk_Offset(LineGraphOptions, inverted), 0, NULL, RESET}, + {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify", + "center", -1, Tk_Offset(LineGraphOptions, titleTextStyle.justify), + 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-leftmargin", "leftMargin", "Margin", + "0", -1, Tk_Offset(LineGraphOptions, leftMargin.reqSize), 0, NULL, RESET}, + {TK_OPTION_SYNONYM, "-lm", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-leftmargin", 0}, + {TK_OPTION_BORDER, "-plotbackground", "plotbackground", "PlotBackground", + STD_NORMAL_BACKGROUND, -1, Tk_Offset(LineGraphOptions, plotBg), + 0, NULL, CACHE}, + {TK_OPTION_PIXELS, "-plotborderwidth", "plotBorderWidth", "PlotBorderWidth", + STD_BORDERWIDTH, -1, Tk_Offset(LineGraphOptions, plotBW), 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-plotpadx", "plotPadX", "PlotPad", + "0", -1, Tk_Offset(LineGraphOptions, xPad), 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-plotpady", "plotPadY", "PlotPad", + "0", -1, Tk_Offset(LineGraphOptions, yPad), 0, NULL, RESET}, + {TK_OPTION_RELIEF, "-plotrelief", "plotRelief", "Relief", + "flat", -1, Tk_Offset(LineGraphOptions, plotRelief), 0, NULL, RESET}, + {TK_OPTION_RELIEF, "-relief", "relief", "Relief", + "flat", -1, Tk_Offset(LineGraphOptions, relief), 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-rightmargin", "rightMargin", "Margin", + "0", -1, Tk_Offset(LineGraphOptions, rightMargin.reqSize), 0, NULL, RESET}, + {TK_OPTION_SYNONYM, "-rm", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-rightmargin", 0}, + {TK_OPTION_PIXELS, "-searchhalo", "searchhalo", "SearchHalo", + "2m", -1, Tk_Offset(LineGraphOptions, search.halo), 0, NULL, 0}, + {TK_OPTION_STRING_TABLE, "-searchmode", "searchMode", "SearchMode", + "points", -1, Tk_Offset(LineGraphOptions, search.mode), + 0, &searchModeObjOption, 0}, + {TK_OPTION_STRING_TABLE, "-searchalong", "searchAlong", "SearchAlong", + "both", -1, Tk_Offset(LineGraphOptions, search.along), + 0, &searchAlongObjOption, 0}, + {TK_OPTION_BOOLEAN, "-stackaxes", "stackAxes", "StackAxes", + "no", -1, Tk_Offset(LineGraphOptions, stackAxes), 0, NULL, RESET}, + {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus", + NULL, -1, Tk_Offset(LineGraphOptions, takeFocus), + TK_OPTION_NULL_OK, NULL, 0}, + {TK_OPTION_STRING, "-title", "title", "Title", + NULL, -1, Tk_Offset(LineGraphOptions, title), + TK_OPTION_NULL_OK, NULL, RESET}, + {TK_OPTION_SYNONYM, "-tm", NULL, NULL, + NULL, 0, -1, 0, (ClientData)"-topmargin", 0}, + {TK_OPTION_PIXELS, "-topmargin", "topMargin", "TopMargin", + "0", -1, Tk_Offset(LineGraphOptions, topMargin.reqSize), 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-width", "width", "Width", + "5i", -1, Tk_Offset(LineGraphOptions, reqWidth), 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-plotwidth", "plotWidth", "PlotWidth", + "0", -1, Tk_Offset(LineGraphOptions, reqPlotWidth), 0, NULL, RESET}, + {TK_OPTION_PIXELS, "-plotheight", "plotHeight", "PlotHeight", + "0", -1, Tk_Offset(LineGraphOptions, reqPlotHeight), 0, NULL, RESET}, + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} +}; + +// Create + +LineGraph::LineGraph(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) + : Graph(clientData, interp, objc, objv) +{ + // problems so far? + if (!valid_) + return; + + ops_ = (LineGraphOptions*)calloc(1, sizeof(LineGraphOptions)); + LineGraphOptions* ops = (LineGraphOptions*)ops_; + + Tk_SetClass(tkwin_, "Graph"); + + ops->bottomMargin.site = MARGIN_BOTTOM; + ops->leftMargin.site = MARGIN_LEFT; + ops->topMargin.site = MARGIN_TOP; + ops->rightMargin.site = MARGIN_RIGHT; + + ops->titleTextStyle.anchor = TK_ANCHOR_N; + ops->titleTextStyle.color =NULL; + ops->titleTextStyle.font =NULL; + ops->titleTextStyle.angle =0; + ops->titleTextStyle.justify =TK_JUSTIFY_LEFT; + + optionTable_ = Tk_CreateOptionTable(interp_, optionSpecs); + if ((Tk_InitOptions(interp_, (char*)ops_, optionTable_, tkwin_) != TCL_OK) || (GraphObjConfigure(this, interp_, objc-2, objv+2) != TCL_OK)) { + valid_ =0; + return; + } + + // do this last after Tk_SetClass set + legend_ = new Legend(this); + crosshairs_ = new Crosshairs(this); + postscript_ = new Postscript(this); + + if (createPen("active", 0, NULL) != TCL_OK) { + valid_ =0; + return; + } + + if (createAxes() != TCL_OK) { + valid_ =0; + return; + } + + adjustAxes(); + + Tcl_SetStringObj(Tcl_GetObjResult(interp_), Tk_PathName(tkwin_), -1); +} + +LineGraph::~LineGraph() +{ +} + +int LineGraph::createPen(const char* penName, int objc, Tcl_Obj* const objv[]) +{ + int isNew; + Tcl_HashEntry *hPtr = + Tcl_CreateHashEntry(&penTable_, penName, &isNew); + if (!isNew) { + Tcl_AppendResult(interp_, "pen \"", penName, "\" already exists in \"", + Tk_PathName(tkwin_), "\"", (char *)NULL); + return TCL_ERROR; + } + + Pen* penPtr = new LinePen(this, penName, hPtr); + if (!penPtr) + return TCL_ERROR; + + Tcl_SetHashValue(hPtr, penPtr); + + if ((Tk_InitOptions(interp_, (char*)penPtr->ops(), penPtr->optionTable(), tkwin_) != TCL_OK) || (PenObjConfigure(this, penPtr, interp_, objc-4, objv+4) != TCL_OK)) { + delete penPtr; + return TCL_ERROR; + } + + return TCL_OK; +} + +int LineGraph::createElement(int objc, Tcl_Obj* const objv[]) +{ + char *name = Tcl_GetString(objv[3]); + if (name[0] == '-') { + Tcl_AppendResult(interp_, "name of element \"", name, + "\" can't start with a '-'", NULL); + return TCL_ERROR; + } + + int isNew; + Tcl_HashEntry* hPtr = + Tcl_CreateHashEntry(&elements_.table, name, &isNew); + if (!isNew) { + Tcl_AppendResult(interp_, "element \"", name, + "\" already exists in \"", Tcl_GetString(objv[0]), + "\"", NULL); + return TCL_ERROR; + } + + Element* elemPtr = new LineElement(this, name, hPtr); + if (!elemPtr) + return TCL_ERROR; + + Tcl_SetHashValue(hPtr, elemPtr); + + if ((Tk_InitOptions(interp_, (char*)elemPtr->ops(), elemPtr->optionTable(), tkwin_) != TCL_OK) || (ElementObjConfigure(elemPtr, interp_, objc-4, objv+4) != TCL_OK)) { + delete elemPtr; + return TCL_ERROR; + } + + elemPtr->link = elements_.displayList->append(elemPtr); + + return TCL_OK; +} diff --git a/generic/tkbltGraphLine.h b/generic/tkbltGraphLine.h new file mode 100644 index 0000000..ea8d2a0 --- /dev/null +++ b/generic/tkbltGraphLine.h @@ -0,0 +1,76 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltGraphLine_h__ +#define __BltGraphLine_h__ + +#include + +#include "tkbltGraph.h" + +namespace Blt { + + typedef struct { + double aspect; + Tk_3DBorder normalBg; + int borderWidth; + Margin margins[4]; + Tk_Cursor cursor; + Blt::TextStyleOptions titleTextStyle; + int reqHeight; + XColor* highlightBgColor; + XColor* highlightColor; + int highlightWidth; + int inverted; + Tk_3DBorder plotBg; + int plotBW; + int xPad; + int yPad; + int plotRelief; + int relief; + ClosestSearch search; + int stackAxes; + const char *takeFocus; // nor used in C code + const char *title; + int reqWidth; + int reqPlotWidth; + int reqPlotHeight; + } LineGraphOptions; + + class LineGraph : public Graph { + public: + LineGraph(ClientData, Tcl_Interp*, int objc, Tcl_Obj* const []); + virtual ~LineGraph(); + + int createElement(int, Tcl_Obj* const []); + int createPen(const char*, int, Tcl_Obj* const []); + }; +}; + +#endif diff --git a/generic/tkbltGraphOp.C b/generic/tkbltGraphOp.C new file mode 100644 index 0000000..ada2758 --- /dev/null +++ b/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 + +#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; +} + diff --git a/generic/tkbltGraphOp.h b/generic/tkbltGraphOp.h new file mode 100644 index 0000000..ff3f6ef --- /dev/null +++ b/generic/tkbltGraphOp.h @@ -0,0 +1,44 @@ +/* + * 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. + */ + +#ifndef __BltGraphOp_h__ +#define __BltGraphOp_h__ + +#include + +extern int GraphObjConfigure(Blt::Graph* graphPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); + +extern Tcl_ObjCmdProc GraphInstCmdProc; +extern Tcl_CmdDeleteProc GraphInstCmdDeleteProc; +extern Tk_EventProc GraphEventProc; +extern Tcl_IdleProc DisplayGraph; +extern Tcl_FreeProc DestroyGraph; + +#endif diff --git a/generic/tkbltGraphSup.C b/generic/tkbltGraphSup.C new file mode 100644 index 0000000..d5eb3d1 --- /dev/null +++ b/generic/tkbltGraphSup.C @@ -0,0 +1,686 @@ +/* + * 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 1993-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 +#include + +#include + +#include "tkbltGraph.h" +#include "tkbltGrAxis.h" +#include "tkbltGrElem.h" +#include "tkbltGrLegd.h" +#include "tkbltGrMisc.h" + +using namespace Blt; + +#define AXIS_PAD_TITLE 2 +#define ROTATE_0 0 +#define ROTATE_90 1 +#define ROTATE_180 2 +#define ROTATE_270 3 + +/* + *--------------------------------------------------------------------------- + * + * layoutGraph -- + * + * Calculate the layout of the graph. Based upon the data, axis limits, + * X and Y titles, and title height, determine the cavity left which is + * the plotting surface. The first step get the data and axis limits for + * calculating the space needed for the top, bottom, left, and right + * margins. + * + * 1) The LEFT margin is the area from the left border to the Y axis + * (not including ticks). It composes the border width, the width an + * optional Y axis label and its padding, and the tick numeric labels. + * The Y axis label is rotated 90 degrees so that the width is the + * font height. + * + * 2) The RIGHT margin is the area from the end of the graph + * to the right window border. It composes the border width, + * some padding, the font height (this may be dubious. It + * appears to provide a more even border), the max of the + * legend width and 1/2 max X tick number. This last part is + * so that the last tick label is not clipped. + * + * Window Width + * ___________________________________________________________ + * | | | | + * | | TOP height of title | | + * | | | | + * | | x2 title | | + * | | | | + * | | height of x2-axis | | + * |__________|_______________________________|_______________| W + * | | -plotpady | | i + * |__________|_______________________________|_______________| n + * | | top right | | d + * | | | | o + * | LEFT | | RIGHT | w + * | | | | + * | y | Free area = 104% | y2 | H + * | | Plotting surface = 100% | | e + * | t | Tick length = 2 + 2% | t | i + * | i | | i | g + * | t | | t legend| h + * | l | | l width| t + * | e | | e | + * | height| |height | + * | of | | of | + * | y-axis| |y2-axis | + * | | | | + * | |origin 0,0 | | + * |__________|_left_________________bottom___|_______________| + * | |-plotpady | | + * |__________|_______________________________|_______________| + * | | (xoffset, yoffset) | | + * | | | | + * | | height of x-axis | | + * | | | | + * | | BOTTOM x title | | + * |__________|_______________________________|_______________| + * + * 3) The TOP margin is the area from the top window border to the top + * of the graph. It composes the border width, twice the height of + * the title font (if one is given) and some padding between the + * title. + * + * 4) The BOTTOM margin is area from the bottom window border to the + * X axis (not including ticks). It composes the border width, the height + * an optional X axis label and its padding, the height of the font + * of the tick labels. + * + * The plotting area is between the margins which includes the X and Y axes + * including the ticks but not the tick numeric labels. The length of the + * ticks and its padding is 5% of the entire plotting area. Hence the entire + * plotting area is scaled as 105% of the width and height of the area. + * + * The axis labels, ticks labels, title, and legend may or may not be + * displayed which must be taken into account. + * + * if reqWidth > 0 : set outer size + * if reqPlotWidth > 0 : set plot size + *--------------------------------------------------------------------------- + */ + +void Graph::layoutGraph() +{ + GraphOptions* ops = (GraphOptions*)ops_; + + int width = width_; + int height = height_; + + // Step 1 + // Compute the amount of space needed to display the axes + // associated with each margin. They can be overridden by + // -leftmargin, -rightmargin, -bottommargin, and -topmargin + // graph options, respectively. + int left = getMarginGeometry(&ops->leftMargin); + int right = getMarginGeometry(&ops->rightMargin); + int top = getMarginGeometry(&ops->topMargin); + int bottom = getMarginGeometry(&ops->bottomMargin); + + int pad = ops->bottomMargin.maxTickWidth; + if (pad < ops->topMargin.maxTickWidth) + pad = ops->topMargin.maxTickWidth; + + pad = pad / 2 + 3; + if (right < pad) + right = pad; + + if (left < pad) + left = pad; + + pad = ops->leftMargin.maxTickHeight; + if (pad < ops->rightMargin.maxTickHeight) + pad = ops->rightMargin.maxTickHeight; + + pad = pad / 2; + if (top < pad) + top = pad; + + if (bottom < pad) + bottom = pad; + + if (ops->leftMargin.reqSize > 0) + left = ops->leftMargin.reqSize; + + if (ops->rightMargin.reqSize > 0) + right = ops->rightMargin.reqSize; + + if (ops->topMargin.reqSize > 0) + top = ops->topMargin.reqSize; + + if (ops->bottomMargin.reqSize > 0) + bottom = ops->bottomMargin.reqSize; + + // Step 2 + // Add the graph title height to the top margin. + if (ops->title) + top += titleHeight_ + 6; + + int inset = (inset_ + ops->plotBW); + int inset2 = 2 * inset; + + // Step 3 + // Estimate the size of the plot area from the remaining + // space. This may be overridden by the -plotwidth and + // -plotheight graph options. We use this to compute the + // size of the legend. + if (width == 0) + width = 400; + + if (height == 0) + height = 400; + + int plotWidth = (ops->reqPlotWidth > 0) ? ops->reqPlotWidth : + width - (inset2 + left + right); + int plotHeight = (ops->reqPlotHeight > 0) ? ops->reqPlotHeight : + height - (inset2 + top + bottom); + legend_->map(plotWidth, plotHeight); + + // Step 4 + // Add the legend to the appropiate margin. + if (!legend_->isHidden()) { + switch (legend_->position()) { + case Legend::RIGHT: + right += legend_->width_ + 2; + break; + case Legend::LEFT: + left += legend_->width_ + 2; + break; + case Legend::TOP: + top += legend_->height_ + 2; + break; + case Legend::BOTTOM: + bottom += legend_->height_ + 2; + break; + case Legend::XY: + case Legend::PLOT: + break; + } + } + + // Recompute the plotarea or graph size, now accounting for the legend. + if (ops->reqPlotWidth == 0) { + plotWidth = width - (inset2 + left + right); + if (plotWidth < 1) + plotWidth = 1; + } + if (ops->reqPlotHeight == 0) { + plotHeight = height - (inset2 + top + bottom); + if (plotHeight < 1) + plotHeight = 1; + } + + // Step 5 + // If necessary, correct for the requested plot area aspect ratio. + if ((ops->reqPlotWidth == 0) && (ops->reqPlotHeight == 0) && + (ops->aspect > 0.0)) { + double ratio; + + // Shrink one dimension of the plotarea to fit the requested + // width/height aspect ratio. + ratio = plotWidth / plotHeight; + if (ratio > ops->aspect) { + // Shrink the width + int scaledWidth = (int)(plotHeight * ops->aspect); + if (scaledWidth < 1) + scaledWidth = 1; + + // Add the difference to the right margin. + // CHECK THIS: w = scaledWidth + right += (plotWidth - scaledWidth); + } + else { + // Shrink the height + int scaledHeight = (int)(plotWidth / ops->aspect); + if (scaledHeight < 1) + scaledHeight = 1; + + // Add the difference to the top margin + // CHECK THIS: h = scaledHeight; + top += (plotHeight - scaledHeight); + } + } + + // Step 6 + // If there's multiple axes in a margin, the axis titles will be + // displayed in the adjoining margins. Make sure there's room + // for the longest axis titles. + if (top < ops->leftMargin.axesTitleLength) + top = ops->leftMargin.axesTitleLength; + + if (right < ops->bottomMargin.axesTitleLength) + right = ops->bottomMargin.axesTitleLength; + + if (top < ops->rightMargin.axesTitleLength) + top = ops->rightMargin.axesTitleLength; + + if (right < ops->topMargin.axesTitleLength) + right = ops->topMargin.axesTitleLength; + + // Step 7 + // Override calculated values with requested margin sizes. + if (ops->leftMargin.reqSize > 0) + left = ops->leftMargin.reqSize; + + if (ops->rightMargin.reqSize > 0) + right = ops->rightMargin.reqSize; + + if (ops->topMargin.reqSize > 0) + top = ops->topMargin.reqSize; + + if (ops->bottomMargin.reqSize > 0) + bottom = ops->bottomMargin.reqSize; + + if (ops->reqPlotWidth > 0) { + // Width of plotarea is constained. If there's extra space, add it to + // the left and/or right margins. If there's too little, grow the + // graph width to accomodate it. + int w = plotWidth + inset2 + left + right; + + // Extra space in window + if (width > w) { + int extra = (width - w) / 2; + if (ops->leftMargin.reqSize == 0) { + left += extra; + if (ops->rightMargin.reqSize == 0) + right += extra; + else + left += extra; + } + else if (ops->rightMargin.reqSize == 0) + right += extra + extra; + } + else if (width < w) + width = w; + } + + // Constrain the plotarea height + if (ops->reqPlotHeight > 0) { + + // Height of plotarea is constained. If there's extra space, + // add it to th top and/or bottom margins. If there's too little, + // grow the graph height to accomodate it. + int h = plotHeight + inset2 + top + bottom; + + // Extra space in window + if (height > h) { + int extra = (height - h) / 2; + if (ops->topMargin.reqSize == 0) { + top += extra; + if (ops->bottomMargin.reqSize == 0) + bottom += extra; + else + top += extra; + } + else if (ops->bottomMargin.reqSize == 0) + bottom += extra + extra; + } + else if (height < h) + height = h; + } + + width_ = width; + height_ = height; + left_ = left + inset; + top_ = top + inset; + right_ = width - right - inset; + bottom_ = height - bottom - inset; + + ops->leftMargin.width = left + inset_; + ops->rightMargin.width = right + inset_; + ops->topMargin.height = top + inset_; + ops->bottomMargin.height = bottom + inset_; + + vOffset_ = top_ + ops->yPad; + vRange_ = plotHeight - 2*ops->yPad; + hOffset_ = left_ + ops->xPad; + hRange_ = plotWidth - 2*ops->xPad; + + if (vRange_ < 1) + vRange_ = 1; + + if (hRange_ < 1) + hRange_ = 1; + + hScale_ = 1.0 / hRange_; + vScale_ = 1.0 / vRange_; + + // Calculate the placement of the graph title so it is centered within the + // space provided for it in the top margin + titleY_ = 3 + inset_; + titleX_ = (right_ + left_) / 2; +} + +int Graph::getMarginGeometry(Margin *marginPtr) +{ + GraphOptions* ops = (GraphOptions*)ops_; + int isHoriz = !(marginPtr->site & 0x1); /* Even sites are horizontal */ + + // Count the visible axes. + unsigned int nVisible = 0; + unsigned int l =0; + int w =0; + int h =0; + + marginPtr->maxTickWidth =0; + marginPtr->maxTickHeight =0; + + if (ops->stackAxes) { + for (ChainLink* link = Chain_FirstLink(marginPtr->axes); link; + link = Chain_NextLink(link)) { + Axis* axisPtr = (Axis*)Chain_GetValue(link); + AxisOptions* ops = (AxisOptions*)axisPtr->ops(); + if (!ops->hide && axisPtr->use_) { + nVisible++; + axisPtr->getGeometry(); + + if (isHoriz) { + if (h < axisPtr->height_) + h = axisPtr->height_; + } + else { + if (w < axisPtr->width_) + w = axisPtr->width_; + } + if (axisPtr->maxTickWidth_ > marginPtr->maxTickWidth) + marginPtr->maxTickWidth = axisPtr->maxTickWidth_; + + if (axisPtr->maxTickHeight_ > marginPtr->maxTickHeight) + marginPtr->maxTickHeight = axisPtr->maxTickHeight_; + } + } + } + else { + for (ChainLink* link = Chain_FirstLink(marginPtr->axes); link; + link = Chain_NextLink(link)) { + Axis* axisPtr = (Axis*)Chain_GetValue(link); + AxisOptions* ops = (AxisOptions*)axisPtr->ops(); + if (!ops->hide && axisPtr->use_) { + nVisible++; + axisPtr->getGeometry(); + + if ((ops->titleAlternate) && (l < axisPtr->titleWidth_)) + l = axisPtr->titleWidth_; + + if (isHoriz) + h += axisPtr->height_; + else + w += axisPtr->width_; + + if (axisPtr->maxTickWidth_ > marginPtr->maxTickWidth) + marginPtr->maxTickWidth = axisPtr->maxTickWidth_; + + if (axisPtr->maxTickHeight_ > marginPtr->maxTickHeight) + marginPtr->maxTickHeight = axisPtr->maxTickHeight_; + } + } + } + // Enforce a minimum size for margins. + if (w < 3) + w = 3; + + if (h < 3) + h = 3; + + marginPtr->nAxes = nVisible; + marginPtr->axesTitleLength = l; + marginPtr->width = w; + marginPtr->height = h; + marginPtr->axesOffset = (isHoriz) ? h : w; + return marginPtr->axesOffset; +} + +void Graph::getTextExtents(Tk_Font font, const char *text, int textLen, + int* ww, int* hh) +{ + if (!text) { + *ww =0; + *hh =0; + return; + } + + Tk_FontMetrics fm; + Tk_GetFontMetrics(font, &fm); + int lineHeight = fm.linespace; + + if (textLen < 0) + textLen = strlen(text); + + int maxWidth =0; + int maxHeight =0; + int lineLen =0; + const char *line =NULL; + const char *p, *pend; + for (p =line=text, pend=text+textLen; p 0) { + int lineWidth = Tk_TextWidth(font, line, lineLen); + if (lineWidth > maxWidth) + maxWidth = lineWidth; + } + maxHeight += lineHeight; + line = p + 1; /* Point to the start of the next line. */ + lineLen = 0; /* Reset counter to indicate the start of a + * new line. */ + continue; + } + lineLen++; + } + + if ((lineLen > 0) && (*(p - 1) != '\n')) { + maxHeight += lineHeight; + int lineWidth = Tk_TextWidth(font, line, lineLen); + if (lineWidth > maxWidth) + maxWidth = lineWidth; + } + + *ww = maxWidth; + *hh = maxHeight; +} + +/* + *--------------------------------------------------------------------------- + * + * Computes the dimensions of the bounding box surrounding a rectangle + * rotated about its center. If pointArr isn't NULL, the coordinates of + * the rotated rectangle are also returned. + * + * The dimensions are determined by rotating the rectangle, and doubling + * the maximum x-coordinate and y-coordinate. + * + * w = 2 * maxX, h = 2 * maxY + * + * Since the rectangle is centered at 0,0, the coordinates of the + * bounding box are (-w/2,-h/2 w/2,-h/2, w/2,h/2 -w/2,h/2). + * + * 0 ------- 1 + * | | + * | x | + * | | + * 3 ------- 2 + * + * Results: + * The width and height of the bounding box containing the rotated + * rectangle are returned. + * + *--------------------------------------------------------------------------- + */ +void Graph::getBoundingBox(int width, int height, double angle, + double *rotWidthPtr, double *rotHeightPtr, + Point2d *bbox) +{ + angle = fmod(angle, 360.0); + if (fmod(angle, 90.0) == 0.0) { + int ll, ur, ul, lr; + double rotWidth, rotHeight; + + // Handle right-angle rotations specially + int quadrant = (int)(angle / 90.0); + switch (quadrant) { + case ROTATE_270: + ul = 3, ur = 0, lr = 1, ll = 2; + rotWidth = (double)height; + rotHeight = (double)width; + break; + case ROTATE_90: + ul = 1, ur = 2, lr = 3, ll = 0; + rotWidth = (double)height; + rotHeight = (double)width; + break; + case ROTATE_180: + ul = 2, ur = 3, lr = 0, ll = 1; + rotWidth = (double)width; + rotHeight = (double)height; + break; + default: + case ROTATE_0: + ul = 0, ur = 1, lr = 2, ll = 3; + rotWidth = (double)width; + rotHeight = (double)height; + break; + } + if (bbox) { + double x = rotWidth * 0.5; + double y = rotHeight * 0.5; + bbox[ll].x = bbox[ul].x = -x; + bbox[ur].y = bbox[ul].y = -y; + bbox[lr].x = bbox[ur].x = x; + bbox[ll].y = bbox[lr].y = y; + } + *rotWidthPtr = rotWidth; + *rotHeightPtr = rotHeight; + return; + } + + // Set the four corners of the rectangle whose center is the origin + Point2d corner[4]; + corner[1].x = corner[2].x = (double)width * 0.5; + corner[0].x = corner[3].x = -corner[1].x; + corner[2].y = corner[3].y = (double)height * 0.5; + corner[0].y = corner[1].y = -corner[2].y; + + double radians = (-angle / 180.0) * M_PI; + double sinTheta = sin(radians); + double cosTheta = cos(radians); + double xMax =0; + double yMax =0; + + // Rotate the four corners and find the maximum X and Y coordinates + for (int ii=0; ii<4; ii++) { + double x = (corner[ii].x * cosTheta) - (corner[ii].y * sinTheta); + double y = (corner[ii].x * sinTheta) + (corner[ii].y * cosTheta); + if (x > xMax) + xMax = x; + + if (y > yMax) + yMax = y; + + if (bbox) { + bbox[ii].x = x; + bbox[ii].y = y; + } + } + + // By symmetry, the width and height of the bounding box are twice the + // maximum x and y coordinates. + *rotWidthPtr = xMax + xMax; + *rotHeightPtr = yMax + yMax; +} + +/* + *--------------------------------------------------------------------------- + * + * Blt_AnchorPoint -- + * + * Translates a position, using both the dimensions of the bounding box, + * and the anchor direction, returning the coordinates of the upper-left + * corner of the box. The anchor indicates where the given x-y position + * is in relation to the bounding box. + * + * 7 nw --- 0 n --- 1 ne + * | | + * 6 w 8 center 2 e + * | | + * 5 sw --- 4 s --- 3 se + * + * The coordinates returned are translated to the origin of the bounding + * box (suitable for giving to XCopyArea, XCopyPlane, etc.) + * + * Results: + * The translated coordinates of the bounding box are returned. + * + *--------------------------------------------------------------------------- + */ +Point2d Graph::anchorPoint(double x, double y, double w, double h, + Tk_Anchor anchor) +{ + Point2d t; + + switch (anchor) { + case TK_ANCHOR_NW: /* 7 Upper left corner */ + break; + case TK_ANCHOR_W: /* 6 Left center */ + y -= (h * 0.5); + break; + case TK_ANCHOR_SW: /* 5 Lower left corner */ + y -= h; + break; + case TK_ANCHOR_N: /* 0 Top center */ + x -= (w * 0.5); + break; + case TK_ANCHOR_CENTER: /* 8 Center */ + x -= (w * 0.5); + y -= (h * 0.5); + break; + case TK_ANCHOR_S: /* 4 Bottom center */ + x -= (w * 0.5); + y -= h; + break; + case TK_ANCHOR_NE: /* 1 Upper right corner */ + x -= w; + break; + case TK_ANCHOR_E: /* 2 Right center */ + x -= w; + y -= (h * 0.5); + break; + case TK_ANCHOR_SE: /* 3 Lower right corner */ + x -= w; + y -= h; + break; + } + + t.x = x; + t.y = y; + return t; +} + diff --git a/generic/tkbltInt.C b/generic/tkbltInt.C new file mode 100644 index 0000000..5e9dde7 --- /dev/null +++ b/generic/tkbltInt.C @@ -0,0 +1,74 @@ +/* + * 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 +#include +using namespace std; + +extern "C" { +Tcl_AppInitProc Tkblt_Init; +Tcl_AppInitProc Tkblt_SafeInit; +}; + +Tcl_AppInitProc Blt_VectorCmdInitProc; +Tcl_AppInitProc Blt_GraphCmdInitProc; + +#include "tkbltStubInit.c" + +int Tkblt_Init(Tcl_Interp* interp) +{ + Tcl_Namespace *nsPtr; + + if (Tcl_InitStubs(interp, TCL_PATCH_LEVEL, 0) == NULL) + return TCL_ERROR; + if (Tk_InitStubs(interp, TK_PATCH_LEVEL, 0) == NULL) + return TCL_ERROR; + + nsPtr = Tcl_FindNamespace(interp, "::blt", (Tcl_Namespace *)NULL, 0); + if (nsPtr == NULL) { + nsPtr = Tcl_CreateNamespace(interp, "::blt", NULL, NULL); + if (nsPtr == NULL) + return TCL_ERROR; + } + + if (Blt_VectorCmdInitProc(interp) != TCL_OK) + return TCL_ERROR; + if (Blt_GraphCmdInitProc(interp) != TCL_OK) + return TCL_ERROR; + + if (Tcl_PkgProvideEx(interp, PACKAGE_NAME, PACKAGE_VERSION, (ClientData)&tkbltStubs) != TCL_OK) + return TCL_ERROR; + + return TCL_OK; +} + +int Tkblt_SafeInit(Tcl_Interp* interp) +{ + return Tkblt_Init(interp); +} diff --git a/generic/tkbltInt.h b/generic/tkbltInt.h new file mode 100644 index 0000000..2bf96ee --- /dev/null +++ b/generic/tkbltInt.h @@ -0,0 +1,58 @@ +/* + * Copyright 2017 Patzschke+Rasp Software GmbH + * + * 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. + */ + +#ifndef __TKBLT_INT_H__ + +#if defined(_MSC_VER) + +#include + +#if !defined(NAN) +#define NAN (std::numeric_limits::quiet_NaN()) +#endif + +#if !defined(isnan) +#define isnan(x) _isnan(x) +#endif + +#if !defined(isfinite) +#define isfinite(x) _finite(x) +#endif + +#if !defined(isinf) +#define isinf(x) !_finite(x) +#endif + +#if !defined(numeric_limits) +#define numeric_limits(x) _numeric_limits(x) +#endif + +#if _MSC_VER < 1900 +#define snprintf _snprintf +#else +#include //sprintf +#endif + +#endif /* _MSC_VER */ + +#endif /* __TKBLT_INT_H__ */ diff --git a/generic/tkbltNsUtil.C b/generic/tkbltNsUtil.C new file mode 100644 index 0000000..f2ecfa3 --- /dev/null +++ b/generic/tkbltNsUtil.C @@ -0,0 +1,129 @@ +/* + * 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 1997-2008 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. + */ + +#ifdef __CYGWIN__ +extern "C" { +#include +} +#else +#include +#endif + +#include "tkbltNsUtil.h" + +using namespace Blt; + +Tcl_Namespace* Blt::GetCommandNamespace(Tcl_Command cmdToken) +{ + Command* cmdPtr = (Command*)cmdToken; + return (Tcl_Namespace *)cmdPtr->nsPtr; +} + +int Blt::ParseObjectName(Tcl_Interp* interp, const char *path, + Blt_ObjectName *namePtr, unsigned int flags) +{ + namePtr->nsPtr = NULL; + namePtr->name = NULL; + char* colon = NULL; + + /* Find the last namespace separator in the qualified name. */ + char* last = (char *)(path + strlen(path)); + while (--last > path) { + if ((*last == ':') && (*(last - 1) == ':')) { + last++; /* just after the last "::" */ + colon = last - 2; + break; + } + } + if (colon == NULL) { + namePtr->name = path; + if ((flags & BLT_NO_DEFAULT_NS) == 0) { + namePtr->nsPtr = Tcl_GetCurrentNamespace(interp); + } + return 1; /* No namespace designated in name. */ + } + + /* Separate the namespace and the object name. */ + *colon = '\0'; + if (path[0] == '\0') { + namePtr->nsPtr = Tcl_GetGlobalNamespace(interp); + } else { + namePtr->nsPtr = Tcl_FindNamespace(interp, (char *)path, NULL, + (flags & BLT_NO_ERROR_MSG) ? 0 : TCL_LEAVE_ERR_MSG); + } + /* Repair the string. */ *colon = ':'; + + if (namePtr->nsPtr == NULL) { + return 0; /* Namespace doesn't exist. */ + } + namePtr->name =last; + return 1; +} + +char* Blt::MakeQualifiedName(Blt_ObjectName *namePtr, Tcl_DString *resultPtr) +{ + Tcl_DStringInit(resultPtr); + if ((namePtr->nsPtr->fullName[0] != ':') || + (namePtr->nsPtr->fullName[1] != ':') || + (namePtr->nsPtr->fullName[2] != '\0')) { + Tcl_DStringAppend(resultPtr, namePtr->nsPtr->fullName, -1); + } + Tcl_DStringAppend(resultPtr, "::", -1); + Tcl_DStringAppend(resultPtr, (char *)namePtr->name, -1); + return Tcl_DStringValue(resultPtr); +} + +static Tcl_Namespace* NamespaceOfVariable(Var *varPtr) +{ + if (varPtr->flags & VAR_IN_HASHTABLE) { + VarInHash *vhashPtr = (VarInHash *)varPtr; + TclVarHashTable *vtablePtr; + + vtablePtr = (TclVarHashTable *)vhashPtr->entry.tablePtr; + return (Tcl_Namespace*)(vtablePtr->nsPtr); + } + return NULL; +} + +Tcl_Namespace* Blt::GetVariableNamespace(Tcl_Interp* interp, const char *path) +{ + Blt_ObjectName objName; + if (!ParseObjectName(interp, path, &objName, BLT_NO_DEFAULT_NS)) + return NULL; + + if (objName.nsPtr == NULL) { + Var*varPtr = (Var*)Tcl_FindNamespaceVar(interp, (char *)path, + (Tcl_Namespace *)NULL, + TCL_GLOBAL_ONLY); + if (varPtr) + return NamespaceOfVariable(varPtr); + + } + return objName.nsPtr; +} diff --git a/generic/tkbltNsUtil.h b/generic/tkbltNsUtil.h new file mode 100644 index 0000000..950a48a --- /dev/null +++ b/generic/tkbltNsUtil.h @@ -0,0 +1,64 @@ +/* + * 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 1993-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. + */ + +#ifndef BLT_NS_UTIL_H +#define BLT_NS_UTIL_H 1 + +#define NS_SEARCH_NONE (0) +#define NS_SEARCH_CURRENT (1<<0) +#define NS_SEARCH_GLOBAL (1<<1) +#define NS_SEARCH_BOTH (NS_SEARCH_GLOBAL | NS_SEARCH_CURRENT) + +#define BLT_NO_DEFAULT_NS (1<<0) +#define BLT_NO_ERROR_MSG (1<<1) + +namespace Blt { + + typedef struct { + const char *name; + Tcl_Namespace *nsPtr; + } Blt_ObjectName; + + extern Tcl_Namespace* GetVariableNamespace(Tcl_Interp* interp, + const char *varName); + + extern Tcl_Namespace* GetCommandNamespace(Tcl_Command cmdToken); + + extern int ParseObjectName(Tcl_Interp* interp, const char *name, + Blt_ObjectName *objNamePtr, + unsigned int flags); + + extern char* MakeQualifiedName(Blt_ObjectName *objNamePtr, + Tcl_DString *resultPtr); +}; + +#endif /* BLT_NS_UTIL_H */ diff --git a/generic/tkbltOp.C b/generic/tkbltOp.C new file mode 100644 index 0000000..4199a44 --- /dev/null +++ b/generic/tkbltOp.C @@ -0,0 +1,171 @@ +/* + * 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 + +#include "tkbltOp.h" + +using namespace Blt; + +static int BinaryOpSearch(Blt_OpSpec *specs, int nSpecs, const char *string, + int length) +{ + int low = 0; + int high = nSpecs - 1; + char c = string[0]; + while (low <= high) { + int median = (low + high) >> 1; + Blt_OpSpec *specPtr = specs + median; + + /* Test the first character */ + int compare = c - specPtr->name[0]; + if (compare == 0) { + /* Now test the entire string */ + compare = strncmp(string, specPtr->name, length); + if (compare == 0) { + if ((int)length < specPtr->minChars) { + return -2; /* Ambiguous operation name */ + } + } + } + if (compare < 0) { + high = median - 1; + } else if (compare > 0) { + low = median + 1; + } else { + return median; /* Op found. */ + } + } + return -1; /* Can't find operation */ +} + +static int LinearOpSearch(Blt_OpSpec *specs, int nSpecs, const char *string, + int length) +{ + char c = string[0]; + int nMatches = 0; + int last = -1; + int i =0; + for (Blt_OpSpec *specPtr = specs; iname[0]) && + (strncmp(string, specPtr->name, length) == 0)) { + last = i; + nMatches++; + if ((int)length == specPtr->minChars) { + break; + } + } + } + if (nMatches > 1) + return -2; /* Ambiguous operation name */ + + if (nMatches == 0) + return -1; /* Can't find operation */ + + return last; /* Op found. */ +} + +void* Blt::GetOpFromObj(Tcl_Interp* interp, int nSpecs, Blt_OpSpec *specs, + int operPos, int objc, Tcl_Obj* const objv[], + int flags) +{ + Blt_OpSpec *specPtr; + int n; + + if (objc <= operPos) { /* No operation argument */ + Tcl_AppendResult(interp, "wrong # args: ", (char *)NULL); + usage: + Tcl_AppendResult(interp, "should be one of...", (char *)NULL); + for (n = 0; n < nSpecs; n++) { + Tcl_AppendResult(interp, "\n ", (char *)NULL); + for (int ii = 0; ii < operPos; ii++) { + Tcl_AppendResult(interp, Tcl_GetString(objv[ii]), " ", + (char *)NULL); + } + specPtr = specs + n; + Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage, + (char *)NULL); + } + return NULL; + } + + int length; + const char* string = Tcl_GetStringFromObj(objv[operPos], &length); + if (flags & BLT_OP_LINEAR_SEARCH) + n = LinearOpSearch(specs, nSpecs, string, length); + else + n = BinaryOpSearch(specs, nSpecs, string, length); + + if (n == -2) { + char c; + + Tcl_AppendResult(interp, "ambiguous", (char *)NULL); + if (operPos > 2) { + Tcl_AppendResult(interp, " ", Tcl_GetString(objv[operPos - 1]), + (char *)NULL); + } + Tcl_AppendResult(interp, " operation \"", string, "\" matches: ", + (char *)NULL); + + c = string[0]; + for (n = 0; n < nSpecs; n++) { + specPtr = specs + n; + if ((c == specPtr->name[0]) && + (strncmp(string, specPtr->name, length) == 0)) { + Tcl_AppendResult(interp, " ", specPtr->name, (char *)NULL); + } + } + return NULL; + + } else if (n == -1) { /* Can't find operation, display help */ + Tcl_AppendResult(interp, "bad", (char *)NULL); + if (operPos > 2) { + Tcl_AppendResult(interp, " ", Tcl_GetString(objv[operPos - 1]), + (char *)NULL); + } + Tcl_AppendResult(interp, " operation \"", string, "\": ", (char *)NULL); + goto usage; + } + specPtr = specs + n; + if ((objc < specPtr->minArgs) || + ((specPtr->maxArgs > 0) && (objc > specPtr->maxArgs))) { + int i; + + Tcl_AppendResult(interp, "wrong # args: should be \"", (char *)NULL); + for (i = 0; i < operPos; i++) { + Tcl_AppendResult(interp, Tcl_GetString(objv[i]), " ", + (char *)NULL); + } + Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage, "\"", + (char *)NULL); + return NULL; + } + return specPtr->proc; +} + diff --git a/generic/tkbltOp.h b/generic/tkbltOp.h new file mode 100644 index 0000000..fc9ffb7 --- /dev/null +++ b/generic/tkbltOp.h @@ -0,0 +1,67 @@ +/* + * 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 1993-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. + */ + +#ifndef __BltOp_h__ +#define __BltOp_h__ + +#include + +#define BLT_OP_BINARY_SEARCH 0 +#define BLT_OP_LINEAR_SEARCH 1 + +namespace Blt { + + typedef struct { + const char *name; /* Name of operation */ + int minChars; /* Minimum # characters to disambiguate */ + void *proc; + int minArgs; /* Minimum # args required */ + int maxArgs; /* Maximum # args required */ + const char *usage; /* Usage message */ + } Blt_OpSpec; + + typedef enum { + BLT_OP_ARG0, /* Op is the first argument. */ + BLT_OP_ARG1, /* Op is the second argument. */ + BLT_OP_ARG2, /* Op is the third argument. */ + BLT_OP_ARG3, /* Op is the fourth argument. */ + BLT_OP_ARG4 /* Op is the fifth argument. */ + + } Blt_OpIndex; + + void *GetOpFromObj(Tcl_Interp* interp, int nSpecs, + Blt_OpSpec *specs, int operPos, int objc, + Tcl_Obj* const objv[], int flags); +}; + +#endif + diff --git a/generic/tkbltParse.C b/generic/tkbltParse.C new file mode 100644 index 0000000..095b16a --- /dev/null +++ b/generic/tkbltParse.C @@ -0,0 +1,388 @@ +/* + * Smithsonian Astrophysical Observatory, Cambridge, MA, USA + * This code has been modified under the terms listed below and is made + * available under the same terms. + */ + +/* + * + * This file is copied from tclParse.c in the TCL library distribution. + * + * Copyright (c) 1987-1993 The Regents of the University of + * California. + * + * Copyright (c) 1994-1998 Sun Microsystems, Inc. + * + */ + +/* + * Since TCL 8.1.0 these routines have been replaced by ones that + * generate byte-codes. But since these routines are used in vector + * expressions, where no such byte-compilation is necessary, I now + * include them. In fact, the byte-compiled versions would be slower + * since the compiled code typically runs only one time. + */ + +#include +#include + +#include +#include +#include +using namespace std; + +#include + +#include "tkbltParse.h" + +using namespace Blt; + +/* + * A table used to classify input characters to assist in parsing + * TCL commands. The table should be indexed with a signed character + * using the CHAR_TYPE macro. The character may have a negative + * value. The CHAR_TYPE macro takes a pointer to a signed character + * and a pointer to the last character in the source string. If the + * src pointer is pointing at the terminating null of the string, + * CHAR_TYPE returns TCL_COMMAND_END. + */ + +#define STATIC_STRING_SPACE 150 +#define TCL_NORMAL 0x01 +#define TCL_SPACE 0x02 +#define TCL_COMMAND_END 0x04 +#define TCL_QUOTE 0x08 +#define TCL_OPEN_BRACKET 0x10 +#define TCL_OPEN_BRACE 0x20 +#define TCL_CLOSE_BRACE 0x40 +#define TCL_BACKSLASH 0x80 +#define TCL_DOLLAR 0x00 + +/* + * The following table assigns a type to each character. Only types + * meaningful to TCL parsing are represented here. The table is + * designed to be referenced with either signed or unsigned characters, + * so it has 384 entries. The first 128 entries correspond to negative + * character values, the next 256 correspond to positive character + * values. The last 128 entries are identical to the first 128. The + * table is always indexed with a 128-byte offset (the 128th entry + * corresponds to a 0 character value). + */ + +static unsigned char tclTypeTable[] = +{ + /* + * Negative character values, from -128 to -1: + */ + + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + + /* + * Positive character values, from 0-127: + */ + + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_SPACE, TCL_COMMAND_END, TCL_SPACE, + TCL_SPACE, TCL_SPACE, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_SPACE, TCL_NORMAL, TCL_QUOTE, TCL_NORMAL, + TCL_DOLLAR, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_COMMAND_END, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_OPEN_BRACKET, + TCL_BACKSLASH, TCL_COMMAND_END, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_OPEN_BRACE, + TCL_NORMAL, TCL_CLOSE_BRACE, TCL_NORMAL, TCL_NORMAL, + + /* + * Large unsigned character values, from 128-255: + */ + + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, + TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, +}; + +#define CHAR_TYPE(src,last) \ + (((src)==(last))?TCL_COMMAND_END:(tclTypeTable+128)[(int)*(src)]) + +int Blt::ParseNestedCmd(Tcl_Interp* interp, const char *string, + int flags, const char **termPtr, ParseValue *parsePtr) + +{ + return TCL_ERROR; +} + +int Blt::ParseBraces(Tcl_Interp* interp, const char *string, + const char **termPtr, ParseValue *parsePtr) +{ + int level; + const char *src; + char *dest, *end; + char c; + const char *lastChar = string + strlen(string); + + src = string; + dest = parsePtr->next; + end = parsePtr->end; + level = 1; + + /* + * Copy the characters one at a time to the result area, stopping + * when the matching close-brace is found. + */ + + for (;;) { + c = *src; + src++; + + if (dest == end) { + parsePtr->next = dest; + (*parsePtr->expandProc) (parsePtr, 20); + dest = parsePtr->next; + end = parsePtr->end; + } + *dest = c; + dest++; + + if (CHAR_TYPE(src - 1, lastChar) == TCL_NORMAL) { + continue; + } else if (c == '{') { + level++; + } else if (c == '}') { + level--; + if (level == 0) { + dest--; /* Don't copy the last close brace. */ + break; + } + } else if (c == '\\') { + int count; + + /* + * Must always squish out backslash-newlines, even when in + * braces. This is needed so that this sequence can appear + * anywhere in a command, such as the middle of an expression. + */ + + if (*src == '\n') { + dest[-1] = Tcl_Backslash(src - 1, &count); + src += count - 1; + } else { + Tcl_Backslash(src - 1, &count); + while (count > 1) { + if (dest == end) { + parsePtr->next = dest; + (*parsePtr->expandProc) (parsePtr, 20); + dest = parsePtr->next; + end = parsePtr->end; + } + *dest = *src; + dest++; + src++; + count--; + } + } + } else if (c == '\0') { + Tcl_AppendResult(interp, "missing close-brace", (char *)NULL); + *termPtr = string - 1; + return TCL_ERROR; + } + } + + *dest = '\0'; + parsePtr->next = dest; + *termPtr = src; + return TCL_OK; +} + +void Blt::ExpandParseValue(ParseValue *parsePtr, int needed) + +{ + /* + * Either double the size of the buffer or add enough new space + * to meet the demand, whichever produces a larger new buffer. + */ + int size = (parsePtr->end - parsePtr->buffer) + 1; + if (size < needed) + size += needed; + else + size += size; + + char* buffer = (char*)malloc((unsigned int)size); + + /* + * Copy from old buffer to new, free old buffer if needed, and + * mark new buffer as malloc-ed. + */ + memcpy((VOID *) buffer, (VOID *) parsePtr->buffer, + (size_t) (parsePtr->next - parsePtr->buffer)); + parsePtr->next = buffer + (parsePtr->next - parsePtr->buffer); + if (parsePtr->clientData != 0) { + free(parsePtr->buffer); + } + parsePtr->buffer = buffer; + parsePtr->end = buffer + size - 1; + parsePtr->clientData = (ClientData)1; +} + +int Blt::ParseQuotes(Tcl_Interp* interp, const char *string, int termChar, + int flags, const char **termPtr, ParseValue *parsePtr) +{ + const char *src; + char *dest, c; + const char *lastChar = string + strlen(string); + + src = string; + dest = parsePtr->next; + + for (;;) { + if (dest == parsePtr->end) { + /* + * Target buffer space is about to run out. Make more space. + */ + parsePtr->next = dest; + (*parsePtr->expandProc) (parsePtr, 1); + dest = parsePtr->next; + } + c = *src; + src++; + if (c == termChar) { + *dest = '\0'; + parsePtr->next = dest; + *termPtr = src; + return TCL_OK; + } else if (CHAR_TYPE(src - 1, lastChar) == TCL_NORMAL) { + copy: + *dest = c; + dest++; + continue; + } else if (c == '$') { + int length; + const char *value; + + value = Tcl_ParseVar(interp, src - 1, termPtr); + if (value == NULL) { + return TCL_ERROR; + } + src = *termPtr; + length = strlen(value); + if ((parsePtr->end - dest) <= length) { + parsePtr->next = dest; + (*parsePtr->expandProc) (parsePtr, length); + dest = parsePtr->next; + } + strcpy(dest, value); + dest += length; + continue; + } else if (c == '[') { + int result; + + parsePtr->next = dest; + result = ParseNestedCmd(interp, src, flags, termPtr, parsePtr); + if (result != TCL_OK) { + return result; + } + src = *termPtr; + dest = parsePtr->next; + continue; + } else if (c == '\\') { + int nRead; + + src--; + *dest = Tcl_Backslash(src, &nRead); + dest++; + src += nRead; + continue; + } else if (c == '\0') { + Tcl_ResetResult(interp); + ostringstream str; + str << "missing " << termChar << ends; + Tcl_SetStringObj(Tcl_GetObjResult(interp), str.str().c_str(), 9); + *termPtr = string - 1; + return TCL_ERROR; + } else { + goto copy; + } + } +} + diff --git a/generic/tkbltParse.h b/generic/tkbltParse.h new file mode 100644 index 0000000..ee215a5 --- /dev/null +++ b/generic/tkbltParse.h @@ -0,0 +1,55 @@ +/* + * 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 1993-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. + */ + +#ifndef _BLT_PARSE_H +#define _BLT_PARSE_H + +namespace Blt { + + typedef struct _ParseValue ParseValue; + struct _ParseValue { + char *buffer; + char *next; + char *end; + void (*expandProc)(ParseValue *pvPtr, int needed); + ClientData clientData; + }; + + extern int ParseBraces(Tcl_Interp* interp, const char *string, + const char **termPtr, ParseValue *pvPtr); + extern int ParseNestedCmd(Tcl_Interp* interp, const char *string, + int flags, const char **termPtr, + ParseValue *pvPtr); + extern int ParseQuotes(Tcl_Interp* interp, const char *string, + int termChar, int flags, const char **termPtr, + ParseValue * pvPtr); + extern void ExpandParseValue(ParseValue *pvPtr, int needed); +} + +#endif diff --git a/generic/tkbltStubInit.c b/generic/tkbltStubInit.c new file mode 100644 index 0000000..354b7f1 --- /dev/null +++ b/generic/tkbltStubInit.c @@ -0,0 +1,30 @@ +#include "tkbltVector.h" + +/* !BEGIN!: Do not edit below this line. */ + +const TkbltStubs tkbltStubs = { + TCL_STUB_MAGIC, + 0, + Blt_CreateVector, /* 0 */ + Blt_CreateVector2, /* 1 */ + Blt_DeleteVectorByName, /* 2 */ + Blt_DeleteVector, /* 3 */ + Blt_GetVector, /* 4 */ + Blt_GetVectorFromObj, /* 5 */ + Blt_ResetVector, /* 6 */ + Blt_ResizeVector, /* 7 */ + Blt_VectorExists, /* 8 */ + Blt_VectorExists2, /* 9 */ + Blt_AllocVectorId, /* 10 */ + Blt_GetVectorById, /* 11 */ + Blt_SetVectorChangedProc, /* 12 */ + Blt_FreeVectorId, /* 13 */ + Blt_NameOfVectorId, /* 14 */ + Blt_NameOfVector, /* 15 */ + Blt_ExprVector, /* 16 */ + Blt_InstallIndexProc, /* 17 */ + Blt_VecMin, /* 18 */ + Blt_VecMax, /* 19 */ +}; + +/* !END!: Do not edit above this line. */ diff --git a/generic/tkbltStubLib.C b/generic/tkbltStubLib.C new file mode 100644 index 0000000..e973063 --- /dev/null +++ b/generic/tkbltStubLib.C @@ -0,0 +1,15 @@ +#ifndef USE_TCL_STUBS +#define USE_TCL_STUBS +#endif + +#include + +ClientData tkbltStubsPtr =NULL; + +const char* Tkblt_InitStubs(Tcl_Interp* interp, const char* version, int exact) +{ + const char* actualVersion = + Tcl_PkgRequireEx(interp, "tkblt", version, exact, &tkbltStubsPtr); + + return (actualVersion && tkbltStubsPtr) ? actualVersion : NULL; +} diff --git a/generic/tkbltSwitch.C b/generic/tkbltSwitch.C new file mode 100644 index 0000000..bb80663 --- /dev/null +++ b/generic/tkbltSwitch.C @@ -0,0 +1,407 @@ +/* + * 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 +#include + +#include +#include +#include +using namespace std; + +#include + +#include "tkbltSwitch.h" + +using namespace Blt; + +#define COUNT_NNEG 0 +#define COUNT_POS 1 +#define COUNT_ANY 2 + +static char* Blt_Strdup(const char *string) +{ + size_t size = strlen(string) + 1; + char* ptr = (char*)malloc(size * sizeof(char)); + if (ptr != NULL) { + strcpy(ptr, string); + } + return ptr; +} + +static int Blt_GetCountFromObj(Tcl_Interp* interp, Tcl_Obj *objPtr, int check, + long *valuePtr) +{ + long count; + if (Tcl_GetLongFromObj(interp, objPtr, &count) != TCL_OK) + return TCL_ERROR; + + switch (check) { + case COUNT_NNEG: + if (count < 0) { + Tcl_AppendResult(interp, "bad value \"", Tcl_GetString(objPtr), + "\": can't be negative", (char *)NULL); + return TCL_ERROR; + } + break; + case COUNT_POS: + if (count <= 0) { + Tcl_AppendResult(interp, "bad value \"", Tcl_GetString(objPtr), + "\": must be positive", (char *)NULL); + return TCL_ERROR; + } + break; + case COUNT_ANY: + break; + } + *valuePtr = count; + return TCL_OK; +} + +static void DoHelp(Tcl_Interp* interp, Blt_SwitchSpec *specs) +{ + Tcl_DString ds; + Tcl_DStringInit(&ds); + Tcl_DStringAppend(&ds, "following switches are available:", -1); + for (Blt_SwitchSpec *sp = specs; sp->type != BLT_SWITCH_END; sp++) { + Tcl_DStringAppend(&ds, "\n ", 4); + Tcl_DStringAppend(&ds, sp->switchName, -1); + Tcl_DStringAppend(&ds, " ", 1); + Tcl_DStringAppend(&ds, sp->help, -1); + } + Tcl_AppendResult(interp, Tcl_DStringValue(&ds), (char *)NULL); + Tcl_DStringFree(&ds); +} + +static Blt_SwitchSpec *FindSwitchSpec(Tcl_Interp* interp, Blt_SwitchSpec *specs, + const char *name, int length, + int needFlags, int hateFlags) +{ + char c = name[1]; + Blt_SwitchSpec *matchPtr = NULL; + for (Blt_SwitchSpec *sp = specs; sp->type != BLT_SWITCH_END; sp++) { + if (sp->switchName == NULL) + continue; + + if (((sp->flags & needFlags) != needFlags) || (sp->flags & hateFlags)) + continue; + + if ((sp->switchName[1] != c) || (strncmp(sp->switchName,name,length)!=0)) + continue; + + if (sp->switchName[length] == '\0') + return sp; /* Stop on a perfect match. */ + + if (matchPtr != NULL) { + Tcl_AppendResult(interp, "ambiguous switch \"", name, "\"\n", + (char *) NULL); + DoHelp(interp, specs); + return NULL; + } + matchPtr = sp; + } + + if (strcmp(name, "-help") == 0) { + DoHelp(interp, specs); + return NULL; + } + + if (matchPtr == NULL) { + Tcl_AppendResult(interp, "unknown switch \"", name, "\"\n", + (char *)NULL); + DoHelp(interp, specs); + return NULL; + } + + return matchPtr; +} + +static int DoSwitch(Tcl_Interp* interp, Blt_SwitchSpec *sp, + Tcl_Obj *objPtr, void *record) +{ + do { + char *ptr = (char *)record + sp->offset; + switch (sp->type) { + case BLT_SWITCH_BOOLEAN: + { + int boo; + + if (Tcl_GetBooleanFromObj(interp, objPtr, &boo) != TCL_OK) { + return TCL_ERROR; + } + if (sp->mask > 0) { + if (boo) { + *((int *)ptr) |= sp->mask; + } else { + *((int *)ptr) &= ~sp->mask; + } + } else { + *((int *)ptr) = boo; + } + } + break; + + case BLT_SWITCH_DOUBLE: + if (Tcl_GetDoubleFromObj(interp, objPtr, (double *)ptr) != TCL_OK) { + return TCL_ERROR; + } + break; + + case BLT_SWITCH_OBJ: + Tcl_IncrRefCount(objPtr); + *(Tcl_Obj **)ptr = objPtr; + break; + + case BLT_SWITCH_FLOAT: + { + double value; + + if (Tcl_GetDoubleFromObj(interp, objPtr, &value) != TCL_OK) { + return TCL_ERROR; + } + *(float *)ptr = (float)value; + } + break; + + case BLT_SWITCH_INT: + if (Tcl_GetIntFromObj(interp, objPtr, (int *)ptr) != TCL_OK) { + return TCL_ERROR; + } + break; + + case BLT_SWITCH_INT_NNEG: + { + long value; + + if (Blt_GetCountFromObj(interp, objPtr, COUNT_NNEG, + &value) != TCL_OK) { + return TCL_ERROR; + } + *(int *)ptr = (int)value; + } + break; + + case BLT_SWITCH_INT_POS: + { + long value; + + if (Blt_GetCountFromObj(interp, objPtr, COUNT_POS, + &value) != TCL_OK) { + return TCL_ERROR; + } + *(int *)ptr = (int)value; + } + break; + + case BLT_SWITCH_LIST: + { + int argc; + + if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &argc, + (const char ***)ptr) != TCL_OK) { + return TCL_ERROR; + } + } + break; + + case BLT_SWITCH_LONG: + if (Tcl_GetLongFromObj(interp, objPtr, (long *)ptr) != TCL_OK) { + return TCL_ERROR; + } + break; + + case BLT_SWITCH_LONG_NNEG: + { + long value; + + if (Blt_GetCountFromObj(interp, objPtr, COUNT_NNEG, + &value) != TCL_OK) { + return TCL_ERROR; + } + *(long *)ptr = value; + } + break; + + case BLT_SWITCH_LONG_POS: + { + long value; + + if (Blt_GetCountFromObj(interp, objPtr, COUNT_POS, &value) + != TCL_OK) { + return TCL_ERROR; + } + *(long *)ptr = value; + } + break; + + case BLT_SWITCH_STRING: + { + char *value; + + value = Tcl_GetString(objPtr); + value = (*value == '\0') ? NULL : Blt_Strdup(value); + if (*(char **)ptr != NULL) { + free(*(char **)ptr); + } + *(char **)ptr = value; + } + break; + + case BLT_SWITCH_CUSTOM: + if ((*sp->customPtr->parseProc)(sp->customPtr->clientData, interp, + sp->switchName, objPtr, (char *)record, sp->offset, sp->flags) + != TCL_OK) { + return TCL_ERROR; + } + break; + + default: + ostringstream str; + str << sp->type << ends; + Tcl_AppendResult(interp, "bad switch table: unknown type \"", + str.str().c_str(), "\"", NULL); + return TCL_ERROR; + } + sp++; + } while ((sp->switchName == NULL) && (sp->type != BLT_SWITCH_END)); + return TCL_OK; +} + +int Blt::ParseSwitches(Tcl_Interp* interp, Blt_SwitchSpec *specs, + int objc, Tcl_Obj* const objv[], void *record, + int flags) +{ + Blt_SwitchSpec *sp; + int needFlags = flags & ~(BLT_SWITCH_USER_BIT - 1); + int hateFlags = 0; + + /* + * Pass 1: Clear the change flags on all the specs so that we + * can check it later. + */ + for (sp = specs; sp->type != BLT_SWITCH_END; sp++) + sp->flags &= ~BLT_SWITCH_SPECIFIED; + + /* + * Pass 2: Process the arguments that match entries in the specs. + * It's an error if the argument doesn't match anything. + */ + int count; + for (count = 0; count < objc; count++) { + char *arg; + int length; + + arg = Tcl_GetStringFromObj(objv[count], &length); + if (flags & BLT_SWITCH_OBJV_PARTIAL) { + /* + * If the argument doesn't start with a '-' (not a switch) or is + * '--', stop processing and return the number of arguments + * comsumed. + */ + if (arg[0] != '-') { + return count; + } + if ((arg[1] == '-') && (arg[2] == '\0')) { + return count + 1; /* include the "--" in the count. */ + } + } + sp = FindSwitchSpec(interp, specs, arg, length, needFlags, hateFlags); + if (sp == NULL) { + return -1; + } + if (sp->type == BLT_SWITCH_BITMASK) { + char *ptr; + + ptr = (char *)record + sp->offset; + *((int *)ptr) |= sp->mask; + } else if (sp->type == BLT_SWITCH_BITMASK_INVERT) { + char *ptr; + + ptr = (char *)record + sp->offset; + *((int *)ptr) &= ~sp->mask; + } else if (sp->type == BLT_SWITCH_VALUE) { + char *ptr; + + ptr = (char *)record + sp->offset; + *((int *)ptr) = sp->mask; + } else { + count++; + if (count == objc) { + Tcl_AppendResult(interp, "value for \"", arg, "\" missing", + (char *) NULL); + return -1; + } + if (DoSwitch(interp, sp, objv[count], record) != TCL_OK) { + ostringstream str; + str << "\n (processing \"" << sp->switchName << "\" switch)" << ends; + Tcl_AddErrorInfo(interp, str.str().c_str()); + return -1; + } + } + sp->flags |= BLT_SWITCH_SPECIFIED; + } + + return count; +} + +void Blt::FreeSwitches(Blt_SwitchSpec *specs, void *record, int needFlags) +{ + for (Blt_SwitchSpec *sp = specs; sp->type != BLT_SWITCH_END; sp++) { + if ((sp->flags & needFlags) == needFlags) { + char *ptr = (char *)record + sp->offset; + switch (sp->type) { + case BLT_SWITCH_STRING: + case BLT_SWITCH_LIST: + if (*((char **) ptr) != NULL) { + free(*((char **) ptr)); + *((char **) ptr) = NULL; + } + break; + + case BLT_SWITCH_OBJ: + if (*((Tcl_Obj **) ptr) != NULL) { + Tcl_DecrRefCount(*((Tcl_Obj **)ptr)); + *((Tcl_Obj **) ptr) = NULL; + } + break; + + case BLT_SWITCH_CUSTOM: + if ((*(char **)ptr != NULL) && + (sp->customPtr->freeProc != NULL)) { + (*sp->customPtr->freeProc)((char *)record, sp->offset, + sp->flags); + } + break; + + default: + break; + } + } + } +} diff --git a/generic/tkbltSwitch.h b/generic/tkbltSwitch.h new file mode 100644 index 0000000..eed7b31 --- /dev/null +++ b/generic/tkbltSwitch.h @@ -0,0 +1,129 @@ +/* + * 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 1993-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. + */ + +#ifndef BLT_SWITCH_H +#define BLT_SWITCH_H + +#include + +#define BLT_SWITCH_DEFAULTS (0) +#define BLT_SWITCH_ARGV_PARTIAL (1<<1) +#define BLT_SWITCH_OBJV_PARTIAL (1<<1) + + /* + * Possible flag values for Blt_SwitchSpec structures. Any bits at or + * above BLT_SWITCH_USER_BIT may be used by clients for selecting + * certain entries. + */ +#define BLT_SWITCH_NULL_OK (1<<0) +#define BLT_SWITCH_DONT_SET_DEFAULT (1<<3) +#define BLT_SWITCH_SPECIFIED (1<<4) +#define BLT_SWITCH_USER_BIT (1<<8) + +namespace Blt { + + typedef int (Blt_SwitchParseProc)(ClientData clientData, Tcl_Interp* interp, + const char *switchName, + Tcl_Obj *valueObjPtr, char *record, + int offset, int flags); + typedef void (Blt_SwitchFreeProc)(char *record, int offset, int flags); + + typedef struct { + Blt_SwitchParseProc *parseProc; /* Procedure to parse a switch + * value and store it in its * + * converted form in the data * + * record. */ + + Blt_SwitchFreeProc *freeProc; /* Procedure to free a switch. */ + + ClientData clientData; /* Arbitrary one-word value used by + * switch parser, passed to + * parseProc. */ + } Blt_SwitchCustom; + + /* + * Type values for Blt_SwitchSpec structures. See the user + * documentation for details. + */ + typedef enum { + BLT_SWITCH_BOOLEAN, + BLT_SWITCH_DOUBLE, + BLT_SWITCH_BITMASK, + BLT_SWITCH_BITMASK_INVERT, + BLT_SWITCH_FLOAT, + BLT_SWITCH_INT, + BLT_SWITCH_INT_NNEG, + BLT_SWITCH_INT_POS, + BLT_SWITCH_LIST, + BLT_SWITCH_LONG, + BLT_SWITCH_LONG_NNEG, + BLT_SWITCH_LONG_POS, + BLT_SWITCH_OBJ, + BLT_SWITCH_STRING, + BLT_SWITCH_VALUE, + BLT_SWITCH_CUSTOM, + BLT_SWITCH_END + } Blt_SwitchTypes; + + typedef struct { + Blt_SwitchTypes type; /* Type of option, such as + * BLT_SWITCH_COLOR; see definitions + * below. Last option in table must + * have type BLT_SWITCH_END. */ + + const char *switchName; /* Switch used to specify option in + * argv. NULL means this spec is part + * of a group. */ + + const char *help; /* Help string. */ + int offset; /* Where in widget record to store + * value; use Blt_Offset macro to + * generate values for this. */ + + int flags; /* Any combination of the values + * defined below. */ + + unsigned int mask; + + Blt_SwitchCustom *customPtr; /* If type is BLT_SWITCH_CUSTOM then + * this is a pointer to info about how + * to parse and print the option. + * Otherwise it is irrelevant. */ + } Blt_SwitchSpec; + + extern int ParseSwitches(Tcl_Interp* interp, Blt_SwitchSpec *specPtr, + int objc, Tcl_Obj *const *objv, void *rec, + int flags); + extern void FreeSwitches(Blt_SwitchSpec *specs, void *rec, int flags); +}; + +#endif /* BLT_SWITCH_H */ diff --git a/generic/tkbltVecCmd.C b/generic/tkbltVecCmd.C new file mode 100644 index 0000000..feaddab --- /dev/null +++ b/generic/tkbltVecCmd.C @@ -0,0 +1,1820 @@ +/* + * 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 1995-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. + * + * Code for binary data read operation was donated by Harold Kirsch. + * + */ + +/* + * TODO: + * o Add H. Kirsch's vector binary read operation + * x binread file0 + * x binread -file file0 + * + * o Add ASCII/binary file reader + * x read fileName + * + * o Allow Tcl-based client notifications. + * vector x + * x notify call Display + * x notify delete Display + * x notify reorder #1 #2 + */ + +#include +#include +#include +#include + +#include + +#include "tkbltVecInt.h" +#include "tkbltOp.h" +#include "tkbltNsUtil.h" +#include "tkbltSwitch.h" + +using namespace Blt; + +extern int Blt_SimplifyLine (Point2d *origPts, int low, int high, + double tolerance, int *indices); + +typedef int (VectorCmdProc)(Vector *vPtr, Tcl_Interp* interp, int objc, + Tcl_Obj* const objv[]); +typedef int (QSortCompareProc) (const void *, const void *); + +static Blt_SwitchParseProc ObjToFFTVector; +static Blt_SwitchCustom fftVectorSwitch = { + ObjToFFTVector, NULL, (ClientData)0, +}; + +static Blt_SwitchParseProc ObjToIndex; +static Blt_SwitchCustom indexSwitch = { + ObjToIndex, NULL, (ClientData)0, +}; + +typedef struct { + Tcl_Obj *formatObjPtr; + int from, to; +} PrintSwitches; + +static Blt_SwitchSpec printSwitches[] = + { + {BLT_SWITCH_OBJ, "-format", "string", + Tk_Offset(PrintSwitches, formatObjPtr), 0}, + {BLT_SWITCH_CUSTOM, "-from", "index", + Tk_Offset(PrintSwitches, from), 0, 0, &indexSwitch}, + {BLT_SWITCH_CUSTOM, "-to", "index", + Tk_Offset(PrintSwitches, to), 0, 0, &indexSwitch}, + {BLT_SWITCH_END} + }; + + +typedef struct { + int flags; +} SortSwitches; + +#define SORT_DECREASING (1<<0) +#define SORT_UNIQUE (1<<1) + +static Blt_SwitchSpec sortSwitches[] = + { + {BLT_SWITCH_BITMASK, "-decreasing", "", + Tk_Offset(SortSwitches, flags), 0, SORT_DECREASING}, + {BLT_SWITCH_BITMASK, "-reverse", "", + Tk_Offset(SortSwitches, flags), 0, SORT_DECREASING}, + {BLT_SWITCH_BITMASK, "-uniq", "", + Tk_Offset(SortSwitches, flags), 0, SORT_UNIQUE}, + {BLT_SWITCH_END} + }; + +typedef struct { + double delta; + Vector *imagPtr; /* Vector containing imaginary part. */ + Vector *freqPtr; /* Vector containing frequencies. */ + VectorInterpData *dataPtr; + int mask; /* Flags controlling FFT. */ +} FFTData; + + +static Blt_SwitchSpec fftSwitches[] = { + {BLT_SWITCH_CUSTOM, "-imagpart", "vector", + Tk_Offset(FFTData, imagPtr), 0, 0, &fftVectorSwitch}, + {BLT_SWITCH_BITMASK, "-noconstant", "", + Tk_Offset(FFTData, mask), 0, FFT_NO_CONSTANT}, + {BLT_SWITCH_BITMASK, "-spectrum", "", + Tk_Offset(FFTData, mask), 0, FFT_SPECTRUM}, + {BLT_SWITCH_BITMASK, "-bartlett", "", + Tk_Offset(FFTData, mask), 0, FFT_BARTLETT}, + {BLT_SWITCH_DOUBLE, "-delta", "float", + Tk_Offset(FFTData, mask), 0, 0, }, + {BLT_SWITCH_CUSTOM, "-frequencies", "vector", + Tk_Offset(FFTData, freqPtr), 0, 0, &fftVectorSwitch}, + {BLT_SWITCH_END} +}; + +static int Blt_ExprIntFromObj(Tcl_Interp* interp, Tcl_Obj *objPtr, + int *valuePtr) +{ + // First try to extract the value as a simple integer. + if (Tcl_GetIntFromObj((Tcl_Interp *)NULL, objPtr, valuePtr) == TCL_OK) + return TCL_OK; + + // Otherwise try to parse it as an expression. + long lvalue; + if (Tcl_ExprLong(interp, Tcl_GetString(objPtr), &lvalue) == TCL_OK) { + *valuePtr = lvalue; + return TCL_OK; + } + + return TCL_ERROR; +} + +static int Blt_ExprDoubleFromObj(Tcl_Interp* interp, Tcl_Obj *objPtr, + double *valuePtr) +{ + // First try to extract the value as a double precision number. + if (Tcl_GetDoubleFromObj((Tcl_Interp *)NULL, objPtr, valuePtr) == TCL_OK) + return TCL_OK; + + // Interpret the empty string as NaN + int length; + char *string; + string = Tcl_GetStringFromObj(objPtr, &length); + if (length == 0) { + *valuePtr = NAN; + return TCL_OK; + } + + // Then try to parse it as an expression. + if (Tcl_ExprDouble(interp, string, valuePtr) == TCL_OK) + return TCL_OK; + + return TCL_ERROR; +} + +static int ObjToFFTVector(ClientData clientData, Tcl_Interp* interp, + const char *switchName, Tcl_Obj *objPtr, + char *record, int offset, int flags) +{ + FFTData *dataPtr = (FFTData *)record; + Vector *vPtr; + Vector **vPtrPtr = (Vector **)(record + offset); + int isNew; /* Not used. */ + char *string; + + string = Tcl_GetString(objPtr); + vPtr = Vec_Create(dataPtr->dataPtr, string, string, string, &isNew); + if (vPtr == NULL) { + return TCL_ERROR; + } + *vPtrPtr = vPtr; + + return TCL_OK; +} + +static int ObjToIndex(ClientData clientData, Tcl_Interp* interp, + const char *switchName, Tcl_Obj *objPtr, char *record, + int offset, int flags) +{ + Vector *vPtr = (Vector*)clientData; + int *indexPtr = (int *)(record + offset); + int index; + + if (Vec_GetIndex(interp, vPtr, Tcl_GetString(objPtr), &index, + INDEX_CHECK, (Blt_VectorIndexProc **)NULL) != TCL_OK) { + return TCL_ERROR; + } + *indexPtr = index; + + return TCL_OK; +} + +static Tcl_Obj* GetValues(Vector *vPtr, int first, int last) +{ + Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + for (double *vp=vPtr->valueArr+first, *vend=vPtr->valueArr+last; + vp <= vend; vp++) + Tcl_ListObjAppendElement(vPtr->interp, listObjPtr, Tcl_NewDoubleObj(*vp)); + + return listObjPtr; +} + +static void ReplicateValue(Vector *vPtr, int first, int last, double value) +{ + for (double *vp=vPtr->valueArr+first, *vend=vPtr->valueArr+last; + vp <= vend; vp++) + *vp = value; + + vPtr->notifyFlags |= UPDATE_RANGE; +} + +static int CopyList(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + if (Vec_SetLength(interp, vPtr, objc) != TCL_OK) + return TCL_ERROR; + + for (int ii = 0; ii < objc; ii++) { + double value; + if (Blt_ExprDoubleFromObj(interp, objv[ii], &value) != TCL_OK) { + Vec_SetLength(interp, vPtr, ii); + return TCL_ERROR; + } + vPtr->valueArr[ii] = value; + } + + return TCL_OK; +} + +static int AppendVector(Vector *destPtr, Vector *srcPtr) +{ + size_t oldSize = destPtr->length; + size_t newSize = oldSize + srcPtr->last - srcPtr->first + 1; + if (Vec_ChangeLength(destPtr->interp, destPtr, newSize) != TCL_OK) { + return TCL_ERROR; + } + size_t nBytes = (newSize - oldSize) * sizeof(double); + memcpy((char *)(destPtr->valueArr + oldSize), + (srcPtr->valueArr + srcPtr->first), nBytes); + destPtr->notifyFlags |= UPDATE_RANGE; + return TCL_OK; +} + +static int AppendList(Vector *vPtr, int objc, Tcl_Obj* const objv[]) +{ + Tcl_Interp* interp = vPtr->interp; + + int oldSize = vPtr->length; + if (Vec_ChangeLength(interp, vPtr, vPtr->length + objc) != TCL_OK) + return TCL_ERROR; + + int count = oldSize; + for (int i = 0; i < objc; i++) { + double value; + if (Blt_ExprDoubleFromObj(interp, objv[i], &value) != TCL_OK) { + Vec_ChangeLength(interp, vPtr, count); + return TCL_ERROR; + } + vPtr->valueArr[count++] = value; + } + vPtr->notifyFlags |= UPDATE_RANGE; + + return TCL_OK; +} + +// Vector instance option commands + +static int AppendOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + for (int i = 2; i < objc; i++) { + Vector* v2Ptr = Vec_ParseElement((Tcl_Interp *)NULL, vPtr->dataPtr, + Tcl_GetString(objv[i]), + (const char **)NULL, NS_SEARCH_BOTH); + int result; + if (v2Ptr != NULL) + result = AppendVector(vPtr, v2Ptr); + else { + int nElem; + Tcl_Obj **elemObjArr; + + if (Tcl_ListObjGetElements(interp, objv[i], &nElem, &elemObjArr) + != TCL_OK) { + return TCL_ERROR; + } + result = AppendList(vPtr, nElem, elemObjArr); + } + + if (result != TCL_OK) + return TCL_ERROR; + } + + if (objc > 2) { + if (vPtr->flush) + Vec_FlushCache(vPtr); + Vec_UpdateClients(vPtr); + } + + return TCL_OK; +} + +static int ClearOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Vec_FlushCache(vPtr); + return TCL_OK; +} + +static int DeleteOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + // FIXME: Don't delete vector with no indices + if (objc == 2) { + Vec_Free(vPtr); + return TCL_OK; + } + + // Allocate an "unset" bitmap the size of the vector + unsigned char* unsetArr = + (unsigned char*)calloc(sizeof(unsigned char), (vPtr->length + 7) / 8); +#define SetBit(i) (unsetArr[(i) >> 3] |= (1 << ((i) & 0x07))) +#define GetBit(i) (unsetArr[(i) >> 3] & (1 << ((i) & 0x07))) + + for (int i = 2; i < objc; i++) { + char* string = Tcl_GetString(objv[i]); + if (Vec_GetIndexRange(interp, vPtr, string, (INDEX_COLON | INDEX_CHECK), + (Blt_VectorIndexProc **) NULL) != TCL_OK) { + free(unsetArr); + return TCL_ERROR; + } + + // Mark the range of elements for deletion + for (int j = vPtr->first; j <= vPtr->last; j++) + SetBit(j); + } + + int count = 0; + for (int i = 0; i < vPtr->length; i++) { + // Skip elements marked for deletion + if (GetBit(i)) + continue; + + if (count < i) { + vPtr->valueArr[count] = vPtr->valueArr[i]; + } + count++; + } + free(unsetArr); + vPtr->length = count; + + if (vPtr->flush) + Vec_FlushCache(vPtr); + Vec_UpdateClients(vPtr); + + return TCL_OK; +} + +static int DupOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + for (int i = 2; i < objc; i++) { + char* name = Tcl_GetString(objv[i]); + int isNew; + Vector* v2Ptr = Vec_Create(vPtr->dataPtr, name, name, name, &isNew); + if (v2Ptr == NULL) + return TCL_ERROR; + + if (v2Ptr == vPtr) + continue; + + if (Vec_Duplicate(v2Ptr, vPtr) != TCL_OK) + return TCL_ERROR; + + if (!isNew) { + if (v2Ptr->flush) + Vec_FlushCache(v2Ptr); + Vec_UpdateClients(v2Ptr); + } + } + + return TCL_OK; +} + +static int FFTOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + FFTData data; + memset(&data, 0, sizeof(data)); + data.delta = 1.0; + + char* realVecName = Tcl_GetString(objv[2]); + int isNew; + Vector* v2Ptr = Vec_Create(vPtr->dataPtr, realVecName, realVecName, + realVecName, &isNew); + if (v2Ptr == NULL) + return TCL_ERROR; + + if (v2Ptr == vPtr) { + Tcl_AppendResult(interp, "real vector \"", realVecName, "\"", + " can't be the same as the source", (char *)NULL); + return TCL_ERROR; + } + + if (ParseSwitches(interp, fftSwitches, objc - 3, objv + 3, &data, + BLT_SWITCH_DEFAULTS) < 0) + return TCL_ERROR; + + if (Vec_FFT(interp, v2Ptr, data.imagPtr, data.freqPtr, data.delta, + data.mask, vPtr) != TCL_OK) + return TCL_ERROR; + + // Update bookkeeping + if (!isNew) { + if (v2Ptr->flush) + Vec_FlushCache(v2Ptr); + Vec_UpdateClients(v2Ptr); + } + + if (data.imagPtr != NULL) { + if (data.imagPtr->flush) + Vec_FlushCache(data.imagPtr); + Vec_UpdateClients(data.imagPtr); + } + + if (data.freqPtr != NULL) { + if (data.freqPtr->flush) + Vec_FlushCache(data.freqPtr); + Vec_UpdateClients(data.freqPtr); + } + + return TCL_OK; +} + +static int InverseFFTOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + char* name = Tcl_GetString(objv[2]); + Vector *srcImagPtr; + if (Vec_LookupName(vPtr->dataPtr, name, &srcImagPtr) != TCL_OK ) + return TCL_ERROR; + + name = Tcl_GetString(objv[3]); + int isNew; + Vector* destRealPtr = Vec_Create(vPtr->dataPtr, name, name, name, &isNew); + name = Tcl_GetString(objv[4]); + Vector* destImagPtr = Vec_Create(vPtr->dataPtr, name, name, name, &isNew); + + if (Vec_InverseFFT(interp, srcImagPtr, destRealPtr, destImagPtr, vPtr) + != TCL_OK ) + return TCL_ERROR; + + if (destRealPtr->flush) + Vec_FlushCache(destRealPtr); + Vec_UpdateClients(destRealPtr); + + if (destImagPtr->flush) + Vec_FlushCache(destImagPtr); + Vec_UpdateClients(destImagPtr); + + return TCL_OK; +} + +static int IndexOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + char* string = Tcl_GetString(objv[2]); + if (Vec_GetIndexRange(interp, vPtr, string, INDEX_ALL_FLAGS, + (Blt_VectorIndexProc **) NULL) != TCL_OK) + return TCL_ERROR; + + int first = vPtr->first; + int last = vPtr->last; + if (objc == 3) { + Tcl_Obj *listObjPtr; + + if (first == vPtr->length) { + Tcl_AppendResult(interp, "can't get index \"", string, "\"", + (char *)NULL); + return TCL_ERROR; /* Can't read from index "++end" */ + } + listObjPtr = GetValues(vPtr, first, last); + Tcl_SetObjResult(interp, listObjPtr); + } + else { + // FIXME: huh? Why set values here? + if (first == SPECIAL_INDEX) { + Tcl_AppendResult(interp, "can't set index \"", string, "\"", + (char *)NULL); + // Tried to set "min" or "max" + return TCL_ERROR; + } + + double value; + if (Blt_ExprDoubleFromObj(interp, objv[3], &value) != TCL_OK) + return TCL_ERROR; + + if (first == vPtr->length) { + if (Vec_ChangeLength(interp, vPtr, vPtr->length + 1) != TCL_OK) + return TCL_ERROR; + } + + ReplicateValue(vPtr, first, last, value); + Tcl_SetObjResult(interp, objv[3]); + if (vPtr->flush) + Vec_FlushCache(vPtr); + Vec_UpdateClients(vPtr); + } + + return TCL_OK; +} + +static int LengthOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + if (objc == 3) { + int nElem; + if (Tcl_GetIntFromObj(interp, objv[2], &nElem) != TCL_OK) + return TCL_ERROR; + + if (nElem < 0) { + Tcl_AppendResult(interp, "bad vector size \"", + Tcl_GetString(objv[2]), "\"", (char *)NULL); + return TCL_ERROR; + } + + if ((Vec_SetSize(interp, vPtr, nElem) != TCL_OK) || + (Vec_SetLength(interp, vPtr, nElem) != TCL_OK)) + return TCL_ERROR; + + if (vPtr->flush) + Vec_FlushCache(vPtr); + Vec_UpdateClients(vPtr); + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), vPtr->length); + + return TCL_OK; +} + +static int MapOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + if (objc > 2) { + if (Vec_MapVariable(interp, vPtr, Tcl_GetString(objv[2])) + != TCL_OK) + return TCL_ERROR; + } + + if (vPtr->arrayName != NULL) + Tcl_SetStringObj(Tcl_GetObjResult(interp), vPtr->arrayName, -1); + + return TCL_OK; +} + +static int MaxOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Tcl_SetDoubleObj(Tcl_GetObjResult(interp), Vec_Max(vPtr)); + return TCL_OK; +} + +static int MergeOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + // Allocate an array of vector pointers of each vector to be + // merged in the current vector. + Vector** vecArr = (Vector**)malloc(sizeof(Vector *) * objc); + Vector** vPtrPtr = vecArr; + + int refSize = -1; + int nElem = 0; + for (int i = 2; i < objc; i++) { + Vector *v2Ptr; + if (Vec_LookupName(vPtr->dataPtr, Tcl_GetString(objv[i]), &v2Ptr) + != TCL_OK) { + free(vecArr); + return TCL_ERROR; + } + + // Check that all the vectors are the same length + int length = v2Ptr->last - v2Ptr->first + 1; + if (refSize < 0) + refSize = length; + else if (length != refSize) { + Tcl_AppendResult(vPtr->interp, "vectors \"", vPtr->name, + "\" and \"", v2Ptr->name, "\" differ in length", + (char *)NULL); + free(vecArr); + return TCL_ERROR; + } + *vPtrPtr++ = v2Ptr; + nElem += refSize; + } + *vPtrPtr = NULL; + + double* valueArr = (double*)malloc(sizeof(double) * nElem); + if (valueArr == NULL) { + Tcl_AppendResult(vPtr->interp, "not enough memory to allocate ", + Itoa(nElem), " vector elements", (char *)NULL); + return TCL_ERROR; + } + + // Merge the values from each of the vectors into the current vector + double* valuePtr = valueArr; + for (int i = 0; i < refSize; i++) { + for (Vector** vpp = vecArr; *vpp != NULL; vpp++) { + *valuePtr++ = (*vpp)->valueArr[i + (*vpp)->first]; + } + } + free(vecArr); + Vec_Reset(vPtr, valueArr, nElem, nElem, TCL_DYNAMIC); + + return TCL_OK; +} + +static int MinOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Tcl_SetDoubleObj(Tcl_GetObjResult(interp), Vec_Min(vPtr)); + return TCL_OK; +} + +static int NormalizeOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Vec_UpdateRange(vPtr); + double range = vPtr->max - vPtr->min; + if (objc > 2) { + char* string = Tcl_GetString(objv[2]); + int isNew; + Vector* v2Ptr = Vec_Create(vPtr->dataPtr, string, string, string, &isNew); + if (v2Ptr == NULL) + return TCL_ERROR; + + if (Vec_SetLength(interp, v2Ptr, vPtr->length) != TCL_OK) + return TCL_ERROR; + + for (int i = 0; i < vPtr->length; i++) + v2Ptr->valueArr[i] = (vPtr->valueArr[i] - vPtr->min) / range; + + Vec_UpdateRange(v2Ptr); + if (!isNew) { + if (v2Ptr->flush) { + Vec_FlushCache(v2Ptr); + } + Vec_UpdateClients(v2Ptr); + } + } + else { + Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + for (int i = 0; i < vPtr->length; i++) { + double norm = (vPtr->valueArr[i] - vPtr->min) / range; + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewDoubleObj(norm)); + } + Tcl_SetObjResult(interp, listObjPtr); + } + + return TCL_OK; +} + +static int NotifyOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + enum optionIndices { + OPTION_ALWAYS, OPTION_NEVER, OPTION_WHENIDLE, + OPTION_NOW, OPTION_CANCEL, OPTION_PENDING + }; + static const char *optionArr[] = { + "always", "never", "whenidle", "now", "cancel", "pending", NULL + }; + + int option; + if (Tcl_GetIndexFromObj(interp, objv[2], optionArr, "qualifier", TCL_EXACT, + &option) != TCL_OK) + return TCL_OK; + + switch (option) { + case OPTION_ALWAYS: + vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK; + vPtr->notifyFlags |= NOTIFY_ALWAYS; + break; + case OPTION_NEVER: + vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK; + vPtr->notifyFlags |= NOTIFY_NEVER; + break; + case OPTION_WHENIDLE: + vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK; + vPtr->notifyFlags |= NOTIFY_WHENIDLE; + break; + case OPTION_NOW: + // FIXME: How does this play when an update is pending? + Blt_Vec_NotifyClients(vPtr); + break; + case OPTION_CANCEL: + if (vPtr->notifyFlags & NOTIFY_PENDING) { + vPtr->notifyFlags &= ~NOTIFY_PENDING; + Tcl_CancelIdleCall(Blt_Vec_NotifyClients, (ClientData)vPtr); + } + break; + case OPTION_PENDING: + int boll = (vPtr->notifyFlags & NOTIFY_PENDING); + Tcl_SetBooleanObj(Tcl_GetObjResult(interp), boll); + break; + } + + return TCL_OK; +} + +static int PopulateOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + char* string = Tcl_GetString(objv[2]); + int isNew; + Vector* v2Ptr = Vec_Create(vPtr->dataPtr, string, string, string, &isNew); + if (v2Ptr == NULL) + return TCL_ERROR; + + // Source vector is empty + if (vPtr->length == 0) + return TCL_OK; + + int density; + if (Tcl_GetIntFromObj(interp, objv[3], &density) != TCL_OK) + return TCL_ERROR; + + if (density < 1) { + Tcl_AppendResult(interp, "bad density \"", Tcl_GetString(objv[3]), + "\"", (char *)NULL); + return TCL_ERROR; + } + int size = (vPtr->length - 1) * (density + 1) + 1; + if (Vec_SetLength(interp, v2Ptr, size) != TCL_OK) + return TCL_ERROR; + + int count = 0; + double* valuePtr = v2Ptr->valueArr; + int i; + for (i = 0; i < (vPtr->length - 1); i++) { + double range = vPtr->valueArr[i + 1] - vPtr->valueArr[i]; + double slice = range / (double)(density + 1); + for (int j = 0; j <= density; j++) { + *valuePtr = vPtr->valueArr[i] + (slice * (double)j); + valuePtr++; + count++; + } + } + count++; + *valuePtr = vPtr->valueArr[i]; + if (!isNew) { + if (v2Ptr->flush) + Vec_FlushCache(v2Ptr); + Vec_UpdateClients(v2Ptr); + } + + return TCL_OK; +} + +static int ValuesOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + PrintSwitches switches; + switches.formatObjPtr = NULL; + switches.from = 0; + switches.to = vPtr->length - 1; + indexSwitch.clientData = vPtr; + if (ParseSwitches(interp, printSwitches, objc - 2, objv + 2, &switches, + BLT_SWITCH_DEFAULTS) < 0) + return TCL_ERROR; + + if (switches.from > switches.to) { + // swap positions + int tmp = switches.to; + switches.to = switches.from; + switches.from = tmp; + } + + if (switches.formatObjPtr == NULL) { + Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + for (int i = switches.from; i <= switches.to; i++) + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewDoubleObj(vPtr->valueArr[i])); + + Tcl_SetObjResult(interp, listObjPtr); + } + else { + Tcl_DString ds; + Tcl_DStringInit(&ds); + const char* fmt = Tcl_GetString(switches.formatObjPtr); + for (int i = switches.from; i <= switches.to; i++) { + char buffer[200]; + sprintf(buffer, fmt, vPtr->valueArr[i]); + Tcl_DStringAppend(&ds, buffer, -1); + } + Tcl_DStringResult(interp, &ds); + Tcl_DStringFree(&ds); + } + + return TCL_OK; +} + +static int RangeOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + int first; + int last; + + if (objc == 2) { + first = 0; + last = vPtr->length - 1; + } + else if (objc == 4) { + if ((Vec_GetIndex(interp, vPtr, Tcl_GetString(objv[2]), &first, + INDEX_CHECK, (Blt_VectorIndexProc **) NULL) != TCL_OK) || + (Vec_GetIndex(interp, vPtr, Tcl_GetString(objv[3]), &last, + INDEX_CHECK, (Blt_VectorIndexProc **) NULL) != TCL_OK)) + return TCL_ERROR; + + } + else { + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetString(objv[0]), " range ?first last?", + (char *)NULL); + return TCL_ERROR; + } + + Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + if (first > last) { + // Return the list reversed + for (int i=last; i<=first; i++) + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewDoubleObj(vPtr->valueArr[i])); + } + else { + for (int i=first; i<=last; i++) + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewDoubleObj(vPtr->valueArr[i])); + } + + Tcl_SetObjResult(interp, listObjPtr); + + return TCL_OK; +} + +static int InRange(double value, double min, double max) +{ + double range = max - min; + if (range < DBL_EPSILON) + return (fabs(max - value) < DBL_EPSILON); + + double norm = (value - min) / range; + return ((norm >= -DBL_EPSILON) && ((norm - 1.0) < DBL_EPSILON)); +} + +enum NativeFormats { + FMT_UNKNOWN = -1, + FMT_UCHAR, FMT_CHAR, + FMT_USHORT, FMT_SHORT, + FMT_UINT, FMT_INT, + FMT_ULONG, FMT_LONG, + FMT_FLOAT, FMT_DOUBLE +}; + +/* + *--------------------------------------------------------------------------- + * + * GetBinaryFormat + * + * Translates a format string into a native type. Valid formats are + * + * signed i1, i2, i4, i8 + * unsigned u1, u2, u4, u8 + * real r4, r8, r16 + * + * There must be a corresponding native type. For example, this for + * reading 2-byte binary integers from an instrument and converting them + * to unsigned shorts or ints. + * + *--------------------------------------------------------------------------- + */ +static enum NativeFormats GetBinaryFormat(Tcl_Interp* interp, char *string, + int *sizePtr) +{ + char c = tolower(string[0]); + if (Tcl_GetInt(interp, string + 1, sizePtr) != TCL_OK) { + Tcl_AppendResult(interp, "unknown binary format \"", string, + "\": incorrect byte size", (char *)NULL); + return FMT_UNKNOWN; + } + + switch (c) { + case 'r': + if (*sizePtr == sizeof(double)) + return FMT_DOUBLE; + else if (*sizePtr == sizeof(float)) + return FMT_FLOAT; + + break; + + case 'i': + if (*sizePtr == sizeof(char)) + return FMT_CHAR; + else if (*sizePtr == sizeof(int)) + return FMT_INT; + else if (*sizePtr == sizeof(long)) + return FMT_LONG; + else if (*sizePtr == sizeof(short)) + return FMT_SHORT; + + break; + + case 'u': + if (*sizePtr == sizeof(unsigned char)) + return FMT_UCHAR; + else if (*sizePtr == sizeof(unsigned int)) + return FMT_UINT; + else if (*sizePtr == sizeof(unsigned long)) + return FMT_ULONG; + else if (*sizePtr == sizeof(unsigned short)) + return FMT_USHORT; + + break; + + default: + Tcl_AppendResult(interp, "unknown binary format \"", string, + "\": should be either i#, r#, u# (where # is size in bytes)", + (char *)NULL); + return FMT_UNKNOWN; + } + Tcl_AppendResult(interp, "can't handle format \"", string, "\"", + (char *)NULL); + + return FMT_UNKNOWN; +} + +static int CopyValues(Vector *vPtr, char *byteArr, enum NativeFormats fmt, + int size, int length, int swap, int *indexPtr) +{ + if ((swap) && (size > 1)) { + int nBytes = size * length; + for (int i = 0; i < nBytes; i += size) { + unsigned char* p = (unsigned char *)(byteArr + i); + int left, right; + for (left = 0, right = size - 1; left < right; left++, right--) { + p[left] ^= p[right]; + p[right] ^= p[left]; + p[left] ^= p[right]; + } + } + } + + int newSize = *indexPtr + length; + if (newSize > vPtr->length) { + if (Vec_ChangeLength(vPtr->interp, vPtr, newSize) != TCL_OK) + return TCL_ERROR; + } + +#define CopyArrayToVector(vPtr, arr) \ + for (int i = 0, n = *indexPtr; i < length; i++, n++) { \ + (vPtr)->valueArr[n] = (double)(arr)[i]; \ + } + + switch (fmt) { + case FMT_CHAR: + CopyArrayToVector(vPtr, (char *)byteArr); + break; + + case FMT_UCHAR: + CopyArrayToVector(vPtr, (unsigned char *)byteArr); + break; + + case FMT_INT: + CopyArrayToVector(vPtr, (int *)byteArr); + break; + + case FMT_UINT: + CopyArrayToVector(vPtr, (unsigned int *)byteArr); + break; + + case FMT_LONG: + CopyArrayToVector(vPtr, (long *)byteArr); + break; + + case FMT_ULONG: + CopyArrayToVector(vPtr, (unsigned long *)byteArr); + break; + + case FMT_SHORT: + CopyArrayToVector(vPtr, (short int *)byteArr); + break; + + case FMT_USHORT: + CopyArrayToVector(vPtr, (unsigned short int *)byteArr); + break; + + case FMT_FLOAT: + CopyArrayToVector(vPtr, (float *)byteArr); + break; + + case FMT_DOUBLE: + CopyArrayToVector(vPtr, (double *)byteArr); + break; + + case FMT_UNKNOWN: + break; + } + *indexPtr += length; + return TCL_OK; +} + +/* + *--------------------------------------------------------------------------- + * + * BinreadOp -- + * + * Reads binary values from a TCL channel. Values are either appended to + * the end of the vector or placed at a given index (using the "-at" + * option), overwriting existing values. Data is read until EOF is found + * on the channel or a specified number of values are read. (note that + * this is not necessarily the same as the number of bytes). + * + * The following flags are supported: + * -swap Swap bytes + * -at index Start writing data at the index. + * -format fmt Specifies the format of the data. + * + * This binary reader was created and graciously donated by Harald Kirsch + * (kir@iitb.fhg.de). Anything that's wrong is due to my (gah) munging + * of the code. + * + * Results: + * Returns a standard TCL result. The interpreter result will contain the + * number of values (not the number of bytes) read. + * + * Caveats: + * Channel reads must end on an element boundary. + * + *--------------------------------------------------------------------------- + */ + +static int BinreadOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + enum NativeFormats fmt; + + char* string = Tcl_GetString(objv[2]); + int mode; + Tcl_Channel channel = Tcl_GetChannel(interp, string, &mode); + if (channel == NULL) + return TCL_ERROR; + + if ((mode & TCL_READABLE) == 0) { + Tcl_AppendResult(interp, "channel \"", string, + "\" wasn't opened for reading", (char *)NULL); + return TCL_ERROR; + } + int first = vPtr->length; + fmt = FMT_DOUBLE; + int size = sizeof(double); + int swap = 0; + int count = 0; + + if (objc > 3) { + string = Tcl_GetString(objv[3]); + if (string[0] != '-') { + long int value; + // Get the number of values to read. + if (Tcl_GetLongFromObj(interp, objv[3], &value) != TCL_OK) + return TCL_ERROR; + + if (value < 0) { + Tcl_AppendResult(interp, "count can't be negative", (char *)NULL); + return TCL_ERROR; + } + count = (size_t)value; + objc--, objv++; + } + } + + // Process any option-value pairs that remain. + for (int i = 3; i < objc; i++) { + string = Tcl_GetString(objv[i]); + if (strcmp(string, "-swap") == 0) + swap = 1; + else if (strcmp(string, "-format") == 0) { + i++; + if (i >= objc) { + Tcl_AppendResult(interp, "missing arg after \"", string, + "\"", (char *)NULL); + return TCL_ERROR; + } + + string = Tcl_GetString(objv[i]); + fmt = GetBinaryFormat(interp, string, &size); + if (fmt == FMT_UNKNOWN) + return TCL_ERROR; + } + else if (strcmp(string, "-at") == 0) { + i++; + if (i >= objc) { + Tcl_AppendResult(interp, "missing arg after \"", string, + "\"", (char *)NULL); + return TCL_ERROR; + } + + string = Tcl_GetString(objv[i]); + if (Vec_GetIndex(interp, vPtr, string, &first, 0, + (Blt_VectorIndexProc **)NULL) != TCL_OK) + return TCL_ERROR; + + if (first > vPtr->length) { + Tcl_AppendResult(interp, "index \"", string, + "\" is out of range", (char *)NULL); + return TCL_ERROR; + } + } + } + +#define BUFFER_SIZE 1024 + int arraySize = (count == 0) ? BUFFER_SIZE*size : count*size; + + char* byteArr = (char*)malloc(arraySize); + // FIXME: restore old channel translation later? + if (Tcl_SetChannelOption(interp, channel, "-translation","binary") != TCL_OK) + return TCL_ERROR; + + int total = 0; + while (!Tcl_Eof(channel)) { + int bytesRead = Tcl_Read(channel, byteArr, arraySize); + if (bytesRead < 0) { + Tcl_AppendResult(interp, "error reading channel: ", + Tcl_PosixError(interp), (char *)NULL); + return TCL_ERROR; + } + + if ((bytesRead % size) != 0) { + Tcl_AppendResult(interp, "error reading channel: short read", + (char *)NULL); + return TCL_ERROR; + } + + int length = bytesRead / size; + if (CopyValues(vPtr, byteArr, fmt, size, length, swap, &first) != TCL_OK) + return TCL_ERROR; + + total += length; + if (count > 0) + break; + } + free(byteArr); + + if (vPtr->flush) + Vec_FlushCache(vPtr); + Vec_UpdateClients(vPtr); + + // Set the result as the number of values read + Tcl_SetIntObj(Tcl_GetObjResult(interp), total); + + return TCL_OK; +} + +static int SearchOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + int wantValue = 0; + char* string = Tcl_GetString(objv[2]); + if ((string[0] == '-') && (strcmp(string, "-value") == 0)) { + wantValue = 1; + objv++, objc--; + } + double min; + if (Blt_ExprDoubleFromObj(interp, objv[2], &min) != TCL_OK) + return TCL_ERROR; + + double max = min; + if (objc > 4) { + Tcl_AppendResult(interp, "wrong # arguments: should be \"", + Tcl_GetString(objv[0]), " search ?-value? min ?max?", + (char *)NULL); + return TCL_ERROR; + } + + if ((objc > 3) && (Blt_ExprDoubleFromObj(interp, objv[3], &max) != TCL_OK)) + return TCL_ERROR; + + // Bogus range. Don't bother looking + if ((min - max) >= DBL_EPSILON) + return TCL_OK; + + Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + if (wantValue) { + for (int i = 0; i < vPtr->length; i++) { + if (InRange(vPtr->valueArr[i], min, max)) + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewDoubleObj(vPtr->valueArr[i])); + } + } + else { + for (int i = 0; i < vPtr->length; i++) { + if (InRange(vPtr->valueArr[i], min, max)) + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewIntObj(i + vPtr->offset)); + } + } + Tcl_SetObjResult(interp, listObjPtr); + + return TCL_OK; +} + +static int OffsetOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + if (objc == 3) { + int newOffset; + if (Tcl_GetIntFromObj(interp, objv[2], &newOffset) != TCL_OK) + return TCL_ERROR; + + vPtr->offset = newOffset; + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), vPtr->offset); + + return TCL_OK; +} + +static int RandomOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + for (int i = 0; i < vPtr->length; i++) + vPtr->valueArr[i] = drand48(); + + if (vPtr->flush) + Vec_FlushCache(vPtr); + Vec_UpdateClients(vPtr); + + return TCL_OK; +} + +static int SeqOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + double start; + if (Blt_ExprDoubleFromObj(interp, objv[2], &start) != TCL_OK) + return TCL_ERROR; + + double stop; + if (Blt_ExprDoubleFromObj(interp, objv[3], &stop) != TCL_OK) + return TCL_ERROR; + + int n = vPtr->length; + if ((objc > 4) && (Blt_ExprIntFromObj(interp, objv[4], &n) != TCL_OK)) + return TCL_ERROR; + + if (n > 1) { + if (Vec_SetLength(interp, vPtr, n) != TCL_OK) + return TCL_ERROR; + + double step = (stop - start) / (double)(n - 1); + for (int i = 0; i < n; i++) + vPtr->valueArr[i] = start + (step * i); + + if (vPtr->flush) + Vec_FlushCache(vPtr); + + Vec_UpdateClients(vPtr); + } + return TCL_OK; +} + +static int SetOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + int nElem; + Tcl_Obj **elemObjArr; + + // The source can be either a list of numbers or another vector. + + Vector* v2Ptr = Vec_ParseElement((Tcl_Interp *)NULL, vPtr->dataPtr, + Tcl_GetString(objv[2]), NULL, + NS_SEARCH_BOTH); + int result; + if (v2Ptr != NULL) { + if (vPtr == v2Ptr) { + // Source and destination vectors are the same. Copy the source + // first into a temporary vector to avoid memory overlaps. + Vector* tmpPtr = Vec_New(vPtr->dataPtr); + result = Vec_Duplicate(tmpPtr, v2Ptr); + if (result == TCL_OK) { + result = Vec_Duplicate(vPtr, tmpPtr); + } + Vec_Free(tmpPtr); + } + else + result = Vec_Duplicate(vPtr, v2Ptr); + } + else if (Tcl_ListObjGetElements(interp, objv[2], &nElem, &elemObjArr) + == TCL_OK) + result = CopyList(vPtr, interp, nElem, elemObjArr); + else + return TCL_ERROR; + + if (result == TCL_OK) { + // The vector has changed; so flush the array indices (they're wrong + // now), find the new range of the data, and notify the vector's + //clients that it's been modified. + if (vPtr->flush) + Vec_FlushCache(vPtr); + Vec_UpdateClients(vPtr); + } + + return result; +} + +static int SimplifyOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + double tolerance = 10.0; + + int nPoints = vPtr->length / 2; + int* simple = (int*)malloc(nPoints * sizeof(int)); + Point2d* reduced = (Point2d*)malloc(nPoints * sizeof(Point2d)); + Point2d* orig = (Point2d *)vPtr->valueArr; + int n = Blt_SimplifyLine(orig, 0, nPoints - 1, tolerance, simple); + for (int i = 0; i < n; i++) + reduced[i] = orig[simple[i]]; + + free(simple); + Vec_Reset(vPtr, (double *)reduced, n * 2, vPtr->length, TCL_DYNAMIC); + // The vector has changed; so flush the array indices (they're wrong + // now), find the new range of the data, and notify the vector's + // clients that it's been modified. + if (vPtr->flush) + Vec_FlushCache(vPtr); + Vec_UpdateClients(vPtr); + + return TCL_OK; +} + +static int SplitOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + int nVectors = objc - 2; + if ((vPtr->length % nVectors) != 0) { + Tcl_AppendResult(interp, "can't split vector \"", vPtr->name, + "\" into ", Itoa(nVectors), " even parts.", (char *)NULL); + return TCL_ERROR; + } + + if (nVectors > 0) { + int extra = vPtr->length / nVectors; + for (int i = 0; i < nVectors; i++) { + char* string = Tcl_GetString(objv[i+2]); + int isNew; + Vector* v2Ptr = Vec_Create(vPtr->dataPtr, string, string, string, &isNew); + int oldSize = v2Ptr->length; + int newSize = oldSize + extra; + if (Vec_SetLength(interp, v2Ptr, newSize) != TCL_OK) + return TCL_ERROR; + + int j,k; + for (j = i, k = oldSize; j < vPtr->length; j += nVectors, k++) + v2Ptr->valueArr[k] = vPtr->valueArr[j]; + + Vec_UpdateClients(v2Ptr); + if (v2Ptr->flush) { + Vec_FlushCache(v2Ptr); + } + } + } + return TCL_OK; +} + + +// Pointer to the array of values currently being sorted. +static Vector **sortVectors; +// Indicates the ordering of the sort. If non-zero, the vectors are sorted in +// decreasing order +static int sortDecreasing; +static int nSortVectors; + +static int CompareVectors(void *a, void *b) +{ + int sign = (sortDecreasing) ? -1 : 1; + for (int i = 0; i < nSortVectors; i++) { + Vector* vPtr = sortVectors[i]; + double delta = vPtr->valueArr[*(int *)a] - vPtr->valueArr[*(int *)b]; + if (delta < 0.0) + return (-1 * sign); + else if (delta > 0.0) + return (1 * sign); + } + + return 0; +} + +size_t* Blt::Vec_SortMap(Vector **vectors, int nVectors) +{ + Vector *vPtr = *vectors; + int length = vPtr->last - vPtr->first + 1; + size_t* map = (size_t*)malloc(sizeof(size_t) * length); + for (int i = vPtr->first; i <= vPtr->last; i++) + map[i] = i; + + // Set global variables for sorting routine + sortVectors = vectors; + nSortVectors = nVectors; + qsort((char *)map, length, sizeof(size_t),(QSortCompareProc *)CompareVectors); + + return map; +} + +static size_t* SortVectors(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + + Vector** vectors = (Vector**)malloc(sizeof(Vector *) * (objc + 1)); + vectors[0] = vPtr; + size_t* map = NULL; + for (int i = 0; i < objc; i++) { + Vector* v2Ptr; + if (Vec_LookupName(vPtr->dataPtr, Tcl_GetString(objv[i]), + &v2Ptr) != TCL_OK) + goto error; + + if (v2Ptr->length != vPtr->length) { + Tcl_AppendResult(interp, "vector \"", v2Ptr->name, + "\" is not the same size as \"", vPtr->name, "\"", + (char *)NULL); + goto error; + } + vectors[i + 1] = v2Ptr; + } + map = Vec_SortMap(vectors, objc + 1); + + error: + free(vectors); + + return map; +} + +static int SortOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + sortDecreasing = 0; + SortSwitches switches; + switches.flags = 0; + int i = ParseSwitches(interp, sortSwitches, objc - 2, objv + 2, &switches, + BLT_SWITCH_OBJV_PARTIAL); + if (i < 0) + return TCL_ERROR; + + objc -= i, objv += i; + sortDecreasing = (switches.flags & SORT_DECREASING); + + size_t *map = (objc > 2) ? SortVectors(vPtr, interp, objc - 2, objv + 2) : + Vec_SortMap(&vPtr, 1); + + if (map == NULL) + return TCL_ERROR; + + int sortLength = vPtr->length; + + // Create an array to store a copy of the current values of the + // vector. We'll merge the values back into the vector based upon the + // indices found in the index array. + size_t nBytes = sizeof(double) * sortLength; + double* copy = (double*)malloc(nBytes); + memcpy((char *)copy, (char *)vPtr->valueArr, nBytes); + if (switches.flags & SORT_UNIQUE) { + int count =1; + for (int n = 1; n < sortLength; n++) { + size_t next = map[n]; + size_t prev = map[n - 1]; + if (copy[next] != copy[prev]) { + map[count] = next; + count++; + } + } + sortLength = count; + nBytes = sortLength * sizeof(double); + } + + if (sortLength != vPtr->length) + Vec_SetLength(interp, vPtr, sortLength); + + for (int n = 0; n < sortLength; n++) + vPtr->valueArr[n] = copy[map[n]]; + + if (vPtr->flush) + Vec_FlushCache(vPtr); + Vec_UpdateClients(vPtr); + + // Now sort any other vectors in the same fashion. The vectors must be + // the same size as the map though + int result = TCL_ERROR; + for (int i = 2; i < objc; i++) { + Vector *v2Ptr; + if (Vec_LookupName(vPtr->dataPtr, Tcl_GetString(objv[i]), &v2Ptr) != TCL_OK) + goto error; + + if (sortLength != v2Ptr->length) + Vec_SetLength(interp, v2Ptr, sortLength); + + memcpy((char *)copy, (char *)v2Ptr->valueArr, nBytes); + for (int n = 0; n < sortLength; n++) + v2Ptr->valueArr[n] = copy[map[n]]; + + Vec_UpdateClients(v2Ptr); + if (v2Ptr->flush) + Vec_FlushCache(v2Ptr); + } + result = TCL_OK; + + error: + free(copy); + free(map); + + return result; +} + +static int InstExprOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + if (Blt_ExprVector(interp, Tcl_GetString(objv[2]), (Blt_Vector *)vPtr) != TCL_OK) + return TCL_ERROR; + + if (vPtr->flush) + Vec_FlushCache(vPtr); + Vec_UpdateClients(vPtr); + + return TCL_OK; +} + +static int ArithOp(Vector *vPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + double value; + double scalar; + + Vector* v2Ptr = Vec_ParseElement((Tcl_Interp *)NULL, vPtr->dataPtr, + Tcl_GetString(objv[2]), NULL, + NS_SEARCH_BOTH); + if (v2Ptr != NULL) { + int length = v2Ptr->last - v2Ptr->first + 1; + if (length != vPtr->length) { + Tcl_AppendResult(interp, "vectors \"", Tcl_GetString(objv[0]), + "\" and \"", Tcl_GetString(objv[2]), + "\" are not the same length", (char *)NULL); + return TCL_ERROR; + } + + char* string = Tcl_GetString(objv[1]); + Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + switch (string[0]) { + case '*': + for (int i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) { + value = vPtr->valueArr[i] * v2Ptr->valueArr[j]; + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); + } + break; + + case '/': + for (int i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) { + value = vPtr->valueArr[i] / v2Ptr->valueArr[j]; + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); + } + break; + + case '-': + for (int i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) { + value = vPtr->valueArr[i] - v2Ptr->valueArr[j]; + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); + } + break; + + case '+': + for (int i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) { + value = vPtr->valueArr[i] + v2Ptr->valueArr[j]; + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); + } + break; + } + Tcl_SetObjResult(interp, listObjPtr); + + } + else if (Blt_ExprDoubleFromObj(interp, objv[2], &scalar) == TCL_OK) { + Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + char* string = Tcl_GetString(objv[1]); + switch (string[0]) { + case '*': + for (int i = 0; i < vPtr->length; i++) { + value = vPtr->valueArr[i] * scalar; + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); + } + break; + + case '/': + for (int i = 0; i < vPtr->length; i++) { + value = vPtr->valueArr[i] / scalar; + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); + } + break; + + case '-': + for (int i = 0; i < vPtr->length; i++) { + value = vPtr->valueArr[i] - scalar; + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); + } + break; + + case '+': + for (int i = 0; i < vPtr->length; i++) { + value = vPtr->valueArr[i] + scalar; + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); + } + break; + } + Tcl_SetObjResult(interp, listObjPtr); + } + else + return TCL_ERROR; + + return TCL_OK; +} + +static Blt_OpSpec vectorInstOps[] = + { + {"*", 1, (void*)ArithOp, 3, 3, "item",}, /*Deprecated*/ + {"+", 1, (void*)ArithOp, 3, 3, "item",}, /*Deprecated*/ + {"-", 1, (void*)ArithOp, 3, 3, "item",}, /*Deprecated*/ + {"/", 1, (void*)ArithOp, 3, 3, "item",}, /*Deprecated*/ + {"append", 1, (void*)AppendOp, 3, 0, "item ?item...?",}, + {"binread", 1, (void*)BinreadOp, 3, 0, "channel ?numValues? ?flags?",}, + {"clear", 1, (void*)ClearOp, 2, 2, "",}, + {"delete", 2, (void*)DeleteOp, 2, 0, "index ?index...?",}, + {"dup", 2, (void*)DupOp, 3, 0, "vecName",}, + {"expr", 1, (void*)InstExprOp, 3, 3, "expression",}, + {"fft", 1, (void*)FFTOp, 3, 0, "vecName ?switches?",}, + {"index", 3, (void*)IndexOp, 3, 4, "index ?value?",}, + {"inversefft",3, (void*)InverseFFTOp,4, 4, "vecName vecName",}, + {"length", 1, (void*)LengthOp, 2, 3, "?newSize?",}, + {"max", 2, (void*)MaxOp, 2, 2, "",}, + {"merge", 2, (void*)MergeOp, 3, 0, "vecName ?vecName...?",}, + {"min", 2, (void*)MinOp, 2, 2, "",}, + {"normalize", 3, (void*)NormalizeOp, 2, 3, "?vecName?",}, /*Deprecated*/ + {"notify", 3, (void*)NotifyOp, 3, 3, "keyword",}, + {"offset", 1, (void*)OffsetOp, 2, 3, "?offset?",}, + {"populate", 1, (void*)PopulateOp, 4, 4, "vecName density",}, + {"random", 4, (void*)RandomOp, 2, 2, "",}, /*Deprecated*/ + {"range", 4, (void*)RangeOp, 2, 4, "first last",}, + {"search", 3, (void*)SearchOp, 3, 5, "?-value? value ?value?",}, + {"seq", 3, (void*)SeqOp, 4, 5, "begin end ?num?",}, + {"set", 3, (void*)SetOp, 3, 3, "list",}, + {"simplify", 2, (void*)SimplifyOp, 2, 2, }, + {"sort", 2, (void*)SortOp, 2, 0, "?switches? ?vecName...?",}, + {"split", 2, (void*)SplitOp, 2, 0, "?vecName...?",}, + {"values", 3, (void*)ValuesOp, 2, 0, "?switches?",}, + {"variable", 3, (void*)MapOp, 2, 3, "?varName?",}, + }; + +static int nInstOps = sizeof(vectorInstOps) / sizeof(Blt_OpSpec); + +int Blt::Vec_InstCmd(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + Vector* vPtr = (Vector*)clientData; + vPtr->first = 0; + vPtr->last = vPtr->length - 1; + VectorCmdProc *proc = + (VectorCmdProc*)GetOpFromObj(interp, nInstOps, vectorInstOps, + BLT_OP_ARG1, objc, objv, 0); + if (proc == NULL) + return TCL_ERROR; + + return (*proc) (vPtr, interp, objc, objv); +} + +#define MAX_ERR_MSG 1023 +static char message[MAX_ERR_MSG + 1]; +char* Blt::Vec_VarTrace(ClientData clientData, Tcl_Interp* interp, + const char *part1, const char *part2, int flags) +{ + Blt_VectorIndexProc *indexProc; + Vector* vPtr = (Vector*)clientData; + + if (part2 == NULL) { + if (flags & TCL_TRACE_UNSETS) { + free((void*)(vPtr->arrayName)); + vPtr->arrayName = NULL; + if (vPtr->freeOnUnset) + Vec_Free(vPtr); + } + + return NULL; + } + + int first; + int last; + int varFlags; + + if (Vec_GetIndexRange(interp, vPtr, part2, INDEX_ALL_FLAGS, &indexProc) + != TCL_OK) + goto error; + + first = vPtr->first; + last = vPtr->last; + varFlags = TCL_LEAVE_ERR_MSG | (TCL_GLOBAL_ONLY & flags); + if (flags & TCL_TRACE_WRITES) { + // Tried to set "min" or "max" + if (first == SPECIAL_INDEX) + return (char *)"read-only index"; + + Tcl_Obj* objPtr = Tcl_GetVar2Ex(interp, part1, part2, varFlags); + if (objPtr == NULL) + goto error; + + double value; + if (Blt_ExprDoubleFromObj(interp, objPtr, &value) != TCL_OK) { + // Single numeric index. Reset the array element to + // its old value on errors + if ((last == first) && (first >= 0)) + Tcl_SetVar2Ex(interp, part1, part2, objPtr, varFlags); + goto error; + } + + if (first == vPtr->length) { + if (Vec_ChangeLength((Tcl_Interp *)NULL, vPtr, vPtr->length + 1) + != TCL_OK) + return (char *)"error resizing vector"; + } + + // Set possibly an entire range of values + ReplicateValue(vPtr, first, last, value); + } + else if (flags & TCL_TRACE_READS) { + Tcl_Obj *objPtr; + + if (vPtr->length == 0) { + if (Tcl_SetVar2(interp, part1, part2, "", varFlags) == NULL) + goto error; + + return NULL; + } + + if (first == vPtr->length) + return (char *)"write-only index"; + + if (first == last) { + double value; + if (first >= 0) + value = vPtr->valueArr[first]; + else { + vPtr->first = 0, vPtr->last = vPtr->length - 1; + value = (*indexProc) ((Blt_Vector *) vPtr); + } + + objPtr = Tcl_NewDoubleObj(value); + if (Tcl_SetVar2Ex(interp, part1, part2, objPtr, varFlags) == NULL) { + Tcl_DecrRefCount(objPtr); + goto error; + } + } + else { + objPtr = GetValues(vPtr, first, last); + if (Tcl_SetVar2Ex(interp, part1, part2, objPtr, varFlags) == NULL) + Tcl_DecrRefCount(objPtr); + goto error; + } + } + else if (flags & TCL_TRACE_UNSETS) { + if ((first == vPtr->length) || (first == SPECIAL_INDEX)) + return (char *)"special vector index"; + + // Collapse the vector from the point of the first unset element. + // Also flush any array variable entries so that the shift is + // reflected when the array variable is read. + for (int i = first, j = last + 1; j < vPtr->length; i++, j++) + vPtr->valueArr[i] = vPtr->valueArr[j]; + + vPtr->length -= ((last - first) + 1); + if (vPtr->flush) + Vec_FlushCache(vPtr); + + } + else + return (char *)"unknown variable trace flag"; + + if (flags & (TCL_TRACE_UNSETS | TCL_TRACE_WRITES)) + Vec_UpdateClients(vPtr); + + Tcl_ResetResult(interp); + return NULL; + + error: + strncpy(message, Tcl_GetStringResult(interp), MAX_ERR_MSG); + message[MAX_ERR_MSG] = '\0'; + return message; +} diff --git a/generic/tkbltVecInt.h b/generic/tkbltVecInt.h new file mode 100644 index 0000000..cc516a1 --- /dev/null +++ b/generic/tkbltVecInt.h @@ -0,0 +1,202 @@ +/* + * 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 1995-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 "tkbltChain.h" +#include "tkbltVector.h" + +#define VECTOR_THREAD_KEY "BLT Vector Data" +#define VECTOR_MAGIC ((unsigned int) 0x46170277) + +/* These defines allow parsing of different types of indices */ + +#define INDEX_SPECIAL (1<<0) /* Recognize "min", "max", and "++end" as + * valid indices */ +#define INDEX_COLON (1<<1) /* Also recognize a range of indices separated + * by a colon */ +#define INDEX_CHECK (1<<2) /* Verify that the specified index or range of + * indices are within limits */ +#define INDEX_ALL_FLAGS (INDEX_SPECIAL | INDEX_COLON | INDEX_CHECK) + +#define SPECIAL_INDEX -2 + +#define FFT_NO_CONSTANT (1<<0) +#define FFT_BARTLETT (1<<1) +#define FFT_SPECTRUM (1<<2) + +#define NOTIFY_UPDATED ((int)BLT_VECTOR_NOTIFY_UPDATE) +#define NOTIFY_DESTROYED ((int)BLT_VECTOR_NOTIFY_DESTROY) + +#define NOTIFY_NEVER (1<<3) /* Never notify clients of updates to + * the vector */ +#define NOTIFY_ALWAYS (1<<4) /* Notify clients after each update + * of the vector is made */ +#define NOTIFY_WHENIDLE (1<<5) /* Notify clients at the next idle point + * that the vector has been updated. */ + +#define NOTIFY_PENDING (1<<6) /* A do-when-idle notification of the + * vector's clients is pending. */ +#define NOTIFY_NOW (1<<7) /* Notify clients of changes once + * immediately */ + +#define NOTIFY_WHEN_MASK (NOTIFY_NEVER|NOTIFY_ALWAYS|NOTIFY_WHENIDLE) + +#define UPDATE_RANGE (1<<9) /* The data of the vector has changed. + * Update the min and max limits when + * they are needed */ + +#define FindRange(array, first, last, min, max) \ + { \ + min = max = 0.0; \ + if (first <= last) { \ + register int i; \ + min = max = array[first]; \ + for (i = first + 1; i <= last; i++) { \ + if (min > array[i]) { \ + min = array[i]; \ + } else if (max < array[i]) { \ + max = array[i]; \ + } \ + } \ + } \ + } + +namespace Blt { + + typedef struct { + double x; + double y; + } Point2d; + + typedef struct { + Tcl_HashTable vectorTable; /* Table of vectors */ + Tcl_HashTable mathProcTable; /* Table of vector math functions */ + Tcl_HashTable indexProcTable; + Tcl_Interp* interp; + unsigned int nextId; + } VectorInterpData; + + typedef struct { + // If you change these fields, make sure you change the definition of + // Blt_Vector in blt.h too. + double *valueArr; /* Array of values (malloc-ed) */ + int length; /* Current number of values in the array. */ + int size; /* Maximum number of values that can be stored + * in the value array. */ + double min, max; /* Minimum and maximum values in the vector */ + int dirty; /* Indicates if the vector has been updated */ + int reserved; + + /* The following fields are local to this module */ + + const char *name; /* The namespace-qualified name of the vector. + * It points to the hash key allocated for the + * entry in the vector hash table. */ + VectorInterpData *dataPtr; + Tcl_Interp* interp; /* Interpreter associated with the vector */ + Tcl_HashEntry *hashPtr; /* If non-NULL, pointer in a hash table to + * track the vectors in use. */ + Tcl_FreeProc *freeProc; /* Address of procedure to call to release + * storage for the value array, Optionally can + * be one of the following: TCL_STATIC, + * TCL_DYNAMIC, or TCL_VOLATILE. */ + const char *arrayName; /* The name of the TCL array variable mapped + * to the vector (malloc'ed). If NULL, + * indicates that the vector isn't mapped to + * any variable */ + Tcl_Namespace *nsPtr; /* Namespace context of the vector itself. */ + int offset; /* Offset from zero of the vector's starting + * index */ + Tcl_Command cmdToken; /* Token for vector's TCL command. */ + Chain* chain; /* List of clients using this vector */ + int notifyFlags; /* Notification flags. See definitions + * below */ + int varFlags; /* Indicate if the variable is global, + * namespace, or local */ + int freeOnUnset; /* For backward compatibility only: If + * non-zero, free the vector when its variable + * is unset. */ + int flush; + int first, last; /* Selected region of vector. This is used + * mostly for the math routines */ + } Vector; + + extern const char* Itoa(int value); + extern int Vec_GetIndex(Tcl_Interp* interp, Vector *vPtr, + const char *string, int *indexPtr, int flags, + Blt_VectorIndexProc **procPtrPtr); + extern int Vec_GetIndexRange(Tcl_Interp* interp, Vector *vPtr, + const char *string, int flags, + Blt_VectorIndexProc **procPtrPtr); + extern Vector* Vec_ParseElement(Tcl_Interp* interp, VectorInterpData *dataPtr, + const char *start, const char **endPtr, + int flags); + extern int Vec_SetLength(Tcl_Interp* interp, Vector *vPtr, int length); + extern int Vec_SetSize(Tcl_Interp* interp, Vector *vPtr, int size); + extern void Vec_FlushCache(Vector *vPtr); + extern void Vec_UpdateRange(Vector *vPtr); + extern void Vec_UpdateClients(Vector *vPtr); + extern void Vec_Free(Vector *vPtr); + extern Vector* Vec_New(VectorInterpData *dataPtr); + extern int Vec_MapVariable(Tcl_Interp* interp, Vector *vPtr, + const char *name); + extern int Vec_ChangeLength(Tcl_Interp* interp, Vector *vPtr, int length); + extern Vector* Vec_Create(VectorInterpData *dataPtr, const char *name, + const char *cmdName, const char *varName, + int *newPtr); + extern int Vec_LookupName(VectorInterpData *dataPtr, const char *vecName, + Vector **vPtrPtr); + extern VectorInterpData* Vec_GetInterpData (Tcl_Interp* interp); + extern int Vec_Reset(Vector *vPtr, double *dataArr, int nValues, + int arraySize, Tcl_FreeProc *freeProc); + extern int Vec_FFT(Tcl_Interp* interp, Vector *realPtr, + Vector *phasesPtr, Vector *freqPtr, double delta, + int flags, Vector *srcPtr); + extern int Vec_InverseFFT(Tcl_Interp* interp, Vector *iSrcPtr, + Vector *rDestPtr, Vector *iDestPtr, + Vector *srcPtr); + extern int Vec_Duplicate(Vector *destPtr, Vector *srcPtr); + extern size_t *Vec_SortMap(Vector **vectors, int nVectors); + extern double Vec_Max(Vector *vecObjPtr); + extern double Vec_Min(Vector *vecObjPtr); + extern int ExprVector(Tcl_Interp* interp, char *string, Blt_Vector *vector); + + extern Tcl_ObjCmdProc Vec_InstCmd; + extern Tcl_VarTraceProc Vec_VarTrace; + extern void Vec_InstallMathFunctions(Tcl_HashTable *tablePtr); + extern void Vec_UninstallMathFunctions(Tcl_HashTable *tablePtr); + extern void Vec_InstallSpecialIndices(Tcl_HashTable *tablePtr); +}; + +extern Tcl_IdleProc Blt_Vec_NotifyClients; + +#ifdef _WIN32 +double drand48(void); +void srand48(long int seed); +#endif diff --git a/generic/tkbltVecMath.C b/generic/tkbltVecMath.C new file mode 100644 index 0000000..03277d4 --- /dev/null +++ b/generic/tkbltVecMath.C @@ -0,0 +1,1612 @@ +/* + * 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 1995-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 + +#include +#include +#include +#include +#include + +#include "tkbltInt.h" +#include "tkbltVecInt.h" +#include "tkbltNsUtil.h" +#include "tkbltParse.h" + +using namespace std; +using namespace Blt; + +/* + * Three types of math functions: + * + * ComponentProc Function is applied in multiple calls to + * each component of the vector. + * VectorProc Entire vector is passed, each component is + * modified. + * ScalarProc Entire vector is passed, single scalar value + * is returned. + */ + +typedef double (ComponentProc)(double value); +typedef int (VectorProc)(Vector *vPtr); +typedef double (ScalarProc)(Vector *vPtr); + +/* + * Built-in math functions: + */ +typedef int (GenericMathProc) (void*, Tcl_Interp*, Vector*); + +/* + * MathFunction -- + * + * Contains information about math functions that can be called + * for vectors. The table of math functions is global within the + * application. So you can't define two different "sqrt" + * functions. + */ +typedef struct { + const char *name; /* Name of built-in math function. If + * NULL, indicates that the function + * was user-defined and dynamically + * allocated. Function names are + * global across all interpreters. */ + + void *proc; /* Procedure that implements this math + * function. */ + + ClientData clientData; /* Argument to pass when invoking the + * function. */ + +} MathFunction; + +/* The data structure below is used to describe an expression value, + * which can be either a double-precision floating-point value, or a + * string. A given number has only one value at a time. */ + +#define STATIC_STRING_SPACE 150 + +/* + * Tokens -- + * + * The token types are defined below. In addition, there is a + * table associating a precedence with each operator. The order + * of types is important. Consult the code before changing it. + */ +enum Tokens { + VALUE, OPEN_PAREN, CLOSE_PAREN, COMMA, END, UNKNOWN, + MULT = 8, DIVIDE, MOD, PLUS, MINUS, + LEFT_SHIFT, RIGHT_SHIFT, + LESS, GREATER, LEQ, GEQ, EQUAL, NEQ, + OLD_BIT_AND, EXPONENT, OLD_BIT_OR, OLD_QUESTY, OLD_COLON, + AND, OR, UNARY_MINUS, OLD_UNARY_PLUS, NOT, OLD_BIT_NOT +}; + +typedef struct { + Vector *vPtr; + char staticSpace[STATIC_STRING_SPACE]; + ParseValue pv; /* Used to hold a string value, if any. */ +} Value; + +/* + * ParseInfo -- + * + * The data structure below describes the state of parsing an + * expression. It's passed among the routines in this module. + */ +typedef struct { + const char *expr; /* The entire right-hand side of the + * expression, as originally passed to + * Blt_ExprVector. */ + + const char *nextPtr; /* Position of the next character to + * be scanned from the expression + * string. */ + + enum Tokens token; /* Type of the last token to be parsed + * from nextPtr. See below for + * definitions. Corresponds to the + * characters just before nextPtr. */ + +} ParseInfo; + +/* + * Precedence table. The values for non-operator token types are ignored. + */ +static int precTable[] = + { + 0, 0, 0, 0, 0, 0, 0, 0, + 12, 12, 12, /* MULT, DIVIDE, MOD */ + 11, 11, /* PLUS, MINUS */ + 10, 10, /* LEFT_SHIFT, RIGHT_SHIFT */ + 9, 9, 9, 9, /* LESS, GREATER, LEQ, GEQ */ + 8, 8, /* EQUAL, NEQ */ + 7, /* OLD_BIT_AND */ + 13, /* EXPONENTIATION */ + 5, /* OLD_BIT_OR */ + 4, /* AND */ + 3, /* OR */ + 2, /* OLD_QUESTY */ + 1, /* OLD_COLON */ + 14, 14, 14, 14 /* UNARY_MINUS, OLD_UNARY_PLUS, NOT, + * OLD_BIT_NOT */ + }; + + +/* + * Forward declarations. + */ + +static int NextValue(Tcl_Interp* interp, ParseInfo *piPtr, int prec, + Value *valuePtr); + +static int Sort(Vector *vPtr) +{ + size_t* map = Vec_SortMap(&vPtr, 1); + double* values = (double*)malloc(sizeof(double) * vPtr->length); + for(int ii = vPtr->first; ii <= vPtr->last; ii++) + values[ii] = vPtr->valueArr[map[ii]]; + + free(map); + for (int ii = vPtr->first; ii <= vPtr->last; ii++) + vPtr->valueArr[ii] = values[ii]; + + free(values); + return TCL_OK; +} + +static double Length(Blt_Vector *vectorPtr) +{ + Vector *vPtr = (Vector *)vectorPtr; + return (double)(vPtr->last - vPtr->first + 1); +} + +double Blt_VecMax(Blt_Vector *vectorPtr) +{ + Vector *vPtr = (Vector *)vectorPtr; + return Vec_Max(vPtr); +} + +double Blt_VecMin(Blt_Vector *vectorPtr) +{ + Vector *vPtr = (Vector *)vectorPtr; + return Vec_Min(vPtr); +} + +int Blt_ExprVector(Tcl_Interp* interp, char *string, Blt_Vector *vector) +{ + return ExprVector(interp,string,vector); +} + +static double Product(Blt_Vector *vectorPtr) +{ + Vector *vPtr = (Vector *)vectorPtr; + double prod; + double *vp, *vend; + + prod = 1.0; + for(vp = vPtr->valueArr + vPtr->first, + vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) { + prod *= *vp; + } + return prod; +} + +static double Sum(Blt_Vector *vectorPtr) +{ + // Kahan summation algorithm + + Vector *vPtr = (Vector *)vectorPtr; + double* vp = vPtr->valueArr + vPtr->first; + double sum = *vp++; + double c = 0.0; /* A running compensation for lost + * low-order bits.*/ + for (double* vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) { + double y = *vp - c; /* So far, so good: c is zero.*/ + double t = sum + y; /* Alas, sum is big, y small, so + * low-order digits of y are lost.*/ + c = (t - sum) - y; /* (t - sum) recovers the high-order + * part of y; subtracting y recovers + * -(low part of y) */ + sum = t; + } + + return sum; +} + +static double Mean(Blt_Vector *vectorPtr) +{ + Vector *vPtr = (Vector *)vectorPtr; + double sum = Sum(vectorPtr); + int n = vPtr->last - vPtr->first + 1; + + return sum / (double)n; +} + +// var = 1/N Sum( (x[i] - mean)^2 ) +static double Variance(Blt_Vector *vectorPtr) +{ + Vector *vPtr = (Vector *)vectorPtr; + double mean = Mean(vectorPtr); + double var = 0.0; + int count = 0; + for(double *vp=vPtr->valueArr+vPtr->first, *vend=vPtr->valueArr+vPtr->last; + vp <= vend; vp++) { + double dx = *vp - mean; + var += dx * dx; + count++; + } + + if (count < 2) + return 0.0; + + var /= (double)(count - 1); + return var; +} + +// skew = Sum( (x[i] - mean)^3 ) / (var^3/2) +static double Skew(Blt_Vector *vectorPtr) +{ + Vector *vPtr = (Vector *)vectorPtr; + double mean = Mean(vectorPtr); + double var = 0; + double skew = 0; + int count = 0; + for(double *vp=vPtr->valueArr+vPtr->first, *vend=vPtr->valueArr+vPtr->last; + vp <= vend; vp++) { + double diff = *vp - mean; + diff = fabs(diff); + double diffsq = diff * diff; + var += diffsq; + skew += diffsq * diff; + count++; + } + + if (count < 2) + return 0.0; + + var /= (double)(count - 1); + skew /= count * var * sqrt(var); + return skew; +} + +static double StdDeviation(Blt_Vector *vectorPtr) +{ + double var; + + var = Variance(vectorPtr); + if (var > 0.0) { + return sqrt(var); + } + return 0.0; +} + +static double AvgDeviation(Blt_Vector *vectorPtr) +{ + Vector *vPtr = (Vector *)vectorPtr; + double mean = Mean(vectorPtr); + double avg = 0.0; + int count = 0; + for(double *vp=vPtr->valueArr+vPtr->first, *vend=vPtr->valueArr+vPtr->last; + vp <= vend; vp++) { + double diff = *vp - mean; + avg += fabs(diff); + count++; + } + + if (count < 2) + return 0.0; + + avg /= (double)count; + return avg; +} + +static double Kurtosis(Blt_Vector *vectorPtr) +{ + Vector *vPtr = (Vector *)vectorPtr; + double mean = Mean(vectorPtr); + double var = 0; + double kurt = 0; + int count = 0; + for(double *vp=vPtr->valueArr+vPtr->first, *vend=vPtr->valueArr+vPtr->last; + vp <= vend; vp++) { + double diff = *vp - mean; + double diffsq = diff * diff; + var += diffsq; + kurt += diffsq * diffsq; + count++; + } + + if (count < 2) + return 0.0; + + var /= (double)(count - 1); + + if (var == 0.0) + return 0.0; + + kurt /= (count * var * var); + return kurt - 3.0; /* Fisher Kurtosis */ +} + +static double Median(Blt_Vector *vectorPtr) +{ + Vector *vPtr = (Vector *)vectorPtr; + size_t *map; + double q2; + int mid; + + if (vPtr->length == 0) { + return -DBL_MAX; + } + map = Vec_SortMap(&vPtr, 1); + mid = (vPtr->length - 1) / 2; + + /* + * Determine Q2 by checking if the number of elements [0..n-1] is + * odd or even. If even, we must take the average of the two + * middle values. + */ + if (vPtr->length & 1) { /* Odd */ + q2 = vPtr->valueArr[map[mid]]; + } else { /* Even */ + q2 = (vPtr->valueArr[map[mid]] + + vPtr->valueArr[map[mid + 1]]) * 0.5; + } + free(map); + return q2; +} + +static double Q1(Blt_Vector *vectorPtr) +{ + Vector *vPtr = (Vector *)vectorPtr; + double q1; + size_t *map; + + if (vPtr->length == 0) { + return -DBL_MAX; + } + map = Vec_SortMap(&vPtr, 1); + + if (vPtr->length < 4) { + q1 = vPtr->valueArr[map[0]]; + } else { + int mid, q; + + mid = (vPtr->length - 1) / 2; + q = mid / 2; + + /* + * Determine Q1 by checking if the number of elements in the + * bottom half [0..mid) is odd or even. If even, we must + * take the average of the two middle values. + */ + if (mid & 1) { /* Odd */ + q1 = vPtr->valueArr[map[q]]; + } else { /* Even */ + q1 = (vPtr->valueArr[map[q]] + + vPtr->valueArr[map[q + 1]]) * 0.5; + } + } + free(map); + return q1; +} + +static double Q3(Blt_Vector *vectorPtr) +{ + Vector *vPtr = (Vector *)vectorPtr; + double q3; + size_t *map; + + if (vPtr->length == 0) { + return -DBL_MAX; + } + + map = Vec_SortMap(&vPtr, 1); + + if (vPtr->length < 4) { + q3 = vPtr->valueArr[map[vPtr->length - 1]]; + } else { + int mid, q; + + mid = (vPtr->length - 1) / 2; + q = (vPtr->length + mid) / 2; + + /* + * Determine Q3 by checking if the number of elements in the + * upper half (mid..n-1] is odd or even. If even, we must + * take the average of the two middle values. + */ + if (mid & 1) { /* Odd */ + q3 = vPtr->valueArr[map[q]]; + } else { /* Even */ + q3 = (vPtr->valueArr[map[q]] + + vPtr->valueArr[map[q + 1]]) * 0.5; + } + } + free(map); + return q3; +} + +static int Norm(Blt_Vector *vector) +{ + Vector *vPtr = (Vector *)vector; + double norm, range, min, max; + int i; + + min = Vec_Min(vPtr); + max = Vec_Max(vPtr); + range = max - min; + for (i = 0; i < vPtr->length; i++) { + norm = (vPtr->valueArr[i] - min) / range; + vPtr->valueArr[i] = norm; + } + return TCL_OK; +} + +static double Nonzeros(Blt_Vector *vector) +{ + Vector *vPtr = (Vector *)vector; + int count; + double *vp, *vend; + + count = 0; + for(vp = vPtr->valueArr + vPtr->first, vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) { + if (*vp == 0.0) + count++; + } + return (double) count; +} + +static double Fabs(double value) +{ + if (value < 0.0) + return -value; + return value; +} + +static double Round(double value) +{ + if (value < 0.0) + return ceil(value - 0.5); + else + return floor(value + 0.5); +} + +static double Fmod(double x, double y) +{ + if (y == 0.0) + return 0.0; + return x - (floor(x / y) * y); +} + +/* + *--------------------------------------------------------------------------- + * + * MathError -- + * + * This procedure is called when an error occurs during a + * floating-point operation. It reads errno and sets + * interp->result accordingly. + * + * Results: + * Interp->result is set to hold an error message. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ +static void MathError(Tcl_Interp* interp, double value) +{ + if ((errno == EDOM) || (value != value)) { + Tcl_AppendResult(interp, "domain error: argument not in valid range", + (char *)NULL); + Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", + Tcl_GetStringResult(interp), (char *)NULL); + } + else if ((errno == ERANGE) || isinf(value)) { + if (value == 0.0) { + Tcl_AppendResult(interp, + "floating-point value too small to represent", + (char *)NULL); + Tcl_SetErrorCode(interp, "ARITH", "UNDERFLOW", + Tcl_GetStringResult(interp), (char *)NULL); + } + else { + Tcl_AppendResult(interp, + "floating-point value too large to represent", + (char *)NULL); + Tcl_SetErrorCode(interp, "ARITH", "OVERFLOW", + Tcl_GetStringResult(interp), (char *)NULL); + } + } + else { + Tcl_AppendResult(interp, "unknown floating-point error, ", + "errno = ", Itoa(errno), (char *)NULL); + Tcl_SetErrorCode(interp, "ARITH", "UNKNOWN", + Tcl_GetStringResult(interp), (char *)NULL); + } +} + +static int ParseString(Tcl_Interp* interp, const char *string, Value *valuePtr) +{ + const char *endPtr; + double value; + + errno = 0; + + /* + * The string can be either a number or a vector. First try to + * convert the string to a number. If that fails then see if + * we can find a vector by that name. + */ + + value = strtod(string, (char **)&endPtr); + if ((endPtr != string) && (*endPtr == '\0')) { + if (errno != 0) { + Tcl_ResetResult(interp); + MathError(interp, value); + return TCL_ERROR; + } + /* Numbers are stored as single element vectors. */ + if (Vec_ChangeLength(interp, valuePtr->vPtr, 1) != TCL_OK) { + return TCL_ERROR; + } + valuePtr->vPtr->valueArr[0] = value; + return TCL_OK; + } else { + Vector *vPtr; + + while (isspace((unsigned char)(*string))) { + string++; /* Skip spaces leading the vector name. */ + } + vPtr = Vec_ParseElement(interp, valuePtr->vPtr->dataPtr, + string, &endPtr, NS_SEARCH_BOTH); + if (vPtr == NULL) { + return TCL_ERROR; + } + if (*endPtr != '\0') { + Tcl_AppendResult(interp, "extra characters after vector", + (char *)NULL); + return TCL_ERROR; + } + /* Copy the designated vector to our temporary. */ + Vec_Duplicate(valuePtr->vPtr, vPtr); + } + return TCL_OK; +} + +static int ParseMathFunction(Tcl_Interp* interp, const char *start, + ParseInfo *piPtr, Value *valuePtr) +{ + Tcl_HashEntry *hPtr; + MathFunction *mathPtr; /* Info about math function. */ + char *p; + VectorInterpData *dataPtr; /* Interpreter-specific data. */ + GenericMathProc *proc; + + /* + * Find the end of the math function's name and lookup the + * record for the function. + */ + p = (char *)start; + while (isspace((unsigned char)(*p))) { + p++; + } + piPtr->nextPtr = p; + while (isalnum((unsigned char)(*p)) || (*p == '_')) { + p++; + } + if (*p != '(') { + return TCL_RETURN; /* Must start with open parenthesis */ + } + dataPtr = valuePtr->vPtr->dataPtr; + *p = '\0'; + hPtr = Tcl_FindHashEntry(&dataPtr->mathProcTable, piPtr->nextPtr); + *p = '('; + if (hPtr == NULL) { + return TCL_RETURN; /* Name doesn't match any known function */ + } + /* Pick up the single value as the argument to the function */ + piPtr->token = OPEN_PAREN; + piPtr->nextPtr = p + 1; + valuePtr->pv.next = valuePtr->pv.buffer; + if (NextValue(interp, piPtr, -1, valuePtr) != TCL_OK) { + return TCL_ERROR; /* Parse error */ + } + if (piPtr->token != CLOSE_PAREN) { + Tcl_AppendResult(interp, "unmatched parentheses in expression \"", + piPtr->expr, "\"", (char *)NULL); + return TCL_ERROR; /* Missing right parenthesis */ + } + mathPtr = (MathFunction*)Tcl_GetHashValue(hPtr); + proc = (GenericMathProc*)mathPtr->proc; + if ((*proc) (mathPtr->clientData, interp, valuePtr->vPtr) != TCL_OK) { + return TCL_ERROR; /* Function invocation error */ + } + piPtr->token = VALUE; + return TCL_OK; +} + +static int NextToken(Tcl_Interp* interp, ParseInfo *piPtr, Value *valuePtr) +{ + const char *p; + const char *endPtr; + const char *var; + int result; + + p = piPtr->nextPtr; + while (isspace((unsigned char)(*p))) { + p++; + } + if (*p == '\0') { + piPtr->token = END; + piPtr->nextPtr = p; + return TCL_OK; + } + /* + * Try to parse the token as a floating-point number. But check + * that the first character isn't a "-" or "+", which "strtod" + * will happily accept as an unary operator. Otherwise, we might + * accidently treat a binary operator as unary by mistake, which + * will eventually cause a syntax error. + */ + if ((*p != '-') && (*p != '+')) { + double value; + + errno = 0; + value = strtod(p, (char **)&endPtr); + if (endPtr != p) { + if (errno != 0) { + MathError(interp, value); + return TCL_ERROR; + } + piPtr->token = VALUE; + piPtr->nextPtr = endPtr; + + /* + * Save the single floating-point value as an 1-component vector. + */ + if (Vec_ChangeLength(interp, valuePtr->vPtr, 1) != TCL_OK) { + return TCL_ERROR; + } + valuePtr->vPtr->valueArr[0] = value; + return TCL_OK; + } + } + piPtr->nextPtr = p + 1; + switch (*p) { + case '$': + piPtr->token = VALUE; + var = Tcl_ParseVar(interp, p, &endPtr); + if (var == NULL) { + return TCL_ERROR; + } + piPtr->nextPtr = endPtr; + Tcl_ResetResult(interp); + result = ParseString(interp, var, valuePtr); + return result; + + case '[': + piPtr->token = VALUE; + result = ParseNestedCmd(interp, p + 1, 0, &endPtr, &valuePtr->pv); + if (result != TCL_OK) { + return result; + } + piPtr->nextPtr = endPtr; + Tcl_ResetResult(interp); + result = ParseString(interp, valuePtr->pv.buffer, valuePtr); + return result; + + case '"': + piPtr->token = VALUE; + result = ParseQuotes(interp, p + 1, '"', 0, &endPtr, &valuePtr->pv); + if (result != TCL_OK) { + return result; + } + piPtr->nextPtr = endPtr; + Tcl_ResetResult(interp); + result = ParseString(interp, valuePtr->pv.buffer, valuePtr); + return result; + + case '{': + piPtr->token = VALUE; + result = ParseBraces(interp, p + 1, &endPtr, &valuePtr->pv); + if (result != TCL_OK) { + return result; + } + piPtr->nextPtr = endPtr; + Tcl_ResetResult(interp); + result = ParseString(interp, valuePtr->pv.buffer, valuePtr); + return result; + + case '(': + piPtr->token = OPEN_PAREN; + break; + + case ')': + piPtr->token = CLOSE_PAREN; + break; + + case ',': + piPtr->token = COMMA; + break; + + case '*': + piPtr->token = MULT; + break; + + case '/': + piPtr->token = DIVIDE; + break; + + case '%': + piPtr->token = MOD; + break; + + case '+': + piPtr->token = PLUS; + break; + + case '-': + piPtr->token = MINUS; + break; + + case '^': + piPtr->token = EXPONENT; + break; + + case '<': + switch (*(p + 1)) { + case '<': + piPtr->nextPtr = p + 2; + piPtr->token = LEFT_SHIFT; + break; + case '=': + piPtr->nextPtr = p + 2; + piPtr->token = LEQ; + break; + default: + piPtr->token = LESS; + break; + } + break; + + case '>': + switch (*(p + 1)) { + case '>': + piPtr->nextPtr = p + 2; + piPtr->token = RIGHT_SHIFT; + break; + case '=': + piPtr->nextPtr = p + 2; + piPtr->token = GEQ; + break; + default: + piPtr->token = GREATER; + break; + } + break; + + case '=': + if (*(p + 1) == '=') { + piPtr->nextPtr = p + 2; + piPtr->token = EQUAL; + } else { + piPtr->token = UNKNOWN; + } + break; + + case '&': + if (*(p + 1) == '&') { + piPtr->nextPtr = p + 2; + piPtr->token = AND; + } else { + piPtr->token = UNKNOWN; + } + break; + + case '|': + if (*(p + 1) == '|') { + piPtr->nextPtr = p + 2; + piPtr->token = OR; + } else { + piPtr->token = UNKNOWN; + } + break; + + case '!': + if (*(p + 1) == '=') { + piPtr->nextPtr = p + 2; + piPtr->token = NEQ; + } else { + piPtr->token = NOT; + } + break; + + default: + piPtr->token = VALUE; + result = ParseMathFunction(interp, p, piPtr, valuePtr); + if ((result == TCL_OK) || (result == TCL_ERROR)) { + return result; + } else { + Vector *vPtr; + + while (isspace((unsigned char)(*p))) { + p++; /* Skip spaces leading the vector name. */ + } + vPtr = Vec_ParseElement(interp, valuePtr->vPtr->dataPtr, + p, &endPtr, NS_SEARCH_BOTH); + if (vPtr == NULL) { + return TCL_ERROR; + } + Vec_Duplicate(valuePtr->vPtr, vPtr); + piPtr->nextPtr = endPtr; + } + } + return TCL_OK; +} + +static int NextValue(Tcl_Interp* interp, ParseInfo *piPtr, + int prec, Value *valuePtr) +{ + Value value2; /* Second operand for current operator. */ + int oper; /* Current operator (either unary or binary). */ + int gotOp; /* Non-zero means already lexed the operator + * (while picking up value for unary operator). + * Don't lex again. */ + int result; + Vector *vPtr, *v2Ptr; + int i; + + /* + * There are two phases to this procedure. First, pick off an initial + * value. Then, parse (binary operator, value) pairs until done. + */ + + vPtr = valuePtr->vPtr; + v2Ptr = Vec_New(vPtr->dataPtr); + gotOp = 0; + value2.vPtr = v2Ptr; + value2.pv.buffer = value2.pv.next = value2.staticSpace; + value2.pv.end = value2.pv.buffer + STATIC_STRING_SPACE - 1; + value2.pv.expandProc = ExpandParseValue; + value2.pv.clientData = NULL; + + result = NextToken(interp, piPtr, valuePtr); + if (result != TCL_OK) { + goto done; + } + if (piPtr->token == OPEN_PAREN) { + + /* Parenthesized sub-expression. */ + + result = NextValue(interp, piPtr, -1, valuePtr); + if (result != TCL_OK) { + goto done; + } + if (piPtr->token != CLOSE_PAREN) { + Tcl_AppendResult(interp, "unmatched parentheses in expression \"", + piPtr->expr, "\"", (char *)NULL); + result = TCL_ERROR; + goto done; + } + } else { + if (piPtr->token == MINUS) { + piPtr->token = UNARY_MINUS; + } + if (piPtr->token >= UNARY_MINUS) { + oper = piPtr->token; + result = NextValue(interp, piPtr, precTable[oper], valuePtr); + if (result != TCL_OK) { + goto done; + } + gotOp = 1; + /* Process unary operators. */ + switch (oper) { + case UNARY_MINUS: + for(i = 0; i < vPtr->length; i++) { + vPtr->valueArr[i] = -(vPtr->valueArr[i]); + } + break; + + case NOT: + for(i = 0; i < vPtr->length; i++) { + vPtr->valueArr[i] = (double)(!vPtr->valueArr[i]); + } + break; + default: + Tcl_AppendResult(interp, "unknown operator", (char *)NULL); + goto error; + } + } else if (piPtr->token != VALUE) { + Tcl_AppendResult(interp, "missing operand", (char *)NULL); + goto error; + } + } + if (!gotOp) { + result = NextToken(interp, piPtr, &value2); + if (result != TCL_OK) { + goto done; + } + } + /* + * Got the first operand. Now fetch (operator, operand) pairs. + */ + for (;;) { + oper = piPtr->token; + + value2.pv.next = value2.pv.buffer; + if ((oper < MULT) || (oper >= UNARY_MINUS)) { + if ((oper == END) || (oper == CLOSE_PAREN) || + (oper == COMMA)) { + result = TCL_OK; + goto done; + } else { + Tcl_AppendResult(interp, "bad operator", (char *)NULL); + goto error; + } + } + if (precTable[oper] <= prec) { + result = TCL_OK; + goto done; + } + result = NextValue(interp, piPtr, precTable[oper], &value2); + if (result != TCL_OK) { + goto done; + } + if ((piPtr->token < MULT) && (piPtr->token != VALUE) && + (piPtr->token != END) && (piPtr->token != CLOSE_PAREN) && + (piPtr->token != COMMA)) { + Tcl_AppendResult(interp, "unexpected token in expression", + (char *)NULL); + goto error; + } + /* + * At this point we have two vectors and an operator. + */ + + if (v2Ptr->length == 1) { + double *opnd; + double scalar; + + /* + * 2nd operand is a scalar. + */ + scalar = v2Ptr->valueArr[0]; + opnd = vPtr->valueArr; + switch (oper) { + case MULT: + for(i = 0; i < vPtr->length; i++) { + opnd[i] *= scalar; + } + break; + + case DIVIDE: + if (scalar == 0.0) { + Tcl_AppendResult(interp, "divide by zero", (char *)NULL); + goto error; + } + for(i = 0; i < vPtr->length; i++) { + opnd[i] /= scalar; + } + break; + + case PLUS: + for(i = 0; i < vPtr->length; i++) { + opnd[i] += scalar; + } + break; + + case MINUS: + for(i = 0; i < vPtr->length; i++) { + opnd[i] -= scalar; + } + break; + + case EXPONENT: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = pow(opnd[i], scalar); + } + break; + + case MOD: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = Fmod(opnd[i], scalar); + } + break; + + case LESS: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(opnd[i] < scalar); + } + break; + + case GREATER: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(opnd[i] > scalar); + } + break; + + case LEQ: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(opnd[i] <= scalar); + } + break; + + case GEQ: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(opnd[i] >= scalar); + } + break; + + case EQUAL: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(opnd[i] == scalar); + } + break; + + case NEQ: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(opnd[i] != scalar); + } + break; + + case AND: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(opnd[i] && scalar); + } + break; + + case OR: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(opnd[i] || scalar); + } + break; + + case LEFT_SHIFT: + { + int offset; + + offset = (int)scalar % vPtr->length; + if (offset > 0) { + double *hold; + int j; + + hold = (double*)malloc(sizeof(double) * offset); + for (i = 0; i < offset; i++) { + hold[i] = opnd[i]; + } + for (i = offset, j = 0; i < vPtr->length; i++, j++) { + opnd[j] = opnd[i]; + } + for (i = 0, j = vPtr->length - offset; + j < vPtr->length; i++, j++) { + opnd[j] = hold[i]; + } + free(hold); + } + } + break; + + case RIGHT_SHIFT: + { + int offset; + + offset = (int)scalar % vPtr->length; + if (offset > 0) { + double *hold; + int j; + + hold = (double*)malloc(sizeof(double) * offset); + for (i = vPtr->length - offset, j = 0; + i < vPtr->length; i++, j++) { + hold[j] = opnd[i]; + } + for (i = vPtr->length - offset - 1, + j = vPtr->length - 1; i >= 0; i--, j--) { + opnd[j] = opnd[i]; + } + for (i = 0; i < offset; i++) { + opnd[i] = hold[i]; + } + free(hold); + } + } + break; + + default: + Tcl_AppendResult(interp, "unknown operator in expression", + (char *)NULL); + goto error; + } + + } else if (vPtr->length == 1) { + double *opnd; + double scalar; + + /* + * 1st operand is a scalar. + */ + scalar = vPtr->valueArr[0]; + Vec_Duplicate(vPtr, v2Ptr); + opnd = vPtr->valueArr; + switch (oper) { + case MULT: + for(i = 0; i < vPtr->length; i++) { + opnd[i] *= scalar; + } + break; + + case PLUS: + for(i = 0; i < vPtr->length; i++) { + opnd[i] += scalar; + } + break; + + case DIVIDE: + for(i = 0; i < vPtr->length; i++) { + if (opnd[i] == 0.0) { + Tcl_AppendResult(interp, "divide by zero", + (char *)NULL); + goto error; + } + opnd[i] = (scalar / opnd[i]); + } + break; + + case MINUS: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = scalar - opnd[i]; + } + break; + + case EXPONENT: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = pow(scalar, opnd[i]); + } + break; + + case MOD: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = Fmod(scalar, opnd[i]); + } + break; + + case LESS: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(scalar < opnd[i]); + } + break; + + case GREATER: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(scalar > opnd[i]); + } + break; + + case LEQ: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(scalar >= opnd[i]); + } + break; + + case GEQ: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(scalar <= opnd[i]); + } + break; + + case EQUAL: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(opnd[i] == scalar); + } + break; + + case NEQ: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(opnd[i] != scalar); + } + break; + + case AND: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(opnd[i] && scalar); + } + break; + + case OR: + for(i = 0; i < vPtr->length; i++) { + opnd[i] = (double)(opnd[i] || scalar); + } + break; + + case LEFT_SHIFT: + case RIGHT_SHIFT: + Tcl_AppendResult(interp, "second shift operand must be scalar", + (char *)NULL); + goto error; + + default: + Tcl_AppendResult(interp, "unknown operator in expression", + (char *)NULL); + goto error; + } + } else { + double *opnd1, *opnd2; + /* + * Carry out the function of the specified operator. + */ + if (vPtr->length != v2Ptr->length) { + Tcl_AppendResult(interp, "vectors are different lengths", + (char *)NULL); + goto error; + } + opnd1 = vPtr->valueArr, opnd2 = v2Ptr->valueArr; + switch (oper) { + case MULT: + for (i = 0; i < vPtr->length; i++) { + opnd1[i] *= opnd2[i]; + } + break; + + case DIVIDE: + for (i = 0; i < vPtr->length; i++) { + if (opnd2[i] == 0.0) { + Tcl_AppendResult(interp, + "can't divide by 0.0 vector component", + (char *)NULL); + goto error; + } + opnd1[i] /= opnd2[i]; + } + break; + + case PLUS: + for (i = 0; i < vPtr->length; i++) { + opnd1[i] += opnd2[i]; + } + break; + + case MINUS: + for (i = 0; i < vPtr->length; i++) { + opnd1[i] -= opnd2[i]; + } + break; + + case MOD: + for (i = 0; i < vPtr->length; i++) { + opnd1[i] = Fmod(opnd1[i], opnd2[i]); + } + break; + + case EXPONENT: + for (i = 0; i < vPtr->length; i++) { + opnd1[i] = pow(opnd1[i], opnd2[i]); + } + break; + + case LESS: + for (i = 0; i < vPtr->length; i++) { + opnd1[i] = (double)(opnd1[i] < opnd2[i]); + } + break; + + case GREATER: + for (i = 0; i < vPtr->length; i++) { + opnd1[i] = (double)(opnd1[i] > opnd2[i]); + } + break; + + case LEQ: + for (i = 0; i < vPtr->length; i++) { + opnd1[i] = (double)(opnd1[i] <= opnd2[i]); + } + break; + + case GEQ: + for (i = 0; i < vPtr->length; i++) { + opnd1[i] = (double)(opnd1[i] >= opnd2[i]); + } + break; + + case EQUAL: + for (i = 0; i < vPtr->length; i++) { + opnd1[i] = (double)(opnd1[i] == opnd2[i]); + } + break; + + case NEQ: + for (i = 0; i < vPtr->length; i++) { + opnd1[i] = (double)(opnd1[i] != opnd2[i]); + } + break; + + case AND: + for (i = 0; i < vPtr->length; i++) { + opnd1[i] = (double)(opnd1[i] && opnd2[i]); + } + break; + + case OR: + for (i = 0; i < vPtr->length; i++) { + opnd1[i] = (double)(opnd1[i] || opnd2[i]); + } + break; + + case LEFT_SHIFT: + case RIGHT_SHIFT: + Tcl_AppendResult(interp, "second shift operand must be scalar", + (char *)NULL); + goto error; + + default: + Tcl_AppendResult(interp, "unknown operator in expression", + (char *)NULL); + goto error; + } + } + } + done: + if (value2.pv.buffer != value2.staticSpace) { + free(value2.pv.buffer); + } + Vec_Free(v2Ptr); + return result; + + error: + if (value2.pv.buffer != value2.staticSpace) { + free(value2.pv.buffer); + } + Vec_Free(v2Ptr); + return TCL_ERROR; +} + +static int EvaluateExpression(Tcl_Interp* interp, char *string, + Value *valuePtr) +{ + ParseInfo info; + int result; + Vector *vPtr; + double *vp, *vend; + + info.expr = info.nextPtr = string; + valuePtr->pv.buffer = valuePtr->pv.next = valuePtr->staticSpace; + valuePtr->pv.end = valuePtr->pv.buffer + STATIC_STRING_SPACE - 1; + valuePtr->pv.expandProc = ExpandParseValue; + valuePtr->pv.clientData = NULL; + + result = NextValue(interp, &info, -1, valuePtr); + if (result != TCL_OK) { + return result; + } + if (info.token != END) { + Tcl_AppendResult(interp, ": syntax error in expression \"", + string, "\"", (char *)NULL); + return TCL_ERROR; + } + vPtr = valuePtr->vPtr; + + /* Check for NaN's and overflows. */ + for (vp = vPtr->valueArr, vend = vp + vPtr->length; vp < vend; vp++) { + if (!isfinite(*vp)) { + /* + * IEEE floating-point error. + */ + MathError(interp, *vp); + return TCL_ERROR; + } + } + return TCL_OK; +} + +static int ComponentFunc(ClientData clientData, Tcl_Interp* interp, + Vector *vPtr) +{ + ComponentProc *procPtr = (ComponentProc *) clientData; + double *vp, *vend; + + errno = 0; + for(vp = vPtr->valueArr + vPtr->first, + vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) { + *vp = (*procPtr) (*vp); + if (errno != 0) { + MathError(interp, *vp); + return TCL_ERROR; + } + if (!isfinite(*vp)) { + /* + * IEEE floating-point error. + */ + MathError(interp, *vp); + return TCL_ERROR; + } + } + return TCL_OK; +} + +static int ScalarFunc(ClientData clientData, Tcl_Interp* interp, Vector *vPtr) +{ + double value; + ScalarProc *procPtr = (ScalarProc *) clientData; + + errno = 0; + value = (*procPtr) (vPtr); + if (errno != 0) { + MathError(interp, value); + return TCL_ERROR; + } + if (Vec_ChangeLength(interp, vPtr, 1) != TCL_OK) { + return TCL_ERROR; + } + vPtr->valueArr[0] = value; + return TCL_OK; +} + +static int VectorFunc(ClientData clientData, Tcl_Interp* interp, Vector *vPtr) +{ + VectorProc *procPtr = (VectorProc *) clientData; + + return (*procPtr) (vPtr); +} + + +static MathFunction mathFunctions[] = + { + {"abs", (void*)ComponentFunc, (ClientData)Fabs}, + {"acos", (void*)ComponentFunc, (ClientData)(double (*)(double))acos}, + {"asin", (void*)ComponentFunc, (ClientData)(double (*)(double))asin}, + {"atan", (void*)ComponentFunc, (ClientData)(double (*)(double))atan}, + {"adev", (void*)ScalarFunc, (ClientData)AvgDeviation}, + {"ceil", (void*)ComponentFunc, (ClientData)(double (*)(double))ceil}, + {"cos", (void*)ComponentFunc, (ClientData)(double (*)(double))cos}, + {"cosh", (void*)ComponentFunc, (ClientData)(double (*)(double))cosh}, + {"exp", (void*)ComponentFunc, (ClientData)(double (*)(double))exp}, + {"floor", (void*)ComponentFunc, (ClientData)(double (*)(double))floor}, + {"kurtosis",(void*)ScalarFunc, (ClientData)Kurtosis}, + {"length", (void*)ScalarFunc, (ClientData)Length}, + {"log", (void*)ComponentFunc, (ClientData)(double (*)(double))log}, + {"log10", (void*)ComponentFunc, (ClientData)(double (*)(double))log10}, + {"max", (void*)ScalarFunc, (ClientData)Blt_VecMax}, + {"mean", (void*)ScalarFunc, (ClientData)Mean}, + {"median", (void*)ScalarFunc, (ClientData)Median}, + {"min", (void*)ScalarFunc, (ClientData)Blt_VecMin}, + {"norm", (void*)VectorFunc, (ClientData)Norm}, + {"nz", (void*)ScalarFunc, (ClientData)Nonzeros}, + {"q1", (void*)ScalarFunc, (ClientData)Q1}, + {"q3", (void*)ScalarFunc, (ClientData)Q3}, + {"prod", (void*)ScalarFunc, (ClientData)Product}, + {"random", (void*)ComponentFunc, (ClientData)drand48}, + {"round", (void*)ComponentFunc, (ClientData)Round}, + {"sdev", (void*)ScalarFunc, (ClientData)StdDeviation}, + {"sin", (void*)ComponentFunc, (ClientData)(double (*)(double))sin}, + {"sinh", (void*)ComponentFunc, (ClientData)(double (*)(double))sinh}, + {"skew", (void*)ScalarFunc, (ClientData)Skew}, + {"sort", (void*)VectorFunc, (ClientData)Sort}, + {"sqrt", (void*)ComponentFunc, (ClientData)(double (*)(double))sqrt}, + {"sum", (void*)ScalarFunc, (ClientData)Sum}, + {"tan", (void*)ComponentFunc, (ClientData)(double (*)(double))tan}, + {"tanh", (void*)ComponentFunc, (ClientData)(double (*)(double))tanh}, + {"var", (void*)ScalarFunc, (ClientData)Variance}, + {(char *)NULL,}, + }; + +void Blt::Vec_InstallMathFunctions(Tcl_HashTable *tablePtr) +{ + MathFunction *mathPtr; + + for (mathPtr = mathFunctions; mathPtr->name != NULL; mathPtr++) { + Tcl_HashEntry *hPtr; + int isNew; + + hPtr = Tcl_CreateHashEntry(tablePtr, mathPtr->name, &isNew); + Tcl_SetHashValue(hPtr, (ClientData)mathPtr); + } +} + +void Blt::Vec_UninstallMathFunctions(Tcl_HashTable *tablePtr) +{ + Tcl_HashEntry *hPtr; + Tcl_HashSearch cursor; + + for (hPtr = Tcl_FirstHashEntry(tablePtr, &cursor); hPtr != NULL; + hPtr = Tcl_NextHashEntry(&cursor)) { + MathFunction *mathPtr = (MathFunction*)Tcl_GetHashValue(hPtr); + if (mathPtr->name == NULL) + free(mathPtr); + } +} + +static void InstallIndexProc(Tcl_HashTable *tablePtr, const char *string, + Blt_VectorIndexProc *procPtr) +{ + Tcl_HashEntry *hPtr; + int dummy; + + hPtr = Tcl_CreateHashEntry(tablePtr, string, &dummy); + if (procPtr == NULL) + Tcl_DeleteHashEntry(hPtr); + else + Tcl_SetHashValue(hPtr, (ClientData)procPtr); +} + +void Blt::Vec_InstallSpecialIndices(Tcl_HashTable *tablePtr) +{ + InstallIndexProc(tablePtr, "min", Blt_VecMin); + InstallIndexProc(tablePtr, "max", Blt_VecMax); + InstallIndexProc(tablePtr, "mean", Mean); + InstallIndexProc(tablePtr, "sum", Sum); + InstallIndexProc(tablePtr, "prod", Product); +} + +int Blt::ExprVector(Tcl_Interp* interp, char *string, Blt_Vector *vector) +{ + VectorInterpData *dataPtr; /* Interpreter-specific data. */ + Vector *vPtr = (Vector *)vector; + Value value; + + dataPtr = (vector != NULL) ? vPtr->dataPtr : Vec_GetInterpData(interp); + value.vPtr = Vec_New(dataPtr); + if (EvaluateExpression(interp, string, &value) != TCL_OK) { + Vec_Free(value.vPtr); + return TCL_ERROR; + } + if (vPtr != NULL) { + Vec_Duplicate(vPtr, value.vPtr); + } else { + Tcl_Obj *listObjPtr; + double *vp, *vend; + + /* No result vector. Put values in interp->result. */ + listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + for (vp = value.vPtr->valueArr, vend = vp + value.vPtr->length; + vp < vend; vp++) { + Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(*vp)); + } + Tcl_SetObjResult(interp, listObjPtr); + } + Vec_Free(value.vPtr); + return TCL_OK; +} + +#ifdef _WIN32 +double drand48(void) +{ + return (double)rand() / (double)RAND_MAX; +} + +void srand48(long int seed) +{ + srand(seed); +} +#endif diff --git a/generic/tkbltVecOp.C b/generic/tkbltVecOp.C new file mode 100644 index 0000000..6c84723 --- /dev/null +++ b/generic/tkbltVecOp.C @@ -0,0 +1,56 @@ +/* + * 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 + +#include "tkbltVecInt.h" + +using namespace Blt; + +extern Tcl_ObjCmdProc VectorObjCmd; + +int Blt_VectorCmdInitProc(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::vector"; + Tcl_Command cmdToken = Tcl_FindCommand(interp, cmdPath, NULL, 0); + if (cmdToken) + return TCL_OK; + cmdToken = Tcl_CreateObjCommand(interp, cmdPath, VectorObjCmd, + Vec_GetInterpData(interp), NULL); + if (Tcl_Export(interp, nsPtr, "vector", 0) != TCL_OK) + return TCL_ERROR; + + return TCL_OK; +} diff --git a/generic/tkbltVector.C b/generic/tkbltVector.C new file mode 100644 index 0000000..0837917 --- /dev/null +++ b/generic/tkbltVector.C @@ -0,0 +1,1875 @@ +/* + * 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 1995-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. + */ + +/* + * TODO: + * o Add H. Kirsch's vector binary read operation + * x binread file0 + * x binread -file file0 + * + * o Add ASCII/binary file reader + * x read fileName + * + * o Allow Tcl-based client notifications. + * vector x + * x notify call Display + * x notify delete Display + * x notify reorder #1 #2 + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "tkbltInt.h" +#include "tkbltVecInt.h" +#include "tkbltNsUtil.h" +#include "tkbltSwitch.h" +#include "tkbltOp.h" + +using namespace Blt; + +#define DEF_ARRAY_SIZE 64 +#define TRACE_ALL (TCL_TRACE_WRITES | TCL_TRACE_READS | TCL_TRACE_UNSETS) + + +#define VECTOR_CHAR(c) ((isalnum((unsigned char)(c))) || \ + (c == '_') || (c == ':') || (c == '@') || (c == '.')) + +/* + * VectorClient -- + * + * A vector can be shared by several clients. Each client allocates this + * structure that acts as its key for using the vector. Clients can also + * designate a callback routine that is executed whenever the vector is + * updated or destroyed. + * + */ +typedef struct { + unsigned int magic; /* Magic value designating whether this really + * is a vector token or not */ + Vector* serverPtr; /* Pointer to the master record of the vector. + * If NULL, indicates that the vector has been + * destroyed but as of yet, this client hasn't + * recognized it. */ + Blt_VectorChangedProc *proc;/* Routine to call when the contents of the + * vector change or the vector is deleted. */ + ClientData clientData; /* Data passed whenever the vector change + * procedure is called. */ + ChainLink* link; /* Used to quickly remove this entry from its + * server's client chain. */ +} VectorClient; + +static Tcl_CmdDeleteProc VectorInstDeleteProc; +extern Tcl_ObjCmdProc VectorObjCmd; +static Tcl_InterpDeleteProc VectorInterpDeleteProc; + +typedef struct { + char *varName; /* Requested variable name. */ + char *cmdName; /* Requested command name. */ + int flush; /* Flush */ + int watchUnset; /* Watch when variable is unset. */ +} CreateSwitches; + +static Blt_SwitchSpec createSwitches[] = + { + {BLT_SWITCH_STRING, "-variable", "varName", + Tk_Offset(CreateSwitches, varName), BLT_SWITCH_NULL_OK}, + {BLT_SWITCH_STRING, "-command", "command", + Tk_Offset(CreateSwitches, cmdName), BLT_SWITCH_NULL_OK}, + {BLT_SWITCH_BOOLEAN, "-watchunset", "bool", + Tk_Offset(CreateSwitches, watchUnset), 0}, + {BLT_SWITCH_BOOLEAN, "-flush", "bool", + Tk_Offset(CreateSwitches, flush), 0}, + {BLT_SWITCH_END} + }; + +typedef int (VectorCmdProc)(Vector* vecObjPtr, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]); + +static char stringRep[200]; + +const char *Blt::Itoa(int value) +{ + snprintf(stringRep, 200, "%d", value); + return stringRep; +} + +static char* Blt_Strdup(const char *string) +{ + size_t size = strlen(string) + 1; + char* ptr = (char*)malloc(size * sizeof(char)); + if (ptr != NULL) + strcpy(ptr, string); + + return ptr; +} + +static Vector* FindVectorInNamespace(VectorInterpData *dataPtr, + Blt_ObjectName *objNamePtr) +{ + Tcl_DString dString; + const char* name = MakeQualifiedName(objNamePtr, &dString); + Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&dataPtr->vectorTable, name); + Tcl_DStringFree(&dString); + if (hPtr != NULL) + return (Vector*)Tcl_GetHashValue(hPtr); + + return NULL; +} + +static Vector* GetVectorObject(VectorInterpData *dataPtr, const char *name, + int flags) +{ + Tcl_Interp* interp = dataPtr->interp; + Blt_ObjectName objName; + if (!ParseObjectName(interp, name, &objName, BLT_NO_ERROR_MSG | BLT_NO_DEFAULT_NS)) + return NULL; + + Vector* vPtr = NULL; + if (objName.nsPtr != NULL) + vPtr = FindVectorInNamespace(dataPtr, &objName); + else { + if (flags & NS_SEARCH_CURRENT) { + objName.nsPtr = Tcl_GetCurrentNamespace(interp); + vPtr = FindVectorInNamespace(dataPtr, &objName); + } + if ((vPtr == NULL) && (flags & NS_SEARCH_GLOBAL)) { + objName.nsPtr = Tcl_GetGlobalNamespace(interp); + vPtr = FindVectorInNamespace(dataPtr, &objName); + } + } + + return vPtr; +} + +void Blt::Vec_UpdateRange(Vector* vPtr) +{ + double* vp = vPtr->valueArr + vPtr->first; + double* vend = vPtr->valueArr + vPtr->last; + double min = *vp; + double max = *vp++; + for (/* empty */; vp <= vend; vp++) { + if (min > *vp) + min = *vp; + else if (max < *vp) + max = *vp; + } + vPtr->min = min; + vPtr->max = max; + vPtr->notifyFlags &= ~UPDATE_RANGE; +} + +int Blt::Vec_GetIndex(Tcl_Interp* interp, Vector* vPtr, const char *string, + int *indexPtr, int flags, Blt_VectorIndexProc **procPtrPtr) +{ + int value; + char c = string[0]; + + // Treat the index "end" like a numeric index + if ((c == 'e') && (strcmp(string, "end") == 0)) { + if (vPtr->length < 1) { + if (interp != NULL) { + Tcl_AppendResult(interp, "bad index \"end\": vector is empty", + (char *)NULL); + } + return TCL_ERROR; + } + *indexPtr = vPtr->length - 1; + return TCL_OK; + } else if ((c == '+') && (strcmp(string, "++end") == 0)) { + *indexPtr = vPtr->length; + return TCL_OK; + } + if (procPtrPtr != NULL) { + Tcl_HashEntry *hPtr; + + hPtr = Tcl_FindHashEntry(&vPtr->dataPtr->indexProcTable, string); + if (hPtr != NULL) { + *indexPtr = SPECIAL_INDEX; + *procPtrPtr = (Blt_VectorIndexProc*)Tcl_GetHashValue(hPtr); + return TCL_OK; + } + } + if (Tcl_GetInt(interp, (char *)string, &value) != TCL_OK) { + long int lvalue; + /* + * Unlike Tcl_GetInt, Tcl_ExprLong needs a valid interpreter, but the + * interp passed in may be NULL. So we have to use vPtr->interp and + * then reset the result. + */ + if (Tcl_ExprLong(vPtr->interp, (char *)string, &lvalue) != TCL_OK) { + Tcl_ResetResult(vPtr->interp); + if (interp != NULL) { + Tcl_AppendResult(interp, "bad index \"", string, "\"", + (char *)NULL); + } + return TCL_ERROR; + } + value = (int)lvalue; + } + /* + * Correct the index by the current value of the offset. This makes all + * the numeric indices non-negative, which is how we distinguish the + * special non-numeric indices. + */ + value -= vPtr->offset; + + if ((value < 0) || ((flags & INDEX_CHECK) && (value >= vPtr->length))) { + if (interp != NULL) { + Tcl_AppendResult(interp, "index \"", string, "\" is out of range", + (char *)NULL); + } + return TCL_ERROR; + } + *indexPtr = (int)value; + return TCL_OK; +} + +int Blt::Vec_GetIndexRange(Tcl_Interp* interp, Vector* vPtr, const char *string, + int flags, Blt_VectorIndexProc** procPtrPtr) +{ + int ielem; + char* colon = NULL; + if (flags & INDEX_COLON) + colon = (char*)strchr(string, ':'); + + if (colon != NULL) { + if (string == colon) { + vPtr->first = 0; /* Default to the first index */ + } + else { + int result; + + *colon = '\0'; + result = Vec_GetIndex(interp, vPtr, string, &ielem, flags, + (Blt_VectorIndexProc **) NULL); + *colon = ':'; + if (result != TCL_OK) { + return TCL_ERROR; + } + vPtr->first = ielem; + } + if (*(colon + 1) == '\0') { + /* Default to the last index */ + vPtr->last = (vPtr->length > 0) ? vPtr->length - 1 : 0; + } else { + if (Vec_GetIndex(interp, vPtr, colon + 1, &ielem, flags, + (Blt_VectorIndexProc **) NULL) != TCL_OK) { + return TCL_ERROR; + } + vPtr->last = ielem; + } + if (vPtr->first > vPtr->last) { + if (interp != NULL) { + Tcl_AppendResult(interp, "bad range \"", string, + "\" (first > last)", (char *)NULL); + } + return TCL_ERROR; + } + } else { + if (Vec_GetIndex(interp, vPtr, string, &ielem, flags, + procPtrPtr) != TCL_OK) { + return TCL_ERROR; + } + vPtr->last = vPtr->first = ielem; + } + return TCL_OK; +} + +Vector* Blt::Vec_ParseElement(Tcl_Interp* interp, VectorInterpData *dataPtr, + const char* start, const char** endPtr, int flags) +{ + char* p = (char*)start; + // Find the end of the vector name + while (VECTOR_CHAR(*p)) { + p++; + } + char saved = *p; + *p = '\0'; + + Vector* vPtr = GetVectorObject(dataPtr, start, flags); + if (vPtr == NULL) { + if (interp != NULL) { + Tcl_AppendResult(interp, "can't find vector \"", start, "\"", + (char *)NULL); + } + *p = saved; + return NULL; + } + *p = saved; + vPtr->first = 0; + vPtr->last = vPtr->length - 1; + if (*p == '(') { + int count, result; + + start = p + 1; + p++; + + /* Find the matching right parenthesis */ + count = 1; + while (*p != '\0') { + if (*p == ')') { + count--; + if (count == 0) { + break; + } + } else if (*p == '(') { + count++; + } + p++; + } + if (count > 0) { + if (interp != NULL) { + Tcl_AppendResult(interp, "unbalanced parentheses \"", start, + "\"", (char *)NULL); + } + return NULL; + } + *p = '\0'; + result = Vec_GetIndexRange(interp, vPtr, start, (INDEX_COLON | INDEX_CHECK), (Blt_VectorIndexProc **) NULL); + *p = ')'; + if (result != TCL_OK) { + return NULL; + } + p++; + } + if (endPtr != NULL) { + *endPtr = p; + } + return vPtr; +} + +void Blt_Vec_NotifyClients(ClientData clientData) +{ + Vector* vPtr = (Vector*)clientData; + ChainLink *link, *next; + Blt_VectorNotify notify; + + notify = (vPtr->notifyFlags & NOTIFY_DESTROYED) + ? BLT_VECTOR_NOTIFY_DESTROY : BLT_VECTOR_NOTIFY_UPDATE; + vPtr->notifyFlags &= ~(NOTIFY_UPDATED | NOTIFY_DESTROYED | NOTIFY_PENDING); + for (link = Chain_FirstLink(vPtr->chain); link; link = next) { + next = Chain_NextLink(link); + VectorClient *clientPtr = (VectorClient*)Chain_GetValue(link); + if ((clientPtr->proc != NULL) && (clientPtr->serverPtr != NULL)) { + (*clientPtr->proc) (vPtr->interp, clientPtr->clientData, notify); + } + } + + // Some clients may not handle the "destroy" callback properly (they + // should call Blt_FreeVectorId to release the client identifier), so mark + // any remaining clients to indicate that vector's server has gone away. + if (notify == BLT_VECTOR_NOTIFY_DESTROY) { + for (link = Chain_FirstLink(vPtr->chain); link; + link = Chain_NextLink(link)) { + VectorClient *clientPtr = (VectorClient*)Chain_GetValue(link); + clientPtr->serverPtr = NULL; + } + } +} + +void Blt::Vec_UpdateClients(Vector* vPtr) +{ + vPtr->dirty++; + vPtr->max = vPtr->min = NAN; + if (vPtr->notifyFlags & NOTIFY_NEVER) { + return; + } + vPtr->notifyFlags |= NOTIFY_UPDATED; + if (vPtr->notifyFlags & NOTIFY_ALWAYS) { + Blt_Vec_NotifyClients(vPtr); + return; + } + if (!(vPtr->notifyFlags & NOTIFY_PENDING)) { + vPtr->notifyFlags |= NOTIFY_PENDING; + Tcl_DoWhenIdle(Blt_Vec_NotifyClients, vPtr); + } +} + +void Blt::Vec_FlushCache(Vector* vPtr) +{ + Tcl_Interp* interp = vPtr->interp; + + if (vPtr->arrayName == NULL) + return; + + /* Turn off the trace temporarily so that we can unset all the + * elements in the array. */ + + Tcl_UntraceVar2(interp, vPtr->arrayName, (char *)NULL, + TRACE_ALL | vPtr->varFlags, Vec_VarTrace, vPtr); + + /* Clear all the element entries from the entire array */ + Tcl_UnsetVar2(interp, vPtr->arrayName, (char *)NULL, vPtr->varFlags); + + /* Restore the "end" index by default and the trace on the entire array */ + Tcl_SetVar2(interp, vPtr->arrayName, "end", "", vPtr->varFlags); + Tcl_TraceVar2(interp, vPtr->arrayName, (char *)NULL, + TRACE_ALL | vPtr->varFlags, Vec_VarTrace, vPtr); +} + +int Blt::Vec_LookupName(VectorInterpData *dataPtr, const char *vecName, + Vector** vPtrPtr) +{ + + const char *endPtr; + Vector* vPtr = Vec_ParseElement(dataPtr->interp, dataPtr, vecName, &endPtr, NS_SEARCH_BOTH); + if (vPtr == NULL) + return TCL_ERROR; + + if (*endPtr != '\0') { + Tcl_AppendResult(dataPtr->interp, + "extra characters after vector name", (char *)NULL); + return TCL_ERROR; + } + + *vPtrPtr = vPtr; + return TCL_OK; +} + +double Blt::Vec_Min(Vector* vecObjPtr) +{ + double* vp = vecObjPtr->valueArr + vecObjPtr->first; + double* vend = vecObjPtr->valueArr + vecObjPtr->last; + double min = *vp++; + for (/* empty */; vp <= vend; vp++) { + if (min > *vp) + min = *vp; + } + vecObjPtr->min = min; + return vecObjPtr->min; +} + +double Blt::Vec_Max(Vector* vecObjPtr) +{ + double max = NAN; + double* vp = vecObjPtr->valueArr + vecObjPtr->first; + double* vend = vecObjPtr->valueArr + vecObjPtr->last; + max = *vp++; + for (/* empty */; vp <= vend; vp++) { + if (max < *vp) + max = *vp; + } + vecObjPtr->max = max; + return vecObjPtr->max; +} + +static void DeleteCommand(Vector* vPtr) +{ + Tcl_Interp* interp = vPtr->interp; + char *qualName; + Tcl_CmdInfo cmdInfo; + Tcl_DString dString; + Blt_ObjectName objName; + + Tcl_DStringInit(&dString); + objName.name = Tcl_GetCommandName(interp, vPtr->cmdToken); + objName.nsPtr = GetCommandNamespace(vPtr->cmdToken); + qualName = MakeQualifiedName(&objName, &dString); + if (Tcl_GetCommandInfo(interp, qualName, &cmdInfo)) { + // Disable the callback before deleting the TCL command + cmdInfo.deleteProc = NULL; + Tcl_SetCommandInfo(interp, qualName, &cmdInfo); + Tcl_DeleteCommandFromToken(interp, vPtr->cmdToken); + } + Tcl_DStringFree(&dString); + vPtr->cmdToken = 0; +} + +static void UnmapVariable(Vector* vPtr) +{ + Tcl_Interp* interp = vPtr->interp; + + // Unset the entire array + Tcl_UntraceVar2(interp, vPtr->arrayName, (char *)NULL, + (TRACE_ALL | vPtr->varFlags), Vec_VarTrace, vPtr); + Tcl_UnsetVar2(interp, vPtr->arrayName, (char *)NULL, vPtr->varFlags); + + if (vPtr->arrayName != NULL) { + free((void*)(vPtr->arrayName)); + vPtr->arrayName = NULL; + } +} + +int Blt::Vec_MapVariable(Tcl_Interp* interp, Vector* vPtr, const char *path) +{ + Blt_ObjectName objName; + char *newPath; + const char *result; + Tcl_DString dString; + + if (vPtr->arrayName != NULL) { + UnmapVariable(vPtr); + } + if ((path == NULL) || (path[0] == '\0')) { + return TCL_OK; /* If the variable pathname is the empty + * string, simply return after removing any + * existing variable. */ + } + /* Get the variable name (without the namespace qualifier). */ + if (!ParseObjectName(interp, path, &objName, BLT_NO_DEFAULT_NS)) { + return TCL_ERROR; + } + if (objName.nsPtr == NULL) { + /* + * If there was no namespace qualifier, try harder to see if the + * variable is non-local. + */ + objName.nsPtr = GetVariableNamespace(interp, objName.name); + } + Tcl_DStringInit(&dString); + vPtr->varFlags = 0; + if (objName.nsPtr != NULL) { /* Global or namespace variable. */ + newPath = MakeQualifiedName(&objName, &dString); + vPtr->varFlags |= (TCL_GLOBAL_ONLY); + } else { /* Local variable. */ + newPath = (char *)objName.name; + } + + /* + * To play it safe, delete the variable first. This has the benefical + * side-effect of unmapping the variable from another vector that may be + * currently associated with it. + */ + Tcl_UnsetVar2(interp, newPath, (char *)NULL, 0); + + /* + * Set the index "end" in the array. This will create the variable + * immediately so that we can check its namespace context. + */ + result = Tcl_SetVar2(interp, newPath, "end", "", TCL_LEAVE_ERR_MSG); + if (result == NULL) { + Tcl_DStringFree(&dString); + return TCL_ERROR; + } + /* Create a full-array trace on reads, writes, and unsets. */ + Tcl_TraceVar2(interp, newPath, (char *)NULL, TRACE_ALL, Vec_VarTrace, + vPtr); + vPtr->arrayName = Blt_Strdup(newPath); + Tcl_DStringFree(&dString); + return TCL_OK; +} + +int Blt::Vec_SetSize(Tcl_Interp* interp, Vector* vPtr, int newSize) +{ + if (newSize <= 0) { + newSize = DEF_ARRAY_SIZE; + } + if (newSize == vPtr->size) { + /* Same size, use the current array. */ + return TCL_OK; + } + if (vPtr->freeProc == TCL_DYNAMIC) { + /* Old memory was dynamically allocated, so use realloc. */ + double* newArr = (double*)realloc(vPtr->valueArr, newSize * sizeof(double)); + if (newArr == NULL) { + if (interp != NULL) { + Tcl_AppendResult(interp, "can't reallocate ", + Itoa(newSize), " elements for vector \"", + vPtr->name, "\"", (char *)NULL); + } + return TCL_ERROR; + } + vPtr->size = newSize; + vPtr->valueArr = newArr; + return TCL_OK; + } + + { + /* Old memory was created specially (static or special allocator). + * Replace with dynamically allocated memory (malloc-ed). */ + + double* newArr = (double*)calloc(newSize, sizeof(double)); + if (newArr == NULL) { + if (interp != NULL) { + Tcl_AppendResult(interp, "can't allocate ", + Itoa(newSize), " elements for vector \"", + vPtr->name, "\"", (char *)NULL); + } + return TCL_ERROR; + } + { + int used, wanted; + + /* Copy the contents of the old memory into the new. */ + used = vPtr->length; + wanted = newSize; + + if (used > wanted) { + used = wanted; + } + /* Copy any previous data */ + if (used > 0) { + memcpy(newArr, vPtr->valueArr, used * sizeof(double)); + } + } + + /* + * We're not using the old storage anymore, so free it if it's not + * TCL_STATIC. It's static because the user previously reset the + * vector with a statically allocated array (setting freeProc to + * TCL_STATIC). + */ + if (vPtr->freeProc != TCL_STATIC) { + if (vPtr->freeProc == TCL_DYNAMIC) { + free(vPtr->valueArr); + } else { + (*vPtr->freeProc) ((char *)vPtr->valueArr); + } + } + vPtr->freeProc = TCL_DYNAMIC; /* Set the type of the new storage */ + vPtr->valueArr = newArr; + vPtr->size = newSize; + } + return TCL_OK; +} + +int Blt::Vec_SetLength(Tcl_Interp* interp, Vector* vPtr, int newLength) +{ + if (vPtr->size < newLength) { + if (Vec_SetSize(interp, vPtr, newLength) != TCL_OK) { + return TCL_ERROR; + } + } + vPtr->length = newLength; + vPtr->first = 0; + vPtr->last = newLength - 1; + return TCL_OK; +} + +int Blt::Vec_ChangeLength(Tcl_Interp* interp, Vector* vPtr, int newLength) +{ + if (newLength < 0) { + newLength = 0; + } + if (newLength > vPtr->size) { + int newSize; /* Size of array in elements */ + + /* Compute the new size of the array. It's a multiple of + * DEF_ARRAY_SIZE. */ + newSize = DEF_ARRAY_SIZE; + while (newSize < newLength) { + newSize += newSize; + } + if (newSize != vPtr->size) { + if (Vec_SetSize(interp, vPtr, newSize) != TCL_OK) { + return TCL_ERROR; + } + } + } + vPtr->length = newLength; + vPtr->first = 0; + vPtr->last = newLength - 1; + return TCL_OK; + +} + +int Blt::Vec_Reset(Vector* vPtr, double *valueArr, int length, + int size, Tcl_FreeProc *freeProc) +{ + if (vPtr->valueArr != valueArr) { /* New array of values resides + * in different memory than + * the current vector. */ + if ((valueArr == NULL) || (size == 0)) { + /* Empty array. Set up default values */ + valueArr = (double*)malloc(sizeof(double) * DEF_ARRAY_SIZE); + size = DEF_ARRAY_SIZE; + if (valueArr == NULL) { + Tcl_AppendResult(vPtr->interp, "can't allocate ", + Itoa(size), " elements for vector \"", + vPtr->name, "\"", (char *)NULL); + return TCL_ERROR; + } + freeProc = TCL_DYNAMIC; + length = 0; + } + else if (freeProc == TCL_VOLATILE) { + /* Data is volatile. Make a copy of the value array. */ + double* newArr = (double*)malloc(size * sizeof(double)); + if (newArr == NULL) { + Tcl_AppendResult(vPtr->interp, "can't allocate ", + Itoa(size), " elements for vector \"", + vPtr->name, "\"", (char *)NULL); + return TCL_ERROR; + } + memcpy((char *)newArr, (char *)valueArr, + sizeof(double) * length); + valueArr = newArr; + freeProc = TCL_DYNAMIC; + } + + if (vPtr->freeProc != TCL_STATIC) { + /* Old data was dynamically allocated. Free it before attaching + * new data. */ + if (vPtr->freeProc == TCL_DYNAMIC) { + free(vPtr->valueArr); + } else { + (*freeProc) ((char *)vPtr->valueArr); + } + } + vPtr->freeProc = freeProc; + vPtr->valueArr = valueArr; + vPtr->size = size; + } + + vPtr->length = length; + if (vPtr->flush) { + Vec_FlushCache(vPtr); + } + Vec_UpdateClients(vPtr); + return TCL_OK; +} + +Vector* Blt::Vec_New(VectorInterpData *dataPtr) +{ + Vector* vPtr = (Vector*)calloc(1, sizeof(Vector)); + vPtr->valueArr = (double*)malloc(sizeof(double) * DEF_ARRAY_SIZE); + if (vPtr->valueArr == NULL) { + free(vPtr); + return NULL; + } + vPtr->size = DEF_ARRAY_SIZE; + vPtr->freeProc = TCL_DYNAMIC; + vPtr->length = 0; + vPtr->interp = dataPtr->interp; + vPtr->hashPtr = NULL; + vPtr->chain = new Chain(); + vPtr->flush = 0; + vPtr->min = vPtr->max = NAN; + vPtr->notifyFlags = NOTIFY_WHENIDLE; + vPtr->dataPtr = dataPtr; + return vPtr; +} + +void Blt::Vec_Free(Vector* vPtr) +{ + ChainLink* link; + + if (vPtr->cmdToken != 0) { + DeleteCommand(vPtr); + } + if (vPtr->arrayName != NULL) { + UnmapVariable(vPtr); + } + vPtr->length = 0; + + /* Immediately notify clients that vector is going away */ + if (vPtr->notifyFlags & NOTIFY_PENDING) { + vPtr->notifyFlags &= ~NOTIFY_PENDING; + Tcl_CancelIdleCall(Blt_Vec_NotifyClients, vPtr); + } + vPtr->notifyFlags |= NOTIFY_DESTROYED; + Blt_Vec_NotifyClients(vPtr); + + for (link = Chain_FirstLink(vPtr->chain); link; link = Chain_NextLink(link)) { + VectorClient *clientPtr = (VectorClient*)Chain_GetValue(link); + free(clientPtr); + } + delete vPtr->chain; + if ((vPtr->valueArr != NULL) && (vPtr->freeProc != TCL_STATIC)) { + if (vPtr->freeProc == TCL_DYNAMIC) { + free(vPtr->valueArr); + } else { + (*vPtr->freeProc) ((char *)vPtr->valueArr); + } + } + if (vPtr->hashPtr != NULL) { + Tcl_DeleteHashEntry(vPtr->hashPtr); + } +#ifdef NAMESPACE_DELETE_NOTIFY + if (vPtr->nsPtr != NULL) { + Blt_DestroyNsDeleteNotify(vPtr->interp, vPtr->nsPtr, vPtr); + } +#endif /* NAMESPACE_DELETE_NOTIFY */ + free(vPtr); +} + +static void VectorInstDeleteProc(ClientData clientData) +{ + Vector* vPtr = (Vector*)clientData; + vPtr->cmdToken = 0; + Vec_Free(vPtr); +} + +Vector* Blt::Vec_Create(VectorInterpData *dataPtr, const char *vecName, + const char *cmdName, const char *varName, int *isNewPtr) +{ + Tcl_DString dString; + Blt_ObjectName objName; + char *qualName; + Tcl_HashEntry *hPtr; + Tcl_Interp* interp = dataPtr->interp; + + int isNew = 0; + Vector* vPtr = NULL; + + if (!ParseObjectName(interp, vecName, &objName, 0)) + return NULL; + + Tcl_DStringInit(&dString); + if ((objName.name[0] == '#') && (strcmp(objName.name, "#auto") == 0)) { + + do { /* Generate a unique vector name. */ + char string[200]; + + snprintf(string, 200, "vector%d", dataPtr->nextId++); + objName.name = string; + qualName = MakeQualifiedName(&objName, &dString); + hPtr = Tcl_FindHashEntry(&dataPtr->vectorTable, qualName); + } while (hPtr != NULL); + } else { + const char *p; + + for (p = objName.name; *p != '\0'; p++) { + if (!VECTOR_CHAR(*p)) { + Tcl_AppendResult(interp, "bad vector name \"", objName.name, + "\": must contain digits, letters, underscore, or period", + (char *)NULL); + goto error; + } + } + qualName = MakeQualifiedName(&objName, &dString); + vPtr = Vec_ParseElement((Tcl_Interp *)NULL, dataPtr, qualName, + NULL, NS_SEARCH_CURRENT); + } + if (vPtr == NULL) { + hPtr = Tcl_CreateHashEntry(&dataPtr->vectorTable, qualName, &isNew); + vPtr = Vec_New(dataPtr); + vPtr->hashPtr = hPtr; + vPtr->nsPtr = objName.nsPtr; + + vPtr->name = (const char*)Tcl_GetHashKey(&dataPtr->vectorTable, hPtr); +#ifdef NAMESPACE_DELETE_NOTIFY + Blt_CreateNsDeleteNotify(interp, objName.nsPtr, vPtr, + VectorInstDeleteProc); +#endif /* NAMESPACE_DELETE_NOTIFY */ + Tcl_SetHashValue(hPtr, vPtr); + } + if (cmdName != NULL) { + Tcl_CmdInfo cmdInfo; + + if ((cmdName == vecName) || + ((cmdName[0] == '#') && (strcmp(cmdName, "#auto")==0))) { + cmdName = qualName; + } + if (Tcl_GetCommandInfo(interp, (char *)cmdName, &cmdInfo)) { + if (vPtr != cmdInfo.objClientData) { + Tcl_AppendResult(interp, "command \"", cmdName, + "\" already exists", (char *)NULL); + goto error; + } + /* We get here only if the old name is the same as the new. */ + goto checkVariable; + } + } + if (vPtr->cmdToken != 0) { + DeleteCommand(vPtr); /* Command already exists, delete old first */ + } + if (cmdName != NULL) { + Tcl_DString dString2; + + Tcl_DStringInit(&dString2); + if (cmdName != qualName) { + if (!ParseObjectName(interp, cmdName, &objName, 0)) { + goto error; + } + cmdName = MakeQualifiedName(&objName, &dString2); + } + vPtr->cmdToken = Tcl_CreateObjCommand(interp, (char *)cmdName, Vec_InstCmd, + vPtr, VectorInstDeleteProc); + Tcl_DStringFree(&dString2); + } + checkVariable: + if (varName != NULL) { + if ((varName[0] == '#') && (strcmp(varName, "#auto") == 0)) { + varName = qualName; + } + if (Vec_MapVariable(interp, vPtr, varName) != TCL_OK) { + goto error; + } + } + + Tcl_DStringFree(&dString); + *isNewPtr = isNew; + return vPtr; + + error: + Tcl_DStringFree(&dString); + if (vPtr != NULL) { + Vec_Free(vPtr); + } + return NULL; +} + +int Blt::Vec_Duplicate(Vector* destPtr, Vector* srcPtr) +{ + size_t nBytes; + size_t length; + + if (destPtr == srcPtr) { + /* Copying the same vector. */ + } + length = srcPtr->last - srcPtr->first + 1; + if (Vec_ChangeLength(destPtr->interp, destPtr, length) != TCL_OK) { + return TCL_ERROR; + } + nBytes = length * sizeof(double); + memcpy(destPtr->valueArr, srcPtr->valueArr + srcPtr->first, nBytes); + destPtr->offset = srcPtr->offset; + return TCL_OK; +} + + +static int VectorNamesOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + VectorInterpData* dataPtr = (VectorInterpData*)clientData; + Tcl_Obj *listObjPtr; + + listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + if (objc == 2) { + Tcl_HashEntry *hPtr; + Tcl_HashSearch cursor; + + for (hPtr = Tcl_FirstHashEntry(&dataPtr->vectorTable, &cursor); + hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { + char *name = (char*)Tcl_GetHashKey(&dataPtr->vectorTable, hPtr); + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewStringObj(name, -1)); + } + } else { + Tcl_HashEntry *hPtr; + Tcl_HashSearch cursor; + + for (hPtr = Tcl_FirstHashEntry(&dataPtr->vectorTable, &cursor); + hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { + char *name = (char*)Tcl_GetHashKey(&dataPtr->vectorTable, hPtr); + int i; + for (i = 2; i < objc; i++) { + char *pattern; + + pattern = Tcl_GetString(objv[i]); + if (Tcl_StringMatch(name, pattern)) { + Tcl_ListObjAppendElement(interp, listObjPtr, + Tcl_NewStringObj(name, -1)); + break; + } + } + } + } + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; +} + +static int VectorCreate2(ClientData clientData, Tcl_Interp* interp, + int argStart, int objc, Tcl_Obj* const objv[]) +{ + VectorInterpData *dataPtr = (VectorInterpData*)clientData; + Vector* vPtr; + int count, i; + CreateSwitches switches; + + // Handle switches to the vector command and collect the vector name + // arguments into an array. + count = 0; + vPtr = NULL; + for (i = argStart; i < objc; i++) { + char *string; + + string = Tcl_GetString(objv[i]); + if (string[0] == '-') { + break; + } + } + count = i - argStart; + if (count == 0) { + Tcl_AppendResult(interp, "no vector names supplied", (char *)NULL); + return TCL_ERROR; + } + memset(&switches, 0, sizeof(switches)); + if (ParseSwitches(interp, createSwitches, objc - i, objv + i, + &switches, BLT_SWITCH_DEFAULTS) < 0) { + return TCL_ERROR; + } + if (count > 1) { + if (switches.cmdName != NULL) { + Tcl_AppendResult(interp, + "can't specify more than one vector with \"-command\" switch", + (char *)NULL); + goto error; + } + if (switches.varName != NULL) { + Tcl_AppendResult(interp, + "can't specify more than one vector with \"-variable\" switch", + (char *)NULL); + goto error; + } + } + for (i = 0; i < count; i++) { + char *leftParen, *rightParen; + char *string; + int isNew; + int size, first, last; + + size = first = last = 0; + string = Tcl_GetString(objv[i + argStart]); + leftParen = strchr(string, '('); + rightParen = strchr(string, ')'); + if (((leftParen != NULL) && (rightParen == NULL)) || + ((leftParen == NULL) && (rightParen != NULL)) || + (leftParen > rightParen)) { + Tcl_AppendResult(interp, "bad vector specification \"", string, + "\"", (char *)NULL); + goto error; + } + if (leftParen != NULL) { + int result; + char *colon; + + *rightParen = '\0'; + colon = strchr(leftParen + 1, ':'); + if (colon != NULL) { + + /* Specification is in the form vecName(first:last) */ + *colon = '\0'; + result = Tcl_GetInt(interp, leftParen + 1, &first); + if ((*(colon + 1) != '\0') && (result == TCL_OK)) { + result = Tcl_GetInt(interp, colon + 1, &last); + if (first > last) { + Tcl_AppendResult(interp, "bad vector range \"", + string, "\"", (char *)NULL); + result = TCL_ERROR; + } + size = (last - first) + 1; + } + *colon = ':'; + } else { + /* Specification is in the form vecName(size) */ + result = Tcl_GetInt(interp, leftParen + 1, &size); + } + *rightParen = ')'; + if (result != TCL_OK) { + goto error; + } + if (size < 0) { + Tcl_AppendResult(interp, "bad vector size \"", string, "\"", + (char *)NULL); + goto error; + } + } + if (leftParen != NULL) { + *leftParen = '\0'; + } + /* + * By default, we create a TCL command by the name of the vector. + */ + vPtr = Vec_Create(dataPtr, string, + (switches.cmdName == NULL) ? string : switches.cmdName, + (switches.varName == NULL) ? string : switches.varName, &isNew); + if (leftParen != NULL) { + *leftParen = '('; + } + if (vPtr == NULL) { + goto error; + } + vPtr->freeOnUnset = switches.watchUnset; + vPtr->flush = switches.flush; + vPtr->offset = first; + if (size > 0) { + if (Vec_ChangeLength(interp, vPtr, size) != TCL_OK) { + goto error; + } + } + if (!isNew) { + if (vPtr->flush) { + Vec_FlushCache(vPtr); + } + Vec_UpdateClients(vPtr); + } + } + FreeSwitches(createSwitches, (char *)&switches, 0); + if (vPtr != NULL) { + /* Return the name of the last vector created */ + Tcl_SetStringObj(Tcl_GetObjResult(interp), vPtr->name, -1); + } + return TCL_OK; + error: + FreeSwitches(createSwitches, (char *)&switches, 0); + return TCL_ERROR; +} + +static int VectorCreateOp(ClientData clientData, Tcl_Interp* interp, + int objc, Tcl_Obj* const objv[]) +{ + return VectorCreate2(clientData, interp, 2, objc, objv); +} + +static int VectorDestroyOp(ClientData clientData, Tcl_Interp* interp, + int objc,Tcl_Obj* const objv[]) +{ + VectorInterpData *dataPtr = (VectorInterpData*)clientData; + + for (int ii=2; ii 1) { + char *string; + char c; + int i; + Blt_OpSpec *specPtr; + + string = Tcl_GetString(objv[1]); + c = string[0]; + for (specPtr = vectorCmdOps, i = 0; i < nCmdOps; i++, specPtr++) { + if ((c == specPtr->name[0]) && + (strcmp(string, specPtr->name) == 0)) { + goto doOp; + } + } + // The first argument is not an operation, so assume that its + // actually the name of a vector to be created + return VectorCreate2(clientData, interp, 1, objc, objv); + } + doOp: + /* Do the usual vector operation lookup now. */ + proc = (VectorCmdProc*)GetOpFromObj(interp, nCmdOps, vectorCmdOps, + BLT_OP_ARG1, objc, objv,0); + if (proc == NULL) { + return TCL_ERROR; + } + return (*proc) ((Vector*)clientData, interp, objc, objv); +} + +static void VectorInterpDeleteProc(ClientData clientData, Tcl_Interp* interp) +{ + VectorInterpData *dataPtr = (VectorInterpData*)clientData; + Tcl_HashEntry *hPtr; + Tcl_HashSearch cursor; + + for (hPtr = Tcl_FirstHashEntry(&dataPtr->vectorTable, &cursor); + hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { + Vector* vPtr = (Vector*)Tcl_GetHashValue(hPtr); + vPtr->hashPtr = NULL; + Vec_Free(vPtr); + } + Tcl_DeleteHashTable(&dataPtr->vectorTable); + + /* If any user-defined math functions were installed, remove them. */ + Vec_UninstallMathFunctions(&dataPtr->mathProcTable); + Tcl_DeleteHashTable(&dataPtr->mathProcTable); + + Tcl_DeleteHashTable(&dataPtr->indexProcTable); + Tcl_DeleteAssocData(interp, VECTOR_THREAD_KEY); + free(dataPtr); +} + +VectorInterpData* Blt::Vec_GetInterpData(Tcl_Interp* interp) +{ + VectorInterpData *dataPtr; + Tcl_InterpDeleteProc *proc; + + dataPtr = (VectorInterpData *) + Tcl_GetAssocData(interp, VECTOR_THREAD_KEY, &proc); + if (dataPtr == NULL) { + dataPtr = (VectorInterpData*)malloc(sizeof(VectorInterpData)); + dataPtr->interp = interp; + dataPtr->nextId = 0; + Tcl_SetAssocData(interp, VECTOR_THREAD_KEY, VectorInterpDeleteProc, + dataPtr); + Tcl_InitHashTable(&dataPtr->vectorTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&dataPtr->mathProcTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&dataPtr->indexProcTable, TCL_STRING_KEYS); + Vec_InstallMathFunctions(&dataPtr->mathProcTable); + Vec_InstallSpecialIndices(&dataPtr->indexProcTable); + srand48((long)time((time_t *) NULL)); + } + return dataPtr; +} + +/* C Application interface to vectors */ + +int Blt_CreateVector2(Tcl_Interp* interp, const char *vecName, + const char *cmdName, const char *varName, + int initialSize, Blt_Vector* *vecPtrPtr) +{ + VectorInterpData *dataPtr; /* Interpreter-specific data. */ + Vector* vPtr; + int isNew; + char *nameCopy; + + if (initialSize < 0) { + Tcl_AppendResult(interp, "bad vector size \"", Itoa(initialSize), + "\"", (char *)NULL); + return TCL_ERROR; + } + dataPtr = Vec_GetInterpData(interp); + + nameCopy = Blt_Strdup(vecName); + vPtr = Vec_Create(dataPtr, nameCopy, cmdName, varName, &isNew); + free(nameCopy); + + if (vPtr == NULL) { + return TCL_ERROR; + } + if (initialSize > 0) { + if (Vec_ChangeLength(interp, vPtr, initialSize) != TCL_OK) { + return TCL_ERROR; + } + } + if (vecPtrPtr != NULL) { + *vecPtrPtr = (Blt_Vector* ) vPtr; + } + return TCL_OK; +} + +int Blt_CreateVector(Tcl_Interp* interp, const char *name, int size, + Blt_Vector* *vecPtrPtr) +{ + return Blt_CreateVector2(interp, name, name, name, size, vecPtrPtr); +} + +int Blt_DeleteVector(Blt_Vector* vecPtr) +{ + Vector* vPtr = (Vector* )vecPtr; + + Vec_Free(vPtr); + return TCL_OK; +} + +int Blt_DeleteVectorByName(Tcl_Interp* interp, const char *name) +{ + // If the vector name was passed via a read-only string (e.g. "x"), the + // Vec_ParseElement routine will segfault when it tries to write into + // the string. Therefore make a writable copy and free it when we're done. + char* nameCopy = Blt_Strdup(name); + VectorInterpData *dataPtr = Vec_GetInterpData(interp); + Vector* vPtr; + int result = Vec_LookupName(dataPtr, nameCopy, &vPtr); + free(nameCopy); + + if (result != TCL_OK) + return TCL_ERROR; + + Vec_Free(vPtr); + return TCL_OK; +} + +int Blt_VectorExists2(Tcl_Interp* interp, const char *vecName) +{ + VectorInterpData *dataPtr; + + dataPtr = Vec_GetInterpData(interp); + if (GetVectorObject(dataPtr, vecName, NS_SEARCH_BOTH) != NULL) { + return 1; + } + return 0; +} + +int Blt_VectorExists(Tcl_Interp* interp, const char *vecName) +{ + char *nameCopy; + int result; + + /* + * If the vector name was passed via a read-only string (e.g. "x"), the + * Blt_VectorParseName routine will segfault when it tries to write into + * the string. Therefore make a writable copy and free it when we're + * done. + */ + nameCopy = Blt_Strdup(vecName); + result = Blt_VectorExists2(interp, nameCopy); + free(nameCopy); + return result; +} + +int Blt_GetVector(Tcl_Interp* interp, const char *name, Blt_Vector* *vecPtrPtr) +{ + VectorInterpData *dataPtr; /* Interpreter-specific data. */ + Vector* vPtr; + char *nameCopy; + int result; + + dataPtr = Vec_GetInterpData(interp); + /* + * If the vector name was passed via a read-only string (e.g. "x"), the + * Blt_VectorParseName routine will segfault when it tries to write into + * the string. Therefore make a writable copy and free it when we're + * done. + */ + nameCopy = Blt_Strdup(name); + result = Vec_LookupName(dataPtr, nameCopy, &vPtr); + free(nameCopy); + if (result != TCL_OK) { + return TCL_ERROR; + } + Vec_UpdateRange(vPtr); + *vecPtrPtr = (Blt_Vector* ) vPtr; + return TCL_OK; +} + +int Blt_GetVectorFromObj(Tcl_Interp* interp, Tcl_Obj *objPtr, + Blt_Vector* *vecPtrPtr) +{ + VectorInterpData *dataPtr; /* Interpreter-specific data. */ + Vector* vPtr; + + dataPtr = Vec_GetInterpData(interp); + if (Vec_LookupName(dataPtr, Tcl_GetString(objPtr), &vPtr) != TCL_OK) { + return TCL_ERROR; + } + Vec_UpdateRange(vPtr); + *vecPtrPtr = (Blt_Vector* ) vPtr; + return TCL_OK; +} + +int Blt_ResetVector(Blt_Vector* vecPtr, double *valueArr, int length, + int size, Tcl_FreeProc *freeProc) +{ + Vector* vPtr = (Vector* )vecPtr; + + if (size < 0) { + Tcl_AppendResult(vPtr->interp, "bad array size", (char *)NULL); + return TCL_ERROR; + } + return Vec_Reset(vPtr, valueArr, length, size, freeProc); +} + +int Blt_ResizeVector(Blt_Vector* vecPtr, int length) +{ + Vector* vPtr = (Vector* )vecPtr; + + if (Vec_ChangeLength((Tcl_Interp *)NULL, vPtr, length) != TCL_OK) { + Tcl_AppendResult(vPtr->interp, "can't resize vector \"", vPtr->name, + "\"", (char *)NULL); + return TCL_ERROR; + } + if (vPtr->flush) { + Vec_FlushCache(vPtr); + } + Vec_UpdateClients(vPtr); + return TCL_OK; +} + +Blt_VectorId Blt_AllocVectorId(Tcl_Interp* interp, const char *name) +{ + VectorInterpData *dataPtr; /* Interpreter-specific data. */ + Vector* vPtr; + VectorClient *clientPtr; + Blt_VectorId clientId; + int result; + char *nameCopy; + + dataPtr = Vec_GetInterpData(interp); + /* + * If the vector name was passed via a read-only string (e.g. "x"), the + * Blt_VectorParseName routine will segfault when it tries to write into + * the string. Therefore make a writable copy and free it when we're + * done. + */ + nameCopy = Blt_Strdup(name); + result = Vec_LookupName(dataPtr, nameCopy, &vPtr); + free(nameCopy); + + if (result != TCL_OK) { + return (Blt_VectorId) 0; + } + /* Allocate a new client structure */ + clientPtr = (VectorClient*)calloc(1, sizeof(VectorClient)); + clientPtr->magic = VECTOR_MAGIC; + + /* Add the new client to the server's list of clients */ + clientPtr->link = vPtr->chain->append(clientPtr); + clientPtr->serverPtr = vPtr; + clientId = (Blt_VectorId) clientPtr; + return clientId; +} + +void Blt_SetVectorChangedProc(Blt_VectorId clientId, + Blt_VectorChangedProc *proc, + ClientData clientData) +{ + VectorClient *clientPtr = (VectorClient *)clientId; + + if (clientPtr->magic != VECTOR_MAGIC) { + return; /* Not a valid token */ + } + clientPtr->clientData = clientData; + clientPtr->proc = proc; +} + +void Blt_FreeVectorId(Blt_VectorId clientId) +{ + VectorClient *clientPtr = (VectorClient *)clientId; + + if (clientPtr->magic != VECTOR_MAGIC) + return; + + if (clientPtr->serverPtr != NULL) { + // Remove the client from the server's list + clientPtr->serverPtr->chain->deleteLink(clientPtr->link); + } + free(clientPtr); +} + +const char* Blt_NameOfVectorId(Blt_VectorId clientId) +{ + VectorClient *clientPtr = (VectorClient *)clientId; + + if ((clientPtr->magic != VECTOR_MAGIC) || (clientPtr->serverPtr == NULL)) { + return NULL; + } + return clientPtr->serverPtr->name; +} + +const char* Blt_NameOfVector(Blt_Vector* vecPtr) /* Vector to query. */ +{ + Vector* vPtr = (Vector* )vecPtr; + return vPtr->name; +} + +int Blt_GetVectorById(Tcl_Interp* interp, Blt_VectorId clientId, + Blt_Vector* *vecPtrPtr) +{ + VectorClient *clientPtr = (VectorClient *)clientId; + + if (clientPtr->magic != VECTOR_MAGIC) { + Tcl_AppendResult(interp, "bad vector token", (char *)NULL); + return TCL_ERROR; + } + if (clientPtr->serverPtr == NULL) { + Tcl_AppendResult(interp, "vector no longer exists", (char *)NULL); + return TCL_ERROR; + } + Vec_UpdateRange(clientPtr->serverPtr); + *vecPtrPtr = (Blt_Vector* ) clientPtr->serverPtr; + return TCL_OK; +} + +void Blt_InstallIndexProc(Tcl_Interp* interp, const char *string, + Blt_VectorIndexProc *procPtr) +{ + VectorInterpData *dataPtr; /* Interpreter-specific data. */ + Tcl_HashEntry *hPtr; + int isNew; + + dataPtr = Vec_GetInterpData(interp); + hPtr = Tcl_CreateHashEntry(&dataPtr->indexProcTable, string, &isNew); + if (procPtr == NULL) { + Tcl_DeleteHashEntry(hPtr); + } else { + Tcl_SetHashValue(hPtr, procPtr); + } +} + +#define SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr + +/* routine by Brenner + * data is the array of complex data points, perversely + * starting at 1 + * nn is the number of complex points, i.e. half the length of data + * isign is 1 for forward, -1 for inverse + */ +static void four1(double *data, unsigned long nn, int isign) +{ + unsigned long n,mmax,m,j,istep,i; + double wtemp,wr,wpr,wpi,wi,theta; + double tempr,tempi; + + n=nn << 1; + j=1; + for (i = 1;i i) { + SWAP(data[j],data[i]); + SWAP(data[j+1],data[i+1]); + } + m=n >> 1; + while (m >= 2 && j > m) { + j -= m; + m >>= 1; + } + j += m; + } + mmax=2; + while (n > mmax) { + istep=mmax << 1; + theta=isign*(6.28318530717959/mmax); + wtemp=sin(0.5*theta); + wpr = -2.0*wtemp*wtemp; + wpi=sin(theta); + wr=1.0; + wi=0.0; + for (m=1;mlast - srcPtr->first + 1; + /* new length */ + pow2len = smallest_power_of_2_not_less_than( length ); + + /* We do not do in-place FFTs */ + if (realPtr == srcPtr) { + Tcl_AppendResult(interp, "real vector \"", realPtr->name, + "\" can't be the same as the source", (char *)NULL); + return TCL_ERROR; + } + if (phasesPtr != NULL) { + if (phasesPtr == srcPtr) { + Tcl_AppendResult(interp, "imaginary vector \"", phasesPtr->name, + "\" can't be the same as the source", (char *)NULL); + return TCL_ERROR; + } + if (Vec_ChangeLength(interp, phasesPtr, + pow2len/2-noconstant+middle) != TCL_OK) { + return TCL_ERROR; + } + } + if (freqPtr != NULL) { + if (freqPtr == srcPtr) { + Tcl_AppendResult(interp, "frequency vector \"", freqPtr->name, + "\" can't be the same as the source", (char *)NULL); + return TCL_ERROR; + } + if (Vec_ChangeLength(interp, freqPtr, + pow2len/2-noconstant+middle) != TCL_OK) { + return TCL_ERROR; + } + } + + /* Allocate memory zero-filled array. */ + paddedData = (double*)calloc(pow2len * 2, sizeof(double)); + if (paddedData == NULL) { + Tcl_AppendResult(interp, "can't allocate memory for padded data", + (char *)NULL); + return TCL_ERROR; + } + + /* + * Since we just do real transforms, only even locations will be + * filled with data. + */ + if (flags & FFT_BARTLETT) { /* Bartlett window 1 - ( (x - N/2) / (N/2) ) */ + double Nhalf = pow2len*0.5; + double Nhalf_1 = 1.0 / Nhalf; + double w; + + for (i = 0; i < length; i++) { + w = 1.0 - fabs( (i-Nhalf) * Nhalf_1 ); + Wss += w; + paddedData[2*i] = w * srcPtr->valueArr[i]; + } + for(/*empty*/; i < pow2len; i++) { + w = 1.0 - fabs((i-Nhalf) * Nhalf_1); + Wss += w; + } + } else { /* Squared window, i.e. no data windowing. */ + for (i = 0; i < length; i++) { + paddedData[2*i] = srcPtr->valueArr[i]; + } + Wss = pow2len; + } + + /* Fourier */ + four1(paddedData-1, pow2len, 1); + + /* + for(i=0;ivalueArr; + + for (i = 0 + noconstant; i < pow2len / 2; i++) { + re = paddedData[2*i]; + im = paddedData[2*i+1]; + reS = paddedData[2*pow2len-2*i-2]; + imS = paddedData[2*pow2len-2*i-1]; + v[i - noconstant] = factor * ( +# if 0 + hypot( paddedData[2*i], paddedData[2*i+1] ) + + hypot( + paddedData[pow2len*2-2*i-2], + paddedData[pow2len*2-2*i-1] + ) +# else + sqrt( re*re + im* im ) + sqrt( reS*reS + imS*imS ) +# endif + ); + } + } else { + for(i = 0 + noconstant; i < pow2len / 2 + middle; i++) { + realPtr->valueArr[i - noconstant] = paddedData[2*i]; + } + } + if( phasesPtr != NULL ){ + for (i = 0 + noconstant; i < pow2len / 2 + middle; i++) { + phasesPtr->valueArr[i-noconstant] = paddedData[2*i+1]; + } + } + + /* Compute frequencies */ + if (freqPtr != NULL) { + double N = pow2len; + double denom = 1.0 / N / delta; + for( i=0+noconstant; ivalueArr[i-noconstant] = ((double) i) * denom; + } + } + + /* Memory is necessarily dynamic, because nobody touched it ! */ + free(paddedData); + + realPtr->offset = 0; + return TCL_OK; +} + + +int Blt::Vec_InverseFFT(Tcl_Interp* interp, Vector* srcImagPtr, + Vector* destRealPtr, Vector* destImagPtr, Vector* srcPtr) +{ + int length; + int pow2len; + double *paddedData; + int i; + double oneOverN; + + if ((destRealPtr == srcPtr) || (destImagPtr == srcPtr )){ + /* we do not do in-place FFTs */ + return TCL_ERROR; + } + length = srcPtr->last - srcPtr->first + 1; + + /* minus one because of the magical middle element! */ + pow2len = smallest_power_of_2_not_less_than( (length-1)*2 ); + oneOverN = 1.0 / pow2len; + + if (Vec_ChangeLength(interp, destRealPtr, pow2len) != TCL_OK) { + return TCL_ERROR; + } + if (Vec_ChangeLength(interp, destImagPtr, pow2len) != TCL_OK) { + return TCL_ERROR; + } + + if( length != (srcImagPtr->last - srcImagPtr->first + 1) ){ + Tcl_AppendResult(srcPtr->interp, + "the length of the imagPart vector must ", + "be the same as the real one", (char *)NULL); + return TCL_ERROR; + } + + paddedData = (double*)malloc( pow2len*2*sizeof(double) ); + if( paddedData == NULL ){ + if (interp != NULL) { + Tcl_AppendResult(interp, "memory allocation failed", (char *)NULL); + } + return TCL_ERROR; + } + for(i=0;ivalueArr[i]; + paddedData[2*i+1] = srcImagPtr->valueArr[i]; + paddedData[pow2len*2 - 2*i - 2 ] = srcPtr->valueArr[i+1]; + paddedData[pow2len*2 - 2*i - 1 ] = - srcImagPtr->valueArr[i+1]; + } + /* mythical middle element */ + paddedData[(length-1)*2] = srcPtr->valueArr[length-1]; + paddedData[(length-1)*2+1] = srcImagPtr->valueArr[length-1]; + + /* + for(i=0;ivalueArr[i] = paddedData[2*i] * oneOverN; + destImagPtr->valueArr[i] = paddedData[2*i+1] * oneOverN; + } + + /* memory is necessarily dynamic, because nobody touched it ! */ + free( paddedData ); + + return TCL_OK; +} + +static double FindSplit(Point2d *points, int i, int j, int *split) +{ + double maxDist2; + + maxDist2 = -1.0; + if ((i + 1) < j) { + int k; + double a, b, c; + + /* + * + * dist2 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++) { + double dist2; + + dist2 = (points[k].x * a) + (points[k].y * b) + c; + if (dist2 < 0.0) { + dist2 = -dist2; + } + if (dist2 > maxDist2) { + maxDist2 = dist2; /* Track the maximum. */ + *split = k; + } + } + /* Correction for segment length---should be redone if can == 0 */ + maxDist2 *= maxDist2 / (a * a + b * b); + } + return maxDist2; +} + +// Douglas-Peucker line simplification algorithm */ +int Blt_SimplifyLine(Point2d *inputPts, int low, int 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 dist2, tolerance2; + int s = -1; /* Points to top stack item. */ + int count; + + stack = (int*)malloc(sizeof(int) * (high - low + 1)); + StackPush(high); + count = 0; + indices[count++] = 0; + tolerance2 = tolerance * tolerance; + while (!StackEmpty()) { + dist2 = FindSplit(inputPts, low, StackTop(), &split); + if (dist2 > tolerance2) { + StackPush(split); + } else { + indices[count++] = StackTop(); + StackPop(low); + } + } + free(stack); + return count; +} + diff --git a/generic/tkbltVector.h b/generic/tkbltVector.h new file mode 100644 index 0000000..45ddf6e --- /dev/null +++ b/generic/tkbltVector.h @@ -0,0 +1,140 @@ +/* + * 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 1993-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. + */ + +#ifndef _BLT_VECTOR_H +#define _BLT_VECTOR_H + +#include + +#ifdef BUILD_tkblt +# define TKBLT_STORAGE_CLASS DLLEXPORT +#else +# ifdef USE_TCL_STUBS +# define TKBLT_STORAGE_CLASS /* */ +# else +# define TKBLT_STORAGE_CLASS DLLIMPORT +# endif +#endif + + +typedef enum { + BLT_VECTOR_NOTIFY_UPDATE = 1, /* The vector's values has been updated */ + BLT_VECTOR_NOTIFY_DESTROY /* The vector has been destroyed and the client + * should no longer use its data (calling + * Blt_FreeVectorId) */ +} Blt_VectorNotify; + +typedef struct _Blt_VectorId *Blt_VectorId; + +typedef void (Blt_VectorChangedProc)(Tcl_Interp* interp, ClientData clientData, + Blt_VectorNotify notify); + +typedef struct { + double *valueArr; /* Array of values (possibly malloc-ed) */ + int numValues; /* Number of values in the array */ + int arraySize; /* Size of the allocated space */ + double min, max; /* Minimum and maximum values in the vector */ + int dirty; /* Indicates if the vector has been updated */ + int reserved; /* Reserved for future use */ + +} Blt_Vector; + +typedef double (Blt_VectorIndexProc)(Blt_Vector * vecPtr); + +typedef enum { + BLT_MATH_FUNC_SCALAR = 1, /* The function returns a single double + * precision value. */ + BLT_MATH_FUNC_VECTOR /* The function processes the entire vector. */ +} Blt_MathFuncType; + +/* + * To be safe, use the macros below, rather than the fields of the + * structure directly. + * + * The Blt_Vector is basically an opaque type. But it's also the + * actual memory address of the vector itself. I wanted to make the + * API as unobtrusive as possible. So instead of giving you a copy of + * the vector, providing various functions to access and update the + * vector, you get your hands on the actual memory (array of doubles) + * shared by all the vector's clients. + * + * The trade-off for speed and convenience is safety. You can easily + * break things by writing into the vector when other clients are + * using it. Use Blt_ResetVector to get around this. At least the + * macros are a reminder it isn't really safe to reset the data + * fields, except by the API routines. + */ +#define Blt_VecData(v) ((v)->valueArr) +#define Blt_VecLength(v) ((v)->numValues) +#define Blt_VecSize(v) ((v)->arraySize) +#define Blt_VecDirty(v) ((v)->dirty) + +#ifdef __cplusplus +extern "C" { +#endif + TKBLT_STORAGE_CLASS int Blt_CreateVector(Tcl_Interp* interp, const char *vecName, + int size, Blt_Vector** vecPtrPtr); + TKBLT_STORAGE_CLASS int Blt_CreateVector2(Tcl_Interp* interp, const char *vecName, + const char *cmdName, const char *varName, + int initialSize, Blt_Vector **vecPtrPtr); + TKBLT_STORAGE_CLASS int Blt_DeleteVectorByName(Tcl_Interp* interp, const char *vecName); + TKBLT_STORAGE_CLASS int Blt_DeleteVector(Blt_Vector *vecPtr); + TKBLT_STORAGE_CLASS int Blt_GetVector(Tcl_Interp* interp, const char *vecName, + Blt_Vector **vecPtrPtr); + TKBLT_STORAGE_CLASS int Blt_GetVectorFromObj(Tcl_Interp* interp, Tcl_Obj *objPtr, + Blt_Vector **vecPtrPtr); + TKBLT_STORAGE_CLASS int Blt_ResetVector(Blt_Vector *vecPtr, double *dataArr, int n, + int arraySize, Tcl_FreeProc *freeProc); + TKBLT_STORAGE_CLASS int Blt_ResizeVector(Blt_Vector *vecPtr, int n); + TKBLT_STORAGE_CLASS int Blt_VectorExists(Tcl_Interp* interp, const char *vecName); + TKBLT_STORAGE_CLASS int Blt_VectorExists2(Tcl_Interp* interp, const char *vecName); + TKBLT_STORAGE_CLASS Blt_VectorId Blt_AllocVectorId(Tcl_Interp* interp, const char *vecName); + TKBLT_STORAGE_CLASS int Blt_GetVectorById(Tcl_Interp* interp, Blt_VectorId clientId, + Blt_Vector **vecPtrPtr); + TKBLT_STORAGE_CLASS void Blt_SetVectorChangedProc(Blt_VectorId clientId, + Blt_VectorChangedProc *proc, + ClientData clientData); + TKBLT_STORAGE_CLASS void Blt_FreeVectorId(Blt_VectorId clientId); + TKBLT_STORAGE_CLASS const char *Blt_NameOfVectorId(Blt_VectorId clientId); + TKBLT_STORAGE_CLASS const char *Blt_NameOfVector(Blt_Vector *vecPtr); + TKBLT_STORAGE_CLASS int Blt_ExprVector(Tcl_Interp* interp, char *expr, Blt_Vector *vecPtr); + TKBLT_STORAGE_CLASS void Blt_InstallIndexProc(Tcl_Interp* interp, const char *indexName, + Blt_VectorIndexProc * procPtr); + TKBLT_STORAGE_CLASS double Blt_VecMin(Blt_Vector *vPtr); + TKBLT_STORAGE_CLASS double Blt_VecMax(Blt_Vector *vPtr); +#ifdef __cplusplus +} +#endif + +#include "tkbltDecls.h" + +#endif /* _BLT_VECTOR_H */ diff --git a/src/tkblt.decls b/src/tkblt.decls deleted file mode 100644 index b4b5c67..0000000 --- a/src/tkblt.decls +++ /dev/null @@ -1,92 +0,0 @@ -library tkblt -interface tkblt - -declare 0 generic { - int Blt_CreateVector(Tcl_Interp* interp, const char *vecName, - int size, Blt_Vector** vecPtrPtr) -} - -declare 1 generic { - int Blt_CreateVector2(Tcl_Interp* interp, const char *vecName, - const char *cmdName, const char *varName, - int initialSize, Blt_Vector **vecPtrPtr) -} - -declare 2 generic { - int Blt_DeleteVectorByName(Tcl_Interp* interp, const char *vecName) -} - -declare 3 generic { - int Blt_DeleteVector(Blt_Vector *vecPtr) -} - -declare 4 generic { - int Blt_GetVector(Tcl_Interp* interp, const char *vecName, - Blt_Vector **vecPtrPtr) -} - -declare 5 generic { - int Blt_GetVectorFromObj(Tcl_Interp* interp, Tcl_Obj *objPtr, - Blt_Vector **vecPtrPtr) -} - -declare 6 generic { - int Blt_ResetVector(Blt_Vector *vecPtr, double *dataArr, int n, - int arraySize, Tcl_FreeProc *freeProc) -} - -declare 7 generic { - int Blt_ResizeVector(Blt_Vector *vecPtr, int n) -} - -declare 8 generic { - int Blt_VectorExists(Tcl_Interp* interp, const char *vecName) -} - -declare 9 generic { - int Blt_VectorExists2(Tcl_Interp* interp, const char *vecName) -} - -declare 10 generic { - Blt_VectorId Blt_AllocVectorId(Tcl_Interp* interp, const char *vecName) -} - -declare 11 generic { - int Blt_GetVectorById(Tcl_Interp* interp, Blt_VectorId clientId, - Blt_Vector **vecPtrPtr) -} - -declare 12 generic { - void Blt_SetVectorChangedProc(Blt_VectorId clientId, - Blt_VectorChangedProc *proc, - ClientData clientData) -} - -declare 13 generic { - void Blt_FreeVectorId(Blt_VectorId clientId) -} - -declare 14 generic { - const char *Blt_NameOfVectorId(Blt_VectorId clientId) -} - -declare 15 generic { - const char *Blt_NameOfVector(Blt_Vector *vecPtr) -} - -declare 16 generic { - int Blt_ExprVector(Tcl_Interp* interp, char *expr, Blt_Vector *vecPtr) -} - -declare 17 generic { - void Blt_InstallIndexProc(Tcl_Interp* interp, const char *indexName, - Blt_VectorIndexProc * procPtr) -} - -declare 18 generic { - double Blt_VecMin(Blt_Vector *vPtr) -} - -declare 19 generic { - double Blt_VecMax(Blt_Vector *vPtr) -} diff --git a/src/tkbltChain.C b/src/tkbltChain.C deleted file mode 100644 index dbd317c..0000000 --- a/src/tkbltChain.C +++ /dev/null @@ -1,194 +0,0 @@ -/* - * 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 - -#include "tkbltChain.h" - -using namespace Blt; - -// ChainLink - -ChainLink::ChainLink(void* clientData) -{ - prev_ =NULL; - next_ =NULL; - manage_ =0; - clientData_ = clientData; -} - -ChainLink::ChainLink(size_t ss) -{ - prev_ =NULL; - next_ =NULL; - manage_ =1; - clientData_ = (void*)calloc(1,ss); -} - -ChainLink::~ChainLink() -{ - if (manage_ && clientData_) - free(clientData_); -} - -// Chain - -Chain::Chain() -{ - head_ =NULL; - tail_ =NULL; - nLinks_ =0; -} - -Chain::~Chain() -{ - ChainLink* linkPtr = head_; - while (linkPtr) { - ChainLink* oldPtr =linkPtr; - linkPtr = linkPtr->next_; - delete oldPtr; - } -} - -void Chain::reset() -{ - ChainLink* linkPtr = head_; - while (linkPtr) { - ChainLink* oldPtr = linkPtr; - linkPtr = linkPtr->next_; - delete oldPtr; - } - head_ =NULL; - tail_ =NULL; - nLinks_ =0; -} - -void Chain::linkAfter(ChainLink* linkPtr, ChainLink* afterPtr) -{ - if (!head_) { - head_ = linkPtr; - tail_ = linkPtr; - } - else { - if (!afterPtr) { - linkPtr->next_ = NULL; - linkPtr->prev_ = tail_; - tail_->next_ = linkPtr; - tail_ = linkPtr; - } - else { - linkPtr->next_ = afterPtr->next_; - linkPtr->prev_ = afterPtr; - if (afterPtr == tail_) - tail_ = linkPtr; - else - afterPtr->next_->prev_ = linkPtr; - afterPtr->next_ = linkPtr; - } - } - - nLinks_++; -} - -void Chain::linkBefore(ChainLink* linkPtr, ChainLink* beforePtr) -{ - if (!head_) { - head_ = linkPtr; - tail_ = linkPtr; - } - else { - if (beforePtr == NULL) { - linkPtr->next_ = head_; - linkPtr->prev_ = NULL; - head_->prev_ = linkPtr; - head_ = linkPtr; - } - else { - linkPtr->prev_ = beforePtr->prev_; - linkPtr->next_ = beforePtr; - if (beforePtr == head_) - head_ = linkPtr; - else - beforePtr->prev_->next_ = linkPtr; - beforePtr->prev_ = linkPtr; - } - } - - nLinks_++; -} - -void Chain::unlinkLink(ChainLink* linkPtr) -{ - // Indicates if the link is actually remove from the chain - int unlinked; - - unlinked = 0; - if (head_ == linkPtr) { - head_ = linkPtr->next_; - unlinked = 1; - } - if (tail_ == linkPtr) { - tail_ = linkPtr->prev_; - unlinked = 1; - } - if (linkPtr->next_) { - linkPtr->next_->prev_ = linkPtr->prev_; - unlinked = 1; - } - if (linkPtr->prev_) { - linkPtr->prev_->next_ = linkPtr->next_; - unlinked = 1; - } - if (unlinked) - nLinks_--; - - linkPtr->prev_ =NULL; - linkPtr->next_ =NULL; -} - -void Chain::deleteLink(ChainLink* link) -{ - unlinkLink(link); - delete link; - link = NULL; -} - -ChainLink* Chain::append(void* clientData) -{ - ChainLink* link = new ChainLink(clientData); - linkAfter(link, NULL); - return link; -} - -ChainLink* Chain::prepend(void* clientData) -{ - ChainLink* link = new ChainLink(clientData); - linkBefore(link, NULL); - return link; -} diff --git a/src/tkbltChain.h b/src/tkbltChain.h deleted file mode 100644 index 6e254f9..0000000 --- a/src/tkbltChain.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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 1993-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. - */ -#ifndef _BLT_CHAIN_H -#define _BLT_CHAIN_H - -#define Chain_GetLength(c) (((c) == NULL) ? 0 : (c)->nLinks()) -#define Chain_FirstLink(c) (((c) == NULL) ? NULL : (c)->head()) -#define Chain_LastLink(c) (((c) == NULL) ? NULL : (c)->tail()) - -#define Chain_PrevLink(l) ((l)->prev()) -#define Chain_NextLink(l) ((l)->next()) -#define Chain_GetValue(l) ((l)->clientData()) - -namespace Blt { - - class Chain; - - class ChainLink { - friend class Chain; - - protected: - ChainLink* prev_; - ChainLink* next_; - int manage_; - void* clientData_; - - public: - ChainLink(void*); - ChainLink(size_t); - virtual ~ChainLink(); - - ChainLink* prev() {return prev_;} - ChainLink* next() {return next_;} - void* clientData() {return clientData_;} - void setClientData(void* d) {clientData_ =d;} - }; - - class Chain { - protected: - ChainLink* head_; - ChainLink* tail_; - long nLinks_; - - public: - Chain(); - virtual ~Chain(); - - ChainLink* head() {return head_;} - ChainLink* tail() {return tail_;} - long nLinks() {return nLinks_;} - - void reset(); - void linkAfter(ChainLink* link, ChainLink* after); - void linkBefore(ChainLink* link, ChainLink* before); - void unlinkLink(ChainLink* linkPtr); - void deleteLink(ChainLink* link); - ChainLink* append(void* clientData); - ChainLink* prepend(void* clientData); - }; -}; - -#endif diff --git a/src/tkbltConfig.C b/src/tkbltConfig.C deleted file mode 100644 index 82fea4e..0000000 --- a/src/tkbltConfig.C +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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 (c) 1990-1994 The Regents of the University of California. - * Copyright (c) 1994-1997 Sun Microsystems, Inc. - * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * Copyright 2003-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 -#include - -#include "tkbltConfig.h" -#include "tkbltGrMisc.h" - -using namespace Blt; - -void RestoreProc(ClientData clientData, Tk_Window tkwin, - char *ptr, char *savePtr) -{ - *(double*)ptr = *(double*)savePtr; -} - -// Fill -const char* fillObjOption[] = {"none", "x", "y", "both", NULL}; - -// Dashes -static Tk_CustomOptionSetProc DashesSetProc; -static Tk_CustomOptionGetProc DashesGetProc; -Tk_ObjCustomOption dashesObjOption = - { - "dashes", DashesSetProc, DashesGetProc, NULL, NULL, NULL - }; - -static int DashesSetProc(ClientData clientData, Tcl_Interp *interp, - Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, - int offset, char* save, int flags) -{ - Dashes* dashesPtr = (Dashes*)(widgRec + offset); - - int length; - const char* string = Tcl_GetStringFromObj(*objPtr, &length); - if (!string || !string[0]) { - dashesPtr->values[0] = 0; - return TCL_OK; - } - - if (!strncmp(string, "dot", length)) { - dashesPtr->values[0] = 1; - dashesPtr->values[1] = 0; - } - else if (!strncmp(string, "dash", length)) { - dashesPtr->values[0] = 5; - dashesPtr->values[1] = 2; - dashesPtr->values[2] = 0; - } - else if (!strncmp(string, "dashdot", length)) { - dashesPtr->values[0] = 2; - dashesPtr->values[1] = 4; - dashesPtr->values[2] = 2; - dashesPtr->values[3] = 0; - } - else if (!strncmp(string, "dashdotdot", length)) { - dashesPtr->values[0] = 2; - dashesPtr->values[1] = 4; - dashesPtr->values[2] = 2; - dashesPtr->values[3] = 2; - dashesPtr->values[4] = 0; - } - else { - int objc; - Tcl_Obj** objv; - if (Tcl_ListObjGetElements(interp, *objPtr, &objc, &objv) != TCL_OK) - return TCL_ERROR; - - // This is the postscript limit - if (objc > 11) { - Tcl_AppendResult(interp, "too many values in dash list \"", - string, "\"", (char *)NULL); - return TCL_ERROR; - } - - int ii; - for (ii=0; ii 255)) { - Tcl_AppendResult(interp, "dash value \"", - Tcl_GetString(objv[ii]), "\" is out of range", - (char *)NULL); - return TCL_ERROR; - } - dashesPtr->values[ii] = (unsigned char)value; - } - - // Make sure the array ends with a NULL byte - dashesPtr->values[ii] = 0; - } - - return TCL_OK; -}; - -static Tcl_Obj* DashesGetProc(ClientData clientData, Tk_Window tkwin, - char *widgRec, int offset) -{ - Dashes* dashesPtr = (Dashes*)(widgRec + offset); - - // count how many - int cnt =0; - while (dashesPtr->values[cnt]) - cnt++; - - if (!cnt) - return Tcl_NewListObj(0, (Tcl_Obj**)NULL); - - Tcl_Obj** ll = new Tcl_Obj*[cnt]; - for (int ii=0; iivalues[ii]); - Tcl_Obj* listObjPtr = Tcl_NewListObj(cnt, ll); - delete [] ll; - - return listObjPtr; -}; - -// List -static Tk_CustomOptionSetProc ListSetProc; -static Tk_CustomOptionGetProc ListGetProc; -static Tk_CustomOptionFreeProc ListFreeProc; -Tk_ObjCustomOption listObjOption = - { - "list", ListSetProc, ListGetProc, RestoreProc, ListFreeProc, NULL - }; - -static int ListSetProc(ClientData clientData, Tcl_Interp *interp, - Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, - int offset, char* savePtr, int flags) -{ - const char*** listPtr = (const char***)(widgRec + offset); - *(double*)savePtr = *(double*)listPtr; - - if (!listPtr) - return TCL_OK; - - const char** argv; - int argc; - if (Tcl_SplitList(interp, Tcl_GetString(*objPtr), &argc, &argv) != TCL_OK) - return TCL_ERROR; - - *listPtr = argv; - - return TCL_OK; -}; - -static Tcl_Obj* ListGetProc(ClientData clientData, Tk_Window tkwin, - char *widgRec, int offset) -{ - const char*** listPtr = (const char***)(widgRec + offset); - - if (!listPtr || !(*listPtr)) - return Tcl_NewListObj(0, NULL); - - // count how many - int cnt=0; - for (const char** pp=*listPtr; *pp; pp++,cnt++) {} - if (!cnt) - return Tcl_NewListObj(0, NULL); - - Tcl_Obj** ll = new Tcl_Obj*[cnt]; - for (int ii=0; ii - -extern const char* fillObjOption[]; -extern Tk_ObjCustomOption dashesObjOption; -extern Tk_ObjCustomOption listObjOption; -extern Tk_CustomOptionRestoreProc RestoreProc; - -#endif diff --git a/src/tkbltDecls.h b/src/tkbltDecls.h deleted file mode 100644 index d50e207..0000000 --- a/src/tkbltDecls.h +++ /dev/null @@ -1,152 +0,0 @@ -/* !BEGIN!: Do not edit below this line. */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Exported function declarations: - */ - -/* 0 */ -EXTERN int Blt_CreateVector(Tcl_Interp*interp, - const char *vecName, int size, - Blt_Vector**vecPtrPtr); -/* 1 */ -EXTERN int Blt_CreateVector2(Tcl_Interp*interp, - const char *vecName, const char *cmdName, - const char *varName, int initialSize, - Blt_Vector **vecPtrPtr); -/* 2 */ -EXTERN int Blt_DeleteVectorByName(Tcl_Interp*interp, - const char *vecName); -/* 3 */ -EXTERN int Blt_DeleteVector(Blt_Vector *vecPtr); -/* 4 */ -EXTERN int Blt_GetVector(Tcl_Interp*interp, const char *vecName, - Blt_Vector **vecPtrPtr); -/* 5 */ -EXTERN int Blt_GetVectorFromObj(Tcl_Interp*interp, - Tcl_Obj *objPtr, Blt_Vector **vecPtrPtr); -/* 6 */ -EXTERN int Blt_ResetVector(Blt_Vector *vecPtr, double *dataArr, - int n, int arraySize, Tcl_FreeProc *freeProc); -/* 7 */ -EXTERN int Blt_ResizeVector(Blt_Vector *vecPtr, int n); -/* 8 */ -EXTERN int Blt_VectorExists(Tcl_Interp*interp, - const char *vecName); -/* 9 */ -EXTERN int Blt_VectorExists2(Tcl_Interp*interp, - const char *vecName); -/* 10 */ -EXTERN Blt_VectorId Blt_AllocVectorId(Tcl_Interp*interp, - const char *vecName); -/* 11 */ -EXTERN int Blt_GetVectorById(Tcl_Interp*interp, - Blt_VectorId clientId, - Blt_Vector **vecPtrPtr); -/* 12 */ -EXTERN void Blt_SetVectorChangedProc(Blt_VectorId clientId, - Blt_VectorChangedProc *proc, - ClientData clientData); -/* 13 */ -EXTERN void Blt_FreeVectorId(Blt_VectorId clientId); -/* 14 */ -EXTERN const char * Blt_NameOfVectorId(Blt_VectorId clientId); -/* 15 */ -EXTERN const char * Blt_NameOfVector(Blt_Vector *vecPtr); -/* 16 */ -EXTERN int Blt_ExprVector(Tcl_Interp*interp, char *expr, - Blt_Vector *vecPtr); -/* 17 */ -EXTERN void Blt_InstallIndexProc(Tcl_Interp*interp, - const char *indexName, - Blt_VectorIndexProc *procPtr); -/* 18 */ -EXTERN double Blt_VecMin(Blt_Vector *vPtr); -/* 19 */ -EXTERN double Blt_VecMax(Blt_Vector *vPtr); - -typedef struct TkbltStubs { - int magic; - void *hooks; - - int (*blt_CreateVector) (Tcl_Interp*interp, const char *vecName, int size, Blt_Vector**vecPtrPtr); /* 0 */ - int (*blt_CreateVector2) (Tcl_Interp*interp, const char *vecName, const char *cmdName, const char *varName, int initialSize, Blt_Vector **vecPtrPtr); /* 1 */ - int (*blt_DeleteVectorByName) (Tcl_Interp*interp, const char *vecName); /* 2 */ - int (*blt_DeleteVector) (Blt_Vector *vecPtr); /* 3 */ - int (*blt_GetVector) (Tcl_Interp*interp, const char *vecName, Blt_Vector **vecPtrPtr); /* 4 */ - int (*blt_GetVectorFromObj) (Tcl_Interp*interp, Tcl_Obj *objPtr, Blt_Vector **vecPtrPtr); /* 5 */ - int (*blt_ResetVector) (Blt_Vector *vecPtr, double *dataArr, int n, int arraySize, Tcl_FreeProc *freeProc); /* 6 */ - int (*blt_ResizeVector) (Blt_Vector *vecPtr, int n); /* 7 */ - int (*blt_VectorExists) (Tcl_Interp*interp, const char *vecName); /* 8 */ - int (*blt_VectorExists2) (Tcl_Interp*interp, const char *vecName); /* 9 */ - Blt_VectorId (*blt_AllocVectorId) (Tcl_Interp*interp, const char *vecName); /* 10 */ - int (*blt_GetVectorById) (Tcl_Interp*interp, Blt_VectorId clientId, Blt_Vector **vecPtrPtr); /* 11 */ - void (*blt_SetVectorChangedProc) (Blt_VectorId clientId, Blt_VectorChangedProc *proc, ClientData clientData); /* 12 */ - void (*blt_FreeVectorId) (Blt_VectorId clientId); /* 13 */ - const char * (*blt_NameOfVectorId) (Blt_VectorId clientId); /* 14 */ - const char * (*blt_NameOfVector) (Blt_Vector *vecPtr); /* 15 */ - int (*blt_ExprVector) (Tcl_Interp*interp, char *expr, Blt_Vector *vecPtr); /* 16 */ - void (*blt_InstallIndexProc) (Tcl_Interp*interp, const char *indexName, Blt_VectorIndexProc *procPtr); /* 17 */ - double (*blt_VecMin) (Blt_Vector *vPtr); /* 18 */ - double (*blt_VecMax) (Blt_Vector *vPtr); /* 19 */ -} TkbltStubs; - -extern const TkbltStubs *tkbltStubsPtr; - -#ifdef __cplusplus -} -#endif - -#if defined(USE_TKBLT_STUBS) - -/* - * Inline function declarations: - */ - -#define Blt_CreateVector \ - (tkbltStubsPtr->blt_CreateVector) /* 0 */ -#define Blt_CreateVector2 \ - (tkbltStubsPtr->blt_CreateVector2) /* 1 */ -#define Blt_DeleteVectorByName \ - (tkbltStubsPtr->blt_DeleteVectorByName) /* 2 */ -#define Blt_DeleteVector \ - (tkbltStubsPtr->blt_DeleteVector) /* 3 */ -#define Blt_GetVector \ - (tkbltStubsPtr->blt_GetVector) /* 4 */ -#define Blt_GetVectorFromObj \ - (tkbltStubsPtr->blt_GetVectorFromObj) /* 5 */ -#define Blt_ResetVector \ - (tkbltStubsPtr->blt_ResetVector) /* 6 */ -#define Blt_ResizeVector \ - (tkbltStubsPtr->blt_ResizeVector) /* 7 */ -#define Blt_VectorExists \ - (tkbltStubsPtr->blt_VectorExists) /* 8 */ -#define Blt_VectorExists2 \ - (tkbltStubsPtr->blt_VectorExists2) /* 9 */ -#define Blt_AllocVectorId \ - (tkbltStubsPtr->blt_AllocVectorId) /* 10 */ -#define Blt_GetVectorById \ - (tkbltStubsPtr->blt_GetVectorById) /* 11 */ -#define Blt_SetVectorChangedProc \ - (tkbltStubsPtr->blt_SetVectorChangedProc) /* 12 */ -#define Blt_FreeVectorId \ - (tkbltStubsPtr->blt_FreeVectorId) /* 13 */ -#define Blt_NameOfVectorId \ - (tkbltStubsPtr->blt_NameOfVectorId) /* 14 */ -#define Blt_NameOfVector \ - (tkbltStubsPtr->blt_NameOfVector) /* 15 */ -#define Blt_ExprVector \ - (tkbltStubsPtr->blt_ExprVector) /* 16 */ -#define Blt_InstallIndexProc \ - (tkbltStubsPtr->blt_InstallIndexProc) /* 17 */ -#define Blt_VecMin \ - (tkbltStubsPtr->blt_VecMin) /* 18 */ -#define Blt_VecMax \ - (tkbltStubsPtr->blt_VecMax) /* 19 */ - -#endif /* defined(USE_TKBLT_STUBS) */ - -/* !END!: Do not edit above this line. */ diff --git a/src/tkbltGrAxis.C b/src/tkbltGrAxis.C deleted file mode 100644 index 2d8dbfb..0000000 --- a/src/tkbltGrAxis.C +++ /dev/null @@ -1,1971 +0,0 @@ -/* - * 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 1993-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 -#include -#include - -#include - -#include "tkbltGraph.h" -#include "tkbltGrBind.h" -#include "tkbltGrAxis.h" -#include "tkbltGrAxisOption.h" -#include "tkbltGrPostscript.h" -#include "tkbltGrMisc.h" -#include "tkbltGrDef.h" -#include "tkbltConfig.h" -#include "tkbltGrPSOutput.h" - -using namespace Blt; - -#define AXIS_PAD_TITLE 2 -#define EXP10(x) (pow(10.0,(x))) - -AxisName Blt::axisNames[] = { - { "x", CID_AXIS_X }, - { "y", CID_AXIS_Y }, - { "x2", CID_AXIS_X }, - { "y2", CID_AXIS_Y } -} ; - -// Defs - -extern double AdjustViewport(double offset, double windowSize); - -static Tk_OptionSpec optionSpecs[] = { - {TK_OPTION_COLOR, "-activeforeground", "activeForeground", "ActiveForeground", - STD_ACTIVE_FOREGROUND, -1, Tk_Offset(AxisOptions, activeFgColor), - 0, NULL, CACHE}, - {TK_OPTION_RELIEF, "-activerelief", "activeRelief", "Relief", - "flat", -1, Tk_Offset(AxisOptions, activeRelief), 0, NULL, CACHE}, - {TK_OPTION_DOUBLE, "-autorange", "autoRange", "AutoRange", - "0", -1, Tk_Offset(AxisOptions, windowSize), 0, NULL, RESET}, - {TK_OPTION_BORDER, "-background", "background", "Background", - NULL, -1, Tk_Offset(AxisOptions, normalBg), TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_SYNONYM, "-bg", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-background", 0}, - {TK_OPTION_CUSTOM, "-bindtags", "bindTags", "BindTags", - "all", -1, Tk_Offset(AxisOptions, tags), - TK_OPTION_NULL_OK, &listObjOption, 0}, - {TK_OPTION_SYNONYM, "-bd", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-borderwidth", 0}, - {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", - STD_BORDERWIDTH, -1, Tk_Offset(AxisOptions, borderWidth), 0, NULL, LAYOUT}, - {TK_OPTION_BOOLEAN, "-checklimits", "checkLimits", "CheckLimits", - "no", -1, Tk_Offset(AxisOptions, checkLimits), 0, NULL, RESET}, - {TK_OPTION_COLOR, "-color", "color", "Color", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(AxisOptions, tickColor), - 0, NULL, CACHE}, - {TK_OPTION_STRING, "-command", "command", "Command", - NULL, -1, Tk_Offset(AxisOptions, formatCmd), TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_BOOLEAN, "-descending", "descending", "Descending", - "no", -1, Tk_Offset(AxisOptions, descending), 0, NULL, RESET}, - {TK_OPTION_BOOLEAN, "-exterior", "exterior", "exterior", - "yes", -1, Tk_Offset(AxisOptions, exterior), 0, NULL, LAYOUT}, - {TK_OPTION_SYNONYM, "-fg", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-color", 0}, - {TK_OPTION_SYNONYM, "-foreground", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-color", 0}, - {TK_OPTION_BOOLEAN, "-grid", "grid", "Grid", - "yes", -1, Tk_Offset(AxisOptions, showGrid), 0, NULL, CACHE}, - {TK_OPTION_COLOR, "-gridcolor", "gridColor", "GridColor", - "gray64", -1, Tk_Offset(AxisOptions, major.color), 0, NULL, CACHE}, - {TK_OPTION_CUSTOM, "-griddashes", "gridDashes", "GridDashes", - "dot", -1, Tk_Offset(AxisOptions, major.dashes), - TK_OPTION_NULL_OK, &dashesObjOption, CACHE}, - {TK_OPTION_PIXELS, "-gridlinewidth", "gridLineWidth", "GridLineWidth", - "1", -1, Tk_Offset(AxisOptions, major.lineWidth), 0, NULL, CACHE}, - {TK_OPTION_BOOLEAN, "-gridminor", "gridMinor", "GridMinor", - "yes", -1, Tk_Offset(AxisOptions, showGridMinor), 0, NULL, CACHE}, - {TK_OPTION_COLOR, "-gridminorcolor", "gridMinorColor", "GridMinorColor", - "gray64", -1, Tk_Offset(AxisOptions, minor.color), 0, NULL, CACHE}, - {TK_OPTION_CUSTOM, "-gridminordashes", "gridMinorDashes", "GridMinorDashes", - "dot", -1, Tk_Offset(AxisOptions, minor.dashes), - TK_OPTION_NULL_OK, &dashesObjOption, CACHE}, - {TK_OPTION_PIXELS, "-gridminorlinewidth", "gridMinorLineWidth", - "GridMinorLineWidth", - "1", -1, Tk_Offset(AxisOptions, minor.lineWidth), 0, NULL, CACHE}, - {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", - "no", -1, Tk_Offset(AxisOptions, hide), 0, NULL, LAYOUT}, - {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify", - "c", -1, Tk_Offset(AxisOptions, titleJustify), 0, NULL, LAYOUT}, - {TK_OPTION_BOOLEAN, "-labeloffset", "labelOffset", "LabelOffset", - "no", -1, Tk_Offset(AxisOptions, labelOffset), 0, NULL, LAYOUT}, - {TK_OPTION_COLOR, "-limitscolor", "limitsColor", "LimitsColor", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(AxisOptions, limitsTextStyle.color), - 0, NULL, CACHE}, - {TK_OPTION_FONT, "-limitsfont", "limitsFont", "LimitsFont", - STD_FONT_SMALL, -1, Tk_Offset(AxisOptions, limitsTextStyle.font), - 0, NULL, LAYOUT}, - {TK_OPTION_STRING, "-limitsformat", "limitsFormat", "LimitsFormat", - NULL, -1, Tk_Offset(AxisOptions, limitsFormat), - TK_OPTION_NULL_OK, NULL, LAYOUT}, - {TK_OPTION_PIXELS, "-linewidth", "lineWidth", "LineWidth", - "1", -1, Tk_Offset(AxisOptions, lineWidth), 0, NULL, LAYOUT}, - {TK_OPTION_BOOLEAN, "-logscale", "logScale", "LogScale", - "no", -1, Tk_Offset(AxisOptions, logScale), 0, NULL, RESET}, - {TK_OPTION_BOOLEAN, "-loosemin", "looseMin", "LooseMin", - "no", -1, Tk_Offset(AxisOptions, looseMin), 0, NULL, RESET}, - {TK_OPTION_BOOLEAN, "-loosemax", "looseMax", "LooseMax", - "no", -1, Tk_Offset(AxisOptions, looseMax), 0, NULL, RESET}, - {TK_OPTION_CUSTOM, "-majorticks", "majorTicks", "MajorTicks", - NULL, -1, Tk_Offset(AxisOptions, t1UPtr), - TK_OPTION_NULL_OK, &ticksObjOption, RESET}, - {TK_OPTION_CUSTOM, "-max", "max", "Max", - NULL, -1, Tk_Offset(AxisOptions, reqMax), - TK_OPTION_NULL_OK, &limitObjOption, RESET}, - {TK_OPTION_CUSTOM, "-min", "min", "Min", - NULL, -1, Tk_Offset(AxisOptions, reqMin), - TK_OPTION_NULL_OK, &limitObjOption, RESET}, - {TK_OPTION_CUSTOM, "-minorticks", "minorTicks", "MinorTicks", - NULL, -1, Tk_Offset(AxisOptions, t2UPtr), - TK_OPTION_NULL_OK, &ticksObjOption, RESET}, - {TK_OPTION_RELIEF, "-relief", "relief", "Relief", - "flat", -1, Tk_Offset(AxisOptions, relief), 0, NULL, CACHE}, - {TK_OPTION_DOUBLE, "-rotate", "rotate", "Rotate", - "0", -1, Tk_Offset(AxisOptions, tickAngle), 0, NULL, LAYOUT}, - {TK_OPTION_CUSTOM, "-scrollcommand", "scrollCommand", "ScrollCommand", - NULL, -1, Tk_Offset(AxisOptions, scrollCmdObjPtr), - TK_OPTION_NULL_OK, &objectObjOption, 0}, - {TK_OPTION_PIXELS, "-scrollincrement", "scrollIncrement", "ScrollIncrement", - "10", -1, Tk_Offset(AxisOptions, scrollUnits), 0, NULL, 0}, - {TK_OPTION_CUSTOM, "-scrollmax", "scrollMax", "ScrollMax", - NULL, -1, Tk_Offset(AxisOptions, reqScrollMax), - TK_OPTION_NULL_OK, &limitObjOption, 0}, - {TK_OPTION_CUSTOM, "-scrollmin", "scrollMin", "ScrollMin", - NULL, -1, Tk_Offset(AxisOptions, reqScrollMin), - TK_OPTION_NULL_OK, &limitObjOption, 0}, - {TK_OPTION_DOUBLE, "-shiftby", "shiftBy", "ShiftBy", - "0", -1, Tk_Offset(AxisOptions, shiftBy), 0, NULL, RESET}, - {TK_OPTION_BOOLEAN, "-showticks", "showTicks", "ShowTicks", - "yes", -1, Tk_Offset(AxisOptions, showTicks), 0, NULL, LAYOUT}, - {TK_OPTION_DOUBLE, "-stepsize", "stepSize", "StepSize", - "0", -1, Tk_Offset(AxisOptions, reqStep), 0, NULL, RESET}, - {TK_OPTION_INT, "-subdivisions", "subdivisions", "Subdivisions", - "2", -1, Tk_Offset(AxisOptions, reqNumMinorTicks), 0, NULL, RESET}, - {TK_OPTION_ANCHOR, "-tickanchor", "tickAnchor", "Anchor", - "c", -1, Tk_Offset(AxisOptions, reqTickAnchor), 0, NULL, LAYOUT}, - {TK_OPTION_FONT, "-tickfont", "tickFont", "Font", - STD_FONT_SMALL, -1, Tk_Offset(AxisOptions, tickFont), 0, NULL, LAYOUT}, - {TK_OPTION_PIXELS, "-ticklength", "tickLength", "TickLength", - "8", -1, Tk_Offset(AxisOptions, tickLength), 0, NULL, LAYOUT}, - {TK_OPTION_INT, "-tickdefault", "tickDefault", "TickDefault", - "4", -1, Tk_Offset(AxisOptions, reqNumMajorTicks), 0, NULL, RESET}, - {TK_OPTION_STRING, "-title", "title", "Title", - NULL, -1, Tk_Offset(AxisOptions, title), TK_OPTION_NULL_OK, NULL, LAYOUT}, - {TK_OPTION_BOOLEAN, "-titlealternate", "titleAlternate", "TitleAlternate", - "no", -1, Tk_Offset(AxisOptions, titleAlternate), 0, NULL, LAYOUT}, - {TK_OPTION_COLOR, "-titlecolor", "titleColor", "TitleColor", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(AxisOptions, titleColor), - 0, NULL, CACHE}, - {TK_OPTION_FONT, "-titlefont", "titleFont", "TitleFont", - STD_FONT_NORMAL, -1, Tk_Offset(AxisOptions, titleFont), 0, NULL, LAYOUT}, - {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} -}; - -TickLabel::TickLabel(char* str) -{ - anchorPos.x = DBL_MAX; - anchorPos.y = DBL_MAX; - width =0; - height =0; - string = dupstr(str); -} - -TickLabel::~TickLabel() -{ - if (string) - delete [] string; -} - -Ticks::Ticks(int cnt) -{ - nTicks =cnt; - values = new double[cnt]; -} - -Ticks::~Ticks() -{ - if (values) - delete [] values; -} - -Axis::Axis(Graph* graphPtr, const char* name, int margin, Tcl_HashEntry* hPtr) -{ - ops_ = (AxisOptions*)calloc(1, sizeof(AxisOptions)); - AxisOptions* ops = (AxisOptions*)ops_; - - graphPtr_ = graphPtr; - classId_ = CID_NONE; - name_ = dupstr(name); - className_ = dupstr("none"); - - hashPtr_ = hPtr; - refCount_ =0; - use_ =0; - active_ =0; - - link =NULL; - chain =NULL; - - titlePos_.x =0; - titlePos_.y =0; - titleWidth_ =0; - titleHeight_ =0; - min_ =0; - max_ =0; - scrollMin_ =0; - scrollMax_ =0; - valueRange_.min =0; - valueRange_.max =0; - valueRange_.range =0; - valueRange_.scale =0; - axisRange_.min =0; - axisRange_.max =0; - axisRange_.range =0; - axisRange_.scale =0; - prevMin_ =0; - prevMax_ =0; - t1Ptr_ =NULL; - t2Ptr_ =NULL; - minorSweep_.initial =0; - minorSweep_.step =0; - minorSweep_.nSteps =0; - majorSweep_.initial =0; - majorSweep_.step =0; - majorSweep_.nSteps =0; - - margin_ = margin; - segments_ =NULL; - nSegments_ =0; - tickLabels_ = new Chain(); - left_ =0; - right_ =0; - top_ =0; - bottom_ =0; - width_ =0; - height_ =0; - maxTickWidth_ =0; - maxTickHeight_ =0; - tickAnchor_ = TK_ANCHOR_N; - tickGC_ =NULL; - activeTickGC_ =NULL; - titleAngle_ =0; - titleAnchor_ = TK_ANCHOR_N; - screenScale_ =0; - screenMin_ =0; - screenRange_ =0; - - ops->reqMin =NAN; - ops->reqMax =NAN; - ops->reqScrollMin =NAN; - ops->reqScrollMax =NAN; - - ops->limitsTextStyle.anchor =TK_ANCHOR_NW; - ops->limitsTextStyle.color =NULL; - ops->limitsTextStyle.font =NULL; - ops->limitsTextStyle.angle =0; - ops->limitsTextStyle.justify =TK_JUSTIFY_LEFT; - - optionTable_ = Tk_CreateOptionTable(graphPtr_->interp_, optionSpecs); -} - -Axis::~Axis() -{ - AxisOptions* ops = (AxisOptions*)ops_; - - graphPtr_->bindTable_->deleteBindings(this); - - if (link) - chain->deleteLink(link); - - if (hashPtr_) - Tcl_DeleteHashEntry(hashPtr_); - - if (name_) - delete [] name_; - if (className_) - delete [] className_; - - if (tickGC_) - Tk_FreeGC(graphPtr_->display_, tickGC_); - - if (activeTickGC_) - Tk_FreeGC(graphPtr_->display_, activeTickGC_); - - if (ops->major.segments) - delete [] ops->major.segments; - if (ops->major.gc) - graphPtr_->freePrivateGC(ops->major.gc); - - if (ops->minor.segments) - delete [] ops->minor.segments; - if (ops->minor.gc) - graphPtr_->freePrivateGC(ops->minor.gc); - - if (t1Ptr_) - delete t1Ptr_; - if (t2Ptr_) - delete t2Ptr_; - - freeTickLabels(); - - delete tickLabels_; - - if (segments_) - delete [] segments_; - - Tk_FreeConfigOptions((char*)ops_, optionTable_, graphPtr_->tkwin_); - free(ops_); -} - -int Axis::configure() -{ - AxisOptions* ops = (AxisOptions*)ops_; - - // Check the requested axis limits. Can't allow -min to be greater than - // -max. Do this regardless of -checklimits option. We want to always - // detect when the user has zoomed in beyond the precision of the data - - if (((!isnan(ops->reqMin)) && (!isnan(ops->reqMax))) && - (ops->reqMin >= ops->reqMax)) { - ostringstream str; - str << "impossible axis limits (-min " << ops->reqMin - << " >= -max " << ops->reqMax << ") for \"" - << name_ << "\"" << ends; - Tcl_AppendResult(graphPtr_->interp_, str.str().c_str(), NULL); - return TCL_ERROR; - } - - scrollMin_ = ops->reqScrollMin; - scrollMax_ = ops->reqScrollMax; - if (ops->logScale) { - if (ops->checkLimits) { - // Check that the logscale limits are positive. - if ((!isnan(ops->reqMin)) && (ops->reqMin <= 0.0)) { - ostringstream str; - str << "bad logscale -min limit \"" << ops->reqMin - << "\" for axis \"" << name_ << "\"" << ends; - Tcl_AppendResult(graphPtr_->interp_, str.str().c_str(), NULL); - return TCL_ERROR; - } - } - if ((!isnan(scrollMin_)) && (scrollMin_ <= 0.0)) - scrollMin_ = NAN; - - if ((!isnan(scrollMax_)) && (scrollMax_ <= 0.0)) - scrollMax_ = NAN; - } - - float angle = fmod(ops->tickAngle, 360.0); - if (angle < 0.0f) - angle += 360.0f; - - ops->tickAngle = angle; - resetTextStyles(); - - titleWidth_ = titleHeight_ = 0; - if (ops->title) { - int w, h; - graphPtr_->getTextExtents(ops->titleFont, ops->title, -1, &w, &h); - titleWidth_ = (unsigned short int)w; - titleHeight_ = (unsigned short int)h; - } - - return TCL_OK; -} - -void Axis::map(int offset, int margin) -{ - if (isHorizontal()) { - screenMin_ = graphPtr_->hOffset_; - width_ = graphPtr_->right_ - graphPtr_->left_; - screenRange_ = graphPtr_->hRange_; - } - else { - screenMin_ = graphPtr_->vOffset_; - height_ = graphPtr_->bottom_ - graphPtr_->top_; - screenRange_ = graphPtr_->vRange_; - } - screenScale_ = 1.0 / screenRange_; - - AxisInfo info; - offsets(margin, offset, &info); - makeSegments(&info); -} - -void Axis::mapStacked(int count, int margin) -{ - AxisOptions* ops = (AxisOptions*)ops_; - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - - if (Chain_GetLength(gops->margins[margin_].axes) > 1 - || ops->reqNumMajorTicks <= 0) - ops->reqNumMajorTicks = 4; - - unsigned int slice; - if (isHorizontal()) { - slice = graphPtr_->hRange_ / Chain_GetLength(gops->margins[margin].axes); - screenMin_ = graphPtr_->hOffset_; - width_ = slice; - } - else { - slice = graphPtr_->vRange_ / Chain_GetLength(gops->margins[margin].axes); - screenMin_ = graphPtr_->vOffset_; - height_ = slice; - } - - int w, h; - graphPtr_->getTextExtents(ops->tickFont, "0", 1, &w, &h); - screenMin_ += (slice * count) + 2 + h / 2; - screenRange_ = slice - 2 * 2 - h; - screenScale_ = 1.0f / screenRange_; - - AxisInfo info; - offsets(margin, 0, &info); - makeSegments(&info); -} - -void Axis::mapGridlines() -{ - AxisOptions* ops = (AxisOptions*)ops_; - - Ticks* t1Ptr = t1Ptr_; - if (!t1Ptr) - t1Ptr = generateTicks(&majorSweep_); - - Ticks* t2Ptr = t2Ptr_; - if (!t2Ptr) - t2Ptr = generateTicks(&minorSweep_); - - int needed = t1Ptr->nTicks; - if (ops->showGridMinor) - needed += (t1Ptr->nTicks * t2Ptr->nTicks); - - if (needed == 0) { - if (t1Ptr != t1Ptr_) - delete t1Ptr; - if (t2Ptr != t2Ptr_) - delete t2Ptr; - - return; - } - - needed = t1Ptr->nTicks; - if (needed != ops->major.nAllocated) { - if (ops->major.segments) { - delete [] ops->major.segments; - ops->major.segments = NULL; - } - ops->major.segments = new Segment2d[needed]; - ops->major.nAllocated = needed; - } - needed = (t1Ptr->nTicks * t2Ptr->nTicks); - if (needed != ops->minor.nAllocated) { - if (ops->minor.segments) { - delete [] ops->minor.segments; - ops->minor.segments = NULL; - } - ops->minor.segments = new Segment2d[needed]; - ops->minor.nAllocated = needed; - } - - Segment2d* s1 = ops->major.segments; - Segment2d* s2 = ops->minor.segments; - for (int ii=0; iinTicks; ii++) { - double value = t1Ptr->values[ii]; - if (ops->showGridMinor) { - for (int jj=0; jjnTicks; jj++) { - double subValue = value + (majorSweep_.step * t2Ptr->values[jj]); - if (inRange(subValue, &axisRange_)) { - makeGridLine(subValue, s2); - s2++; - } - } - } - if (inRange(value, &axisRange_)) { - makeGridLine(value, s1); - s1++; - } - } - - if (t1Ptr != t1Ptr_) - delete t1Ptr; - if (t2Ptr != t2Ptr_) - delete t2Ptr; - - ops->major.nUsed = s1 - ops->major.segments; - ops->minor.nUsed = s2 - ops->minor.segments; -} - -void Axis::draw(Drawable drawable) -{ - AxisOptions* ops = (AxisOptions*)ops_; - - if (ops->hide || !use_) - return; - - if (ops->normalBg) { - int relief = active_ ? ops->activeRelief : ops->relief; - Tk_Fill3DRectangle(graphPtr_->tkwin_, drawable, ops->normalBg, - left_, top_, right_ - left_, bottom_ - top_, - ops->borderWidth, relief); - } - - if (ops->title) { - TextStyle ts(graphPtr_); - TextStyleOptions* tops = (TextStyleOptions*)ts.ops(); - - tops->angle = titleAngle_; - tops->font = ops->titleFont; - tops->anchor = titleAnchor_; - tops->color = active_ ? ops->activeFgColor : ops->titleColor; - tops->justify = ops->titleJustify; - - ts.xPad_ = 1; - ts.yPad_ = 0; - ts.drawText(drawable, ops->title, titlePos_.x, titlePos_.y); - } - - if (ops->scrollCmdObjPtr) { - double worldMin = valueRange_.min; - double worldMax = valueRange_.max; - if (!isnan(scrollMin_)) - worldMin = scrollMin_; - if (!isnan(scrollMax_)) - worldMax = scrollMax_; - - double viewMin = min_; - double viewMax = max_; - if (viewMin < worldMin) - viewMin = worldMin; - if (viewMax > worldMax) - viewMax = worldMax; - - if (ops->logScale) { - worldMin = log10(worldMin); - worldMax = log10(worldMax); - viewMin = log10(viewMin); - viewMax = log10(viewMax); - } - - double worldWidth = worldMax - worldMin; - double viewWidth = viewMax - viewMin; - int isHoriz = isHorizontal(); - - double fract; - if (isHoriz != ops->descending) - fract = (viewMin - worldMin) / worldWidth; - else - fract = (worldMax - viewMax) / worldWidth; - - fract = AdjustViewport(fract, viewWidth / worldWidth); - - if (isHoriz != ops->descending) { - viewMin = (fract * worldWidth); - min_ = viewMin + worldMin; - max_ = min_ + viewWidth; - viewMax = viewMin + viewWidth; - if (ops->logScale) { - min_ = EXP10(min_); - max_ = EXP10(max_); - } - updateScrollbar(graphPtr_->interp_, ops->scrollCmdObjPtr, - viewMin, viewMax, worldWidth); - } - else { - viewMax = (fract * worldWidth); - max_ = worldMax - viewMax; - min_ = max_ - viewWidth; - viewMin = viewMax + viewWidth; - if (ops->logScale) { - min_ = EXP10(min_); - max_ = EXP10(max_); - } - updateScrollbar(graphPtr_->interp_, ops->scrollCmdObjPtr, - viewMax, viewMin, worldWidth); - } - } - - if (ops->showTicks) { - TextStyle ts(graphPtr_); - TextStyleOptions* tops = (TextStyleOptions*)ts.ops(); - - tops->angle = ops->tickAngle; - tops->font = ops->tickFont; - tops->anchor = tickAnchor_; - tops->color = active_ ? ops->activeFgColor : ops->tickColor; - - ts.xPad_ = 2; - ts.yPad_ = 0; - - for (ChainLink* link = Chain_FirstLink(tickLabels_); link; - link = Chain_NextLink(link)) { - TickLabel* labelPtr = (TickLabel*)Chain_GetValue(link); - ts.drawText(drawable, labelPtr->string, labelPtr->anchorPos.x, - labelPtr->anchorPos.y); - } - } - - if ((nSegments_ > 0) && (ops->lineWidth > 0)) { - GC gc = active_ ? activeTickGC_ : tickGC_; - graphPtr_->drawSegments(drawable, gc, segments_, nSegments_); - } -} - -void Axis::drawGrids(Drawable drawable) -{ - AxisOptions* ops = (AxisOptions*)ops_; - - if (ops->hide || !ops->showGrid || !use_) - return; - - graphPtr_->drawSegments(drawable, ops->major.gc, - ops->major.segments, ops->major.nUsed); - - if (ops->showGridMinor) - graphPtr_->drawSegments(drawable, ops->minor.gc, - ops->minor.segments, ops->minor.nUsed); -} - -void Axis::drawLimits(Drawable drawable) -{ - AxisOptions* ops = (AxisOptions*)ops_; - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - - if (!ops->limitsFormat) - return; - - int vMin = graphPtr_->left_ + gops->xPad + 2; - int vMax = vMin; - int hMin = graphPtr_->bottom_ - gops->yPad - 2; - int hMax = hMin; - - const int spacing =8; - int isHoriz = isHorizontal(); - char* minPtr =NULL; - char* maxPtr =NULL; - char minString[200]; - char maxString[200]; - const char* fmt = ops->limitsFormat; - if (fmt && *fmt) { - minPtr = minString; - snprintf(minString, 200, fmt, axisRange_.min); - - maxPtr = maxString; - snprintf(maxString, 200, fmt, axisRange_.max); - } - if (ops->descending) { - char *tmp = minPtr; - minPtr = maxPtr; - maxPtr = tmp; - } - - TextStyle ts(graphPtr_, &ops->limitsTextStyle); - if (maxPtr) { - if (isHoriz) { - ops->limitsTextStyle.angle = 90.0; - ops->limitsTextStyle.anchor = TK_ANCHOR_SE; - - int ww, hh; - ts.drawText2(drawable, maxPtr, graphPtr_->right_, hMax, &ww, &hh); - hMax -= (hh + spacing); - } - else { - ops->limitsTextStyle.angle = 0.0; - ops->limitsTextStyle.anchor = TK_ANCHOR_NW; - - int ww, hh; - ts.drawText2(drawable, maxPtr, vMax, graphPtr_->top_, &ww, &hh); - vMax += (ww + spacing); - } - } - if (minPtr) { - ops->limitsTextStyle.anchor = TK_ANCHOR_SW; - - if (isHoriz) { - ops->limitsTextStyle.angle = 90.0; - - int ww, hh; - ts.drawText2(drawable, minPtr, graphPtr_->left_, hMin, &ww, &hh); - hMin -= (hh + spacing); - } - else { - ops->limitsTextStyle.angle = 0.0; - - int ww, hh; - ts.drawText2(drawable, minPtr, vMin, graphPtr_->bottom_, &ww, &hh); - vMin += (ww + spacing); - } - } -} - -void Axis::setClass(ClassId classId) -{ - if (className_) - delete [] className_; - className_ =NULL; - - classId_ = classId; - switch (classId) { - case CID_NONE: - className_ = dupstr("none"); - break; - case CID_AXIS_X: - className_ = dupstr("XAxis"); - break; - case CID_AXIS_Y: - className_ = dupstr("YAxis"); - break; - default: - break; - } -} - -void Axis::logScale(double min, double max) -{ - AxisOptions* ops = (AxisOptions*)ops_; - - double range; - double tickMin, tickMax; - double majorStep, minorStep; - int nMajor, nMinor; - - nMajor = nMinor = 0; - majorStep = minorStep = 0.0; - tickMin = tickMax = NAN; - if (min < max) { - min = (min != 0.0) ? log10(fabs(min)) : 0.0; - max = (max != 0.0) ? log10(fabs(max)) : 1.0; - - tickMin = floor(min); - tickMax = ceil(max); - range = tickMax - tickMin; - - if (range > 10) { - // There are too many decades to display a major tick at every - // decade. Instead, treat the axis as a linear scale - range = niceNum(range, 0); - majorStep = niceNum(range / ops->reqNumMajorTicks, 1); - tickMin = floor(tickMin/majorStep)*majorStep; - tickMax = ceil(tickMax/majorStep)*majorStep; - nMajor = (int)((tickMax - tickMin) / majorStep) + 1; - minorStep = EXP10(floor(log10(majorStep))); - if (minorStep == majorStep) { - nMinor = 4; - minorStep = 0.2; - } - else - nMinor = (majorStep/minorStep) - 1; - } - else { - if (tickMin == tickMax) - tickMax++; - majorStep = 1.0; - nMajor = (int)(tickMax - tickMin + 1); /* FIXME: Check this. */ - - minorStep = 0.0; /* This is a special hack to pass - * information to the GenerateTicks - * routine. An interval of 0.0 tells 1) - * this is a minor sweep and 2) the axis - * is log scale. */ - nMinor = 10; - } - if (!ops->looseMin || (ops->looseMin && !isnan(ops->reqMin))) { - tickMin = min; - nMajor++; - } - if (!ops->looseMax || (ops->looseMax && !isnan(ops->reqMax))) { - tickMax = max; - } - } - majorSweep_.step = majorStep; - majorSweep_.initial = floor(tickMin); - majorSweep_.nSteps = nMajor; - minorSweep_.initial = minorSweep_.step = minorStep; - minorSweep_.nSteps = nMinor; - - setRange(&axisRange_, tickMin, tickMax); -} - -void Axis::linearScale(double min, double max) -{ - AxisOptions* ops = (AxisOptions*)ops_; - - unsigned int nTicks = 0; - double step = 1.0; - double axisMin =NAN; - double axisMax =NAN; - double tickMin =NAN; - double tickMax =NAN; - - if (min < max) { - double range = max - min; - if (ops->reqStep > 0.0) { - step = ops->reqStep; - while ((2 * step) >= range) - step *= 0.5; - } - else { - range = niceNum(range, 0); - step = niceNum(range / ops->reqNumMajorTicks, 1); - } - - axisMin = tickMin = floor(min / step) * step + 0.0; - axisMax = tickMax = ceil(max / step) * step + 0.0; - - nTicks = ((tickMax-tickMin) / step) + 1; - } - majorSweep_.step = step; - majorSweep_.initial = tickMin; - majorSweep_.nSteps = nTicks; - - /* - * The limits of the axis are either the range of the data ("tight") or at - * the next outer tick interval ("loose"). The looseness or tightness has - * to do with how the axis fits the range of data values. This option is - * overridden when the user sets an axis limit (by either -min or -max - * option). The axis limit is always at the selected limit (otherwise we - * assume that user would have picked a different number). - */ - if (!ops->looseMin || (ops->looseMin && !isnan(ops->reqMin))) - axisMin = min; - - if (!ops->looseMax || (ops->looseMax && !isnan(ops->reqMax))) - axisMax = max; - - setRange(&axisRange_, axisMin, axisMax); - - if (ops->reqNumMinorTicks > 0) { - nTicks = ops->reqNumMinorTicks - 1; - step = 1.0 / (nTicks + 1); - } - else { - nTicks = 0; - step = 0.5; - } - minorSweep_.initial = minorSweep_.step = step; - minorSweep_.nSteps = nTicks; -} - -void Axis::setRange(AxisRange *rangePtr, double min, double max) -{ - rangePtr->min = min; - rangePtr->max = max; - rangePtr->range = max - min; - if (fabs(rangePtr->range) < DBL_EPSILON) { - rangePtr->range = 1.0; - } - rangePtr->scale = 1.0 / rangePtr->range; -} - -void Axis::fixRange() -{ - AxisOptions* ops = (AxisOptions*)ops_; - - // When auto-scaling, the axis limits are the bounds of the element data. - // If no data exists, set arbitrary limits (wrt to log/linear scale). - double min = valueRange_.min; - double max = valueRange_.max; - - // Check the requested axis limits. Can't allow -min to be greater - // than -max, or have undefined log scale limits. */ - if (((!isnan(ops->reqMin)) && (!isnan(ops->reqMax))) && - (ops->reqMin >= ops->reqMax)) { - ops->reqMin = ops->reqMax = NAN; - } - if (ops->logScale) { - if ((!isnan(ops->reqMin)) && (ops->reqMin <= 0.0)) - ops->reqMin = NAN; - - if ((!isnan(ops->reqMax)) && (ops->reqMax <= 0.0)) - ops->reqMax = NAN; - } - - if (min == DBL_MAX) { - if (!isnan(ops->reqMin)) - min = ops->reqMin; - else - min = (ops->logScale) ? 0.001 : 0.0; - } - if (max == -DBL_MAX) { - if (!isnan(ops->reqMax)) - max = ops->reqMax; - else - max = 1.0; - } - if (min >= max) { - - // There is no range of data (i.e. min is not less than max), so - // manufacture one. - if (min == 0.0) - min = 0.0, max = 1.0; - else - max = min + (fabs(min) * 0.1); - } - setRange(&valueRange_, min, max); - - // The axis limits are either the current data range or overridden by the - // values selected by the user with the -min or -max options. - min_ = min; - max_ = max; - if (!isnan(ops->reqMin)) - min_ = ops->reqMin; - - if (!isnan(ops->reqMax)) - max_ = ops->reqMax; - - if (max_ < min_) { - // If the limits still don't make sense, it's because one limit - // configuration option (-min or -max) was set and the other default - // (based upon the data) is too small or large. Remedy this by making - // up a new min or max from the user-defined limit. - if (isnan(ops->reqMin)) - min_ = max_ - (fabs(max_) * 0.1); - - if (isnan(ops->reqMax)) - max_ = min_ + (fabs(max_) * 0.1); - } - - // If a window size is defined, handle auto ranging by shifting the axis - // limits. - if ((ops->windowSize > 0.0) && - (isnan(ops->reqMin)) && (isnan(ops->reqMax))) { - if (ops->shiftBy < 0.0) - ops->shiftBy = 0.0; - - max = min_ + ops->windowSize; - if (max_ >= max) { - if (ops->shiftBy > 0.0) - max = ceil(max_/ops->shiftBy)*ops->shiftBy; - min_ = max - ops->windowSize; - } - max_ = max; - } - if ((max_ != prevMax_) || - (min_ != prevMin_)) { - /* and save the previous minimum and maximum values */ - prevMin_ = min_; - prevMax_ = max_; - } -} - -// Reference: Paul Heckbert, "Nice Numbers for Graph Labels", -// Graphics Gems, pp 61-63. -double Axis::niceNum(double x, int round) -{ - double expt; /* Exponent of x */ - double frac; /* Fractional part of x */ - double nice; /* Nice, rounded fraction */ - - expt = floor(log10(x)); - frac = x / EXP10(expt); /* between 1 and 10 */ - if (round) { - if (frac < 1.5) { - nice = 1.0; - } else if (frac < 3.0) { - nice = 2.0; - } else if (frac < 7.0) { - nice = 5.0; - } else { - nice = 10.0; - } - } else { - if (frac <= 1.0) { - nice = 1.0; - } else if (frac <= 2.0) { - nice = 2.0; - } else if (frac <= 5.0) { - nice = 5.0; - } else { - nice = 10.0; - } - } - return nice * EXP10(expt); -} - -int Axis::inRange(double x, AxisRange *rangePtr) -{ - if (rangePtr->range < DBL_EPSILON) - return (fabs(rangePtr->max - x) >= DBL_EPSILON); - else { - double norm; - - norm = (x - rangePtr->min) * rangePtr->scale; - return ((norm >= -DBL_EPSILON) && ((norm - 1.0) < DBL_EPSILON)); - } -} - -int Axis::isHorizontal() -{ - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - return ((classId_ == CID_AXIS_Y) == gops->inverted); -} - -void Axis::freeTickLabels() -{ - Chain* chain = tickLabels_; - for (ChainLink* link = Chain_FirstLink(chain); link; - link = Chain_NextLink(link)) { - TickLabel* labelPtr = (TickLabel*)Chain_GetValue(link); - delete labelPtr; - } - chain->reset(); -} - -TickLabel* Axis::makeLabel(double value) -{ -#define TICK_LABEL_SIZE 200 - - AxisOptions* ops = (AxisOptions*)ops_; - - char string[TICK_LABEL_SIZE + 1]; - if (ops->logScale) - snprintf(string, TICK_LABEL_SIZE, "1E%d", int(value)); - else - snprintf(string, TICK_LABEL_SIZE, "%.*G", 15, value); - - if (ops->formatCmd) { - Tcl_Interp* interp = graphPtr_->interp_; - Tk_Window tkwin = graphPtr_->tkwin_; - - // A TCL proc was designated to format tick labels. Append the path - // name of the widget and the default tick label as arguments when - // invoking it. Copy and save the new label from interp->result. - Tcl_ResetResult(interp); - if (Tcl_VarEval(interp, ops->formatCmd, " ", Tk_PathName(tkwin), - " ", string, NULL) != TCL_OK) { - Tcl_BackgroundError(interp); - } - else { - // The proc could return a string of any length, so arbitrarily - // limit it to what will fit in the return string. - strncpy(string, Tcl_GetStringResult(interp), TICK_LABEL_SIZE); - string[TICK_LABEL_SIZE] = '\0'; - - Tcl_ResetResult(interp); /* Clear the interpreter's result. */ - } - } - - TickLabel* labelPtr = new TickLabel(string); - - return labelPtr; -} - -double Axis::invHMap(double x) -{ - AxisOptions* ops = (AxisOptions*)ops_; - double value; - - x = (double)(x - screenMin_) * screenScale_; - if (ops->descending) { - x = 1.0 - x; - } - value = (x * axisRange_.range) + axisRange_.min; - if (ops->logScale) { - value = EXP10(value); - } - return value; -} - -double Axis::invVMap(double y) -{ - AxisOptions* ops = (AxisOptions*)ops_; - double value; - - y = (double)(y - screenMin_) * screenScale_; - if (ops->descending) { - y = 1.0 - y; - } - value = ((1.0 - y) * axisRange_.range) + axisRange_.min; - if (ops->logScale) { - value = EXP10(value); - } - return value; -} - -double Axis::hMap(double x) -{ - AxisOptions* ops = (AxisOptions*)ops_; - if ((ops->logScale) && (x != 0.0)) { - x = log10(fabs(x)); - } - /* Map graph coordinate to normalized coordinates [0..1] */ - x = (x - axisRange_.min) * axisRange_.scale; - if (ops->descending) { - x = 1.0 - x; - } - return (x * screenRange_ + screenMin_); -} - -double Axis::vMap(double y) -{ - AxisOptions* ops = (AxisOptions*)ops_; - if ((ops->logScale) && (y != 0.0)) { - y = log10(fabs(y)); - } - /* Map graph coordinate to normalized coordinates [0..1] */ - y = (y - axisRange_.min) * axisRange_.scale; - if (ops->descending) { - y = 1.0 - y; - } - return ((1.0 - y) * screenRange_ + screenMin_); -} - -void Axis::getDataLimits(double min, double max) -{ - if (valueRange_.min > min) - valueRange_.min = min; - - if (valueRange_.max < max) - valueRange_.max = max; -} - -void Axis::resetTextStyles() -{ - AxisOptions* ops = (AxisOptions*)ops_; - - XGCValues gcValues; - unsigned long gcMask; - gcMask = (GCForeground | GCLineWidth | GCCapStyle); - gcValues.foreground = ops->tickColor->pixel; - gcValues.font = Tk_FontId(ops->tickFont); - gcValues.line_width = ops->lineWidth; - gcValues.cap_style = CapProjecting; - - GC newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); - if (tickGC_) - Tk_FreeGC(graphPtr_->display_, tickGC_); - tickGC_ = newGC; - - // Assuming settings from above GC - gcValues.foreground = ops->activeFgColor->pixel; - newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); - if (activeTickGC_) - Tk_FreeGC(graphPtr_->display_, activeTickGC_); - activeTickGC_ = newGC; - - gcValues.background = gcValues.foreground = ops->major.color->pixel; - gcValues.line_width = ops->major.lineWidth; - gcMask = (GCForeground | GCBackground | GCLineWidth); - if (LineIsDashed(ops->major.dashes)) { - gcValues.line_style = LineOnOffDash; - gcMask |= GCLineStyle; - } - newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); - if (LineIsDashed(ops->major.dashes)) - graphPtr_->setDashes(newGC, &ops->major.dashes); - - if (ops->major.gc) - graphPtr_->freePrivateGC(ops->major.gc); - - ops->major.gc = newGC; - - gcValues.background = gcValues.foreground = ops->minor.color->pixel; - gcValues.line_width = ops->minor.lineWidth; - gcMask = (GCForeground | GCBackground | GCLineWidth); - if (LineIsDashed(ops->minor.dashes)) { - gcValues.line_style = LineOnOffDash; - gcMask |= GCLineStyle; - } - newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); - if (LineIsDashed(ops->minor.dashes)) - graphPtr_->setDashes(newGC, &ops->minor.dashes); - - if (ops->minor.gc) - graphPtr_->freePrivateGC(ops->minor.gc); - - ops->minor.gc = newGC; -} - -void Axis::makeLine(int line, Segment2d *sp) -{ - AxisOptions* ops = (AxisOptions*)ops_; - - double min = axisRange_.min; - double max = axisRange_.max; - if (ops->logScale) { - min = EXP10(min); - max = EXP10(max); - } - if (isHorizontal()) { - sp->p.x = hMap(min); - sp->q.x = hMap(max); - sp->p.y = sp->q.y = line; - } - else { - sp->q.x = sp->p.x = line; - sp->p.y = vMap(min); - sp->q.y = vMap(max); - } -} - -void Axis::offsets(int margin, int offset, AxisInfo *infoPtr) -{ - AxisOptions* ops = (AxisOptions*)ops_; - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - - int axisLine =0; - int t1 =0; - int t2 =0; - int labelOffset =AXIS_PAD_TITLE; - int tickLabel =0; - - float titleAngle[4] = {0.0, 90.0, 0.0, 270.0}; - titleAngle_ = titleAngle[margin]; - Margin *marginPtr = gops->margins + margin; - - if (ops->lineWidth > 0) { - if (ops->showTicks) { - t1 = ops->tickLength; - t2 = (t1 * 10) / 15; - } - labelOffset = t1 + AXIS_PAD_TITLE; - if (ops->exterior) - labelOffset += ops->lineWidth; - } - - int axisPad =0; - - // Adjust offset for the interior border width and the line width */ - // fixme - int pad = 0; - // int pad = 1; - // if (graphPtr_->plotBW > 0) - // pad += graphPtr_->plotBW + 1; - - // Pre-calculate the x-coordinate positions of the axis, tick labels, and - // the individual major and minor ticks. - int inset = pad + ops->lineWidth / 2; - - switch (margin) { - case MARGIN_TOP: - { - axisLine = graphPtr_->top_; - if (ops->exterior) { - axisLine -= gops->plotBW + axisPad + ops->lineWidth / 2; - tickLabel = axisLine - 2; - if (ops->lineWidth > 0) - tickLabel -= ops->tickLength; - } - else { - if (gops->plotRelief == TK_RELIEF_SOLID) - axisLine--; - - axisLine -= axisPad + ops->lineWidth / 2; - tickLabel = graphPtr_->top_ - gops->plotBW - 2; - } - - int mark = graphPtr_->top_ - offset - pad; - tickAnchor_ = TK_ANCHOR_S; - left_ = screenMin_ - inset - 2; - right_ = screenMin_ + screenRange_ + inset - 1; - if (gops->stackAxes) - top_ = mark - marginPtr->axesOffset; - else - top_ = mark - height_; - bottom_ = mark; - - int x, y; - if (ops->titleAlternate) { - x = graphPtr_->right_ + AXIS_PAD_TITLE; - y = mark - (height_ / 2); - titleAnchor_ = TK_ANCHOR_W; - } - else { - x = (right_ + left_) / 2; - if (gops->stackAxes) - y = mark - marginPtr->axesOffset + AXIS_PAD_TITLE; - else - y = mark - height_ + AXIS_PAD_TITLE; - - titleAnchor_ = TK_ANCHOR_N; - } - titlePos_.x = x; - titlePos_.y = y; - } - break; - - case MARGIN_BOTTOM: - { - /* - * ----------- bottom + plot borderwidth - * mark -------------------------------------------- - * ===================== axisLine (linewidth) - * tick - * title - * - * ===================== axisLine (linewidth) - * ----------- bottom + plot borderwidth - * mark -------------------------------------------- - * tick - * title - */ - axisLine = graphPtr_->bottom_; - if (gops->plotRelief == TK_RELIEF_SOLID) - axisLine++; - - if (ops->exterior) { - axisLine += gops->plotBW + axisPad + ops->lineWidth / 2; - tickLabel = axisLine + 2; - if (ops->lineWidth > 0) - tickLabel += ops->tickLength; - } - else { - axisLine -= axisPad + ops->lineWidth / 2; - tickLabel = graphPtr_->bottom_ + gops->plotBW + 2; - } - - int mark = graphPtr_->bottom_ + offset; - float fangle = fmod(ops->tickAngle, 90.0); - if (fangle == 0.0) - tickAnchor_ = TK_ANCHOR_N; - else { - int quadrant = (int)(ops->tickAngle / 90.0); - if ((quadrant == 0) || (quadrant == 2)) - tickAnchor_ = TK_ANCHOR_NE; - else - tickAnchor_ = TK_ANCHOR_NW; - } - - left_ = screenMin_ - inset - 2; - right_ = screenMin_ + screenRange_ + inset - 1; - top_ = graphPtr_->bottom_ + labelOffset - t1; - if (gops->stackAxes) - bottom_ = mark + marginPtr->axesOffset - 1; - else - bottom_ = mark + height_ - 1; - - int x, y; - if (ops->titleAlternate) { - x = graphPtr_->right_ + AXIS_PAD_TITLE; - y = mark + (height_ / 2); - titleAnchor_ = TK_ANCHOR_W; - } - else { - x = (right_ + left_) / 2; - if (gops->stackAxes) - y = mark + marginPtr->axesOffset - AXIS_PAD_TITLE; - else - y = mark + height_ - AXIS_PAD_TITLE; - titleAnchor_ = TK_ANCHOR_S; - } - titlePos_.x = x; - titlePos_.y = y; - } - break; - - case MARGIN_LEFT: - { - /* - * mark - * | : - * | : - * | : - * | : - * | : - * axisLine - */ - /* - * Exterior axis - * + plotarea right - * |A|B|C|D|E|F|G|H - * |right - * A = plot pad - * B = plot border width - * C = axis pad - * D = axis line - * E = tick length - * F = tick label - * G = graph border width - * H = highlight thickness - */ - /* - * Interior axis - * + plotarea right - * |A|B|C|D|E|F|G|H - * |right - * A = plot pad - * B = tick length - * C = axis line width - * D = axis pad - * E = plot border width - * F = tick label - * G = graph border width - * H = highlight thickness - */ - axisLine = graphPtr_->left_; - if (ops->exterior) { - axisLine -= gops->plotBW + axisPad + ops->lineWidth / 2; - tickLabel = axisLine - 2; - if (ops->lineWidth > 0) - tickLabel -= ops->tickLength; - } - else { - if (gops->plotRelief == TK_RELIEF_SOLID) - axisLine--; - axisLine += axisPad + ops->lineWidth / 2; - tickLabel = graphPtr_->left_ - gops->plotBW - 2; - } - - int mark = graphPtr_->left_ - offset; - tickAnchor_ = TK_ANCHOR_E; - if (gops->stackAxes) - left_ = mark - marginPtr->axesOffset; - else - left_ = mark - width_; - right_ = mark - 3; - top_ = screenMin_ - inset - 2; - bottom_ = screenMin_ + screenRange_ + inset - 1; - - int x, y; - if (ops->titleAlternate) { - x = mark - (width_ / 2); - y = graphPtr_->top_ - AXIS_PAD_TITLE; - titleAnchor_ = TK_ANCHOR_SW; - } - else { - if (gops->stackAxes) - x = mark - marginPtr->axesOffset; - else - x = mark - width_ + AXIS_PAD_TITLE; - y = (bottom_ + top_) / 2; - titleAnchor_ = TK_ANCHOR_W; - } - titlePos_.x = x; - titlePos_.y = y; - } - break; - - case MARGIN_RIGHT: - { - axisLine = graphPtr_->right_; - if (gops->plotRelief == TK_RELIEF_SOLID) - axisLine++; - - if (ops->exterior) { - axisLine += gops->plotBW + axisPad + ops->lineWidth / 2; - tickLabel = axisLine + 2; - if (ops->lineWidth > 0) - tickLabel += ops->tickLength; - } - else { - axisLine -= axisPad + ops->lineWidth / 2; - tickLabel = graphPtr_->right_ + gops->plotBW + 2; - } - - int mark = graphPtr_->right_ + offset + pad; - tickAnchor_ = TK_ANCHOR_W; - left_ = mark; - if (gops->stackAxes) - right_ = mark + marginPtr->axesOffset - 1; - else - right_ = mark + width_ - 1; - - top_ = screenMin_ - inset - 2; - bottom_ = screenMin_ + screenRange_ + inset -1; - - int x, y; - if (ops->titleAlternate) { - x = mark + (width_ / 2); - y = graphPtr_->top_ - AXIS_PAD_TITLE; - titleAnchor_ = TK_ANCHOR_SE; - } - else { - if (gops->stackAxes) - x = mark + marginPtr->axesOffset - AXIS_PAD_TITLE; - else - x = mark + width_ - AXIS_PAD_TITLE; - - y = (bottom_ + top_) / 2; - titleAnchor_ = TK_ANCHOR_E; - } - titlePos_.x = x; - titlePos_.y = y; - } - break; - - case MARGIN_NONE: - axisLine = 0; - break; - } - - if ((margin == MARGIN_LEFT) || (margin == MARGIN_TOP)) { - t1 = -t1; - t2 = -t2; - labelOffset = -labelOffset; - } - - infoPtr->axis = axisLine; - infoPtr->t1 = axisLine + t1; - infoPtr->t2 = axisLine + t2; - if (tickLabel > 0) - infoPtr->label = tickLabel; - else - infoPtr->label = axisLine + labelOffset; - - if (!ops->exterior) { - infoPtr->t1 = axisLine - t1; - infoPtr->t2 = axisLine - t2; - } -} - -void Axis::makeTick(double value, int tick, int line, Segment2d *sp) -{ - AxisOptions* ops = (AxisOptions*)ops_; - - if (ops->logScale) - value = EXP10(value); - - if (isHorizontal()) { - sp->p.x = hMap(value); - sp->p.y = line; - sp->q.x = sp->p.x; - sp->q.y = tick; - } - else { - sp->p.x = line; - sp->p.y = vMap(value); - sp->q.x = tick; - sp->q.y = sp->p.y; - } -} - -void Axis::makeSegments(AxisInfo *infoPtr) -{ - AxisOptions* ops = (AxisOptions*)ops_; - - if (segments_) { - delete [] segments_; - segments_ = NULL; - } - - Ticks* t1Ptr = ops->t1UPtr ? ops->t1UPtr : t1Ptr_; - Ticks* t2Ptr = ops->t2UPtr ? ops->t2UPtr : t2Ptr_; - - int nMajorTicks= t1Ptr ? t1Ptr->nTicks : 0; - int nMinorTicks= t2Ptr ? t2Ptr->nTicks : 0; - - int arraySize = 1 + (nMajorTicks * (nMinorTicks + 1)); - Segment2d* segments = new Segment2d[arraySize]; - Segment2d* sp = segments; - if (ops->lineWidth > 0) { - makeLine(infoPtr->axis, sp); - sp++; - } - - if (ops->showTicks) { - int isHoriz = isHorizontal(); - for (int ii=0; iivalues[ii]; - /* Minor ticks */ - for (int jj=0; jjvalues[jj]); - if (inRange(t2, &axisRange_)) { - makeTick(t2, infoPtr->t2, infoPtr->axis, sp); - sp++; - } - } - if (!inRange(t1, &axisRange_)) - continue; - - /* Major tick */ - makeTick(t1, infoPtr->t1, infoPtr->axis, sp); - sp++; - } - - ChainLink* link = Chain_FirstLink(tickLabels_); - double labelPos = (double)infoPtr->label; - - for (int ii=0; ii< nMajorTicks; ii++) { - double t1 = t1Ptr->values[ii]; - if (ops->labelOffset) - t1 += majorSweep_.step * 0.5; - - if (!inRange(t1, &axisRange_)) - continue; - - TickLabel* labelPtr = (TickLabel*)Chain_GetValue(link); - link = Chain_NextLink(link); - Segment2d seg; - makeTick(t1, infoPtr->t1, infoPtr->axis, &seg); - // Save tick label X-Y position - if (isHoriz) { - labelPtr->anchorPos.x = seg.p.x; - labelPtr->anchorPos.y = labelPos; - } - else { - labelPtr->anchorPos.x = labelPos; - labelPtr->anchorPos.y = seg.p.y; - } - } - } - segments_ = segments; - nSegments_ = sp - segments; -} - -Ticks* Axis::generateTicks(TickSweep *sweepPtr) -{ - Ticks* ticksPtr = new Ticks(sweepPtr->nSteps); - - if (sweepPtr->step == 0.0) { - // Hack: A zero step indicates to use log values - // Precomputed log10 values [1..10] - static double logTable[] = { - 0.0, - 0.301029995663981, - 0.477121254719662, - 0.602059991327962, - 0.698970004336019, - 0.778151250383644, - 0.845098040014257, - 0.903089986991944, - 0.954242509439325, - 1.0 - }; - for (int ii=0; iinSteps; ii++) - ticksPtr->values[ii] = logTable[ii]; - } - else { - double value = sweepPtr->initial; - for (int ii=0; iinSteps; ii++) { - value = (value/sweepPtr->step)*sweepPtr->step; - ticksPtr->values[ii] = value; - value += sweepPtr->step; - } - } - - return ticksPtr; -} - -void Axis::makeGridLine(double value, Segment2d *sp) -{ - AxisOptions* ops = (AxisOptions*)ops_; - - if (ops->logScale) - value = EXP10(value); - - if (isHorizontal()) { - sp->p.x = hMap(value); - sp->p.y = graphPtr_->top_; - sp->q.x = sp->p.x; - sp->q.y = graphPtr_->bottom_; - } - else { - sp->p.x = graphPtr_->left_; - sp->p.y = vMap(value); - sp->q.x = graphPtr_->right_; - sp->q.y = sp->p.y; - } -} - -void Axis::print(PSOutput* psPtr) -{ - AxisOptions* ops = (AxisOptions*)ops_; - PostscriptOptions* pops = (PostscriptOptions*)graphPtr_->postscript_->ops_; - - if (ops->hide || !use_) - return; - - psPtr->format("%% Axis \"%s\"\n", name_); - if (pops->decorations) { - if (ops->normalBg) { - int relief = active_ ? ops->activeRelief : ops->relief; - psPtr->fill3DRectangle(ops->normalBg, left_, top_, - right_-left_, bottom_-top_, - ops->borderWidth, relief); - } - } - else { - psPtr->setClearBackground(); - psPtr->fillRectangle(left_, top_, right_-left_, bottom_-top_); - } - - if (ops->title) { - TextStyle ts(graphPtr_); - TextStyleOptions* tops = (TextStyleOptions*)ts.ops(); - - tops->angle = titleAngle_; - tops->font = ops->titleFont; - tops->anchor = titleAnchor_; - tops->color = active_ ? ops->activeFgColor : ops->titleColor; - tops->justify = ops->titleJustify; - - ts.xPad_ = 1; - ts.yPad_ = 0; - ts.printText(psPtr, ops->title, titlePos_.x, titlePos_.y); - } - - if (ops->showTicks) { - TextStyle ts(graphPtr_); - TextStyleOptions* tops = (TextStyleOptions*)ts.ops(); - - tops->angle = ops->tickAngle; - tops->font = ops->tickFont; - tops->anchor = tickAnchor_; - tops->color = active_ ? ops->activeFgColor : ops->tickColor; - - ts.xPad_ = 2; - ts.yPad_ = 0; - - for (ChainLink* link = Chain_FirstLink(tickLabels_); link; - link = Chain_NextLink(link)) { - TickLabel *labelPtr = (TickLabel*)Chain_GetValue(link); - ts.printText(psPtr, labelPtr->string, labelPtr->anchorPos.x, - labelPtr->anchorPos.y); - } - } - - if ((nSegments_ > 0) && (ops->lineWidth > 0)) { - psPtr->setLineAttributes(active_ ? ops->activeFgColor : ops->tickColor, - ops->lineWidth, (Dashes*)NULL, CapButt, JoinMiter); - psPtr->printSegments(segments_, nSegments_); - } -} - -void Axis::printGrids(PSOutput* psPtr) -{ - AxisOptions* ops = (AxisOptions*)ops_; - - if (ops->hide || !ops->showGrid || !use_) - return; - - psPtr->format("%% Axis %s: grid line attributes\n", name_); - psPtr->setLineAttributes(ops->major.color, ops->major.lineWidth, - &ops->major.dashes, CapButt, JoinMiter); - psPtr->format("%% Axis %s: major grid line segments\n", name_); - psPtr->printSegments(ops->major.segments, ops->major.nUsed); - - if (ops->showGridMinor) { - psPtr->setLineAttributes(ops->minor.color, ops->minor.lineWidth, - &ops->minor.dashes, CapButt, JoinMiter); - psPtr->format("%% Axis %s: minor grid line segments\n", name_); - psPtr->printSegments(ops->minor.segments, ops->minor.nUsed); - } -} - -void Axis::printLimits(PSOutput* psPtr) -{ - AxisOptions* ops = (AxisOptions*)ops_; - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - - if (!ops->limitsFormat) - return; - - double vMin = graphPtr_->left_ + gops->xPad + 2; - double vMax = vMin; - double hMin = graphPtr_->bottom_ - gops->yPad - 2; - double hMax = hMin; - - const int spacing =8; - int isHoriz = isHorizontal(); - char* minPtr =NULL; - char* maxPtr =NULL; - char minString[200]; - char maxString[200]; - const char* fmt = ops->limitsFormat; - if (fmt && *fmt) { - minPtr = minString; - snprintf(minString, 200, fmt, axisRange_.min); - - maxPtr = maxString; - snprintf(maxString, 200, fmt, axisRange_.max); - } - if (ops->descending) { - char *tmp = minPtr; - minPtr = maxPtr; - maxPtr = tmp; - } - - int textWidth, textHeight; - TextStyle ts(graphPtr_, &ops->limitsTextStyle); - if (maxPtr) { - graphPtr_->getTextExtents(ops->tickFont, maxPtr, -1, - &textWidth, &textHeight); - if ((textWidth > 0) && (textHeight > 0)) { - if (isHoriz) { - ops->limitsTextStyle.angle = 90.0; - ops->limitsTextStyle.anchor = TK_ANCHOR_SE; - - ts.printText(psPtr, maxPtr, (double)graphPtr_->right_, hMax); - hMax -= (textWidth + spacing); - } - else { - ops->limitsTextStyle.angle = 0.0; - ops->limitsTextStyle.anchor = TK_ANCHOR_NW; - - ts.printText(psPtr, maxPtr, vMax, (double)graphPtr_->top_); - vMax += (textWidth + spacing); - } - } - } - - if (minPtr) { - graphPtr_->getTextExtents(ops->tickFont, minPtr, -1, - &textWidth, &textHeight); - if ((textWidth > 0) && (textHeight > 0)) { - ops->limitsTextStyle.anchor = TK_ANCHOR_SW; - - if (isHoriz) { - ops->limitsTextStyle.angle = 90.0; - - ts.printText(psPtr, minPtr, (double)graphPtr_->left_, hMin); - hMin -= (textWidth + spacing); - } - else { - ops->limitsTextStyle.angle = 0.0; - - ts.printText(psPtr, minPtr, vMin, (double)graphPtr_->bottom_); - vMin += (textWidth + spacing); - } - } - } -} - -void Axis::updateScrollbar(Tcl_Interp* interp, Tcl_Obj *scrollCmdObjPtr, - int first, int last, int width) -{ - double firstFract =0.0; - double lastFract = 1.0; - if (width > 0) { - firstFract = (double)first / (double)width; - lastFract = (double)last / (double)width; - } - Tcl_Obj *cmdObjPtr = Tcl_DuplicateObj(scrollCmdObjPtr); - Tcl_ListObjAppendElement(interp, cmdObjPtr, Tcl_NewDoubleObj(firstFract)); - Tcl_ListObjAppendElement(interp, cmdObjPtr, Tcl_NewDoubleObj(lastFract)); - Tcl_IncrRefCount(cmdObjPtr); - if (Tcl_EvalObjEx(interp, cmdObjPtr, TCL_EVAL_GLOBAL) != TCL_OK) { - Tcl_BackgroundError(interp); - } - Tcl_DecrRefCount(cmdObjPtr); -} - -void Axis::getGeometry() -{ - AxisOptions* ops = (AxisOptions*)ops_; - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - - freeTickLabels(); - - // Leave room for axis baseline and padding - unsigned int y =0; - if (ops->exterior && (gops->plotRelief != TK_RELIEF_SOLID)) - y += ops->lineWidth + 2; - - maxTickHeight_ = maxTickWidth_ = 0; - - if (t1Ptr_) - delete t1Ptr_; - t1Ptr_ = generateTicks(&majorSweep_); - - if (t2Ptr_) - delete t2Ptr_; - t2Ptr_ = generateTicks(&minorSweep_); - - if (ops->showTicks) { - Ticks* t1Ptr = ops->t1UPtr ? ops->t1UPtr : t1Ptr_; - - int nTicks =0; - if (t1Ptr) - nTicks = t1Ptr->nTicks; - - unsigned int nLabels =0; - for (int ii=0; iivalues[ii]; - double x2 = t1Ptr->values[ii]; - if (ops->labelOffset) - x2 += majorSweep_.step * 0.5; - - if (!inRange(x2, &axisRange_)) - continue; - - TickLabel* labelPtr = makeLabel(x); - tickLabels_->append(labelPtr); - nLabels++; - - // Get the dimensions of each tick label. Remember tick labels - // can be multi-lined and/or rotated. - int lw, lh; - graphPtr_->getTextExtents(ops->tickFont, labelPtr->string, -1, &lw, &lh); - labelPtr->width = lw; - labelPtr->height = lh; - - if (ops->tickAngle != 0.0f) { - // Rotated label width and height - double rlw, rlh; - graphPtr_->getBoundingBox(lw, lh, ops->tickAngle, &rlw, &rlh, NULL); - lw = rlw; - lh = rlh; - } - if (maxTickWidth_ < int(lw)) - maxTickWidth_ = lw; - - if (maxTickHeight_ < int(lh)) - maxTickHeight_ = lh; - } - - unsigned int pad =0; - if (ops->exterior) { - // Because the axis cap style is "CapProjecting", we need to - // account for an extra 1.5 linewidth at the end of each line - pad = ((ops->lineWidth * 12) / 8); - } - if (isHorizontal()) - y += maxTickHeight_ + pad; - else { - y += maxTickWidth_ + pad; - if (maxTickWidth_ > 0) - // Pad either size of label. - y += 5; - } - y += 2 * AXIS_PAD_TITLE; - if ((ops->lineWidth > 0) && ops->exterior) - // Distance from axis line to tick label. - y += ops->tickLength; - - } // showTicks - - if (ops->title) { - if (ops->titleAlternate) { - if (y < titleHeight_) - y = titleHeight_; - } - else - y += titleHeight_ + AXIS_PAD_TITLE; - } - - // Correct for orientation of the axis - if (isHorizontal()) - height_ = y; - else - width_ = y; -} - diff --git a/src/tkbltGrAxis.h b/src/tkbltGrAxis.h deleted file mode 100644 index 2e35d3f..0000000 --- a/src/tkbltGrAxis.h +++ /dev/null @@ -1,264 +0,0 @@ -/* - * 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 1993-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 IMPIED, 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. - */ - -#ifndef ___BltGrAxis_h__ -#define ___BltGrAxis_h__ - -#include - -#include "tkbltChain.h" - -#include "tkbltGrMisc.h" -#include "tkbltGrText.h" -#include "tkbltGrPSOutput.h" - -namespace Blt { - class Graph; - class Postscript; - - typedef struct { - int axis; - int t1; - int t2; - int label; - } AxisInfo; - - typedef struct { - const char* name; - ClassId classId; - } AxisName; - - extern AxisName axisNames[]; - - typedef struct { - Dashes dashes; - int lineWidth; - XColor* color; - GC gc; - Segment2d *segments; - int nUsed; - int nAllocated; - } Grid; - - typedef struct { - double min; - double max; - double range; - double scale; - } AxisRange; - - class TickLabel { - public: - Point2d anchorPos; - unsigned int width; - unsigned int height; - char* string; - - public: - TickLabel(char*); - virtual ~TickLabel(); - }; - - class Ticks { - public: - int nTicks; - double* values; - - public: - Ticks(int); - virtual ~Ticks(); - }; - - typedef struct { - double initial; - double step; - int nSteps; - } TickSweep; - - typedef struct { - const char** tags; - int checkLimits; - int exterior; - int showGrid; - int showGridMinor; - int hide; - int showTicks; - - double windowSize; - const char *formatCmd; - int descending; - int labelOffset; - TextStyleOptions limitsTextStyle; - const char *limitsFormat; - int lineWidth; - int logScale; - int looseMin; - int looseMax; - Ticks* t1UPtr; - Ticks* t2UPtr; - double reqMin; - double reqMax; - Tcl_Obj *scrollCmdObjPtr; - int scrollUnits; - double reqScrollMin; - double reqScrollMax; - double shiftBy; - double reqStep; - int reqNumMajorTicks; - int reqNumMinorTicks; - int tickLength; - const char *title; - int titleAlternate; - - XColor* activeFgColor; - int activeRelief; - Tk_3DBorder normalBg; - int borderWidth; - XColor* tickColor; - Grid major; - Grid minor; - Tk_Justify titleJustify; - int relief; - double tickAngle; - Tk_Anchor reqTickAnchor; - Tk_Font tickFont; - Tk_Font titleFont; - XColor* titleColor; - } AxisOptions; - - class Axis { - protected: - Tk_OptionTable optionTable_; - void* ops_; - - public: - Graph* graphPtr_; - ClassId classId_; - const char* name_; - const char* className_; - - Tcl_HashEntry* hashPtr_; - int refCount_; - int use_; - int active_; - - ChainLink* link; - Chain* chain; - - Point2d titlePos_; - unsigned short int titleWidth_; - unsigned short int titleHeight_; - double min_; - double max_; - double scrollMin_; - double scrollMax_; - AxisRange valueRange_; - AxisRange axisRange_; - double prevMin_; - double prevMax_; - Ticks* t1Ptr_; - Ticks* t2Ptr_; - TickSweep minorSweep_; - TickSweep majorSweep_; - - int margin_; - Segment2d *segments_; - int nSegments_; - Chain* tickLabels_; - short int left_; - short int right_; - short int top_; - short int bottom_; - short int width_; - short int height_; - short int maxTickWidth_; - short int maxTickHeight_; - Tk_Anchor tickAnchor_; - GC tickGC_; - GC activeTickGC_; - double titleAngle_; - Tk_Anchor titleAnchor_; - double screenScale_; - int screenMin_; - int screenRange_; - - protected: - double niceNum(double, int); - void setRange(AxisRange*, double, double); - void makeGridLine(double, Segment2d*); - void makeSegments(AxisInfo*); - void resetTextStyles(); - void makeLine(int, Segment2d*); - void makeTick(double, int, int, Segment2d*); - void offsets(int, int, AxisInfo*); - void updateScrollbar(Tcl_Interp*, Tcl_Obj*, int, int, int); - - public: - Axis(Graph*, const char*, int, Tcl_HashEntry*); - virtual ~Axis(); - - Tk_OptionTable optionTable() {return optionTable_;} - void* ops() {return ops_;} - ClassId classId() {return classId_;} - const char* className() {return className_;} - - int configure(); - void map(int, int); - void draw(Drawable); - void drawGrids(Drawable); - void drawLimits(Drawable); - void print(PSOutput*); - void printGrids(PSOutput*); - void printLimits(PSOutput*); - - void mapStacked(int, int); - void mapGridlines(); - void setClass(ClassId); - void logScale(double, double); - void linearScale(double, double); - void fixRange(); - int isHorizontal(); - void freeTickLabels(); - TickLabel* makeLabel(double); - void getDataLimits(double, double); - Ticks* generateTicks(TickSweep*); - int inRange(double, AxisRange*); - void getGeometry(); - - double invHMap(double x); - double invVMap(double y); - double hMap(double x); - double vMap(double y); - }; -}; - -#endif diff --git a/src/tkbltGrAxisOp.C b/src/tkbltGrAxisOp.C deleted file mode 100644 index 89b2be9..0000000 --- a/src/tkbltGrAxisOp.C +++ /dev/null @@ -1,643 +0,0 @@ -/* - * 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 1993-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 - -#include - -#include "tkbltGrBind.h" -#include "tkbltGraph.h" -#include "tkbltGrAxis.h" -#include "tkbltGrAxisOp.h" -#include "tkbltGrMisc.h" - -using namespace Blt; - -#define EXP10(x) (pow(10.0,(x))) - -static int GetAxisScrollInfo(Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[], - double *offsetPtr, double windowSize, - double scrollUnits, double scale); - -static double Clamp(double x) -{ - return (x < 0.0) ? 0.0 : (x > 1.0) ? 1.0 : x; -} - -int Blt::AxisObjConfigure(Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = axisPtr->graphPtr_; - Tk_SavedOptions savedOptions; - int mask =0; - int error; - Tcl_Obj* errorResult; - - for (error=0; error<=1; error++) { - if (!error) { - if (Tk_SetOptions(interp, (char*)axisPtr->ops(), axisPtr->optionTable(), - objc, objv, graphPtr->tkwin_, &savedOptions, &mask) - != TCL_OK) - continue; - } - else { - errorResult = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(errorResult); - Tk_RestoreSavedOptions(&savedOptions); - } - - if (axisPtr->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<4) - return TCL_ERROR; - - Axis* axisPtr; - if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) - return TCL_ERROR; - - return AxisCgetOp(axisPtr, interp, objc-1, objv+1); -} - -static int ConfigureOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Axis* axisPtr; - if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) - return TCL_ERROR; - - return AxisConfigureOp(axisPtr, interp, objc-1, objv+1); -} - -static int ActivateOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Axis* axisPtr; - if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) - return TCL_ERROR; - - return AxisActivateOp(axisPtr, interp, objc, objv); -} - -static int BindOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc == 3) { - Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - Tcl_HashSearch iter; - for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&graphPtr->axes_.tagTable, &iter); hPtr; hPtr = Tcl_NextHashEntry(&iter)) { - char* tagName = (char*)Tcl_GetHashKey(&graphPtr->axes_.tagTable, hPtr); - Tcl_Obj* objPtr = Tcl_NewStringObj(tagName, -1); - Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); - } - - Tcl_SetObjResult(interp, listObjPtr); - return TCL_OK; - } - else - return graphPtr->bindTable_->configure(graphPtr->axisTag(Tcl_GetString(objv[3])), objc-4, objv+4); -} - -static int CreateOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - if (graphPtr->createAxis(objc, objv) != TCL_OK) - return TCL_ERROR; - Tcl_SetObjResult(interp, objv[3]); - - return TCL_OK; -} - -static int DeleteOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Axis* axisPtr; - if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) - return TCL_ERROR; - - if (axisPtr->refCount_ == 0) - delete axisPtr; - - graphPtr->flags |= RESET; - graphPtr->eventuallyRedraw(); - - return TCL_OK; -} - -static int InvTransformOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Axis* axisPtr; - if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) - return TCL_ERROR; - - return AxisInvTransformOp(axisPtr, interp, objc-1, objv+1); -} - -static int LimitsOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Axis* axisPtr; - if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) - return TCL_ERROR; - - return AxisLimitsOp(axisPtr, interp, objc-1, objv+1); -} - -static int MarginOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Axis* axisPtr; - if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) - return TCL_ERROR; - - return AxisMarginOp(axisPtr, interp, objc-1, objv+1); -} - -static int NamesOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - if (objc == 3) { - Tcl_HashSearch cursor; - for (Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&graphPtr->axes_.table, &cursor); hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { - Axis* axisPtr = (Axis*)Tcl_GetHashValue(hPtr); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj(axisPtr->name_, -1)); - } - } - else { - Tcl_HashSearch cursor; - for (Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&graphPtr->axes_.table, &cursor); hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { - Axis* axisPtr = (Axis*)Tcl_GetHashValue(hPtr); - for (int ii=3; iiname_, pattern)) { - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewStringObj(axisPtr->name_, -1)); - break; - } - } - } - } - 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; - if (objc<4) - return TCL_ERROR; - - Axis* axisPtr; - if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) - return TCL_ERROR; - - return AxisTransformOp(axisPtr, interp, objc-1, objv+1); -} - -static int TypeOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Axis* axisPtr; - if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) - return TCL_ERROR; - - return AxisTypeOp(axisPtr, interp, objc-1, objv+1); -} - -static int ViewOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Axis* axisPtr; - if (graphPtr->getAxis(objv[3], &axisPtr) != TCL_OK) - return TCL_ERROR; - - return AxisViewOp(axisPtr, interp, objc-1, objv+1); -} - -const Ensemble Blt::axisEnsemble[] = { - {"activate", ActivateOp, 0}, - {"bind", BindOp, 0}, - {"cget", CgetOp,0 }, - {"configure", ConfigureOp,0 }, - {"create", CreateOp, 0}, - {"deactivate", ActivateOp, 0}, - {"delete", DeleteOp, 0}, - {"invtransform", InvTransformOp, 0}, - {"limits", LimitsOp, 0}, - {"margin", MarginOp, 0}, - {"names", NamesOp, 0}, - {"transform", TransformOp, 0}, - {"type", TypeOp, 0}, - {"view", ViewOp, 0}, - { 0,0,0 } -}; - -// Support - -double AdjustViewport(double offset, double windowSize) -{ - // Canvas-style scrolling allows the world to be scrolled within the window. - if (windowSize > 1.0) { - if (windowSize < (1.0 - offset)) - offset = 1.0 - windowSize; - - if (offset > 0.0) - offset = 0.0; - } - else { - if ((offset + windowSize) > 1.0) - offset = 1.0 - windowSize; - - if (offset < 0.0) - offset = 0.0; - } - return offset; -} - -static int GetAxisScrollInfo(Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[], - double *offsetPtr, double windowSize, - double scrollUnits, double scale) -{ - const char *string; - char c; - double offset; - int length; - - offset = *offsetPtr; - string = Tcl_GetStringFromObj(objv[0], &length); - c = string[0]; - scrollUnits *= scale; - if ((c == 's') && (strncmp(string, "scroll", length) == 0)) { - int count; - double fract; - - /* Scroll number unit/page */ - if (Tcl_GetIntFromObj(interp, objv[1], &count) != TCL_OK) - return TCL_ERROR; - - string = Tcl_GetStringFromObj(objv[2], &length); - c = string[0]; - if ((c == 'u') && (strncmp(string, "units", length) == 0)) - fract = count * scrollUnits; - else if ((c == 'p') && (strncmp(string, "pages", length) == 0)) - /* A page is 90% of the view-able window. */ - fract = (int)(count * windowSize * 0.9 + 0.5); - else if ((c == 'p') && (strncmp(string, "pixels", length) == 0)) - fract = count * scale; - else { - Tcl_AppendResult(interp, "unknown \"scroll\" units \"", string, - "\"", NULL); - return TCL_ERROR; - } - offset += fract; - } - else if ((c == 'm') && (strncmp(string, "moveto", length) == 0)) { - double fract; - - /* moveto fraction */ - if (Tcl_GetDoubleFromObj(interp, objv[1], &fract) != TCL_OK) { - return TCL_ERROR; - } - offset = fract; - } - else { - int count; - double fract; - - /* Treat like "scroll units" */ - if (Tcl_GetIntFromObj(interp, objv[0], &count) != TCL_OK) { - return TCL_ERROR; - } - fract = (double)count * scrollUnits; - offset += fract; - /* CHECK THIS: return TCL_OK; */ - } - *offsetPtr = AdjustViewport(offset, windowSize); - return TCL_OK; -} - -// Common Ops - -int AxisCgetOp(Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = axisPtr->graphPtr_; - - if (objc != 4) { - Tcl_WrongNumArgs(interp, 2, objv, "cget option"); - return TCL_ERROR; - } - - Tcl_Obj* objPtr = Tk_GetOptionValue(interp, (char*)axisPtr->ops(), - axisPtr->optionTable(), - objv[3], graphPtr->tkwin_); - if (!objPtr) - return TCL_ERROR; - else - Tcl_SetObjResult(interp, objPtr); - return TCL_OK; -} - -int AxisConfigureOp(Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = axisPtr->graphPtr_; - - if (objc <= 4) { - Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)axisPtr->ops(), - axisPtr->optionTable(), - (objc == 4) ? objv[3] : NULL, - graphPtr->tkwin_); - if (!objPtr) - return TCL_ERROR; - else - Tcl_SetObjResult(interp, objPtr); - return TCL_OK; - } - else - return AxisObjConfigure(axisPtr, interp, objc-3, objv+3); -} - -int AxisActivateOp(Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - AxisOptions* ops = (AxisOptions*)axisPtr->ops(); - Graph* graphPtr = axisPtr->graphPtr_; - const char *string; - - string = Tcl_GetString(objv[2]); - axisPtr->active_ = (string[0] == 'a') ? 1 : 0; - - if (!ops->hide && axisPtr->use_) { - graphPtr->flags |= RESET; - graphPtr->eventuallyRedraw(); - } - - return TCL_OK; -} - -int AxisInvTransformOp(Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = axisPtr->graphPtr_; - - if (graphPtr->flags & RESET) - graphPtr->resetAxes(); - - int sy; - if (Tcl_GetIntFromObj(interp, objv[3], &sy) != TCL_OK) - return TCL_ERROR; - - // Is the axis vertical or horizontal? - // Check the site where the axis was positioned. If the axis is - // virtual, all we have to go on is how it was mapped to an - // element (using either -mapx or -mapy options). - double y = axisPtr->isHorizontal() ? - axisPtr->invHMap(sy) : axisPtr->invVMap(sy); - - Tcl_SetDoubleObj(Tcl_GetObjResult(interp), y); - return TCL_OK; -} - -int AxisLimitsOp(Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - AxisOptions* ops = (AxisOptions*)axisPtr->ops(); - Graph* graphPtr = axisPtr->graphPtr_; - - if (graphPtr->flags & RESET) - graphPtr->resetAxes(); - - double min, max; - if (ops->logScale) { - min = EXP10(axisPtr->axisRange_.min); - max = EXP10(axisPtr->axisRange_.max); - } - else { - min = axisPtr->axisRange_.min; - max = axisPtr->axisRange_.max; - } - - Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(min)); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(max)); - - Tcl_SetObjResult(interp, listObjPtr); - return TCL_OK; -} - -int AxisMarginOp(Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - const char *marginName = ""; - if (axisPtr->use_) - marginName = axisNames[axisPtr->margin_].name; - - Tcl_SetStringObj(Tcl_GetObjResult(interp), marginName, -1); - return TCL_OK; -} - -int AxisTransformOp(Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = axisPtr->graphPtr_; - - if (graphPtr->flags & RESET) - graphPtr->resetAxes(); - - double x; - if (Tcl_GetDoubleFromObj(interp, objv[3], &x) != TCL_OK) - return TCL_ERROR; - - if (axisPtr->isHorizontal()) - x = axisPtr->hMap(x); - else - x = axisPtr->vMap(x); - - Tcl_SetIntObj(Tcl_GetObjResult(interp), (int)x); - return TCL_OK; -} - -int AxisTypeOp(Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - const char* typeName = ""; - if (axisPtr->use_) { - if (axisNames[axisPtr->margin_].classId == CID_AXIS_X) - typeName = "x"; - else if (axisNames[axisPtr->margin_].classId == CID_AXIS_Y) - typeName = "y"; - } - - Tcl_SetStringObj(Tcl_GetObjResult(interp), typeName, -1); - return TCL_OK; -} - -int AxisViewOp(Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - AxisOptions* ops = (AxisOptions*)axisPtr->ops(); - Graph* graphPtr = axisPtr->graphPtr_; - double worldMin = axisPtr->valueRange_.min; - double worldMax = axisPtr->valueRange_.max; - /* Override data dimensions with user-selected limits. */ - if (!isnan(axisPtr->scrollMin_)) - worldMin = axisPtr->scrollMin_; - - if (!isnan(axisPtr->scrollMax_)) - worldMax = axisPtr->scrollMax_; - - double viewMin = axisPtr->min_; - double viewMax = axisPtr->max_; - /* Bound the view within scroll region. */ - if (viewMin < worldMin) - viewMin = worldMin; - - if (viewMax > worldMax) - viewMax = worldMax; - - if (ops->logScale) { - worldMin = log10(worldMin); - worldMax = log10(worldMax); - viewMin = log10(viewMin); - viewMax = log10(viewMax); - } - double worldWidth = worldMax - worldMin; - double viewWidth = viewMax - viewMin; - - /* Unlike horizontal axes, vertical axis values run opposite of the - * scrollbar first/last values. So instead of pushing the axis minimum - * around, we move the maximum instead. */ - double axisOffset; - double axisScale; - if (axisPtr->isHorizontal() != ops->descending) { - axisOffset = viewMin - worldMin; - axisScale = graphPtr->hScale_; - } else { - axisOffset = worldMax - viewMax; - axisScale = graphPtr->vScale_; - } - if (objc == 4) { - double first = Clamp(axisOffset / worldWidth); - double last = Clamp((axisOffset + viewWidth) / worldWidth); - Tcl_Obj *listObjPtr = Tcl_NewListObj(0, NULL); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(first)); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(last)); - Tcl_SetObjResult(interp, listObjPtr); - return TCL_OK; - } - double fract = axisOffset / worldWidth; - if (GetAxisScrollInfo(interp, objc, objv, &fract, viewWidth / worldWidth, - ops->scrollUnits, axisScale) != TCL_OK) - return TCL_ERROR; - - if (axisPtr->isHorizontal() != ops->descending) { - ops->reqMin = (fract * worldWidth) + worldMin; - ops->reqMax = ops->reqMin + viewWidth; - } - else { - ops->reqMax = worldMax - (fract * worldWidth); - ops->reqMin = ops->reqMax - viewWidth; - } - if (ops->logScale) { - ops->reqMin = EXP10(ops->reqMin); - ops->reqMax = EXP10(ops->reqMax); - } - - graphPtr->flags |= RESET; - graphPtr->eventuallyRedraw(); - - return TCL_OK; -} - diff --git a/src/tkbltGrAxisOp.h b/src/tkbltGrAxisOp.h deleted file mode 100644 index 777aea7..0000000 --- a/src/tkbltGrAxisOp.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrAxisOp_h__ -#define __BltGrAxisOp_h__ - -#include "tkbltGraph.h" - -namespace Blt { - extern const Ensemble axisEnsemble[]; - extern int AxisObjConfigure(Blt::Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); -}; - -extern int AxisCgetOp(Blt::Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); -extern int AxisConfigureOp(Blt::Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); -extern int AxisActivateOp(Blt::Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); -extern int AxisInvTransformOp(Blt::Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); -extern int AxisLimitsOp(Blt::Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); -extern int AxisMarginOp(Blt::Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); -extern int AxisTransformOp(Blt::Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); -extern int AxisTypeOp(Blt::Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); -extern int AxisViewOp(Blt::Axis* axisPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); - -#endif diff --git a/src/tkbltGrAxisOption.C b/src/tkbltGrAxisOption.C deleted file mode 100644 index d0f78e8..0000000 --- a/src/tkbltGrAxisOption.C +++ /dev/null @@ -1,260 +0,0 @@ -/* - * 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 1993-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 - -#include - -#include "tkbltGraph.h" -#include "tkbltGrAxis.h" -#include "tkbltGrAxisOption.h" -#include "tkbltConfig.h" - -using namespace Blt; - -static Tk_CustomOptionSetProc AxisSetProc; -static Tk_CustomOptionGetProc AxisGetProc; -static Tk_CustomOptionFreeProc AxisFreeProc; -Tk_ObjCustomOption xAxisObjOption = - { - "xaxis", AxisSetProc, AxisGetProc, RestoreProc, AxisFreeProc, - (ClientData)CID_AXIS_X - }; -Tk_ObjCustomOption yAxisObjOption = - { - "yaxis", AxisSetProc, AxisGetProc, RestoreProc, AxisFreeProc, - (ClientData)CID_AXIS_Y - }; - -static int AxisSetProc(ClientData clientData, Tcl_Interp* interp, - Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, - int offset, char* savePtr, int flags) -{ - Axis** axisPtrPtr = (Axis**)(widgRec + offset); - *(double*)savePtr = *(double*)axisPtrPtr; - - if (!axisPtrPtr) - return TCL_OK; - - Graph* graphPtr = getGraphFromWindowData(tkwin); - ClassId classId = (ClassId)(long(clientData)); - - Axis *axisPtr; - if (graphPtr->getAxis(*objPtr, &axisPtr) != TCL_OK) - return TCL_ERROR; - - if (classId != CID_NONE) { - // Set the axis type on the first use of it. - if ((axisPtr->refCount_ == 0) || (axisPtr->classId_ == CID_NONE)) - axisPtr->setClass(classId); - - else if (axisPtr->classId_ != classId) { - Tcl_AppendResult(interp, "axis \"", Tcl_GetString(*objPtr), - "\" is already in use on an opposite ", - axisPtr->className_, "-axis", - NULL); - return TCL_ERROR; - } - axisPtr->refCount_++; - } - - *axisPtrPtr = axisPtr; - return TCL_OK; -}; - -static Tcl_Obj* AxisGetProc(ClientData clientData, Tk_Window tkwin, - char *widgRec, int offset) -{ - Axis* axisPtr = *(Axis**)(widgRec + offset); - if (!axisPtr) - return Tcl_NewStringObj("", -1); - - return Tcl_NewStringObj(axisPtr->name_, -1); -}; - -static void AxisFreeProc(ClientData clientData, Tk_Window tkwin, char *ptr) -{ - Axis* axisPtr = *(Axis**)ptr; - if (axisPtr) { - axisPtr->refCount_--; - if (axisPtr->refCount_ == 0) - delete axisPtr; - } -} - -static Tk_CustomOptionSetProc LimitSetProc; -static Tk_CustomOptionGetProc LimitGetProc; -Tk_ObjCustomOption limitObjOption = - { - "limit", LimitSetProc, LimitGetProc, NULL, NULL, NULL - }; - -static int LimitSetProc(ClientData clientData, Tcl_Interp* interp, - Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, - int offset, char* save, int flags) -{ - double* limitPtr = (double*)(widgRec + offset); - const char* string = Tcl_GetString(*objPtr); - if (!string || !string[0]) { - *limitPtr = NAN; - return TCL_OK; - } - - if (Tcl_GetDoubleFromObj(interp, *objPtr, limitPtr) != TCL_OK) - return TCL_ERROR; - - return TCL_OK; -} - -static Tcl_Obj* LimitGetProc(ClientData clientData, Tk_Window tkwin, - char *widgRec, int offset) -{ - double limit = *(double*)(widgRec + offset); - Tcl_Obj* objPtr; - - if (!isnan(limit)) - objPtr = Tcl_NewDoubleObj(limit); - else - objPtr = Tcl_NewStringObj("", -1); - - return objPtr; -} - -static Tk_CustomOptionSetProc TicksSetProc; -static Tk_CustomOptionGetProc TicksGetProc; -static Tk_CustomOptionFreeProc TicksFreeProc; -Tk_ObjCustomOption ticksObjOption = - { - "ticks", TicksSetProc, TicksGetProc, RestoreProc, TicksFreeProc, NULL - }; - -static int TicksSetProc(ClientData clientData, Tcl_Interp* interp, - Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, - int offset, char* savePtr, int flags) -{ - Ticks** ticksPtrPtr = (Ticks**)(widgRec + offset); - *(double*)savePtr = *(double*)ticksPtrPtr; - - if (!ticksPtrPtr) - return TCL_OK; - - int objc; - Tcl_Obj** objv; - if (Tcl_ListObjGetElements(interp, *objPtr, &objc, &objv) != TCL_OK) - return TCL_ERROR; - - Ticks* ticksPtr = NULL; - if (objc > 0) { - ticksPtr = new Ticks(objc); - for (int ii=0; iivalues[ii] = value; - } - ticksPtr->nTicks = objc; - } - - *ticksPtrPtr = ticksPtr; - - return TCL_OK; -} - -static Tcl_Obj* TicksGetProc(ClientData clientData, Tk_Window tkwin, - char *widgRec, int offset) -{ - Ticks* ticksPtr = *(Ticks**)(widgRec + offset); - - if (!ticksPtr) - return Tcl_NewListObj(0, NULL); - - int cnt = ticksPtr->nTicks; - Tcl_Obj** ll = new Tcl_Obj*[cnt]; - for (int ii = 0; iivalues[ii]); - - Tcl_Obj* listObjPtr = Tcl_NewListObj(cnt, ll); - delete [] ll; - - return listObjPtr; -} - -static void TicksFreeProc(ClientData clientData, Tk_Window tkwin, - char *ptr) -{ - Ticks* ticksPtr = *(Ticks**)ptr; - if (ticksPtr) - delete ticksPtr; -} - -static Tk_CustomOptionSetProc ObjectSetProc; -static Tk_CustomOptionGetProc ObjectGetProc; -static Tk_CustomOptionFreeProc ObjectFreeProc; -Tk_ObjCustomOption objectObjOption = - { - "object", ObjectSetProc, ObjectGetProc, RestoreProc, ObjectFreeProc, NULL, - }; - -static int ObjectSetProc(ClientData clientData, Tcl_Interp* interp, - Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, - int offset, char* savePtr, int flags) -{ - Tcl_Obj** objectPtrPtr = (Tcl_Obj**)(widgRec + offset); - *(double*)savePtr = *(double*)objectPtrPtr; - - if (!objectPtrPtr) - return TCL_OK; - - Tcl_IncrRefCount(*objPtr); - *objectPtrPtr = *objPtr; - - return TCL_OK; -} - -static Tcl_Obj* ObjectGetProc(ClientData clientData, Tk_Window tkwin, - char *widgRec, int offset) -{ - Tcl_Obj** objectPtrPtr = (Tcl_Obj**)(widgRec + offset); - - if (!objectPtrPtr) - return Tcl_NewObj(); - - return *objectPtrPtr; -} - -static void ObjectFreeProc(ClientData clientData, Tk_Window tkwin, - char *ptr) -{ - Tcl_Obj* objectPtr = *(Tcl_Obj**)ptr; - if (objectPtr) - Tcl_DecrRefCount(objectPtr); -} - diff --git a/src/tkbltGrAxisOption.h b/src/tkbltGrAxisOption.h deleted file mode 100644 index 4efa8ee..0000000 --- a/src/tkbltGrAxisOption.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrAxisOption_h__ -#define __BltGrAxisOption_h__ - -#include - -extern Tk_ObjCustomOption xAxisObjOption; -extern Tk_ObjCustomOption yAxisObjOption; -extern Tk_ObjCustomOption limitObjOption; -extern Tk_ObjCustomOption ticksObjOption; -extern Tk_ObjCustomOption objectObjOption; - -#endif diff --git a/src/tkbltGrBind.C b/src/tkbltGrBind.C deleted file mode 100644 index 2873c8f..0000000 --- a/src/tkbltGrBind.C +++ /dev/null @@ -1,228 +0,0 @@ -/* - * 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 1998 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 - -#include -#include -#include -using namespace std; - -#include "tkbltGrBind.h" -#include "tkbltGraph.h" -#include "tkbltGrLegd.h" - -using namespace Blt; - -static Tk_EventProc BindProc; - -BindTable::BindTable(Graph* graphPtr, Pick* pickPtr) -{ - graphPtr_ = graphPtr; - pickPtr_ = pickPtr; - grab_ =0; - table_ = Tk_CreateBindingTable(graphPtr->interp_); - currentItem_ =NULL; - currentContext_ =CID_NONE; - newItem_ =NULL; - newContext_ =CID_NONE; - focusItem_ =NULL; - focusContext_ =CID_NONE; - // pickEvent =NULL; - state_ =0; - - unsigned int mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask | - ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | - PointerMotionMask); - Tk_CreateEventHandler(graphPtr->tkwin_, mask, BindProc, this); -} - -BindTable::~BindTable() -{ - Tk_DeleteBindingTable(table_); - unsigned int mask = (KeyPressMask | KeyReleaseMask | ButtonPressMask | - ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | - PointerMotionMask); - Tk_DeleteEventHandler(graphPtr_->tkwin_, mask, BindProc, this); -} - -int BindTable::configure(ClientData item, int objc, Tcl_Obj* const objv[]) -{ - if (objc == 0) { - Tk_GetAllBindings(graphPtr_->interp_, table_, item); - return TCL_OK; - } - - const char *string = Tcl_GetString(objv[0]); - if (objc == 1) { - const char* command = - Tk_GetBinding(graphPtr_->interp_, table_, item, string); - if (!command) { - Tcl_ResetResult(graphPtr_->interp_); - Tcl_AppendResult(graphPtr_->interp_, "invalid binding event \"", - string, "\"", NULL); - return TCL_ERROR; - } - Tcl_SetStringObj(Tcl_GetObjResult(graphPtr_->interp_), command, -1); - return TCL_OK; - } - - const char* seq = string; - const char* command = Tcl_GetString(objv[1]); - if (command[0] == '\0') - return Tk_DeleteBinding(graphPtr_->interp_, table_, item, seq); - - unsigned long mask; - if (command[0] == '+') - mask = Tk_CreateBinding(graphPtr_->interp_, table_, - item, seq, command+1, 1); - else - mask = Tk_CreateBinding(graphPtr_->interp_, table_, - item, seq, command, 0); - if (!mask) - return TCL_ERROR; - - if (mask & (unsigned) ~(ButtonMotionMask|Button1MotionMask - |Button2MotionMask|Button3MotionMask|Button4MotionMask - |Button5MotionMask|ButtonPressMask|ButtonReleaseMask - |EnterWindowMask|LeaveWindowMask|KeyPressMask - |KeyReleaseMask|PointerMotionMask|VirtualEventMask)) { - Tk_DeleteBinding(graphPtr_->interp_, table_, item, seq); - Tcl_ResetResult(graphPtr_->interp_); - Tcl_AppendResult(graphPtr_->interp_, "requested illegal events; ", - "only key, button, motion, enter, leave, and virtual ", - "events may be used", (char *)NULL); - return TCL_ERROR; - } - - return TCL_OK; -} - -void BindTable::deleteBindings(ClientData object) -{ - Tk_DeleteAllBindings(table_, object); - - if (currentItem_ == object) { - currentItem_ =NULL; - currentContext_ =CID_NONE; - } - - if (newItem_ == object) { - newItem_ =NULL; - newContext_ =CID_NONE; - } - - if (focusItem_ == object) { - focusItem_ =NULL; - focusContext_ =CID_NONE; - } -} - -void BindTable::doEvent(XEvent* eventPtr) -{ - ClientData item = currentItem_; - ClassId classId = currentContext_; - - if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) { - item = focusItem_; - classId = focusContext_; - } - if (!item) - return; - - int nTags; - const char** tagArray = graphPtr_->getTags(item, classId, &nTags); - Tk_BindEvent(table_, eventPtr, graphPtr_->tkwin_, nTags, (void**)tagArray); - - if (tagArray) - delete [] tagArray; -} - -void BindTable::pickItem(XEvent* eventPtr) -{ - int buttonDown = state_ - & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask); - - // A LeaveNotify event automatically means that there's no current item, - if (eventPtr->type != LeaveNotify) { - int x = eventPtr->xcrossing.x; - int y = eventPtr->xcrossing.y; - newItem_ = pickPtr_->pickEntry(x, y, &newContext_); - } - else { - newItem_ =NULL; - newContext_ = CID_NONE; - } - - // Nothing to do: the current item hasn't changed. - if ((newItem_ == currentItem_) && !grab_) - return; - - if (!buttonDown) - grab_ =0; - - if ((newItem_ != currentItem_) && buttonDown) { - grab_ =1; - return; - } - - grab_ =0; - currentItem_ = newItem_; - currentContext_ = newContext_; -} - -static void BindProc(ClientData clientData, XEvent* eventPtr) -{ - BindTable* bindPtr = (BindTable*)clientData; - Tcl_Preserve(bindPtr->graphPtr_); - - switch (eventPtr->type) { - case ButtonPress: - case ButtonRelease: - bindPtr->state_ = eventPtr->xbutton.state; - break; - case EnterNotify: - case LeaveNotify: - bindPtr->state_ = eventPtr->xcrossing.state; - break; - case MotionNotify: - bindPtr->state_ = eventPtr->xmotion.state; - break; - case KeyPress: - case KeyRelease: - bindPtr->state_ = eventPtr->xkey.state; - break; - } - - bindPtr->pickItem(eventPtr); - bindPtr->doEvent(eventPtr); - - Tcl_Release(bindPtr->graphPtr_); -} - diff --git a/src/tkbltGrBind.h b/src/tkbltGrBind.h deleted file mode 100644 index 7947210..0000000 --- a/src/tkbltGrBind.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 1998-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. - */ - -#ifndef __BltGrBind_h__ -#define __BltGrBind_h__ - -#include - -#include "tkbltGrMisc.h" - -namespace Blt { - class Graph; - class Pick; - - class BindTable { - protected: - Tk_BindingTable table_; - unsigned int grab_; - ClientData newItem_; - ClassId newContext_; - Pick* pickPtr_; - - public: - Graph* graphPtr_; - ClientData currentItem_; - ClassId currentContext_; - ClientData focusItem_; - ClassId focusContext_; - int state_; - XEvent pickEvent_; - - public: - BindTable(Graph*, Pick*); - virtual ~BindTable(); - - int configure(ClientData, int, Tcl_Obj *const []); - void deleteBindings(ClientData object); - void doEvent(XEvent*); - void pickItem(XEvent*); - - ClientData currentItem() {return currentItem_;} - }; -}; - - -#endif diff --git a/src/tkbltGrDef.h b/src/tkbltGrDef.h deleted file mode 100644 index d73836a..0000000 --- a/src/tkbltGrDef.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrDef_h__ -#define __BltGrDef_h__ - -#define STD_NORMAL_BACKGROUND "gray85" -#define STD_NORMAL_FOREGROUND "black" -#define STD_ACTIVE_BACKGROUND "gray90" -#define STD_ACTIVE_FOREGROUND "black" - -#define STD_FONT_LARGE "helvetica 16 normal roman" -#define STD_FONT_MEDIUM "helvetica 14 normal roman" -#define STD_FONT_NORMAL "helvetica 12 normal roman" -#define STD_FONT_SMALL "helvetica 10 normal roman" - -#define STD_BORDERWIDTH "2" - -#endif diff --git a/src/tkbltGrElem.C b/src/tkbltGrElem.C deleted file mode 100644 index c80cbc1..0000000 --- a/src/tkbltGrElem.C +++ /dev/null @@ -1,288 +0,0 @@ -/* - * 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 1993-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 -#include -#include - -#include - -#include "tkbltGraph.h" -#include "tkbltGrBind.h" -#include "tkbltGrElem.h" -#include "tkbltGrPen.h" - -using namespace Blt; - -// Class ElemValues - -ElemValues::ElemValues() -{ - values_ =NULL; - nValues_ =0; - min_ =0; - max_ =0; -} - -ElemValues::~ElemValues() -{ - if (values_) - delete [] values_; -} - -void ElemValues::reset() -{ - if (values_) - delete [] values_; - values_ =NULL; - nValues_ =0; - min_ =0; - max_ =0; -} - -ElemValuesSource::ElemValuesSource(int nn) : ElemValues() -{ - nValues_ = nn; - values_ = new double[nn]; -} - -ElemValuesSource::ElemValuesSource(int nn, double* vv) : ElemValues() -{ - nValues_ = nn; - values_ = vv; -} - -ElemValuesSource::~ElemValuesSource() -{ -} - -void ElemValuesSource::findRange() -{ - if (nValues_<1 || !values_) - return; - - min_ = DBL_MAX; - max_ = -DBL_MAX; - for (int ii=0; ii max_) - max_ = values_[ii]; - } - } -} - -ElemValuesVector::ElemValuesVector(Element* ptr, const char* vecName) - : ElemValues() -{ - elemPtr_ = ptr; - Graph* graphPtr = elemPtr_->graphPtr_; - source_ = Blt_AllocVectorId(graphPtr->interp_, vecName); -} - -ElemValuesVector::~ElemValuesVector() -{ - freeSource(); -} - -int ElemValuesVector::getVector() -{ - Graph* graphPtr = elemPtr_->graphPtr_; - - Blt_Vector *vecPtr; - if (Blt_GetVectorById(graphPtr->interp_, source_, &vecPtr) != TCL_OK) - return TCL_ERROR; - - if (fetchValues(vecPtr) != TCL_OK) { - freeSource(); - return TCL_ERROR; - } - - Blt_SetVectorChangedProc(source_, VectorChangedProc, this); - return TCL_OK; -} - -int ElemValuesVector::fetchValues(Blt_Vector* vector) -{ - Graph* graphPtr = elemPtr_->graphPtr_; - - if (values_) - delete [] values_; - values_ = NULL; - nValues_ = 0; - min_ =0; - max_ =0; - - int ss = Blt_VecLength(vector); - if (!ss) - return TCL_OK; - - double* array = new double[ss]; - if (!array) { - Tcl_AppendResult(graphPtr->interp_, "can't allocate new vector", NULL); - return TCL_ERROR; - } - - memcpy(array, Blt_VecData(vector), ss*sizeof(double)); - values_ = array; - nValues_ = Blt_VecLength(vector); - min_ = Blt_VecMin(vector); - max_ = Blt_VecMax(vector); - - return TCL_OK; -} - -void ElemValuesVector::freeSource() -{ - if (source_) { - Blt_SetVectorChangedProc(source_, NULL, NULL); - Blt_FreeVectorId(source_); - source_ = NULL; - } -} - -// Class Element - -Element::Element(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) -{ - graphPtr_ = graphPtr; - name_ = dupstr(name); - optionTable_ =NULL; - ops_ =NULL; - hashPtr_ = hPtr; - - row_ =0; - col_ =0; - activeIndices_ =NULL; - nActiveIndices_ =0; - xRange_ =0; - yRange_ =0; - active_ =0; - labelActive_ =0; - - link =NULL; -} - -Element::~Element() -{ - graphPtr_->bindTable_->deleteBindings(this); - - if (link) - graphPtr_->elements_.displayList->deleteLink(link); - - if (hashPtr_) - Tcl_DeleteHashEntry(hashPtr_); - - if (name_) - delete [] name_; - - if (activeIndices_) - delete [] activeIndices_; - - Tk_FreeConfigOptions((char*)ops_, optionTable_, graphPtr_->tkwin_); - free(ops_); -} - -double Element::FindElemValuesMinimum(ElemValues* valuesPtr, double minLimit) -{ - double min = DBL_MAX; - if (!valuesPtr) - return min; - - for (int ii=0; iinValues(); ii++) { - double x = valuesPtr->values_[ii]; - // What do you do about negative values when using log - // scale values seems like a grey area. Mirror. - if (x < 0.0) - x = -x; - if ((x > minLimit) && (min > x)) - min = x; - } - if (min == DBL_MAX) - min = minLimit; - - return min; -} - -PenStyle** Element::StyleMap() -{ - ElementOptions* ops = (ElementOptions*)ops_; - - int nPoints = NUMBEROFPOINTS(ops); - int nWeights = MIN(ops->w ? ops->w->nValues() : 0, nPoints); - double* w = ops->w ? ops->w->values_ : NULL; - ChainLink* link = Chain_FirstLink(ops->stylePalette); - PenStyle* stylePtr = (PenStyle*)Chain_GetValue(link); - - // Create a style mapping array (data point index to style), - // initialized to the default style. - PenStyle** dataToStyle = new PenStyle*[nPoints]; - for (int ii=0; iistylePalette); link; - link=Chain_PrevLink(link)) { - stylePtr = (PenStyle*)Chain_GetValue(link); - - if (stylePtr->weight.range > 0.0) { - double norm = (w[ii] - stylePtr->weight.min) / stylePtr->weight.range; - if (((norm - 1.0) <= DBL_EPSILON) && - (((1.0 - norm) - 1.0) <= DBL_EPSILON)) { - dataToStyle[ii] = stylePtr; - break; - } - } - } - } - - return dataToStyle; -} - -void Element::freeStylePalette(Chain* stylePalette) -{ - // Skip the first slot. It contains the built-in "normal" pen of the element - ChainLink* link = Chain_FirstLink(stylePalette); - if (link) { - ChainLink* next; - for (link=Chain_NextLink(link); link; link=next) { - next = Chain_NextLink(link); - PenStyle *stylePtr = (PenStyle*)Chain_GetValue(link); - Pen* penPtr = stylePtr->penPtr; - if (penPtr) { - penPtr->refCount_--; - if (penPtr->refCount_ == 0) - delete penPtr; - } - stylePalette->deleteLink(link); - } - } -} - diff --git a/src/tkbltGrElem.h b/src/tkbltGrElem.h deleted file mode 100644 index eabc9e9..0000000 --- a/src/tkbltGrElem.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrElem_h__ -#define __BltGrElem_h__ - -#include - -#include "tkbltVector.h" -#include "tkbltChain.h" - -#include "tkbltGrMisc.h" -#include "tkbltGrPen.h" -#include "tkbltGrPSOutput.h" - -#define SHOW_NONE 0 -#define SHOW_X 1 -#define SHOW_Y 2 -#define SHOW_BOTH 3 - -#define NUMBEROFPOINTS(e) MIN( (e)->coords.x ? (e)->coords.x->nValues() : 0, \ - (e)->coords.y ? (e)->coords.y->nValues() : 0 ) -#define NORMALPEN(e) ((((e)->normalPenPtr == NULL) ? \ - (e)->builtinPenPtr : (e)->normalPenPtr)) - -namespace Blt { - class Axis; - class Element; - class Pen; - class Postscript; - - class ElemValues { - protected: - double min_; - double max_; - int nValues_; - - public: - double* values_; - - public: - ElemValues(); - virtual ~ElemValues(); - - void reset(); - int nValues() {return nValues_;} - double min() {return min_;} - double max() {return max_;} - }; - - class ElemValuesSource : public ElemValues - { - public: - ElemValuesSource(int); - ElemValuesSource(int, double*); - ~ElemValuesSource(); - - void findRange(); - }; - - class ElemValuesVector : public ElemValues - { - public: - Element* elemPtr_; - Blt_VectorId source_; - - public: - ElemValuesVector(Element*, const char*); - ~ElemValuesVector(); - - int getVector(); - int fetchValues(Blt_Vector*); - void freeSource(); - }; - - typedef struct { - Segment2d *segments; - int *map; - int length; - } GraphSegments; - - typedef struct { - ElemValuesSource* x; - ElemValuesSource* y; - } ElemCoords; - - typedef struct { - double min; - double max; - double range; - } Weight; - - typedef struct { - Weight weight; - Pen* penPtr; - } PenStyle; - - typedef struct { - Element* elemPtr; - const char* label; - const char** tags; - Axis* xAxis; - Axis* yAxis; - ElemCoords coords; - ElemValues* w; - ElemValues* xError; - ElemValues* yError; - ElemValues* xHigh; - ElemValues* xLow; - ElemValues* yHigh; - ElemValues* yLow; - int hide; - int legendRelief; - Chain* stylePalette; - Pen* builtinPenPtr; - Pen* activePenPtr; - Pen* normalPenPtr; - PenOptions builtinPen; - } ElementOptions; - - class Element { - protected: - Tk_OptionTable optionTable_; - void* ops_; - - double xRange_; - double yRange_; - - public: - Graph* graphPtr_; - const char* name_; - Tcl_HashEntry* hashPtr_; - unsigned short row_; - unsigned short col_; - int nActiveIndices_; - int* activeIndices_; - int active_; - int labelActive_; - - ChainLink* link; - - protected: - double FindElemValuesMinimum(ElemValues*, double); - PenStyle** StyleMap(); - - public: - Element(Graph*, const char*, Tcl_HashEntry*); - virtual ~Element(); - - virtual int configure() =0; - virtual void map() =0; - virtual void extents(Region2d*) =0; - virtual void draw(Drawable) =0; - virtual void drawActive(Drawable) =0; - virtual void drawSymbol(Drawable, int, int, int) =0; - virtual void closest() =0; - virtual void print(PSOutput*) =0; - virtual void printActive(PSOutput*) =0; - virtual void printSymbol(PSOutput*, double, double, int) =0; - - virtual ClassId classId() =0; - virtual const char* className() =0; - virtual const char* typeName() =0; - - void freeStylePalette (Chain*); - - Tk_OptionTable optionTable() {return optionTable_;} - void* ops() {return ops_;} - }; -}; - -extern void VectorChangedProc(Tcl_Interp* interp, ClientData clientData, - Blt_VectorNotify notify); - - -#endif diff --git a/src/tkbltGrElemBar.C b/src/tkbltGrElemBar.C deleted file mode 100644 index ad3099e..0000000 --- a/src/tkbltGrElemBar.C +++ /dev/null @@ -1,1323 +0,0 @@ -/* - * 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 1993-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 -#include -#include - -#include - -#include "tkbltGraphBar.h" -#include "tkbltGrElemBar.h" -#include "tkbltGrElemOption.h" -#include "tkbltGrAxis.h" -#include "tkbltGrMisc.h" -#include "tkbltGrDef.h" -#include "tkbltConfig.h" -#include "tkbltGrPSOutput.h" - -using namespace Blt; - -#define CLAMP(x,l,h) ((x) = (((x)<(l))? (l) : ((x)>(h)) ? (h) : (x))) -#define MIN3(a,b,c) (((a)<(b))?(((a)<(c))?(a):(c)):(((b)<(c))?(b):(c))) - -#define PointInRectangle(r,x0,y0) \ - (((x0) <= (int)((r)->x + (r)->width - 1)) && ((x0) >= (int)(r)->x) && \ - ((y0) <= (int)((r)->y + (r)->height - 1)) && ((y0) >= (int)(r)->y)) - -// OptionSpecs - -static Tk_ObjCustomOption styleObjOption = - { - "style", StyleSetProc, StyleGetProc, StyleRestoreProc, StyleFreeProc, - (ClientData)sizeof(BarStyle) - }; - -extern Tk_ObjCustomOption penObjOption; -extern Tk_ObjCustomOption pairsObjOption; -extern Tk_ObjCustomOption valuesObjOption; -extern Tk_ObjCustomOption xAxisObjOption; -extern Tk_ObjCustomOption yAxisObjOption; - -static Tk_OptionSpec optionSpecs[] = { - {TK_OPTION_CUSTOM, "-activepen", "activePen", "ActivePen", - "active", -1, Tk_Offset(BarElementOptions, activePenPtr), - TK_OPTION_NULL_OK, &penObjOption, LAYOUT}, - {TK_OPTION_SYNONYM, "-background", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-color", 0}, - {TK_OPTION_DOUBLE, "-barwidth", "barWidth", "BarWidth", - "0", -1, Tk_Offset(BarElementOptions, barWidth), 0, NULL, LAYOUT}, - {TK_OPTION_SYNONYM, "-bd", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-borderwidth", 0}, - {TK_OPTION_SYNONYM, "-bg", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-color", 0}, - {TK_OPTION_CUSTOM, "-bindtags", "bindTags", "BindTags", - "all", -1, Tk_Offset(BarElementOptions, tags), - TK_OPTION_NULL_OK, &listObjOption, 0}, - {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", - STD_BORDERWIDTH, -1, Tk_Offset(BarElementOptions, builtinPen.borderWidth), - 0, NULL, CACHE}, - {TK_OPTION_BORDER, "-color", "color", "color", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(BarElementOptions, builtinPen.fill), - 0, NULL, CACHE}, - {TK_OPTION_CUSTOM, "-data", "data", "Data", - NULL, -1, Tk_Offset(BarElementOptions, coords), - TK_OPTION_NULL_OK, &pairsObjOption, RESET}, - {TK_OPTION_COLOR, "-errorbarcolor", "errorBarColor", "ErrorBarColor", - NULL, -1, Tk_Offset(BarElementOptions, builtinPen.errorBarColor), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_PIXELS,"-errorbarwidth", "errorBarWidth", "ErrorBarWidth", - "1", -1, Tk_Offset(BarElementOptions, builtinPen.errorBarLineWidth), - 0, NULL, CACHE}, - {TK_OPTION_PIXELS, "-errorbarcap", "errorBarCap", "ErrorBarCap", - "0", -1, Tk_Offset(BarElementOptions, builtinPen.errorBarCapWidth), - 0, NULL, LAYOUT}, - {TK_OPTION_SYNONYM, "-fg", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-outline", 0}, - {TK_OPTION_SYNONYM, "-fill", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-color", 0}, - {TK_OPTION_SYNONYM, "-foreground", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-outline", 0}, - {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", - "no", -1, Tk_Offset(BarElementOptions, hide), 0, NULL, LAYOUT}, - {TK_OPTION_STRING, "-label", "label", "Label", - NULL, -1, Tk_Offset(BarElementOptions, label), - TK_OPTION_NULL_OK, NULL, LAYOUT}, - {TK_OPTION_RELIEF, "-legendrelief", "legendRelief", "LegendRelief", - "flat", -1, Tk_Offset(BarElementOptions, legendRelief), 0, NULL, LAYOUT}, - {TK_OPTION_CUSTOM, "-mapx", "mapX", "MapX", - "x", -1, Tk_Offset(BarElementOptions, xAxis), 0, &xAxisObjOption, RESET}, - {TK_OPTION_CUSTOM, "-mapy", "mapY", "MapY", - "y", -1, Tk_Offset(BarElementOptions, yAxis), 0, &yAxisObjOption, RESET}, - {TK_OPTION_COLOR, "-outline", "outline", "Outline", - NULL, -1, Tk_Offset(BarElementOptions, builtinPen.outlineColor), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_CUSTOM, "-pen", "pen", "Pen", - NULL, -1, Tk_Offset(BarElementOptions, normalPenPtr), - TK_OPTION_NULL_OK, &penObjOption, LAYOUT}, - {TK_OPTION_RELIEF, "-relief", "relief", "Relief", - "raised", -1, Tk_Offset(BarElementOptions, builtinPen.relief), - 0, NULL, LAYOUT}, - {TK_OPTION_STRING_TABLE, "-showerrorbars", "showErrorBars", "ShowErrorBars", - "both", -1, Tk_Offset(BarElementOptions, builtinPen.errorBarShow), - 0, &fillObjOption, LAYOUT}, - {TK_OPTION_STRING_TABLE, "-showvalues", "showValues", "ShowValues", - "none", -1, Tk_Offset(BarElementOptions, builtinPen.valueShow), - 0, &fillObjOption, CACHE}, - {TK_OPTION_STRING, "-stack", "stack", "Stack", - NULL, -1, Tk_Offset(BarElementOptions, groupName), - TK_OPTION_NULL_OK, NULL, RESET}, - {TK_OPTION_CUSTOM, "-styles", "styles", "Styles", - "", -1, Tk_Offset(BarElementOptions, stylePalette), - 0, &styleObjOption, RESET}, - {TK_OPTION_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor", - "s", -1, Tk_Offset(BarElementOptions, builtinPen.valueStyle.anchor), - 0, NULL, CACHE}, - {TK_OPTION_COLOR, "-valuecolor", "valueColor", "ValueColor", - STD_NORMAL_FOREGROUND, -1, - Tk_Offset(BarElementOptions,builtinPen.valueStyle.color), 0, NULL, CACHE}, - {TK_OPTION_FONT, "-valuefont", "valueFont", "ValueFont", - STD_FONT_SMALL, -1, Tk_Offset(BarElementOptions, builtinPen.valueStyle.font), - 0, NULL, CACHE}, - {TK_OPTION_STRING, "-valueformat", "valueFormat", "ValueFormat", - "%g", -1, Tk_Offset(BarElementOptions, builtinPen.valueFormat), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate", - "0", -1, Tk_Offset(BarElementOptions, builtinPen.valueStyle.angle), - 0, NULL, CACHE}, - {TK_OPTION_CUSTOM, "-weights", "weights", "Weights", - NULL, -1, Tk_Offset(BarElementOptions, w), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_SYNONYM, "-x", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-xdata", 0}, - {TK_OPTION_CUSTOM, "-xdata", "xData", "XData", - NULL, -1, Tk_Offset(BarElementOptions, coords.x), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_CUSTOM, "-xerror", "xError", "XError", - NULL, -1, Tk_Offset(BarElementOptions, xError), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_CUSTOM, "-xhigh", "xHigh", "XHigh", - NULL, -1, Tk_Offset(BarElementOptions, xHigh), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_CUSTOM, "-xlow", "xLow", "XLow", - NULL, -1, Tk_Offset(BarElementOptions, xLow), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_SYNONYM, "-y", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-ydata", 0}, - {TK_OPTION_CUSTOM, "-ydata", "yData", "YData", - NULL, -1, Tk_Offset(BarElementOptions, coords.y), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_CUSTOM, "-yerror", "yError", "YError", - NULL, -1, Tk_Offset(BarElementOptions, yError), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_CUSTOM, "-yhigh", "yHigh", "YHigh", - NULL, -1, Tk_Offset(BarElementOptions, yHigh), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_CUSTOM, "-ylow", "yLow", "YLow", - NULL, -1, Tk_Offset(BarElementOptions, yLow), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} -}; - -BarElement::BarElement(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) - : Element(graphPtr, name, hPtr) -{ - barToData_ =NULL; - bars_ =NULL; - activeToData_ =NULL; - activeRects_ =NULL; - nBars_ =0; - nActive_ =0; - - xeb_.segments =NULL; - xeb_.map =NULL; - xeb_.length =0; - yeb_.segments =NULL; - yeb_.map =NULL; - yeb_.length =0; - - ops_ = (BarElementOptions*)calloc(1, sizeof(BarElementOptions)); - BarElementOptions* ops = (BarElementOptions*)ops_; - ops->elemPtr = (Element*)this; - - builtinPenPtr = new BarPen(graphPtr_, "builtin", &ops->builtinPen); - ops->builtinPenPtr = builtinPenPtr; - - optionTable_ = Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); - - ops->stylePalette = new Chain(); - - // this is an option and will be freed via Tk_FreeConfigOptions - // By default an element's name and label are the same - ops->label = Tcl_Alloc(strlen(name)+1); - if (name) - strcpy((char*)ops->label,(char*)name); - - Tk_InitOptions(graphPtr_->interp_, (char*)&(ops->builtinPen), - builtinPenPtr->optionTable(), graphPtr->tkwin_); -} - -BarElement::~BarElement() -{ - BarElementOptions* ops = (BarElementOptions*)ops_; - - if (builtinPenPtr) - delete builtinPenPtr; - - reset(); - - if (ops->stylePalette) { - freeStylePalette(ops->stylePalette); - delete ops->stylePalette; - } -} - -int BarElement::configure() -{ - BarElementOptions* ops = (BarElementOptions*)ops_; - - if (builtinPenPtr->configure() != TCL_OK) - return TCL_ERROR; - - // Point to the static normal pen if no external pens have been selected. - ChainLink* link = Chain_FirstLink(ops->stylePalette); - if (!link) { - link = new ChainLink(sizeof(BarStyle)); - ops->stylePalette->linkAfter(link, NULL); - } - BarStyle* stylePtr = (BarStyle*)Chain_GetValue(link); - stylePtr->penPtr = NORMALPEN(ops); - - return TCL_OK; -} - -void BarElement::map() -{ - BarGraph* barGraphPtr_ = (BarGraph*)graphPtr_; - BarElementOptions* ops = (BarElementOptions*)ops_; - BarGraphOptions* gops = (BarGraphOptions*)graphPtr_->ops_; - - if (!link) - return; - - reset(); - if (!ops->coords.x || !ops->coords.y || - !ops->coords.x->nValues() || !ops->coords.y->nValues()) - return; - int nPoints = NUMBEROFPOINTS(ops); - - double barWidth = (ops->barWidth > 0.0f) ? ops->barWidth : gops->barWidth; - AxisOptions* axisyops = (AxisOptions*)ops->yAxis->ops(); - double baseline = (axisyops->logScale) ? 0.0 : gops->baseline; - double barOffset = barWidth * 0.5; - - // Create an array of bars representing the screen coordinates of all the - // segments in the bar. - XRectangle* bars = new XRectangle[nPoints]; - int* barToData = new int[nPoints]; - - double* x = ops->coords.x->values_; - double* y = ops->coords.y->values_; - int count = 0; - - int ii; - XRectangle* rp; - for (rp=bars, ii=0; ii ops->xAxis->axisRange_.max) || - ((x[ii] + barWidth) < ops->xAxis->axisRange_.min)) - continue; - - c1.x = x[ii] - barOffset; - c1.y = y[ii]; - c2.x = c1.x + barWidth; - c2.y = baseline; - - // If the mode is "aligned" or "stacked" we need to adjust the x or y - // coordinates of the two corners. - if ((barGraphPtr_->nBarGroups_ > 0) && - ((BarGraph::BarMode)gops->barMode != BarGraph::INFRONT) && - (!gops->stackAxes)) { - - BarSetKey key; - key.value =x[ii]; - key.xAxis =ops->xAxis; - key.yAxis =NULL; - Tcl_HashEntry* hPtr = - Tcl_FindHashEntry(&barGraphPtr_->setTable_, (char*)&key); - - if (hPtr) { - Tcl_HashTable *tablePtr = (Tcl_HashTable*)Tcl_GetHashValue(hPtr); - const char *name = (ops->groupName) ? ops->groupName:ops->yAxis->name_; - Tcl_HashEntry* hPtr2 = Tcl_FindHashEntry(tablePtr, name); - if (hPtr2) { - BarGroup* groupPtr = (BarGroup*)Tcl_GetHashValue(hPtr2); - double slice = barWidth / (double)barGraphPtr_->maxBarSetSize_; - double offset = (slice * groupPtr->index); - if (barGraphPtr_->maxBarSetSize_ > 1) { - offset += slice * 0.05; - slice *= 0.90; - } - - switch ((BarGraph::BarMode)gops->barMode) { - case BarGraph::STACKED: - groupPtr->count++; - c2.y = groupPtr->lastY; - c1.y += c2.y; - groupPtr->lastY = c1.y; - c1.x += offset; - c2.x = c1.x + slice; - break; - - case BarGraph::ALIGNED: - slice /= groupPtr->nSegments; - c1.x += offset + (slice * groupPtr->count); - c2.x = c1.x + slice; - groupPtr->count++; - break; - - case BarGraph::OVERLAP: - { - slice /= (groupPtr->nSegments + 1); - double width = slice + slice; - groupPtr->count++; - c1.x += offset + - (slice * (groupPtr->nSegments - groupPtr->count)); - c2.x = c1.x + width; - } - break; - - case BarGraph::INFRONT: - break; - } - } - } - } - - int invertBar = 0; - if (c1.y < c2.y) { - // Handle negative bar values by swapping ordinates - double temp = c1.y; - c1.y = c2.y; - c2.y = temp; - invertBar = 1; - } - - // Get the two corners of the bar segment and compute the rectangle - double ybot = c2.y; - c1 = graphPtr_->map2D(c1.x, c1.y, ops->xAxis, ops->yAxis); - c2 = graphPtr_->map2D(c2.x, c2.y, ops->xAxis, ops->yAxis); - if ((ybot == 0.0) && (axisyops->logScale)) - c2.y = graphPtr_->bottom_; - - if (c2.y < c1.y) { - double t = c1.y; - c1.y = c2.y; - c2.y = t; - } - - if (c2.x < c1.x) { - double t = c1.x; - c1.x = c2.x; - c2.x = t; - } - - if ((c1.x > graphPtr_->right_) || (c2.x < graphPtr_->left_) || - (c1.y > graphPtr_->bottom_) || (c2.y < graphPtr_->top_)) - continue; - - // Bound the bars horizontally by the width of the graph window - // Bound the bars vertically by the position of the axis. - double right =0; - double left =0; - double top =0; - double bottom =0; - if (gops->stackAxes) { - top = ops->yAxis->screenMin_; - bottom = ops->yAxis->screenMin_ + ops->yAxis->screenRange_; - left = graphPtr_->left_; - right = graphPtr_->right_; - } - else { - bottom = right = 10000; - // Shouldn't really have a call to Tk_Width or Tk_Height in - // mapping routine. We only want to clamp the bar segment to the - // size of the window if we're actually mapped onscreen - if (Tk_Height(graphPtr_->tkwin_) > 1) - bottom = Tk_Height(graphPtr_->tkwin_); - if (Tk_Width(graphPtr_->tkwin_) > 1) - right = Tk_Width(graphPtr_->tkwin_); - } - - CLAMP(c1.y, top, bottom); - CLAMP(c2.y, top, bottom); - CLAMP(c1.x, left, right); - CLAMP(c2.x, left, right); - double dx = fabs(c1.x - c2.x); - double dy = fabs(c1.y - c2.y); - if ((dx == 0) || (dy == 0)) - continue; - - int height = (int)dy; - if (invertBar) - rp->y = (short int)MIN(c1.y, c2.y); - else - rp->y = (short int)(MAX(c1.y, c2.y)) - height; - - rp->x = (short int)MIN(c1.x, c2.x); - rp->width = (short int)dx + 1; - rp->width |= 0x1; - if (rp->width < 1) - rp->width = 1; - - rp->height = height + 1; - if (rp->height < 1) - rp->height = 1; - - // Save the data index corresponding to the rectangle - barToData[count] = ii; - count++; - rp++; - } - nBars_ = count; - bars_ = bars; - barToData_ = barToData; - if (nActiveIndices_ > 0) - mapActive(); - - int size = 20; - if (count > 0) - size = bars->width; - - // Set the symbol size of all the pen styles - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - BarStyle *stylePtr = (BarStyle*)Chain_GetValue(link); - BarPen* penPtr = stylePtr->penPtr; - BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); - stylePtr->symbolSize = size; - stylePtr->errorBarCapWidth = pops->errorBarCapWidth; - } - - BarStyle** dataToStyle = (BarStyle**)StyleMap(); - if (((ops->yHigh && ops->yHigh->nValues() > 0) && - (ops->yLow && ops->yLow->nValues() > 0)) || - ((ops->xHigh && ops->xHigh->nValues() > 0) && - (ops->xLow && ops->xLow->nValues() > 0)) || - (ops->xError && ops->xError->nValues() > 0) || - (ops->yError && ops->yError->nValues() > 0)) { - mapErrorBars(dataToStyle); - } - - mergePens(dataToStyle); - delete [] dataToStyle; -} - -void BarElement::extents(Region2d *regPtr) -{ - BarGraph* barGraphPtr_ = (BarGraph*)graphPtr_; - BarElementOptions* ops = (BarElementOptions*)ops_; - BarGraphOptions* gops = (BarGraphOptions*)graphPtr_->ops_; - - regPtr->top = regPtr->left = DBL_MAX; - regPtr->bottom = regPtr->right = -DBL_MAX; - - if (!ops->coords.x || !ops->coords.y || - !ops->coords.x->nValues() || !ops->coords.y->nValues()) - return; - - int nPoints = NUMBEROFPOINTS(ops); - - double middle = 0.5; - regPtr->left = ops->coords.x->min() - middle; - regPtr->right = ops->coords.x->max() + middle; - - regPtr->top = ops->coords.y->min(); - regPtr->bottom = ops->coords.y->max(); - if (regPtr->bottom < gops->baseline) - regPtr->bottom = gops->baseline; - - // Handle stacked bar elements specially. - // If element is stacked, the sum of its ordinates may be outside the - // minimum/maximum limits of the element's data points. - if (((BarGraph::BarMode)gops->barMode == BarGraph::STACKED) && - (barGraphPtr_->nBarGroups_ > 0)) - checkStacks(ops->xAxis, ops->yAxis, ®Ptr->top, ®Ptr->bottom); - - // Warning: You get what you deserve if the x-axis is logScale - AxisOptions* axisxops = (AxisOptions*)ops->xAxis->ops(); - AxisOptions* axisyops = (AxisOptions*)ops->yAxis->ops(); - if (axisxops->logScale) - regPtr->left = FindElemValuesMinimum(ops->coords.x, DBL_MIN) + middle; - - // Fix y-min limits for barchart - if (axisyops->logScale) { - if ((regPtr->top <= 0.0) || (regPtr->top > 1.0)) - regPtr->top = 1.0; - } - else { - if (regPtr->top > 0.0) - regPtr->top = 0.0; - } - - // Correct the extents for error bars if they exist - if (ops->xError && (ops->xError->nValues() > 0)) { - nPoints = MIN(ops->xError->nValues(), nPoints); - for (int ii=0; iicoords.x->values_[ii] + ops->xError->values_[ii]; - if (x > regPtr->right) - regPtr->right = x; - - x = ops->coords.x->values_[ii] - ops->xError->values_[ii]; - if (axisxops->logScale) { - // Mirror negative values, instead of ignoring them - if (x < 0.0) - x = -x; - - if ((x > DBL_MIN) && (x < regPtr->left)) - regPtr->left = x; - - } - else if (x < regPtr->left) - regPtr->left = x; - } - } - else { - if ((ops->xHigh) && - (ops->xHigh->nValues() > 0) && - (ops->xHigh->max() > regPtr->right)) - regPtr->right = ops->xHigh->max(); - - if (ops->xLow && (ops->xLow->nValues() > 0)) { - double left; - if ((ops->xLow->min() <= 0.0) && (axisxops->logScale)) - left = FindElemValuesMinimum(ops->xLow, DBL_MIN); - else - left = ops->xLow->min(); - - if (left < regPtr->left) - regPtr->left = left; - } - } - - if (ops->yError && (ops->yError->nValues() > 0)) { - nPoints = MIN(ops->yError->nValues(), nPoints); - - for (int ii=0; iicoords.y->values_[ii] + ops->yError->values_[ii]; - if (y > regPtr->bottom) - regPtr->bottom = y; - - y = ops->coords.y->values_[ii] - ops->yError->values_[ii]; - if (axisyops->logScale) { - // Mirror negative values, instead of ignoring them - if (y < 0.0) - y = -y; - - if ((y > DBL_MIN) && (y < regPtr->left)) - regPtr->top = y; - - } - else if (y < regPtr->top) - regPtr->top = y; - } - } - else { - if ((ops->yHigh) && - (ops->yHigh->nValues() > 0) && - (ops->yHigh->max() > regPtr->bottom)) - regPtr->bottom = ops->yHigh->max(); - - if (ops->yLow && ops->yLow->nValues() > 0) { - double top; - if ((ops->yLow->min() <= 0.0) && - (axisyops->logScale)) - top = FindElemValuesMinimum(ops->yLow, DBL_MIN); - else - top = ops->yLow->min(); - - if (top < regPtr->top) - regPtr->top = top; - } - } -} - -void BarElement::closest() -{ - BarElementOptions* ops = (BarElementOptions*)ops_; - BarGraphOptions* gops = (BarGraphOptions*)graphPtr_->ops_; - - ClosestSearch* searchPtr = &gops->search; - double minDist = searchPtr->dist; - int imin = 0; - - int ii; - XRectangle* bp; - for (bp=bars_, ii=0; iix, searchPtr->y)) { - imin = barToData_[ii]; - minDist = 0.0; - break; - } - double left = bp->x; - double top = bp->y; - double right = (double)(bp->x + bp->width); - double bottom = (double)(bp->y + bp->height); - - Point2d outline[5]; - outline[4].x = outline[3].x = outline[0].x = left; - outline[4].y = outline[1].y = outline[0].y = top; - outline[2].x = outline[1].x = right; - outline[3].y = outline[2].y = bottom; - - Point2d *pp, *pend; - for (pp=outline, pend=outline+4; ppx, searchPtr->y, pp, pp + 1); - if (t.x > right) - t.x = right; - else if (t.x < left) - t.x = left; - - if (t.y > bottom) - t.y = bottom; - else if (t.y < top) - t.y = top; - - double dist = hypot((t.x - searchPtr->x), (t.y - searchPtr->y)); - if (dist < minDist) { - minDist = dist; - imin = barToData_[ii]; - } - } - } - if (minDist < searchPtr->dist) { - searchPtr->elemPtr = (Element*)this; - searchPtr->dist = minDist; - searchPtr->index = imin; - searchPtr->point.x = - ops->coords.x ? (double)ops->coords.x->values_[imin] : 0; - searchPtr->point.y = - ops->coords.y ? (double)ops->coords.y->values_[imin] : 0; - } -} - -void BarElement::draw(Drawable drawable) -{ - BarElementOptions* ops = (BarElementOptions*)ops_; - - if (ops->hide) - return; - - int count = 0; - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - - BarStyle* stylePtr = (BarStyle*)Chain_GetValue(link); - BarPen* penPtr = (BarPen*)stylePtr->penPtr; - BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); - - if (stylePtr->nBars > 0) - drawSegments(drawable, penPtr, stylePtr->bars, stylePtr->nBars); - - if ((stylePtr->xeb.length > 0) && (pops->errorBarShow & SHOW_X)) - graphPtr_->drawSegments(drawable, penPtr->errorBarGC_, - stylePtr->xeb.segments, stylePtr->xeb.length); - - if ((stylePtr->yeb.length > 0) && (pops->errorBarShow & SHOW_Y)) - graphPtr_->drawSegments(drawable, penPtr->errorBarGC_, - stylePtr->yeb.segments, stylePtr->yeb.length); - - if (pops->valueShow != SHOW_NONE) - drawValues(drawable, penPtr, stylePtr->bars, stylePtr->nBars, - barToData_ + count); - - count += stylePtr->nBars; - } -} - -void BarElement::drawActive(Drawable drawable) -{ - BarElementOptions* ops = (BarElementOptions*)ops_; - - if (ops->hide || !active_) - return; - - BarPen* penPtr = (BarPen*)ops->activePenPtr; - if (!penPtr) - return; - BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); - - if (nActiveIndices_ > 0) { - mapActive(); - - drawSegments(drawable, penPtr, activeRects_, nActive_); - if (pops->valueShow != SHOW_NONE) - drawValues(drawable, penPtr, activeRects_, nActive_, activeToData_); - } - else if (nActiveIndices_ < 0) { - drawSegments(drawable, penPtr, bars_, nBars_); - if (pops->valueShow != SHOW_NONE) - drawValues(drawable, penPtr, bars_, nBars_, barToData_); - } -} - -void BarElement::drawSymbol(Drawable drawable, int x, int y, int size) -{ - BarElementOptions* ops = (BarElementOptions*)ops_; - - BarPen* penPtr = NORMALPEN(ops); - BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); - - int radius = (size / 2); - size--; - - x -= radius; - y -= radius; - - Tk_Fill3DRectangle(graphPtr_->tkwin_, drawable, - pops->fill, x, y, size, size, - pops->borderWidth, pops->relief); - - if (pops->outlineColor) - XDrawRectangle(graphPtr_->display_, drawable, penPtr->outlineGC_, - x, y, size, size); -} - -void BarElement::print(PSOutput* psPtr) -{ - BarElementOptions* ops = (BarElementOptions*)ops_; - - if (ops->hide) - return; - - psPtr->format("\n%% Element \"%s\"\n\n", name_); - - int count = 0; - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - - BarStyle *stylePtr = (BarStyle*)Chain_GetValue(link); - BarPen* penPtr = (BarPen*)stylePtr->penPtr; - BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); - - if (stylePtr->nBars > 0) - printSegments(psPtr, penPtr, stylePtr->bars, stylePtr->nBars); - - XColor* colorPtr = pops->errorBarColor; - if (!colorPtr) - colorPtr = pops->outlineColor; - if (!colorPtr) - colorPtr = Tk_3DBorderColor(pops->fill); - - if ((stylePtr->xeb.length > 0) && (pops->errorBarShow & SHOW_X)) { - psPtr->setLineAttributes(colorPtr, pops->errorBarLineWidth, - NULL, CapButt, JoinMiter); - psPtr->printSegments(stylePtr->xeb.segments, stylePtr->xeb.length); - } - - if ((stylePtr->yeb.length > 0) && (pops->errorBarShow & SHOW_Y)) { - psPtr->setLineAttributes(colorPtr, pops->errorBarLineWidth, - NULL, CapButt, JoinMiter); - psPtr->printSegments(stylePtr->yeb.segments, stylePtr->yeb.length); - } - - if (pops->valueShow != SHOW_NONE) - printValues(psPtr, penPtr, stylePtr->bars, stylePtr->nBars, - barToData_ + count); - - count += stylePtr->nBars; - } -} - -void BarElement::printActive(PSOutput* psPtr) -{ - BarElementOptions* ops = (BarElementOptions*)ops_; - - if (ops->hide || !active_) - return; - - BarPen* penPtr = (BarPen*)ops->activePenPtr; - if (!penPtr) - return; - BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); - - psPtr->format("\n%% Active Element \"%s\"\n\n", name_); - - if (nActiveIndices_ > 0) { - mapActive(); - - printSegments(psPtr, penPtr, activeRects_, nActive_); - if (pops->valueShow != SHOW_NONE) - printValues(psPtr, penPtr, activeRects_, nActive_,activeToData_); - } - else if (nActiveIndices_ < 0) { - printSegments(psPtr, penPtr, bars_, nBars_); - if (pops->valueShow != SHOW_NONE) - printValues(psPtr, penPtr, bars_, nBars_, barToData_); - } -} - -void BarElement::printSymbol(PSOutput* psPtr, double x, double y, int size) -{ - BarElementOptions* ops = (BarElementOptions*)ops_; - - BarPen* penPtr = NORMALPEN(ops); - BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); - - x -= size/2.; - y -= size/2.; - - psPtr->fill3DRectangle(pops->fill, x, y, size, size, - pops->borderWidth, pops->relief); - - if (pops->outlineColor) { - psPtr->setForeground(pops->outlineColor); - psPtr->printRectangle(x, y, size, size); - } -} - -// Support - -void BarElement::ResetStylePalette(Chain* stylePalette) -{ - for (ChainLink* link = Chain_FirstLink(stylePalette); link; - link = Chain_NextLink(link)) { - BarStyle *stylePtr = (BarStyle*)Chain_GetValue(link); - stylePtr->xeb.length = 0; - stylePtr->yeb.length = 0; - stylePtr->nBars = 0; - } -} - -void BarElement::checkStacks(Axis* xAxis, Axis* yAxis, - double* minPtr, double* maxPtr) -{ - BarGraph* barGraphPtr_ = (BarGraph*)graphPtr_; - BarGraphOptions* gops = (BarGraphOptions*)graphPtr_->ops_; - if (((BarGraph::BarMode)gops->barMode != BarGraph::STACKED) || - barGraphPtr_->nBarGroups_ == 0) - return; - - for (BarGroup *gp = barGraphPtr_->barGroups_, - *gend = gp + barGraphPtr_->nBarGroups_; gp < gend; gp++) { - if ((gp->xAxis == xAxis) && (gp->yAxis == yAxis)) { - - // Check if any of the y-values (because of stacking) are greater - // than the current limits of the graph. - if (gp->sum < 0.0f) { - if (*minPtr > gp->sum) - *minPtr = gp->sum; - } - else { - if (*maxPtr < gp->sum) - *maxPtr = gp->sum; - } - } - } -} - -void BarElement::mergePens(BarStyle** dataToStyle) -{ - BarElementOptions* ops = (BarElementOptions*)ops_; - - if (Chain_GetLength(ops->stylePalette) < 2) { - ChainLink* link = Chain_FirstLink(ops->stylePalette); - BarStyle *stylePtr = (BarStyle*)Chain_GetValue(link); - stylePtr->nBars = nBars_; - stylePtr->bars = bars_; - stylePtr->symbolSize = bars_->width / 2; - stylePtr->xeb.length = xeb_.length; - stylePtr->xeb.segments = xeb_.segments; - stylePtr->yeb.length = yeb_.length; - stylePtr->yeb.segments = yeb_.segments; - return; - } - - // We have more than one style. Group bar segments of like pen styles together - if (nBars_ > 0) { - XRectangle* bars = new XRectangle[nBars_]; - int* barToData = new int[nBars_]; - XRectangle* bp = bars; - int* ip = barToData; - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - BarStyle *stylePtr = (BarStyle*)Chain_GetValue(link); - stylePtr->symbolSize = bp->width / 2; - stylePtr->bars = bp; - for (int ii=0; iinBars = bp - stylePtr->bars; - } - delete [] bars_; - bars_ = bars; - delete [] barToData_; - barToData_ = barToData; - } - - if (xeb_.length > 0) { - Segment2d* bars = new Segment2d[xeb_.length]; - Segment2d *sp = bars; - int* map = new int[xeb_.length]; - int* ip = map; - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - BarStyle *stylePtr = (BarStyle*)Chain_GetValue(link); - stylePtr->xeb.segments = sp; - for (int ii=0; iixeb.length = sp - stylePtr->xeb.segments; - } - delete [] xeb_.segments; - xeb_.segments = bars; - delete [] xeb_.map; - xeb_.map = map; - } - - if (yeb_.length > 0) { - Segment2d* bars = new Segment2d[yeb_.length]; - Segment2d* sp = bars; - int* map = new int[yeb_.length]; - int* ip = map; - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - BarStyle *stylePtr = (BarStyle*)Chain_GetValue(link); - stylePtr->yeb.segments = sp; - for (int ii=0; iiyeb.length = sp - stylePtr->yeb.segments; - } - delete [] yeb_.segments; - yeb_.segments = bars; - delete [] yeb_.map; - yeb_.map = map; - } -} - -void BarElement::mapActive() -{ - if (activeRects_) { - delete [] activeRects_; - activeRects_ = NULL; - } - if (activeToData_) { - delete [] activeToData_; - activeToData_ = NULL; - } - nActive_ = 0; - - if (nActiveIndices_ > 0) { - XRectangle* activeRects = new XRectangle[nActiveIndices_]; - int* activeToData = new int[nActiveIndices_]; - int count = 0; - for (int ii=0; iistylePalette); - - if (activeRects_) - delete [] activeRects_; - activeRects_ = NULL; - if (activeToData_) - delete [] activeToData_; - activeToData_ = NULL; - - if (xeb_.segments) - delete [] xeb_.segments; - xeb_.segments = NULL; - if (xeb_.map) - delete [] xeb_.map; - xeb_.map = NULL; - xeb_.length = 0; - - if (yeb_.segments) - delete [] yeb_.segments; - yeb_.segments = NULL; - if (yeb_.map) - delete [] yeb_.map; - yeb_.map = NULL; - yeb_.length = 0; - - if (bars_) - delete [] bars_; - bars_ = NULL; - if (barToData_) - delete [] barToData_; - barToData_ = NULL; - - nActive_ = 0; - nBars_ = 0; -} - -void BarElement::mapErrorBars(BarStyle **dataToStyle) -{ - BarElementOptions* ops = (BarElementOptions*)ops_; - - Region2d reg; - graphPtr_->extents(®); - - int nPoints = NUMBEROFPOINTS(ops); - int nn =0; - if (ops->coords.x && ops->coords.y) { - if (ops->xError && (ops->xError->nValues() > 0)) - nn = MIN(ops->xError->nValues(), nPoints); - else - if (ops->xHigh && ops->xLow) - nn = MIN3(ops->xHigh->nValues(), ops->xLow->nValues(), nPoints); - } - - if (nn) { - Segment2d* bars = new Segment2d[nn * 3]; - Segment2d* segPtr = bars; - int* map = new int[nn * 3]; - int* indexPtr = map; - - for (int ii=0; iicoords.x->values_[ii]; - double y = ops->coords.y->values_[ii]; - BarStyle* stylePtr = dataToStyle[ii]; - - if ((isfinite(x)) && (isfinite(y))) { - double high, low; - if (ops->xError->nValues() > 0) { - high = x + ops->xError->values_[ii]; - low = x - ops->xError->values_[ii]; - } - else { - high = ops->xHigh ? ops->xHigh->values_[ii] : 0; - low = ops->xLow ? ops->xLow->values_[ii] : 0; - } - if ((isfinite(high)) && (isfinite(low))) { - Point2d p = graphPtr_->map2D(high, y, ops->xAxis, ops->yAxis); - Point2d q = graphPtr_->map2D(low, y, ops->xAxis, ops->yAxis); - segPtr->p = p; - segPtr->q = q; - if (lineRectClip(®, &segPtr->p, &segPtr->q)) { - segPtr++; - *indexPtr++ = ii; - } - // Left cap - segPtr->p.x = p.x; - segPtr->q.x = p.x; - segPtr->p.y = p.y - stylePtr->errorBarCapWidth; - segPtr->q.y = p.y + stylePtr->errorBarCapWidth; - if (lineRectClip(®, &segPtr->p, &segPtr->q)) { - segPtr++; - *indexPtr++ = ii; - } - // Right cap - segPtr->p.x = q.x; - segPtr->q.x = q.x; - segPtr->p.y = q.y - stylePtr->errorBarCapWidth; - segPtr->q.y = q.y + stylePtr->errorBarCapWidth; - if (lineRectClip(®, &segPtr->p, &segPtr->q)) { - segPtr++; - *indexPtr++ = ii; - } - } - } - } - xeb_.segments = bars; - xeb_.length = segPtr - bars; - xeb_.map = map; - } - - nn =0; - if (ops->coords.x && ops->coords.y) { - if (ops->yError && (ops->yError->nValues() > 0)) - nn = MIN(ops->yError->nValues(), nPoints); - else - if (ops->yHigh && ops->yLow) - nn = MIN3(ops->yHigh->nValues(), ops->yLow->nValues(), nPoints); - } - - if (nn) { - Segment2d* bars = new Segment2d[nn * 3]; - Segment2d* segPtr = bars; - int* map = new int[nn * 3]; - int* indexPtr = map; - - for (int ii=0; iicoords.x->values_[ii]; - double y = ops->coords.y->values_[ii]; - BarStyle *stylePtr = dataToStyle[ii]; - - if ((isfinite(x)) && (isfinite(y))) { - double high, low; - if (ops->yError->nValues() > 0) { - high = y + ops->yError->values_[ii]; - low = y - ops->yError->values_[ii]; - } - else { - high = ops->yHigh->values_[ii]; - low = ops->yLow->values_[ii]; - } - if ((isfinite(high)) && (isfinite(low))) { - Point2d p = graphPtr_->map2D(x, high, ops->xAxis, ops->yAxis); - Point2d q = graphPtr_->map2D(x, low, ops->xAxis, ops->yAxis); - segPtr->p = p; - segPtr->q = q; - if (lineRectClip(®, &segPtr->p, &segPtr->q)) { - segPtr++; - *indexPtr++ = ii; - } - // Top cap - segPtr->p.y = p.y; - segPtr->q.y = p.y; - segPtr->p.x = p.x - stylePtr->errorBarCapWidth; - segPtr->q.x = p.x + stylePtr->errorBarCapWidth; - if (lineRectClip(®, &segPtr->p, &segPtr->q)) { - segPtr++; - *indexPtr++ = ii; - } - // Bottom cap - segPtr->p.y = q.y; - segPtr->q.y = q.y; - segPtr->p.x = q.x - stylePtr->errorBarCapWidth; - segPtr->q.x = q.x + stylePtr->errorBarCapWidth; - if (lineRectClip(®, &segPtr->p, &segPtr->q)) { - segPtr++; - *indexPtr++ = ii; - } - } - } - } - yeb_.segments = bars; - yeb_.length = segPtr - bars; - yeb_.map = map; - } -} - -void BarElement::drawSegments(Drawable drawable, BarPen* penPtr, - XRectangle *bars, int nBars) -{ - BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); - for (XRectangle *rp = bars, *rend = rp + nBars; rp < rend; rp++) { - if ((rp->width < 1) || (rp->height < 1)) - continue; - - Tk_Fill3DRectangle(graphPtr_->tkwin_, drawable, - pops->fill, rp->x, rp->y, rp->width, rp->height, - pops->borderWidth, pops->relief); - - if (pops->outlineColor) - XDrawRectangle(graphPtr_->display_, drawable, penPtr->outlineGC_, - rp->x, rp->y, rp->width, rp->height); - } -} - -void BarElement::drawValues(Drawable drawable, BarPen* penPtr, - XRectangle *bars, int nBars, int *barToData) -{ - BarElementOptions* ops = (BarElementOptions*)ops_; - BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); - BarGraphOptions* gops = (BarGraphOptions*)graphPtr_->ops_; - - const char *fmt = pops->valueFormat; - if (!fmt) - fmt = "%g"; - TextStyle ts(graphPtr_, &pops->valueStyle); - - int count = 0; - for (XRectangle *rp = bars, *rend = rp + nBars; rp < rend; rp++) { - Point2d anchorPos; - char string[TCL_DOUBLE_SPACE * 2 + 2]; - - double x = ops->coords.x->values_[barToData[count]]; - double y = ops->coords.y->values_[barToData[count]]; - - count++; - if (pops->valueShow == SHOW_X) - snprintf(string, TCL_DOUBLE_SPACE, fmt, x); - else if (pops->valueShow == SHOW_Y) - snprintf(string, TCL_DOUBLE_SPACE, fmt, y); - else if (pops->valueShow == SHOW_BOTH) { - snprintf(string, TCL_DOUBLE_SPACE, fmt, x); - strcat(string, ","); - snprintf(string + strlen(string), TCL_DOUBLE_SPACE, fmt, y); - } - - if (gops->inverted) { - anchorPos.y = rp->y + rp->height * 0.5; - anchorPos.x = rp->x + rp->width; - if (x < gops->baseline) - anchorPos.x -= rp->width; - } - else { - anchorPos.x = rp->x + rp->width * 0.5; - anchorPos.y = rp->y; - if (y < gops->baseline) - anchorPos.y += rp->height; - } - - ts.drawText(drawable, string, anchorPos.x, anchorPos.y); - } -} - -void BarElement::printSegments(PSOutput* psPtr, BarPen* penPtr, - XRectangle *bars, int nBars) -{ - BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); - for (XRectangle *rp = bars, *rend = rp + nBars; rp < rend; rp++) { - if ((rp->width < 1) || (rp->height < 1)) - continue; - - psPtr->fill3DRectangle(pops->fill, (double)rp->x, (double)rp->y, - (int)rp->width, (int)rp->height, - pops->borderWidth, pops->relief); - - if (pops->outlineColor) { - psPtr->setForeground(pops->outlineColor); - psPtr->printRectangle((double)rp->x, (double)rp->y, - (int)rp->width, (int)rp->height); - } - } -} - -void BarElement::printValues(PSOutput* psPtr, BarPen* penPtr, - XRectangle *bars, int nBars, int *barToData) -{ - BarPenOptions* pops = (BarPenOptions*)penPtr->ops(); - BarElementOptions* ops = (BarElementOptions*)ops_; - BarGraphOptions* gops = (BarGraphOptions*)graphPtr_->ops_; - - int count = 0; - const char* fmt = pops->valueFormat; - if (!fmt) - fmt = "%g"; - TextStyle ts(graphPtr_, &pops->valueStyle); - - for (XRectangle *rp = bars, *rend = rp + nBars; rp < rend; rp++) { - double x = ops->coords.x->values_[barToData[count]]; - double y = ops->coords.y->values_[barToData[count]]; - - count++; - char string[TCL_DOUBLE_SPACE * 2 + 2]; - if (pops->valueShow == SHOW_X) - snprintf(string, TCL_DOUBLE_SPACE, fmt, x); - else if (pops->valueShow == SHOW_Y) - snprintf(string, TCL_DOUBLE_SPACE, fmt, y); - else if (pops->valueShow == SHOW_BOTH) { - snprintf(string, TCL_DOUBLE_SPACE, fmt, x); - strcat(string, ","); - snprintf(string + strlen(string), TCL_DOUBLE_SPACE, fmt, y); - } - - Point2d anchorPos; - if (gops->inverted) { - anchorPos.y = rp->y + rp->height * 0.5; - anchorPos.x = rp->x + rp->width; - if (x < gops->baseline) - anchorPos.x -= rp->width; - } - else { - anchorPos.x = rp->x + rp->width * 0.5; - anchorPos.y = rp->y; - if (y < gops->baseline) - anchorPos.y += rp->height; - } - - ts.printText(psPtr, string, anchorPos.x, anchorPos.y); - } -} - diff --git a/src/tkbltGrElemBar.h b/src/tkbltGrElemBar.h deleted file mode 100644 index 9207a9f..0000000 --- a/src/tkbltGrElemBar.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrElemBar_h__ -#define __BltGrElemBar_h__ - -#include - -#include - -#include "tkbltGrElem.h" -#include "tkbltGrPenBar.h" - -namespace Blt { - - typedef struct { - float x1; - float y1; - float x2; - float y2; - } BarRegion; - - typedef struct { - Weight weight; - BarPen* penPtr; - XRectangle* bars; - int nBars; - GraphSegments xeb; - GraphSegments yeb; - int symbolSize; - int errorBarCapWidth; - } BarStyle; - - typedef struct { - Element* elemPtr; - const char *label; - char** tags; - Axis* xAxis; - Axis* yAxis; - ElemCoords coords; - ElemValues* w; - ElemValues* xError; - ElemValues* yError; - ElemValues* xHigh; - ElemValues* xLow; - ElemValues* yHigh; - ElemValues* yLow; - int hide; - int legendRelief; - Chain* stylePalette; - BarPen* builtinPenPtr; - BarPen* activePenPtr; - BarPen* normalPenPtr; - BarPenOptions builtinPen; - - // derived - double barWidth; - const char *groupName; - } BarElementOptions; - - class BarElement : public Element { - protected: - BarPen* builtinPenPtr; - int* barToData_; - XRectangle* bars_; - int* activeToData_; - XRectangle* activeRects_; - int nBars_; - int nActive_; - GraphSegments xeb_; - GraphSegments yeb_; - - protected: - void ResetStylePalette(Chain*); - void checkStacks(Axis*, Axis*, double*, double*); - void mergePens(BarStyle**); - void mapActive(); - void reset(); - void mapErrorBars(BarStyle**); - void drawSegments(Drawable, BarPen*, XRectangle*, int); - void drawValues(Drawable, BarPen*, XRectangle*, int, int*); - void printSegments(PSOutput*, BarPen*, XRectangle*, int); - void printValues(PSOutput*, BarPen*, XRectangle*, int, int*); - - public: - BarElement(Graph*, const char*, Tcl_HashEntry*); - virtual ~BarElement(); - - ClassId classId() {return CID_ELEM_BAR;} - const char* className() {return "BarElement";} - const char* typeName() {return "bar";} - - int configure(); - void map(); - void extents(Region2d*); - void closest(); - void draw(Drawable); - void drawActive(Drawable); - void drawSymbol(Drawable, int, int, int); - void print(PSOutput*); - void printActive(PSOutput*); - void printSymbol(PSOutput*, double, double, int); - }; -}; - -#endif diff --git a/src/tkbltGrElemLine.C b/src/tkbltGrElemLine.C deleted file mode 100644 index 8da4279..0000000 --- a/src/tkbltGrElemLine.C +++ /dev/null @@ -1,2486 +0,0 @@ -/* - * 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 (c) 1993 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 -#include -#include - -#include - -#include "tkbltGraph.h" -#include "tkbltGrElemLine.h" -#include "tkbltGrElemOption.h" -#include "tkbltGrAxis.h" -#include "tkbltGrMisc.h" -#include "tkbltGrDef.h" -#include "tkbltConfig.h" -#include "tkbltGrPSOutput.h" - -using namespace Blt; - -#define SEARCH_X 0 -#define SEARCH_Y 1 -#define SEARCH_BOTH 2 - -#define SEARCH_POINTS 0 // closest data point. -#define SEARCH_TRACES 1 // closest point on trace. -#define SEARCH_AUTO 2 // traces if linewidth is > 0 and more than one - -#define MIN3(a,b,c) (((a)<(b))?(((a)<(c))?(a):(c)):(((b)<(c))?(b):(c))) -#define PointInRegion(e,x,y) (((x) <= (e)->right) && ((x) >= (e)->left) && ((y) <= (e)->bottom) && ((y) >= (e)->top)) - -#define BROKEN_TRACE(dir,last,next) (((dir == INCREASING)&&(next < last)) || ((dir == DECREASING)&&(next > last))) -#define DRAW_SYMBOL() (symbolInterval_==0||(symbolCounter_%symbolInterval_)==0) - -static const char* symbolMacros[] = - {"Li", "Sq", "Ci", "Di", "Pl", "Cr", "Sp", "Sc", "Tr", "Ar", "Bm", NULL}; - -// OptionSpecs - -static const char* smoothObjOption[] = - {"linear", "step", "cubic", "quadratic", "catrom", NULL}; - -static const char* penDirObjOption[] = - {"increasing", "decreasing", "both", NULL}; - -static Tk_ObjCustomOption styleObjOption = - { - "styles", StyleSetProc, StyleGetProc, StyleRestoreProc, StyleFreeProc, - (ClientData)sizeof(LineStyle) - }; - -extern Tk_ObjCustomOption penObjOption; -extern Tk_ObjCustomOption pairsObjOption; -extern Tk_ObjCustomOption valuesObjOption; -extern Tk_ObjCustomOption xAxisObjOption; -extern Tk_ObjCustomOption yAxisObjOption; - -static Tk_OptionSpec optionSpecs[] = { - {TK_OPTION_CUSTOM, "-activepen", "activePen", "ActivePen", - "active", -1, Tk_Offset(LineElementOptions, activePenPtr), - TK_OPTION_NULL_OK, &penObjOption, LAYOUT}, - {TK_OPTION_BORDER, "-areabackground", "areaBackground", "AreaBackground", - NULL, -1, Tk_Offset(LineElementOptions, fillBg), - TK_OPTION_NULL_OK, NULL, LAYOUT}, - {TK_OPTION_CUSTOM, "-bindtags", "bindTags", "BindTags", - "all", -1, Tk_Offset(LineElementOptions, tags), - TK_OPTION_NULL_OK, &listObjOption, 0}, - {TK_OPTION_COLOR, "-color", "color", "Color", - STD_NORMAL_FOREGROUND, -1, - Tk_Offset(LineElementOptions, builtinPen.traceColor), 0, NULL, CACHE}, - {TK_OPTION_CUSTOM, "-dashes", "dashes", "Dashes", - NULL, -1, Tk_Offset(LineElementOptions, builtinPen.traceDashes), - TK_OPTION_NULL_OK, &dashesObjOption, CACHE}, - {TK_OPTION_CUSTOM, "-data", "data", "Data", - NULL, -1, Tk_Offset(LineElementOptions, coords), - TK_OPTION_NULL_OK, &pairsObjOption, RESET}, - {TK_OPTION_COLOR, "-errorbarcolor", "errorBarColor", "ErrorBarColor", - NULL, -1, Tk_Offset(LineElementOptions, builtinPen.errorBarColor), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_PIXELS,"-errorbarwidth", "errorBarWidth", "ErrorBarWidth", - "1", -1, Tk_Offset(LineElementOptions, builtinPen.errorBarLineWidth), - 0, NULL, CACHE}, - {TK_OPTION_PIXELS, "-errorbarcap", "errorBarCap", "ErrorBarCap", - "0", -1, Tk_Offset(LineElementOptions, builtinPen.errorBarCapWidth), - 0, NULL, LAYOUT}, - {TK_OPTION_COLOR, "-fill", "fill", "Fill", - NULL, -1, Tk_Offset(LineElementOptions, builtinPen.symbol.fillColor), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", - "no", -1, Tk_Offset(LineElementOptions, hide), 0, NULL, LAYOUT}, - {TK_OPTION_STRING, "-label", "label", "Label", - NULL, -1, Tk_Offset(LineElementOptions, label), - TK_OPTION_NULL_OK | TK_OPTION_DONT_SET_DEFAULT, NULL, LAYOUT}, - {TK_OPTION_RELIEF, "-legendrelief", "legendRelief", "LegendRelief", - "flat", -1, Tk_Offset(LineElementOptions, legendRelief), 0, NULL, LAYOUT}, - {TK_OPTION_PIXELS, "-linewidth", "lineWidth", "LineWidth", - "1", -1, Tk_Offset(LineElementOptions, builtinPen.traceWidth), - 0, NULL, CACHE}, - {TK_OPTION_CUSTOM, "-mapx", "mapX", "MapX", - "x", -1, Tk_Offset(LineElementOptions, xAxis), 0, &xAxisObjOption, RESET}, - {TK_OPTION_CUSTOM, "-mapy", "mapY", "MapY", - "y", -1, Tk_Offset(LineElementOptions, yAxis), 0, &yAxisObjOption, RESET}, - {TK_OPTION_INT, "-maxsymbols", "maxSymbols", "MaxSymbols", - "0", -1, Tk_Offset(LineElementOptions, reqMaxSymbols), 0, NULL, CACHE}, - {TK_OPTION_COLOR, "-offdash", "offDash", "OffDash", - NULL, -1, Tk_Offset(LineElementOptions, builtinPen.traceOffColor), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_COLOR, "-outline", "outline", "Outline", - NULL, -1, Tk_Offset(LineElementOptions, builtinPen.symbol.outlineColor), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_PIXELS, "-outlinewidth", "outlineWidth", "OutlineWidth", - "1", -1, Tk_Offset(LineElementOptions, builtinPen.symbol.outlineWidth), - 0, NULL, CACHE}, - {TK_OPTION_CUSTOM, "-pen", "pen", "Pen", - NULL, -1, Tk_Offset(LineElementOptions, normalPenPtr), - TK_OPTION_NULL_OK, &penObjOption, LAYOUT}, - {TK_OPTION_PIXELS, "-pixels", "pixels", "Pixels", - "0.1i", -1, Tk_Offset(LineElementOptions, builtinPen.symbol.size), - 0, NULL, LAYOUT}, - {TK_OPTION_DOUBLE, "-reduce", "reduce", "Reduce", - "0", -1, Tk_Offset(LineElementOptions, rTolerance), 0, NULL, RESET}, - {TK_OPTION_BOOLEAN, "-scalesymbols", "scaleSymbols", "ScaleSymbols", - "yes", -1, Tk_Offset(LineElementOptions, scaleSymbols), 0, NULL, LAYOUT}, - {TK_OPTION_STRING_TABLE, "-showerrorbars", "showErrorBars", "ShowErrorBars", - "both", -1, Tk_Offset(LineElementOptions, builtinPen.errorBarShow), - 0, &fillObjOption, LAYOUT}, - {TK_OPTION_STRING_TABLE, "-showvalues", "showValues", "ShowValues", - "none", -1, Tk_Offset(LineElementOptions, builtinPen.valueShow), - 0, &fillObjOption, CACHE}, - {TK_OPTION_STRING_TABLE, "-smooth", "smooth", "Smooth", - "linear", -1, Tk_Offset(LineElementOptions, reqSmooth), - 0, &smoothObjOption, LAYOUT}, - {TK_OPTION_CUSTOM, "-styles", "styles", "Styles", - "", -1, Tk_Offset(LineElementOptions, stylePalette), - 0, &styleObjOption, RESET}, - {TK_OPTION_STRING_TABLE, "-symbol", "symbol", "Symbol", - "none", -1, Tk_Offset(LineElementOptions, builtinPen.symbol), - 0, &symbolObjOption, CACHE}, - {TK_OPTION_STRING_TABLE, "-trace", "trace", "Trace", - "both", -1, Tk_Offset(LineElementOptions, penDir), - 0, &penDirObjOption, RESET}, - {TK_OPTION_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor", - "s", -1, Tk_Offset(LineElementOptions, builtinPen.valueStyle.anchor), - 0, NULL, CACHE}, - {TK_OPTION_COLOR, "-valuecolor", "valueColor", "ValueColor", - STD_NORMAL_FOREGROUND,-1, - Tk_Offset(LineElementOptions, builtinPen.valueStyle.color), - 0, NULL, CACHE}, - {TK_OPTION_FONT, "-valuefont", "valueFont", "ValueFont", - STD_FONT_SMALL, -1, - Tk_Offset(LineElementOptions, builtinPen.valueStyle.font), - 0, NULL, CACHE}, - {TK_OPTION_STRING, "-valueformat", "valueFormat", "ValueFormat", - "%g", -1, Tk_Offset(LineElementOptions, builtinPen.valueFormat), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate", - "0", -1, Tk_Offset(LineElementOptions, builtinPen.valueStyle.angle), - 0, NULL, CACHE}, - {TK_OPTION_CUSTOM, "-weights", "weights", "Weights", - NULL, -1, Tk_Offset(LineElementOptions, w), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_SYNONYM, "-x", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-xdata", 0}, - {TK_OPTION_CUSTOM, "-xdata", "xData", "XData", - NULL, -1, Tk_Offset(LineElementOptions, coords.x), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_CUSTOM, "-xerror", "xError", "XError", - NULL, -1, Tk_Offset(LineElementOptions, xError), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_CUSTOM, "-xhigh", "xHigh", "XHigh", - NULL, -1, Tk_Offset(LineElementOptions, xHigh), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_CUSTOM, "-xlow", "xLow", "XLow", - NULL, -1, Tk_Offset(LineElementOptions, xLow), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_SYNONYM, "-y", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-ydata", 0}, - {TK_OPTION_CUSTOM, "-ydata", "yData", "YData", - NULL, -1, Tk_Offset(LineElementOptions, coords.y), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_CUSTOM, "-yerror", "yError", "YError", - NULL, -1, Tk_Offset(LineElementOptions, yError), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_CUSTOM, "-yhigh", "yHigh", "YHigh", - NULL, -1, Tk_Offset(LineElementOptions, yHigh), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_CUSTOM, "-ylow", "yLow", "YLow", - NULL, -1, Tk_Offset(LineElementOptions, yLow), - TK_OPTION_NULL_OK, &valuesObjOption, RESET}, - {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} -}; - -LineElement::LineElement(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) - : Element(graphPtr, name, hPtr) -{ - smooth_ = LINEAR; - fillPts_ =NULL; - nFillPts_ = 0; - - symbolPts_.points =NULL; - symbolPts_.length =0; - symbolPts_.map =NULL; - activePts_.points =NULL; - activePts_.length =0; - activePts_.map =NULL; - - xeb_.segments =NULL; - xeb_.map =NULL; - xeb_.length =0; - yeb_.segments =NULL; - yeb_.map =NULL; - yeb_.length =0; - - symbolInterval_ =0; - symbolCounter_ =0; - traces_ =NULL; - - ops_ = (LineElementOptions*)calloc(1, sizeof(LineElementOptions)); - LineElementOptions* ops = (LineElementOptions*)ops_; - ops->elemPtr = (Element*)this; - - builtinPenPtr = new LinePen(graphPtr, "builtin", &ops->builtinPen); - ops->builtinPenPtr = builtinPenPtr; - - optionTable_ = Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); - - ops->stylePalette = new Chain(); - // this is an option and will be freed via Tk_FreeConfigOptions - // By default an element's name and label are the same - ops->label = Tcl_Alloc(strlen(name)+1); - if (name) - strcpy((char*)ops->label,(char*)name); - - Tk_InitOptions(graphPtr->interp_, (char*)&(ops->builtinPen), - builtinPenPtr->optionTable(), graphPtr->tkwin_); -} - -LineElement::~LineElement() -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - - if (builtinPenPtr) - delete builtinPenPtr; - - reset(); - - if (ops->stylePalette) { - freeStylePalette(ops->stylePalette); - delete ops->stylePalette; - } - - if (fillPts_) - delete [] fillPts_; -} - -int LineElement::configure() -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - - if (builtinPenPtr->configure() != TCL_OK) - return TCL_ERROR; - - // Point to the static normal/active pens if no external pens have been - // selected. - ChainLink* link = Chain_FirstLink(ops->stylePalette); - if (!link) { - link = new ChainLink(sizeof(LineStyle)); - ops->stylePalette->linkAfter(link, NULL); - } - LineStyle* stylePtr = (LineStyle*)Chain_GetValue(link); - stylePtr->penPtr = NORMALPEN(ops); - - return TCL_OK; -} - -void LineElement::map() -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - - if (!link) - return; - - reset(); - if (!ops->coords.x || !ops->coords.y || - !ops->coords.x->nValues() || !ops->coords.y->nValues()) - return; - int np = NUMBEROFPOINTS(ops); - - MapInfo mi; - getScreenPoints(&mi); - mapSymbols(&mi); - - if (nActiveIndices_ > 0) - mapActiveSymbols(); - - // Map connecting line segments if they are to be displayed. - smooth_ = (Smoothing)ops->reqSmooth; - if ((np > 1) && (ops->builtinPen.traceWidth > 0)) { - // Do smoothing if necessary. This can extend the coordinate array, - // so both mi.points and mi.nPoints may change. - switch (smooth_) { - case STEP: - generateSteps(&mi); - break; - - case CUBIC: - case QUADRATIC: - // Can't interpolate with less than three points - if (mi.nScreenPts < 3) - smooth_ = LINEAR; - else - generateSpline(&mi); - break; - - case CATROM: - // Can't interpolate with less than three points - if (mi.nScreenPts < 3) - smooth_ = LINEAR; - else - generateParametricSpline(&mi); - break; - - default: - break; - } - if (ops->rTolerance > 0.0) - reducePoints(&mi, ops->rTolerance); - - if (ops->fillBg) - mapFillArea(&mi); - - mapTraces(&mi); - } - delete [] mi.screenPts; - delete [] mi.map; - - // Set the symbol size of all the pen styles - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - LineStyle* stylePtr = (LineStyle*)Chain_GetValue(link); - LinePen* penPtr = (LinePen *)stylePtr->penPtr; - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - int size = scaleSymbol(penOps->symbol.size); - stylePtr->symbolSize = size; - stylePtr->errorBarCapWidth = penOps->errorBarCapWidth; - } - - LineStyle** styleMap = (LineStyle**)StyleMap(); - if (((ops->yHigh && ops->yHigh->nValues() > 0) && - (ops->yLow && ops->yLow->nValues() > 0)) || - ((ops->xHigh && ops->xHigh->nValues() > 0) && - (ops->xLow && ops->xLow->nValues() > 0)) || - (ops->xError && ops->xError->nValues() > 0) || - (ops->yError && ops->yError->nValues() > 0)) { - mapErrorBars(styleMap); - } - - mergePens(styleMap); - delete [] styleMap; -} - -void LineElement::extents(Region2d *extsPtr) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - - extsPtr->top = extsPtr->left = DBL_MAX; - extsPtr->bottom = extsPtr->right = -DBL_MAX; - - if (!ops->coords.x || !ops->coords.y || - !ops->coords.x->nValues() || !ops->coords.y->nValues()) - return; - int np = NUMBEROFPOINTS(ops); - - extsPtr->right = ops->coords.x->max(); - AxisOptions* axisxops = (AxisOptions*)ops->xAxis->ops(); - if ((ops->coords.x->min() <= 0.0) && (axisxops->logScale)) - extsPtr->left = FindElemValuesMinimum(ops->coords.x, DBL_MIN); - else - extsPtr->left = ops->coords.x->min(); - - extsPtr->bottom = ops->coords.y->max(); - AxisOptions* axisyops = (AxisOptions*)ops->yAxis->ops(); - if ((ops->coords.y->min() <= 0.0) && (axisyops->logScale)) - extsPtr->top = FindElemValuesMinimum(ops->coords.y, DBL_MIN); - else - extsPtr->top = ops->coords.y->min(); - - // Correct the data limits for error bars - if (ops->xError && ops->xError->nValues() > 0) { - np = MIN(ops->xError->nValues(), np); - for (int ii=0; iicoords.x->values_[ii] + ops->xError->values_[ii]; - if (x > extsPtr->right) - extsPtr->right = x; - - x = ops->coords.x->values_[ii] - ops->xError->values_[ii]; - AxisOptions* axisxops = (AxisOptions*)ops->xAxis->ops(); - if (axisxops->logScale) { - // Mirror negative values, instead of ignoring them - if (x < 0.0) - x = -x; - if ((x > DBL_MIN) && (x < extsPtr->left)) - extsPtr->left = x; - } - else if (x < extsPtr->left) - extsPtr->left = x; - } - } - else { - if (ops->xHigh && - (ops->xHigh->nValues() > 0) && - (ops->xHigh->max() > extsPtr->right)) { - extsPtr->right = ops->xHigh->max(); - } - if (ops->xLow && ops->xLow->nValues() > 0) { - double left; - if ((ops->xLow->min() <= 0.0) && (axisxops->logScale)) - left = FindElemValuesMinimum(ops->xLow, DBL_MIN); - else - left = ops->xLow->min(); - - if (left < extsPtr->left) - extsPtr->left = left; - } - } - - if (ops->yError && ops->yError->nValues() > 0) { - np = MIN(ops->yError->nValues(), np); - for (int ii=0; iicoords.y->values_[ii] + ops->yError->values_[ii]; - if (y > extsPtr->bottom) - extsPtr->bottom = y; - - y = ops->coords.y->values_[ii] - ops->yError->values_[ii]; - AxisOptions* axisyops = (AxisOptions*)ops->yAxis->ops(); - if (axisyops->logScale) { - // Mirror negative values, instead of ignoring them - if (y < 0.0) - y = -y; - if ((y > DBL_MIN) && (y < extsPtr->left)) - extsPtr->top = y; - } - else if (y < extsPtr->top) - extsPtr->top = y; - } - } - else { - if (ops->yHigh && (ops->yHigh->nValues() > 0) && - (ops->yHigh->max() > extsPtr->bottom)) - extsPtr->bottom = ops->yHigh->max(); - - if (ops->yLow && ops->yLow->nValues() > 0) { - double top; - if ((ops->yLow->min() <= 0.0) && (axisyops->logScale)) - top = FindElemValuesMinimum(ops->yLow, DBL_MIN); - else - top = ops->yLow->min(); - - if (top < extsPtr->top) - extsPtr->top = top; - } - } -} - -void LineElement::closest() -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - - ClosestSearch* searchPtr = &gops->search; - int mode = searchPtr->mode; - if (mode == SEARCH_AUTO) { - LinePen* penPtr = NORMALPEN(ops); - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - mode = SEARCH_POINTS; - if ((NUMBEROFPOINTS(ops) > 1) && (penOps->traceWidth > 0)) - mode = SEARCH_TRACES; - } - if (mode == SEARCH_POINTS) - closestPoint(searchPtr); - else { - int found = closestTrace(); - if ((!found) && (searchPtr->along != SEARCH_BOTH)) - closestPoint(searchPtr); - } -} - -void LineElement::draw(Drawable drawable) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - LinePen* penPtr = NORMALPEN(ops); - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - - if (ops->hide) - return; - - // Fill area under the curve - if (ops->fillBg && fillPts_) { - XPoint*points = new XPoint[nFillPts_]; - - unsigned int count =0; - for (Point2d *pp = fillPts_, *endp = pp + nFillPts_; pp < endp; pp++) { - points[count].x = pp->x; - points[count].y = pp->y; - count++; - } - Tk_Fill3DPolygon(graphPtr_->tkwin_, drawable, ops->fillBg, points, - nFillPts_, 0, TK_RELIEF_FLAT); - delete [] points; - } - - // traces - if ((Chain_GetLength(traces_) > 0) && (penOps->traceWidth > 0)) - drawTraces(drawable, penPtr); - - // Symbols, error bars, values - if (ops->reqMaxSymbols > 0) { - int total = 0; - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); - total += stylePtr->symbolPts.length; - } - symbolInterval_ = total / ops->reqMaxSymbols; - symbolCounter_ = 0; - } - - unsigned int count =0; - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - LineStyle* stylePtr = (LineStyle*)Chain_GetValue(link); - LinePen* penPtr = (LinePen *)stylePtr->penPtr; - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - - if ((stylePtr->xeb.length > 0) && (penOps->errorBarShow & SHOW_X)) - graphPtr_->drawSegments(drawable, penPtr->errorBarGC_, - stylePtr->xeb.segments, stylePtr->xeb.length); - - if ((stylePtr->yeb.length > 0) && (penOps->errorBarShow & SHOW_Y)) - graphPtr_->drawSegments(drawable, penPtr->errorBarGC_, - stylePtr->yeb.segments, stylePtr->yeb.length); - - if ((stylePtr->symbolPts.length > 0) && - (penOps->symbol.type != SYMBOL_NONE)) - drawSymbols(drawable, penPtr, stylePtr->symbolSize, - stylePtr->symbolPts.length, stylePtr->symbolPts.points); - - if (penOps->valueShow != SHOW_NONE) - drawValues(drawable, penPtr, stylePtr->symbolPts.length, - stylePtr->symbolPts.points, symbolPts_.map + count); - - count += stylePtr->symbolPts.length; - } - - symbolInterval_ = 0; - symbolCounter_ = 0; -} - -void LineElement::drawActive(Drawable drawable) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - LinePen* penPtr = (LinePen*)ops->activePenPtr; - if (!penPtr) - return; - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - - if (ops->hide || !active_) - return; - - int symbolSize = scaleSymbol(penOps->symbol.size); - - if (nActiveIndices_ > 0) { - mapActiveSymbols(); - - if (penOps->symbol.type != SYMBOL_NONE) - drawSymbols(drawable, penPtr, symbolSize, activePts_.length, - activePts_.points); - if (penOps->valueShow != SHOW_NONE) - drawValues(drawable, penPtr, activePts_.length, activePts_.points, - activePts_.map); - } - else if (nActiveIndices_ < 0) { - if ((Chain_GetLength(traces_) > 0) && (penOps->traceWidth > 0)) - drawTraces(drawable, penPtr); - - if (penOps->symbol.type != SYMBOL_NONE) - drawSymbols(drawable, penPtr, symbolSize, symbolPts_.length, - symbolPts_.points); - - if (penOps->valueShow != SHOW_NONE) { - drawValues(drawable, penPtr, symbolPts_.length, symbolPts_.points, - symbolPts_.map); - } - } -} - -void LineElement::drawSymbol(Drawable drawable, int x, int y, int size) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - - LinePen* penPtr = NORMALPEN(ops); - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - - if (penOps->traceWidth > 0) { - // Draw an extra line offset by one pixel from the previous to give a - // thicker appearance. This is only for the legend entry. This routine - // is never called for drawing the actual line segments. - XDrawLine(graphPtr_->display_, drawable, penPtr->traceGC_, x - size, y, - x + size, y); - XDrawLine(graphPtr_->display_, drawable, penPtr->traceGC_, x - size, y + 1, - x + size, y + 1); - } - if (penOps->symbol.type != SYMBOL_NONE) { - Point2d point; - point.x = x; - point.y = y; - drawSymbols(drawable, penPtr, size, 1, &point); - } -} - -void LineElement::print(PSOutput* psPtr) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - LinePen* penPtr = NORMALPEN(ops); - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - - if (ops->hide) - return; - - psPtr->format("\n%% Element \"%s\"\n\n", name_); - - // Draw fill area - if (ops->fillBg && fillPts_) { - psPtr->append("% start fill area\n"); - psPtr->setBackground(ops->fillBg); - psPtr->printPolyline(fillPts_, nFillPts_); - psPtr->append("gsave fill grestore\n"); - psPtr->append("% end fill area\n"); - } - - // traces - if ((Chain_GetLength(traces_) > 0) && (penOps->traceWidth > 0)) - printTraces(psPtr, penPtr); - - // Symbols, error bars, values - if (ops->reqMaxSymbols > 0) { - int total = 0; - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); - total += stylePtr->symbolPts.length; - } - symbolInterval_ = total / ops->reqMaxSymbols; - symbolCounter_ = 0; - } - - unsigned int count =0; - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); - LinePen* penPtr = (LinePen *)stylePtr->penPtr; - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - XColor* colorPtr = penOps->errorBarColor; - if (!colorPtr) - colorPtr = penOps->traceColor; - - if ((stylePtr->xeb.length > 0) && (penOps->errorBarShow & SHOW_X)) { - psPtr->setLineAttributes(colorPtr, penOps->errorBarLineWidth, - NULL, CapButt, JoinMiter); - psPtr->printSegments(stylePtr->xeb.segments, stylePtr->xeb.length); - } - - if ((stylePtr->yeb.length > 0) && (penOps->errorBarShow & SHOW_Y)) { - psPtr->setLineAttributes(colorPtr, penOps->errorBarLineWidth, - NULL, CapButt, JoinMiter); - psPtr->printSegments(stylePtr->yeb.segments, stylePtr->yeb.length); - } - - if ((stylePtr->symbolPts.length > 0) && - (penOps->symbol.type != SYMBOL_NONE)) - printSymbols(psPtr, penPtr, stylePtr->symbolSize, - stylePtr->symbolPts.length, stylePtr->symbolPts.points); - - if (penOps->valueShow != SHOW_NONE) - printValues(psPtr, penPtr, stylePtr->symbolPts.length, - stylePtr->symbolPts.points, symbolPts_.map + count); - - count += stylePtr->symbolPts.length; - } - - symbolInterval_ = 0; - symbolCounter_ = 0; -} - -void LineElement::printActive(PSOutput* psPtr) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - LinePen* penPtr = (LinePen *)ops->activePenPtr; - if (!penPtr) - return; - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - - if (ops->hide || !active_) - return; - - psPtr->format("\n%% Active Element \"%s\"\n\n", name_); - - int symbolSize = scaleSymbol(penOps->symbol.size); - if (nActiveIndices_ > 0) { - mapActiveSymbols(); - - if (penOps->symbol.type != SYMBOL_NONE) - printSymbols(psPtr, penPtr, symbolSize, activePts_.length, - activePts_.points); - - if (penOps->valueShow != SHOW_NONE) - printValues(psPtr, penPtr, activePts_.length, activePts_.points, - activePts_.map); - } - else if (nActiveIndices_ < 0) { - if ((Chain_GetLength(traces_) > 0) && (penOps->traceWidth > 0)) - printTraces(psPtr, (LinePen*)penPtr); - - if (penOps->symbol.type != SYMBOL_NONE) - printSymbols(psPtr, penPtr, symbolSize, symbolPts_.length, - symbolPts_.points); - if (penOps->valueShow != SHOW_NONE) { - printValues(psPtr, penPtr, symbolPts_.length, symbolPts_.points, - symbolPts_.map); - } - } -} - -void LineElement::printSymbol(PSOutput* psPtr, double x, double y, int size) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - - LinePen* penPtr = NORMALPEN(ops); - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - - if (penOps->traceWidth > 0) { - // Draw an extra line offset by one pixel from the previous to give a - // thicker appearance. This is only for the legend entry. This routine - // is never called for drawing the actual line segments. - psPtr->setLineAttributes(penOps->traceColor, penOps->traceWidth, - &penOps->traceDashes, CapButt, JoinMiter); - psPtr->format("%g %g %d Li\n", x, y, size + size); - } - - if (penOps->symbol.type != SYMBOL_NONE) { - Point2d point; - point.x = x; - point.y = y; - printSymbols(psPtr, penPtr, size, 1, &point); - } -} - -// Support - -double LineElement::distanceToLine(int x, int y, Point2d *p, Point2d *q, - Point2d *t) -{ - double right, left, top, bottom; - - *t = getProjection(x, y, p, q); - if (p->x > q->x) - right = p->x, left = q->x; - else - left = p->x, right = q->x; - - if (p->y > q->y) - bottom = p->y, top = q->y; - else - top = p->y, bottom = q->y; - - if (t->x > right) - t->x = right; - else if (t->x < left) - t->x = left; - - if (t->y > bottom) - t->y = bottom; - else if (t->y < top) - t->y = top; - - return hypot((t->x - x), (t->y - y)); -} - -double LineElement::distanceToX(int x, int y, Point2d *p, Point2d *q, - Point2d *t) -{ - double dx, dy; - double d; - - if (p->x > q->x) { - if ((x > p->x) || (x < q->x)) { - return DBL_MAX; /* X-coordinate outside line segment. */ - } - } else { - if ((x > q->x) || (x < p->x)) { - return DBL_MAX; /* X-coordinate outside line segment. */ - } - } - dx = p->x - q->x; - dy = p->y - q->y; - t->x = (double)x; - if (fabs(dx) < DBL_EPSILON) { - double d1, d2; - /* - * Same X-coordinate indicates a vertical line. Pick the closest end - * point. - */ - d1 = p->y - y; - d2 = q->y - y; - if (fabs(d1) < fabs(d2)) { - t->y = p->y, d = d1; - } else { - t->y = q->y, d = d2; - } - } - else if (fabs(dy) < DBL_EPSILON) { - /* Horizontal line. */ - t->y = p->y, d = p->y - y; - } - else { - double m = dy / dx; - double b = p->y - (m * p->x); - t->y = (x * m) + b; - d = y - t->y; - } - - return fabs(d); -} - -double LineElement::distanceToY(int x, int y, Point2d *p, Point2d *q, - Point2d *t) -{ - double dx, dy; - double d; - - if (p->y > q->y) { - if ((y > p->y) || (y < q->y)) { - return DBL_MAX; - } - } - else { - if ((y > q->y) || (y < p->y)) { - return DBL_MAX; - } - } - dx = p->x - q->x; - dy = p->y - q->y; - t->y = y; - if (fabs(dy) < DBL_EPSILON) { - double d1, d2; - - /* Save Y-coordinate indicates an horizontal line. Pick the closest end - * point. */ - d1 = p->x - x; - d2 = q->x - x; - if (fabs(d1) < fabs(d2)) { - t->x = p->x, d = d1; - } - else { - t->x = q->x, d = d2; - } - } - else if (fabs(dx) < DBL_EPSILON) { - /* Vertical line. */ - t->x = p->x, d = p->x - x; - } - else { - double m = dy / dx; - double b = p->y - (m * p->x); - t->x = (y - b) / m; - d = x - t->x; - } - - return fabs(d); -} - -int LineElement::scaleSymbol(int normalSize) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - - double scale = 1.0; - if (ops->scaleSymbols) { - double xRange = (ops->xAxis->max_ - ops->xAxis->min_); - double yRange = (ops->yAxis->max_ - ops->yAxis->min_); - // Save the ranges as a baseline for future scaling - if (!xRange_ || !yRange_) { - xRange_ = xRange; - yRange_ = yRange; - } - else { - // Scale the symbol by the smallest change in the X or Y axes - double xScale = xRange_ / xRange; - double yScale = yRange_ / yRange; - scale = MIN(xScale, yScale); - } - } - int newSize = normalSize * scale; - - // Don't let the size of symbols go unbounded. Both X and Win32 drawing - // routines assume coordinates to be a signed short int. - int maxSize = (int)MIN(graphPtr_->hRange_, graphPtr_->vRange_); - if (newSize > maxSize) - newSize = maxSize; - - // Make the symbol size odd so that its center is a single pixel. - newSize |= 0x01; - - return newSize; -} - -void LineElement::getScreenPoints(MapInfo* mapPtr) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - - if (!ops->coords.x || !ops->coords.y) { - mapPtr->screenPts = NULL; - mapPtr->nScreenPts = 0; - mapPtr->map = NULL; - } - - int np = NUMBEROFPOINTS(ops); - double* x = ops->coords.x->values_; - double* y = ops->coords.y->values_; - Point2d* points = new Point2d[np]; - int* map = new int[np]; - - int count = 0; - if (gops->inverted) { - for (int ii=0; iiyAxis->hMap(y[ii]); - points[count].y = ops->xAxis->vMap(x[ii]); - map[count] = ii; - count++; - } - } - } - else { - for (int ii=0; ii< np; ii++) { - if ((isfinite(x[ii])) && (isfinite(y[ii]))) { - points[count].x = ops->xAxis->hMap(x[ii]); - points[count].y = ops->yAxis->vMap(y[ii]); - map[count] = ii; - count++; - } - } - } - mapPtr->screenPts = points; - mapPtr->nScreenPts = count; - mapPtr->map = map; -} - -void LineElement::reducePoints(MapInfo *mapPtr, double tolerance) -{ - int* simple = new int[mapPtr->nScreenPts]; - int* map = new int[mapPtr->nScreenPts]; - Point2d* screenPts = new Point2d[mapPtr->nScreenPts]; - int np = simplify(mapPtr->screenPts, 0, mapPtr->nScreenPts - 1, - tolerance, simple); - for (int ii=0; iiscreenPts[kk]; - map[ii] = mapPtr->map[kk]; - } - delete [] simple; - - delete [] mapPtr->screenPts; - mapPtr->screenPts = screenPts; - delete [] mapPtr->map; - mapPtr->map = map; - mapPtr->nScreenPts = np; -} - -// Douglas-Peucker line simplification algorithm -int LineElement::simplify(Point2d *inputPts, int low, int 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 split = -1; - double dist2, tolerance2; - int s = -1; /* Points to top stack item. */ - - int* stack = new int[high - low + 1]; - StackPush(high); - int count = 0; - indices[count++] = 0; - tolerance2 = tolerance * tolerance; - while (!StackEmpty()) { - dist2 = findSplit(inputPts, low, StackTop(), &split); - if (dist2 > tolerance2) - StackPush(split); - else { - indices[count++] = StackTop(); - StackPop(low); - } - } - delete [] stack; - return count; -} - -double LineElement::findSplit(Point2d *points, int i, int j, int *split) -{ - double maxDist2 = -1.0; - if ((i + 1) < j) { - double a = points[i].y - points[j].y; - double b = points[j].x - points[i].x; - double c = (points[i].x * points[j].y) - (points[i].y * points[j].x); - for (int kk = (i + 1); kk < j; kk++) { - double dist2 = (points[kk].x * a) + (points[kk].y * b) + c; - if (dist2 < 0.0) - dist2 = -dist2; - - // Track the maximum. - if (dist2 > maxDist2) { - maxDist2 = dist2; - *split = kk; - } - } - // Correction for segment length---should be redone if can == 0 - maxDist2 *= maxDist2 / (a * a + b * b); - } - return maxDist2; -} - -void LineElement::generateSteps(MapInfo *mapPtr) -{ - int newSize = ((mapPtr->nScreenPts - 1) * 2) + 1; - Point2d* screenPts = new Point2d[newSize]; - int* map = new int[newSize]; - screenPts[0] = mapPtr->screenPts[0]; - map[0] = 0; - - int count = 1; - for (int i = 1; i < mapPtr->nScreenPts; i++) { - screenPts[count + 1] = mapPtr->screenPts[i]; - - // Hold last y-coordinate, use new x-coordinate - screenPts[count].x = screenPts[count + 1].x; - screenPts[count].y = screenPts[count - 1].y; - - // Use the same style for both the hold and the step points - map[count] = map[count + 1] = mapPtr->map[i]; - count += 2; - } - delete [] mapPtr->map; - mapPtr->map = map; - delete [] mapPtr->screenPts; - mapPtr->screenPts = screenPts; - mapPtr->nScreenPts = newSize; -} - -void LineElement::generateSpline(MapInfo *mapPtr) -{ - int nOrigPts = mapPtr->nScreenPts; - Point2d* origPts = mapPtr->screenPts; - - // check points are not monotonically increasing - for (int ii=0, jj=1; jj (double)graphPtr_->right_)) || - ((origPts[mapPtr->nScreenPts - 1].x < (double)graphPtr_->left_))) - return; - - // The spline is computed in screen coordinates instead of data points so - // that we can select the abscissas of the interpolated points from each - // pixel horizontally across the plotting area. - int extra = (graphPtr_->right_ - graphPtr_->left_) + 1; - if (extra < 1) - return; - - int niPts = nOrigPts + extra + 1; - Point2d* iPts = new Point2d[niPts]; - int* map = new int[niPts]; - - // Populate the x2 array with both the original X-coordinates and extra - // X-coordinates for each horizontal pixel that the line segment contains - int count = 0; - for (int ii=0, jj=1; jjmap[ii]; - count++; - - // Is any part of the interval (line segment) in the plotting area? - if ((origPts[jj].x >= (double)graphPtr_->left_) || - (origPts[ii].x <= (double)graphPtr_->right_)) { - double x = origPts[ii].x + 1.0; - - /* - * Since the line segment may be partially clipped on the left or - * right side, the points to interpolate are always interior to - * the plotting area. - * - * left right - * x1----|---------------------------|---x2 - * - * Pick the max of the starting X-coordinate and the left edge and - * the min of the last X-coordinate and the right edge. - */ - x = MAX(x, (double)graphPtr_->left_); - double last = MIN(origPts[jj].x, (double)graphPtr_->right_); - - // Add the extra x-coordinates to the interval - while (x < last) { - map[count] = mapPtr->map[ii]; - iPts[count++].x = x; - x++; - } - } - } - niPts = count; - int result = 0; - if (smooth_ == CUBIC) - result = naturalSpline(origPts, nOrigPts, iPts, niPts); - else if (smooth_ == QUADRATIC) - result = quadraticSpline(origPts, nOrigPts, iPts, niPts); - - // The spline interpolation failed. We will fall back to the current - // coordinates and do no smoothing (standard line segments) - if (!result) { - smooth_ = LINEAR; - delete [] iPts; - delete [] map; - } - else { - delete [] mapPtr->map; - mapPtr->map = map; - delete [] mapPtr->screenPts; - mapPtr->screenPts = iPts; - mapPtr->nScreenPts = niPts; - } -} - -void LineElement::generateParametricSpline(MapInfo *mapPtr) -{ - int nOrigPts = mapPtr->nScreenPts; - Point2d *origPts = mapPtr->screenPts; - - Region2d exts; - graphPtr_->extents(&exts); - - /* - * Populate the x2 array with both the original X-coordinates and extra - * X-coordinates for each horizontal pixel that the line segment contains. - */ - int count = 1; - for (int i = 0, j = 1; j < nOrigPts; i++, j++) { - Point2d p = origPts[i]; - Point2d q = origPts[j]; - count++; - if (lineRectClip(&exts, &p, &q)) - count += (int)(hypot(q.x - p.x, q.y - p.y) * 0.5); - } - int niPts = count; - Point2d *iPts = new Point2d[niPts]; - int* map = new int[niPts]; - - /* - * FIXME: This is just plain wrong. The spline should be computed - * and evaluated in separate steps. This will mean breaking - * up this routine since the catrom coefficients can be - * independently computed for original data point. This - * also handles the problem of allocating enough points - * since evaluation is independent of the number of points - * to be evalualted. The interpolated - * line segments should be clipped, not the original segments. - */ - count = 0; - int i,j; - for (i = 0, j = 1; j < nOrigPts; i++, j++) { - Point2d p = origPts[i]; - Point2d q = origPts[j]; - - double d = hypot(q.x - p.x, q.y - p.y); - /* Add the original x-coordinate */ - iPts[count].x = (double)i; - iPts[count].y = 0.0; - - /* Include the starting offset of the point in the offset array */ - map[count] = mapPtr->map[i]; - count++; - - /* Is any part of the interval (line segment) in the plotting - * area? */ - - if (lineRectClip(&exts, &p, &q)) { - double dp, dq; - - /* Distance of original point to p. */ - dp = hypot(p.x - origPts[i].x, p.y - origPts[i].y); - /* Distance of original point to q. */ - dq = hypot(q.x - origPts[i].x, q.y - origPts[i].y); - dp += 2.0; - while(dp <= dq) { - /* Point is indicated by its interval and parameter t. */ - iPts[count].x = (double)i; - iPts[count].y = dp / d; - map[count] = mapPtr->map[i]; - count++; - dp += 2.0; - } - } - } - iPts[count].x = (double)i; - iPts[count].y = 0.0; - map[count] = mapPtr->map[i]; - count++; - niPts = count; - int result = 0; - if (smooth_ == CUBIC) - result = naturalParametricSpline(origPts, nOrigPts, &exts, 0, iPts, niPts); - else if (smooth_ == CATROM) - result = catromParametricSpline(origPts, nOrigPts, iPts, niPts); - - // The spline interpolation failed. We will fall back to the current - // coordinates and do no smoothing (standard line segments) - if (!result) { - smooth_ = LINEAR; - delete [] iPts; - delete [] map; - } - else { - delete [] mapPtr->map; - mapPtr->map = map; - delete [] mapPtr->screenPts; - mapPtr->screenPts = iPts; - mapPtr->nScreenPts = niPts; - } -} - -void LineElement::mapSymbols(MapInfo *mapPtr) -{ - Point2d* points = new Point2d[mapPtr->nScreenPts]; - int *map = new int[mapPtr->nScreenPts]; - - Region2d exts; - graphPtr_->extents(&exts); - - Point2d *pp; - int count = 0; - int i; - for (pp=mapPtr->screenPts, i=0; inScreenPts; i++, pp++) { - if (PointInRegion(&exts, pp->x, pp->y)) { - points[count].x = pp->x; - points[count].y = pp->y; - map[count] = mapPtr->map[i]; - count++; - } - } - symbolPts_.points = points; - symbolPts_.length = count; - symbolPts_.map = map; -} - -void LineElement::mapActiveSymbols() -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - - if (activePts_.points) { - delete [] activePts_.points; - activePts_.points = NULL; - } - if (activePts_.map) { - delete [] activePts_.map; - activePts_.map = NULL; - } - - Region2d exts; - graphPtr_->extents(&exts); - - Point2d *points = new Point2d[nActiveIndices_]; - int* map = new int[nActiveIndices_]; - int np = NUMBEROFPOINTS(ops); - int count = 0; - if (ops->coords.x && ops->coords.y) { - for (int ii=0; ii= np) - continue; - - double x = ops->coords.x->values_[iPoint]; - double y = ops->coords.y->values_[iPoint]; - points[count] = graphPtr_->map2D(x, y, ops->xAxis, ops->yAxis); - map[count] = iPoint; - if (PointInRegion(&exts, points[count].x, points[count].y)) { - count++; - } - } - } - - if (count > 0) { - activePts_.points = points; - activePts_.map = map; - } - else { - delete [] points; - delete [] map; - } - activePts_.length = count; -} - -void LineElement::mergePens(LineStyle **styleMap) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - - if (Chain_GetLength(ops->stylePalette) < 2) { - ChainLink* link = Chain_FirstLink(ops->stylePalette); - LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); - stylePtr->symbolPts.length = symbolPts_.length; - stylePtr->symbolPts.points = symbolPts_.points; - stylePtr->xeb.length = xeb_.length; - stylePtr->xeb.segments = xeb_.segments; - stylePtr->yeb.length = yeb_.length; - stylePtr->yeb.segments = yeb_.segments; - return; - } - - if (symbolPts_.length > 0) { - Point2d* points = new Point2d[symbolPts_.length]; - int* map = new int[symbolPts_.length]; - Point2d *pp = points; - int* ip = map; - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); - stylePtr->symbolPts.points = pp; - for (int ii=0; iisymbolPts.length = pp - stylePtr->symbolPts.points; - } - delete [] symbolPts_.points; - symbolPts_.points = points; - delete [] symbolPts_.map; - symbolPts_.map = map; - } - - if (xeb_.length > 0) { - Segment2d* segments = new Segment2d[xeb_.length]; - Segment2d *sp = segments; - int* map = new int[xeb_.length]; - int* ip = map; - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); - stylePtr->xeb.segments = sp; - for (int ii=0; iixeb.length = sp - stylePtr->xeb.segments; - } - delete [] xeb_.segments; - xeb_.segments = segments; - delete [] xeb_.map; - xeb_.map = map; - } - - if (yeb_.length > 0) { - Segment2d* segments = new Segment2d[yeb_.length]; - Segment2d* sp = segments; - int* map = new int [yeb_.length]; - int* ip = map; - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); - stylePtr->yeb.segments = sp; - for (int ii=0; iiyeb.length = sp - stylePtr->yeb.segments; - } - delete [] yeb_.segments; - yeb_.segments = segments; - delete [] yeb_.map; - yeb_.map = map; - } -} - -#define CLIP_TOP (1<<0) -#define CLIP_BOTTOM (1<<1) -#define CLIP_RIGHT (1<<2) -#define CLIP_LEFT (1<<3) - -int LineElement::outCode(Region2d *extsPtr, Point2d *p) -{ - int code =0; - if (p->x > extsPtr->right) - code |= CLIP_RIGHT; - else if (p->x < extsPtr->left) - code |= CLIP_LEFT; - - if (p->y > extsPtr->bottom) - code |= CLIP_BOTTOM; - else if (p->y < extsPtr->top) - code |= CLIP_TOP; - - return code; -} - -int LineElement::clipSegment(Region2d *extsPtr, int code1, int code2, - Point2d *p, Point2d *q) -{ - int inside = ((code1 | code2) == 0); - int outside = ((code1 & code2) != 0); - - /* - * In the worst case, we'll clip the line segment against each of the four - * sides of the bounding rectangle. - */ - while ((!outside) && (!inside)) { - if (code1 == 0) { - Point2d *tmp; - int code; - - /* Swap pointers and out codes */ - tmp = p, p = q, q = tmp; - code = code1, code1 = code2, code2 = code; - } - if (code1 & CLIP_LEFT) { - p->y += (q->y - p->y) * - (extsPtr->left - p->x) / (q->x - p->x); - p->x = extsPtr->left; - } else if (code1 & CLIP_RIGHT) { - p->y += (q->y - p->y) * - (extsPtr->right - p->x) / (q->x - p->x); - p->x = extsPtr->right; - } else if (code1 & CLIP_BOTTOM) { - p->x += (q->x - p->x) * - (extsPtr->bottom - p->y) / (q->y - p->y); - p->y = extsPtr->bottom; - } else if (code1 & CLIP_TOP) { - p->x += (q->x - p->x) * - (extsPtr->top - p->y) / (q->y - p->y); - p->y = extsPtr->top; - } - code1 = outCode(extsPtr, p); - - inside = ((code1 | code2) == 0); - outside = ((code1 & code2) != 0); - } - return (!inside); -} - -void LineElement::saveTrace(int start, int length, MapInfo* mapPtr) -{ - bltTrace* tracePtr = new bltTrace; - Point2d* screenPts = new Point2d[length]; - int* map = new int[length]; - - // Copy the screen coordinates of the trace into the point array - if (mapPtr->map) { - for (int ii=0, jj=start; iiscreenPts[jj].x; - screenPts[ii].y = mapPtr->screenPts[jj].y; - map[ii] = mapPtr->map[jj]; - } - } - else { - for (int ii=0, jj=start; iiscreenPts[jj].x; - screenPts[ii].y = mapPtr->screenPts[jj].y; - map[ii] = jj; - } - } - tracePtr->screenPts.length = length; - tracePtr->screenPts.points = screenPts; - tracePtr->screenPts.map = map; - tracePtr->start = start; - if (traces_ == NULL) - traces_ = new Chain(); - - traces_->append(tracePtr); -} - -void LineElement::freeTraces() -{ - for (ChainLink* link = Chain_FirstLink(traces_); link; - link = Chain_NextLink(link)) { - bltTrace* tracePtr = (bltTrace*)Chain_GetValue(link); - delete [] tracePtr->screenPts.map; - delete [] tracePtr->screenPts.points; - delete tracePtr; - } - delete traces_; - traces_ = NULL; -} - -void LineElement::mapTraces(MapInfo *mapPtr) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - - Region2d exts; - graphPtr_->extents(&exts); - - int count = 1; - int code1 = outCode(&exts, mapPtr->screenPts); - Point2d* p = mapPtr->screenPts; - Point2d* q = p + 1; - - int start; - int ii; - for (ii=1; iinScreenPts; ii++, p++, q++) { - Point2d s; - s.x = 0; - s.y = 0; - int code2 = outCode(&exts, q); - // Save the coordinates of the last point, before clipping - if (code2 != 0) - s = *q; - - int broken = BROKEN_TRACE(ops->penDir, p->x, q->x); - int offscreen = clipSegment(&exts, code1, code2, p, q); - if (broken || offscreen) { - // The last line segment is either totally clipped by the plotting - // area or the x-direction is wrong, breaking the trace. Either - // way, save information about the last trace (if one exists), - // discarding the current line segment - if (count > 1) { - start = ii - count; - saveTrace(start, count, mapPtr); - count = 1; - } - } - else { - // Add the point to the trace - count++; - - // If the last point is clipped, this means that the trace is - // broken after this point. Restore the original coordinate - // (before clipping) after saving the trace. - if (code2 != 0) { - start = ii - (count - 1); - saveTrace(start, count, mapPtr); - mapPtr->screenPts[ii] = s; - count = 1; - } - } - code1 = code2; - } - if (count > 1) { - start = ii - count; - saveTrace(start, count, mapPtr); - } -} - -void LineElement::mapFillArea(MapInfo *mapPtr) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - - if (fillPts_) { - delete [] fillPts_; - fillPts_ = NULL; - nFillPts_ = 0; - } - if (mapPtr->nScreenPts < 3) - return; - - int np = mapPtr->nScreenPts + 3; - Region2d exts; - graphPtr_->extents(&exts); - - Point2d* origPts = new Point2d[np]; - if (gops->inverted) { - int i; - double minX = (double)ops->yAxis->screenMin_; - for (i = 0; i < mapPtr->nScreenPts; i++) { - origPts[i].x = mapPtr->screenPts[i].x + 1; - origPts[i].y = mapPtr->screenPts[i].y; - if (origPts[i].x < minX) { - minX = origPts[i].x; - } - } - // Add edges to make the polygon fill to the bottom of plotting window - origPts[i].x = minX; - origPts[i].y = origPts[i - 1].y; - i++; - origPts[i].x = minX; - origPts[i].y = origPts[0].y; - i++; - origPts[i] = origPts[0]; - } - else { - int i; - double maxY = (double)ops->yAxis->bottom_; - for (i = 0; i < mapPtr->nScreenPts; i++) { - origPts[i].x = mapPtr->screenPts[i].x + 1; - origPts[i].y = mapPtr->screenPts[i].y; - if (origPts[i].y > maxY) { - maxY = origPts[i].y; - } - } - // Add edges to extend the fill polygon to the bottom of plotting window - origPts[i].x = origPts[i - 1].x; - origPts[i].y = maxY; - i++; - origPts[i].x = origPts[0].x; - origPts[i].y = maxY; - i++; - origPts[i] = origPts[0]; - } - - Point2d *clipPts = new Point2d[np * 3]; - np = polyRectClip(&exts, origPts, np - 1, clipPts); - - delete [] origPts; - if (np < 3) - delete [] clipPts; - else { - fillPts_ = clipPts; - nFillPts_ = np; - } -} - -void LineElement::reset() -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - - freeTraces(); - - for (ChainLink* link = Chain_FirstLink(ops->stylePalette); link; - link = Chain_NextLink(link)) { - LineStyle *stylePtr = (LineStyle*)Chain_GetValue(link); - stylePtr->symbolPts.length = 0; - stylePtr->xeb.length = 0; - stylePtr->yeb.length = 0; - } - - if (symbolPts_.points) { - delete [] symbolPts_.points; - symbolPts_.points = NULL; - } - - if (symbolPts_.map) - delete [] symbolPts_.map; - symbolPts_.map = NULL; - symbolPts_.length = 0; - - if (activePts_.points) - delete [] activePts_.points; - activePts_.points = NULL; - activePts_.length = 0; - - if (activePts_.map) - delete [] activePts_.map; - activePts_.map = NULL; - - if (xeb_.segments) - delete [] xeb_.segments; - xeb_.segments = NULL; - if (xeb_.map) - delete [] xeb_.map; - xeb_.map = NULL; - xeb_.length = 0; - - if (yeb_.segments) - delete [] yeb_.segments; - yeb_.segments = NULL; - if (yeb_.map) - delete [] yeb_.map; - yeb_.map = NULL; - yeb_.length = 0; -} - -void LineElement::mapErrorBars(LineStyle **styleMap) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - - Region2d exts; - graphPtr_->extents(&exts); - - int nn =0; - int np = NUMBEROFPOINTS(ops); - if (ops->coords.x && ops->coords.y) { - if (ops->xError && (ops->xError->nValues() > 0)) - nn = MIN(ops->xError->nValues(), np); - else - if (ops->xHigh && ops->xLow) - nn = MIN3(ops->xHigh->nValues(), ops->xLow->nValues(), np); - } - - if (nn) { - Segment2d* errorBars = new Segment2d[nn * 3]; - Segment2d* segPtr = errorBars; - int* errorToData = new int[nn * 3]; - int* indexPtr = errorToData; - - for (int ii=0; iicoords.x->values_[ii]; - double y = ops->coords.y->values_[ii]; - LineStyle* stylePtr = styleMap[ii]; - - if ((isfinite(x)) && (isfinite(y))) { - double high; - double low; - if (ops->xError->nValues() > 0) { - high = x + ops->xError->values_[ii]; - low = x - ops->xError->values_[ii]; - } - else { - high = ops->xHigh ? ops->xHigh->values_[ii] : 0; - low = ops->xLow ? ops->xLow->values_[ii] : 0; - } - - if ((isfinite(high)) && (isfinite(low))) { - Point2d p = graphPtr_->map2D(high, y, ops->xAxis, ops->yAxis); - Point2d q = graphPtr_->map2D(low, y, ops->xAxis, ops->yAxis); - segPtr->p = p; - segPtr->q = q; - if (lineRectClip(&exts, &segPtr->p, &segPtr->q)) { - segPtr++; - *indexPtr++ = ii; - } - // Left cap - segPtr->p.x = p.x; - segPtr->q.x = p.x; - segPtr->p.y = p.y - stylePtr->errorBarCapWidth; - segPtr->q.y = p.y + stylePtr->errorBarCapWidth; - if (lineRectClip(&exts, &segPtr->p, &segPtr->q)) { - segPtr++; - *indexPtr++ = ii; - } - // Right cap - segPtr->p.x = q.x; - segPtr->q.x = q.x; - segPtr->p.y = q.y - stylePtr->errorBarCapWidth; - segPtr->q.y = q.y + stylePtr->errorBarCapWidth; - if (lineRectClip(&exts, &segPtr->p, &segPtr->q)) { - segPtr++; - *indexPtr++ = ii; - } - } - } - } - xeb_.segments = errorBars; - xeb_.length = segPtr - errorBars; - xeb_.map = errorToData; - } - - nn =0; - if (ops->coords.x && ops->coords.y) { - if (ops->yError && (ops->yError->nValues() > 0)) - nn = MIN(ops->yError->nValues(), np); - else - if (ops->yHigh && ops->yLow) - nn = MIN3(ops->yHigh->nValues(), ops->yLow->nValues(), np); - } - - if (nn) { - Segment2d* errorBars = new Segment2d[nn * 3]; - Segment2d* segPtr = errorBars; - int* errorToData = new int[nn * 3]; - int* indexPtr = errorToData; - - for (int ii=0; iicoords.x->values_[ii]; - double y = ops->coords.y->values_[ii]; - LineStyle* stylePtr = styleMap[ii]; - - if ((isfinite(x)) && (isfinite(y))) { - double high; - double low; - if (ops->yError->nValues() > 0) { - high = y + ops->yError->values_[ii]; - low = y - ops->yError->values_[ii]; - } - else { - high = ops->yHigh->values_[ii]; - low = ops->yLow->values_[ii]; - } - - if ((isfinite(high)) && (isfinite(low))) { - Point2d p = graphPtr_->map2D(x, high, ops->xAxis, ops->yAxis); - Point2d q = graphPtr_->map2D(x, low, ops->xAxis, ops->yAxis); - segPtr->p = p; - segPtr->q = q; - if (lineRectClip(&exts, &segPtr->p, &segPtr->q)) { - segPtr++; - *indexPtr++ = ii; - } - // Top cap - segPtr->p.y = p.y; - segPtr->q.y = p.y; - segPtr->p.x = p.x - stylePtr->errorBarCapWidth; - segPtr->q.x = p.x + stylePtr->errorBarCapWidth; - if (lineRectClip(&exts, &segPtr->p, &segPtr->q)) { - segPtr++; - *indexPtr++ = ii; - } - // Bottom cap - segPtr->p.y = q.y; - segPtr->q.y = q.y; - segPtr->p.x = q.x - stylePtr->errorBarCapWidth; - segPtr->q.x = q.x + stylePtr->errorBarCapWidth; - if (lineRectClip(&exts, &segPtr->p, &segPtr->q)) { - segPtr++; - *indexPtr++ = ii; - } - } - } - } - yeb_.segments = errorBars; - yeb_.length = segPtr - errorBars; - yeb_.map = errorToData; - } -} - -int LineElement::closestTrace() -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - ClosestSearch* searchPtr = &gops->search; - - Point2d closest; - - int iClose = -1; - double dMin = searchPtr->dist; - closest.x = closest.y = 0; - for (ChainLink *link=Chain_FirstLink(traces_); link; - link = Chain_NextLink(link)) { - bltTrace *tracePtr = (bltTrace*)Chain_GetValue(link); - for (Point2d *p=tracePtr->screenPts.points, - *pend=p + (tracePtr->screenPts.length - 1); palong == SEARCH_X) - d = distanceToX(searchPtr->x, searchPtr->y, p, p + 1, &b); - else if (searchPtr->along == SEARCH_Y) - d = distanceToY(searchPtr->x, searchPtr->y, p, p + 1, &b); - else - d = distanceToLine(searchPtr->x, searchPtr->y, p, p + 1, &b); - - if (d < dMin) { - closest = b; - iClose = tracePtr->screenPts.map[p-tracePtr->screenPts.points]; - dMin = d; - } - } - } - if (dMin < searchPtr->dist) { - searchPtr->dist = dMin; - searchPtr->elemPtr = (Element*)this; - searchPtr->index = iClose; - searchPtr->point = graphPtr_->invMap2D(closest.x, closest.y, - ops->xAxis, ops->yAxis); - return 1; - } - - return 0; -} - -void LineElement::closestPoint(ClosestSearch *searchPtr) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - - double dMin = searchPtr->dist; - int iClose = 0; - - // Instead of testing each data point in graph coordinates, look at the - // array of mapped screen coordinates. The advantages are - // 1) only examine points that are visible (unclipped), and - // 2) the computed distance is already in screen coordinates. - int count =0; - for (Point2d *pp = symbolPts_.points; count < symbolPts_.length; - count++, pp++) { - double dx = (double)(searchPtr->x - pp->x); - double dy = (double)(searchPtr->y - pp->y); - double d; - if (searchPtr->along == SEARCH_BOTH) - d = hypot(dx, dy); - else if (searchPtr->along == SEARCH_X) - d = dx; - else if (searchPtr->along == SEARCH_Y) - d = dy; - else - continue; - - if (d < dMin) { - iClose = symbolPts_.map[count]; - dMin = d; - } - } - if (dMin < searchPtr->dist) { - searchPtr->elemPtr = (Element*)this; - searchPtr->dist = dMin; - searchPtr->index = iClose; - searchPtr->point.x = ops->coords.x->values_[iClose]; - searchPtr->point.y = ops->coords.y->values_[iClose]; - } -} - -void LineElement::drawCircle(Display *display, Drawable drawable, - LinePen* penPtr, - int nSymbolPts, Point2d *symbolPts, int radius) -{ - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - - int count = 0; - int s = radius + radius; - XArc* arcs = new XArc[nSymbolPts]; - XArc *ap = arcs; - for (Point2d *pp=symbolPts, *pend=pp+nSymbolPts; ppx = pp->x - radius; - ap->y = pp->y - radius; - ap->width = (unsigned short)s; - ap->height = (unsigned short)s; - ap->angle1 = 0; - ap->angle2 = 23040; - ap++; - count++; - } - symbolCounter_++; - } - - for (XArc *ap=arcs, *aend=ap+count; apsymbol.fillGC) - XFillArc(display, drawable, penOps->symbol.fillGC, - ap->x, ap->y, ap->width, ap->height, ap->angle1, ap->angle2); - - if (penOps->symbol.outlineWidth > 0) - XDrawArc(display, drawable, penOps->symbol.outlineGC, - ap->x, ap->y, ap->width, ap->height, ap->angle1, ap->angle2); - } - - delete [] arcs; -} - -void LineElement::drawSquare(Display *display, Drawable drawable, - LinePen* penPtr, - int nSymbolPts, Point2d *symbolPts, int r) -{ - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - - int s = r + r; - int count =0; - XRectangle* rectangles = new XRectangle[nSymbolPts]; - XRectangle* rp=rectangles; - for (Point2d *pp=symbolPts, *pend=pp+nSymbolPts; ppx = pp->x - r; - rp->y = pp->y - r; - rp->width = (unsigned short)s; - rp->height = (unsigned short)s; - rp++; - count++; - } - symbolCounter_++; - } - - for (XRectangle *rp=rectangles, *rend=rp+count; rpsymbol.fillGC) - XFillRectangle(display, drawable, penOps->symbol.fillGC, - rp->x, rp->y, rp->width, rp->height); - - if (penOps->symbol.outlineWidth > 0) - XDrawRectangle(display, drawable, penOps->symbol.outlineGC, - rp->x, rp->y, rp->width, rp->height); - } - - delete [] rectangles; -} - -void LineElement::drawSCross(Display* display, Drawable drawable, - LinePen* penPtr, - int nSymbolPts, Point2d* symbolPts, int r2) -{ - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - - XPoint pattern[4]; - if (penOps->symbol.type == SYMBOL_SCROSS) { - r2 = (double)r2 * M_SQRT1_2; - pattern[3].y = pattern[2].x = pattern[0].x = pattern[0].y = -r2; - pattern[3].x = pattern[2].y = pattern[1].y = pattern[1].x = r2; - } - else { - pattern[0].y = pattern[1].y = pattern[2].x = pattern[3].x = 0; - pattern[0].x = pattern[2].y = -r2; - pattern[1].x = pattern[3].y = r2; - } - - for (Point2d *pp=symbolPts, *endp=pp+nSymbolPts; ppx; - int rndy = pp->y; - XDrawLine(graphPtr_->display_, drawable, penOps->symbol.outlineGC, - pattern[0].x + rndx, pattern[0].y + rndy, - pattern[1].x + rndx, pattern[1].y + rndy); - XDrawLine(graphPtr_->display_, drawable, penOps->symbol.outlineGC, - pattern[2].x + rndx, pattern[2].y + rndy, - pattern[3].x + rndx, pattern[3].y + rndy); - } - } -} - -void LineElement::drawCross(Display *display, Drawable drawable, - LinePen* penPtr, - int nSymbolPts, Point2d *symbolPts, int r2) -{ - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - - /* - * 2 3 The plus/cross symbol is a closed polygon - * of 12 points. The diagram to the left - * 0,12 1 4 5 represents the positions of the points - * x,y which are computed below. The extra - * 11 10 7 6 (thirteenth) point connects the first and - * last points. - * 9 8 - */ - int d = (r2 / 3); - XPoint pattern[13]; - pattern[0].x = pattern[11].x = pattern[12].x = -r2; - pattern[2].x = pattern[1].x = pattern[10].x = pattern[9].x = -d; - pattern[3].x = pattern[4].x = pattern[7].x = pattern[8].x = d; - pattern[5].x = pattern[6].x = r2; - pattern[2].y = pattern[3].y = -r2; - pattern[0].y = pattern[1].y = pattern[4].y = pattern[5].y = - pattern[12].y = -d; - pattern[11].y = pattern[10].y = pattern[7].y = pattern[6].y = d; - pattern[9].y = pattern[8].y = r2; - - if (penOps->symbol.type == SYMBOL_CROSS) { - // For the cross symbol, rotate the points by 45 degrees - for (int ii=0; ii<12; ii++) { - double dx = (double)pattern[ii].x * M_SQRT1_2; - double dy = (double)pattern[ii].y * M_SQRT1_2; - pattern[ii].x = dx - dy; - pattern[ii].y = dx + dy; - } - pattern[12] = pattern[0]; - } - - int count = 0; - XPoint* polygon = new XPoint[nSymbolPts*13]; - XPoint* xpp = polygon; - for (Point2d *pp = symbolPts, *endp = pp + nSymbolPts; pp < endp; pp++) { - if (DRAW_SYMBOL()) { - int rndx = pp->x; - int rndy = pp->y; - for (int ii=0; ii<13; ii++) { - xpp->x = pattern[ii].x + rndx; - xpp->y = pattern[ii].y + rndy; - xpp++; - } - count++; - } - symbolCounter_++; - } - - if (penOps->symbol.fillGC) { - XPoint* xpp = polygon; - for (int ii=0; iidisplay_, drawable, - penOps->symbol.fillGC, xpp, 13, Complex, - CoordModeOrigin); - } - - if (penOps->symbol.outlineWidth > 0) { - XPoint*xpp = polygon; - for (int ii=0; iidisplay_, drawable, - penOps->symbol.outlineGC, xpp, 13, CoordModeOrigin); - } - - delete [] polygon; -} - -void LineElement::drawDiamond(Display *display, Drawable drawable, - LinePen* penPtr, - int nSymbolPts, Point2d *symbolPts, int r1) -{ - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - - /* - * The plus symbol is a closed polygon - * 1 of 4 points. The diagram to the left - * represents the positions of the points - * 0,4 x,y 2 which are computed below. The extra - * (fifth) point connects the first and - * 3 last points. - */ - XPoint pattern[5]; - pattern[1].y = pattern[0].x = -r1; - pattern[2].y = pattern[3].x = pattern[0].y = pattern[1].x = 0; - pattern[3].y = pattern[2].x = r1; - pattern[4] = pattern[0]; - - int count = 0; - XPoint* polygon = new XPoint[nSymbolPts*5]; - XPoint* xpp = polygon; - for (Point2d *pp = symbolPts, *endp = pp + nSymbolPts; pp < endp; pp++) { - if (DRAW_SYMBOL()) { - int rndx = pp->x; - int rndy = pp->y; - for (int ii=0; ii<5; ii++) { - xpp->x = pattern[ii].x + rndx; - xpp->y = pattern[ii].y + rndy; - xpp++; - } - count++; - } - symbolCounter_++; - } - - if (penOps->symbol.fillGC) { - XPoint* xpp = polygon; - for (int ii=0; iidisplay_, drawable, - penOps->symbol.fillGC, xpp, 5, Convex, CoordModeOrigin); - } - - if (penOps->symbol.outlineWidth > 0) { - XPoint* xpp = polygon; - for (int ii=0; iidisplay_, drawable, - penOps->symbol.outlineGC, xpp, 5, CoordModeOrigin); - } - - delete [] polygon; -} - -#define B_RATIO 1.3467736870885982 -#define TAN30 0.57735026918962573 -#define COS30 0.86602540378443871 -void LineElement::drawArrow(Display *display, Drawable drawable, - LinePen* penPtr, - int nSymbolPts, Point2d *symbolPts, int size) -{ - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - - double b = size * B_RATIO * 0.7; - int b2 = b * 0.5; - int h2 = TAN30 * b2; - int h1 = b2 / COS30; - /* - * The triangle symbol is a closed polygon - * 0,3 of 3 points. The diagram to the left - * represents the positions of the points - * x,y which are computed below. The extra - * (fourth) point connects the first and - * 2 1 last points. - */ - - XPoint pattern[4]; - if (penOps->symbol.type == SYMBOL_ARROW) { - pattern[3].x = pattern[0].x = 0; - pattern[3].y = pattern[0].y = h1; - pattern[1].x = b2; - pattern[2].y = pattern[1].y = -h2; - pattern[2].x = -b2; - } else { - pattern[3].x = pattern[0].x = 0; - pattern[3].y = pattern[0].y = -h1; - pattern[1].x = b2; - pattern[2].y = pattern[1].y = h2; - pattern[2].x = -b2; - } - - int count = 0; - XPoint* polygon = new XPoint[nSymbolPts*4]; - XPoint* xpp = polygon; - for (Point2d *pp = symbolPts, *endp = pp + nSymbolPts; pp < endp; pp++) { - if (DRAW_SYMBOL()) { - int rndx = pp->x; - int rndy = pp->y; - for (int ii=0; ii<4; ii++) { - xpp->x = pattern[ii].x + rndx; - xpp->y = pattern[ii].y + rndy; - xpp++; - } - count++; - } - symbolCounter_++; - } - - if (penOps->symbol.fillGC) { - XPoint* xpp = polygon; - for (int ii=0; iidisplay_, drawable, - penOps->symbol.fillGC, xpp, 4, Convex, CoordModeOrigin); - } - - if (penOps->symbol.outlineWidth > 0) { - XPoint* xpp = polygon; - for (int ii=0; iidisplay_, drawable, - penOps->symbol.outlineGC, xpp, 4, CoordModeOrigin); - } - - delete [] polygon; -} - -#define S_RATIO 0.886226925452758 -void LineElement::drawSymbols(Drawable drawable, LinePen* penPtr, int size, - int nSymbolPts, Point2d* symbolPts) -{ - LinePenOptions* penOps = (LinePenOptions*)penPtr->ops(); - - if (size < 3) { - if (penOps->symbol.fillGC) { - for (Point2d *pp = symbolPts, *endp = pp + nSymbolPts; pp < endp; pp++) - XDrawLine(graphPtr_->display_, drawable, penOps->symbol.fillGC, - pp->x, pp->y, pp->x+1, pp->y+1); - } - return; - } - - int r1 = (int)ceil(size * 0.5); - int r2 = (int)ceil(size * S_RATIO * 0.5); - - switch (penOps->symbol.type) { - case SYMBOL_NONE: - break; - case SYMBOL_SQUARE: - drawSquare(graphPtr_->display_, drawable, penPtr, nSymbolPts,symbolPts,r2); - break; - case SYMBOL_CIRCLE: - drawCircle(graphPtr_->display_, drawable, penPtr, nSymbolPts,symbolPts,r1); - break; - case SYMBOL_SPLUS: - case SYMBOL_SCROSS: - drawSCross(graphPtr_->display_, drawable, penPtr, nSymbolPts,symbolPts,r2); - break; - case SYMBOL_PLUS: - case SYMBOL_CROSS: - drawCross(graphPtr_->display_, drawable, penPtr, nSymbolPts,symbolPts,r2); - break; - case SYMBOL_DIAMOND: - drawDiamond(graphPtr_->display_, drawable, penPtr, nSymbolPts,symbolPts,r1); - break; - case SYMBOL_TRIANGLE: - case SYMBOL_ARROW: - drawArrow(graphPtr_->display_, drawable, penPtr, nSymbolPts,symbolPts,size); - break; - } -} - -void LineElement::drawTraces(Drawable drawable, LinePen* penPtr) -{ - for (ChainLink* link = Chain_FirstLink(traces_); link; - link = Chain_NextLink(link)) { - bltTrace* tracePtr = (bltTrace*)Chain_GetValue(link); - - int count = tracePtr->screenPts.length; - XPoint* points = new XPoint[count]; - XPoint*xpp = points; - for (int ii=0; iix = tracePtr->screenPts.points[ii].x; - xpp->y = tracePtr->screenPts.points[ii].y; - } - XDrawLines(graphPtr_->display_, drawable, penPtr->traceGC_, points, - count, CoordModeOrigin); - delete [] points; - } -} - -void LineElement::drawValues(Drawable drawable, LinePen* penPtr, - int length, Point2d *points, int *map) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - LinePenOptions* pops = (LinePenOptions*)penPtr->ops(); - - char string[TCL_DOUBLE_SPACE * 2 + 2]; - const char* fmt = pops->valueFormat; - if (fmt == NULL) - fmt = "%g"; - TextStyle ts(graphPtr_, &pops->valueStyle); - - double* xval = ops->coords.x->values_; - double* yval = ops->coords.y->values_; - int count = 0; - - for (Point2d *pp = points, *endp = points + length; pp < endp; pp++) { - double x = xval[map[count]]; - double y = yval[map[count]]; - count++; - if (pops->valueShow == SHOW_X) - snprintf(string, TCL_DOUBLE_SPACE, fmt, x); - else if (pops->valueShow == SHOW_Y) - snprintf(string, TCL_DOUBLE_SPACE, fmt, y); - else if (pops->valueShow == SHOW_BOTH) { - snprintf(string, TCL_DOUBLE_SPACE, fmt, x); - strcat(string, ","); - snprintf(string + strlen(string), TCL_DOUBLE_SPACE, fmt, y); - } - - ts.drawText(drawable, string, pp->x, pp->y); - } -} - -void LineElement::printSymbols(PSOutput* psPtr, LinePen* penPtr, int size, - int nSymbolPts, Point2d *symbolPts) -{ - LinePenOptions* pops = (LinePenOptions*)penPtr->ops(); - - double symbolSize; - - // Set line and foreground attributes - XColor* fillColor = pops->symbol.fillColor; - if (!fillColor) - fillColor = pops->traceColor; - - XColor* outlineColor = pops->symbol.outlineColor; - if (!outlineColor) - outlineColor = pops->traceColor; - - if (pops->symbol.type == SYMBOL_NONE) - psPtr->setLineAttributes(pops->traceColor, pops->traceWidth + 2, - &pops->traceDashes, CapButt, JoinMiter); - else { - psPtr->setLineWidth(pops->symbol.outlineWidth); - psPtr->setDashes(NULL); - } - - // build DrawSymbolProc - psPtr->append("\n/DrawSymbolProc {\n"); - switch (pops->symbol.type) { - case SYMBOL_NONE: - break; - default: - psPtr->append(" "); - psPtr->setBackground(fillColor); - psPtr->append(" gsave fill grestore\n"); - - if (pops->symbol.outlineWidth > 0) { - psPtr->append(" "); - psPtr->setForeground(outlineColor); - psPtr->append(" stroke\n"); - } - break; - } - psPtr->append("} def\n\n"); - - // set size - symbolSize = (double)size; - switch (pops->symbol.type) { - case SYMBOL_SQUARE: - case SYMBOL_CROSS: - case SYMBOL_PLUS: - case SYMBOL_SCROSS: - case SYMBOL_SPLUS: - symbolSize = (double)size * S_RATIO; - break; - case SYMBOL_TRIANGLE: - case SYMBOL_ARROW: - symbolSize = (double)size * 0.7; - break; - case SYMBOL_DIAMOND: - symbolSize = (double)size * M_SQRT1_2; - break; - - default: - break; - } - - int count =0; - for (Point2d *pp=symbolPts, *endp=symbolPts + nSymbolPts; pp < endp; pp++) { - if (DRAW_SYMBOL()) { - psPtr->format("%g %g %g %s\n", pp->x, pp->y, symbolSize, - symbolMacros[pops->symbol.type]); - count++; - } - symbolCounter_++; - } -} - -void LineElement::setLineAttributes(PSOutput* psPtr, LinePen* penPtr) -{ - LinePenOptions* pops = (LinePenOptions*)penPtr->ops(); - - psPtr->setLineAttributes(pops->traceColor, pops->traceWidth, - &pops->traceDashes, CapButt, JoinMiter); - - if ((LineIsDashed(pops->traceDashes)) && - (pops->traceOffColor)) { - psPtr->append("/DashesProc {\n gsave\n "); - psPtr->setBackground(pops->traceOffColor); - psPtr->append(" "); - psPtr->setDashes(NULL); - psPtr->append("stroke\n grestore\n} def\n"); - } else { - psPtr->append("/DashesProc {} def\n"); - } -} - -void LineElement::printTraces(PSOutput* psPtr, LinePen* penPtr) -{ - setLineAttributes(psPtr, penPtr); - for (ChainLink* link = Chain_FirstLink(traces_); link; - link = Chain_NextLink(link)) { - bltTrace *tracePtr = (bltTrace*)Chain_GetValue(link); - if (tracePtr->screenPts.length > 0) { - psPtr->append("% start trace\n"); - psPtr->printMaxPolyline(tracePtr->screenPts.points, - tracePtr->screenPts.length); - psPtr->append("% end trace\n"); - } - } -} - -void LineElement::printValues(PSOutput* psPtr, LinePen* penPtr, - int nSymbolPts, Point2d *symbolPts, - int *pointToData) -{ - LineElementOptions* ops = (LineElementOptions*)ops_; - LinePenOptions* pops = (LinePenOptions*)penPtr->ops(); - - const char* fmt = pops->valueFormat; - if (fmt == NULL) - fmt = "%g"; - TextStyle ts(graphPtr_, &pops->valueStyle); - - int count = 0; - for (Point2d *pp=symbolPts, *endp=symbolPts + nSymbolPts; pp < endp; pp++) { - double x = ops->coords.x->values_[pointToData[count]]; - double y = ops->coords.y->values_[pointToData[count]]; - count++; - - char string[TCL_DOUBLE_SPACE * 2 + 2]; - if (pops->valueShow == SHOW_X) - snprintf(string, TCL_DOUBLE_SPACE, fmt, x); - else if (pops->valueShow == SHOW_Y) - snprintf(string, TCL_DOUBLE_SPACE, fmt, y); - else if (pops->valueShow == SHOW_BOTH) { - snprintf(string, TCL_DOUBLE_SPACE, fmt, x); - strcat(string, ","); - snprintf(string + strlen(string), TCL_DOUBLE_SPACE, fmt, y); - } - - ts.printText(psPtr, string, pp->x, pp->y); - } -} - - diff --git a/src/tkbltGrElemLine.h b/src/tkbltGrElemLine.h deleted file mode 100644 index f937615..0000000 --- a/src/tkbltGrElemLine.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrElemLine_h__ -#define __BltGrElemLine_h__ - -#include - -#include "tkbltGraph.h" -#include "tkbltGrElem.h" -#include "tkbltGrPenLine.h" - -namespace Blt { - - typedef struct { - Point2d *screenPts; - int nScreenPts; - int *styleMap; - int *map; - } MapInfo; - - typedef struct { - Point2d *points; - int length; - int *map; - } GraphPoints; - - typedef struct { - int start; - GraphPoints screenPts; - } bltTrace; - - typedef struct { - Weight weight; - LinePen* penPtr; - GraphPoints symbolPts; - GraphSegments xeb; - GraphSegments yeb; - int symbolSize; - int errorBarCapWidth; - } LineStyle; - - typedef struct { - Element* elemPtr; - const char* label; - char** tags; - Axis* xAxis; - Axis* yAxis; - ElemCoords coords; - ElemValues* w; - ElemValues* xError; - ElemValues* yError; - ElemValues* xHigh; - ElemValues* xLow; - ElemValues* yHigh; - ElemValues* yLow; - int hide; - int legendRelief; - Chain* stylePalette; - LinePen *builtinPenPtr; - LinePen *activePenPtr; - LinePen *normalPenPtr; - LinePenOptions builtinPen; - - // derived - Tk_3DBorder fillBg; - int reqMaxSymbols; - double rTolerance; - int scaleSymbols; - int reqSmooth; - int penDir; - } LineElementOptions; - - class LineElement : public Element { - public: - enum PenDirection {INCREASING, DECREASING, BOTH_DIRECTIONS}; - enum Smoothing {LINEAR, STEP, CUBIC, QUADRATIC, CATROM}; - - protected: - LinePen* builtinPenPtr; - Smoothing smooth_; - Point2d *fillPts_; - int nFillPts_; - GraphPoints symbolPts_; - GraphPoints activePts_; - GraphSegments xeb_; - GraphSegments yeb_; - int symbolInterval_; - int symbolCounter_; - Chain* traces_; - - void drawCircle(Display*, Drawable, LinePen*, int, Point2d*, int); - void drawSquare(Display*, Drawable, LinePen*, int, Point2d*, int); - void drawSCross(Display*, Drawable, LinePen*, int, Point2d*, int); - void drawCross(Display*, Drawable, LinePen*, int, Point2d*, int); - void drawDiamond(Display*, Drawable, LinePen*, int, Point2d*, int); - void drawArrow(Display*, Drawable, LinePen*, int, Point2d*, int); - - protected: - int scaleSymbol(int); - void getScreenPoints(MapInfo*); - void reducePoints(MapInfo*, double); - void generateSteps(MapInfo*); - void generateSpline(MapInfo*); - void generateParametricSpline(MapInfo*); - void mapSymbols(MapInfo*); - void mapActiveSymbols(); - void mergePens(LineStyle**); - int outCode(Region2d*, Point2d*); - int clipSegment(Region2d*, int, int, Point2d*, Point2d*); - void saveTrace(int, int, MapInfo*); - void freeTraces(); - void mapTraces(MapInfo*); - void mapFillArea(MapInfo*); - void mapErrorBars(LineStyle**); - void reset(); - int closestTrace(); - void closestPoint(ClosestSearch*); - void drawSymbols(Drawable, LinePen*, int, int, Point2d*); - void drawTraces(Drawable, LinePen*); - void drawValues(Drawable, LinePen*, int, Point2d*, int*); - void setLineAttributes(PSOutput*, LinePen*); - void printTraces(PSOutput*, LinePen*); - void printValues(PSOutput*, LinePen*, int, Point2d*, int*); - void printSymbols(PSOutput*, LinePen*, int, int, Point2d*); - double distanceToLine(int, int, Point2d*, Point2d*, Point2d*); - double distanceToX(int, int, Point2d*, Point2d*, Point2d*); - double distanceToY(int, int, Point2d*, Point2d*, Point2d*); - int simplify(Point2d*, int, int, double, int*); - double findSplit(Point2d*, int, int, int*); - - int naturalSpline(Point2d*, int, Point2d*, int); - int quadraticSpline(Point2d*, int, Point2d*, int); - int naturalParametricSpline(Point2d*, int, Region2d*, int, Point2d*, int); - int catromParametricSpline(Point2d*, int, Point2d*, int); - - public: - LineElement(Graph*, const char*, Tcl_HashEntry*); - virtual ~LineElement(); - - ClassId classId() {return CID_ELEM_LINE;} - const char* className() {return "LineElement";} - const char* typeName() {return "line";} - - int configure(); - void map(); - void extents(Region2d*); - void closest(); - void draw(Drawable); - void drawActive(Drawable); - void drawSymbol(Drawable, int, int, int); - void print(PSOutput*); - void printActive(PSOutput*); - void printSymbol(PSOutput*, double, double, int); - }; -}; - -#endif diff --git a/src/tkbltGrElemLineSpline.C b/src/tkbltGrElemLineSpline.C deleted file mode 100644 index 9224d53..0000000 --- a/src/tkbltGrElemLineSpline.C +++ /dev/null @@ -1,1086 +0,0 @@ -/* - * 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 2009 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 -#include -#include - -#include - -#include "tkbltGrElemLine.h" - -using namespace Blt; - -typedef double TriDiagonalMatrix[3]; -typedef struct { - double b, c, d; -} Cubic2D; - -typedef struct { - double b, c, d, e, f; -} Quint2D; - -// Quadratic spline parameters -#define E1 param[0] -#define E2 param[1] -#define V1 param[2] -#define V2 param[3] -#define W1 param[4] -#define W2 param[5] -#define Z1 param[6] -#define Z2 param[7] -#define Y1 param[8] -#define Y2 param[9] - -/* - *--------------------------------------------------------------------------- - * - * Search -- - * - * Conducts a binary search for a value. This routine is called - * only if key is between x(0) and x(len - 1). - * - * Results: - * Returns the index of the largest value in xtab for which - * x[i] < key. - * - *--------------------------------------------------------------------------- - */ -static int Search(Point2d points[], int nPoints, double key, int *foundPtr) -{ - int low = 0; - int high = nPoints - 1; - - while (high >= low) { - int mid = (high + low) / 2; - if (key > points[mid].x) - low = mid + 1; - else if (key < points[mid].x) - high = mid - 1; - else { - *foundPtr = 1; - return mid; - } - } - *foundPtr = 0; - return low; -} - -/* - *--------------------------------------------------------------------------- - * - * QuadChoose -- - * - * Determines the case needed for the computation of the parame- - * ters of the quadratic spline. - * - * Results: - * Returns a case number (1-4) which controls how the parameters - * of the quadratic spline are evaluated. - * - *--------------------------------------------------------------------------- - */ -static int QuadChoose(Point2d* p, Point2d* q, double m1, double m2, - double epsilon) -{ - // Calculate the slope of the line joining P and Q - double slope = (q->y - p->y) / (q->x - p->x); - - if (slope != 0.0) { - double prod1 = slope * m1; - double prod2 = slope * m2; - - // Find the absolute values of the slopes slope, m1, and m2 - double mref = fabs(slope); - double mref1 = fabs(m1); - double mref2 = fabs(m2); - - // If the relative deviation of m1 or m2 from slope is less than - // epsilon, then choose case 2 or case 3. - double relerr = epsilon * mref; - if ((fabs(slope - m1) > relerr) && (fabs(slope - m2) > relerr) && - (prod1 >= 0.0) && (prod2 >= 0.0)) { - double prod = (mref - mref1) * (mref - mref2); - if (prod < 0.0) { - // l1, the line through (x1,y1) with slope m1, and l2, - // the line through (x2,y2) with slope m2, intersect - // at a point whose abscissa is between x1 and x2. - // The abscissa becomes a knot of the spline. - return 1; - } - if (mref1 > (mref * 2.0)) { - if (mref2 <= ((2.0 - epsilon) * mref)) - return 3; - } - else if (mref2 <= (mref * 2.0)) { - // Both l1 and l2 cross the line through - // (x1+x2)/2.0,y1 and (x1+x2)/2.0,y2, which is the - // midline of the rectangle formed by P and Q or both - // m1 and m2 have signs different than the sign of - // slope, or one of m1 and m2 has opposite sign from - // slope and l1 and l2 intersect to the left of x1 or - // to the right of x2. The point (x1+x2)/2. is a knot - // of the spline. - return 2; - } - else if (mref1 <= ((2.0 - epsilon) * mref)) { - // In cases 3 and 4, sign(m1)=sign(m2)=sign(slope). - // Either l1 or l2 crosses the midline, but not both. - // Choose case 4 if mref1 is greater than - // (2.-epsilon)*mref; otherwise, choose case 3. - return 3; - } - // If neither l1 nor l2 crosses the midline, the spline - // requires two knots between x1 and x2. - return 4; - } - else { - // The sign of at least one of the slopes m1 or m2 does not - // agree with the sign of *slope*. - if ((prod1 < 0.0) && (prod2 < 0.0)) { - return 2; - } - else if (prod1 < 0.0) { - if (mref2 > ((epsilon + 1.0) * mref)) - return 1; - else - return 2; - } - else if (mref1 > ((epsilon + 1.0) * mref)) - return 1; - else - return 2; - } - } - else if ((m1 * m2) >= 0.0) - return 2; - else - return 1; -} - -/* - *--------------------------------------------------------------------------- - * Computes the knots and other parameters of the spline on the - * interval PQ. - * On input-- - * P and Q are the coordinates of the points of interpolation. - * m1 is the slope at P. - * m2 is the slope at Q. - * ncase controls the number and location of the knots. - * On output-- - * - * (v1,v2),(w1,w2),(z1,z2), and (e1,e2) are the coordinates of - * the knots and other parameters of the spline on P. - * (e1,e2) and Q are used only if ncase=4. - *--------------------------------------------------------------------------- - */ -static void QuadCases(Point2d* p, Point2d* q, double m1, double m2, - double param[], int which) -{ - if ((which == 3) || (which == 4)) { - double c1 = p->x + (q->y - p->y) / m1; - double d1 = q->x + (p->y - q->y) / m2; - double h1 = c1 * 2.0 - p->x; - double j1 = d1 * 2.0 - q->x; - double mbar1 = (q->y - p->y) / (h1 - p->x); - double mbar2 = (p->y - q->y) / (j1 - q->x); - - if (which == 4) { - // Case 4 - Y1 = (p->x + c1) / 2.0; - V1 = (p->x + Y1) / 2.0; - V2 = m1 * (V1 - p->x) + p->y; - Z1 = (d1 + q->x) / 2.0; - W1 = (q->x + Z1) / 2.0; - W2 = m2 * (W1 - q->x) + q->y; - double mbar3 = (W2 - V2) / (W1 - V1); - Y2 = mbar3 * (Y1 - V1) + V2; - Z2 = mbar3 * (Z1 - V1) + V2; - E1 = (Y1 + Z1) / 2.0; - E2 = mbar3 * (E1 - V1) + V2; - } - else { - // Case 3 - double k1 = (p->y - q->y + q->x * mbar2 - p->x * mbar1) / (mbar2 - mbar1); - if (fabs(m1) > fabs(m2)) { - Z1 = (k1 + p->x) / 2.0; - } else { - Z1 = (k1 + q->x) / 2.0; - } - V1 = (p->x + Z1) / 2.0; - V2 = p->y + m1 * (V1 - p->x); - W1 = (q->x + Z1) / 2.0; - W2 = q->y + m2 * (W1 - q->x); - Z2 = V2 + (W2 - V2) / (W1 - V1) * (Z1 - V1); - } - } - else if (which == 2) { - // Case 2 - Z1 = (p->x + q->x) / 2.0; - V1 = (p->x + Z1) / 2.0; - V2 = p->y + m1 * (V1 - p->x); - W1 = (Z1 + q->x) / 2.0; - W2 = q->y + m2 * (W1 - q->x); - Z2 = (V2 + W2) / 2.0; - } - else { - // Case 1 - Z1 = (p->y - q->y + m2 * q->x - m1 * p->x) / (m2 - m1); - double ztwo = p->y + m1 * (Z1 - p->x); - V1 = (p->x + Z1) / 2.0; - V2 = (p->y + ztwo) / 2.0; - W1 = (Z1 + q->x) / 2.0; - W2 = (ztwo + q->y) / 2.0; - Z2 = V2 + (W2 - V2) / (W1 - V1) * (Z1 - V1); - } -} - -static int QuadSelect(Point2d* p, Point2d* q, double m1, double m2, - double epsilon, double param[]) -{ - int ncase = QuadChoose(p, q, m1, m2, epsilon); - QuadCases(p, q, m1, m2, param, ncase); - return ncase; -} - -static double QuadGetImage(double p1, double p2, double p3, double x1, - double x2, double x3) -{ - double A = x1 - x2; - double B = x2 - x3; - double C = x1 - x3; - - double y = (p1 * (A * A) + p2 * 2.0 * B * A + p3 * (B * B)) / (C * C); - return y; -} - -/* - *--------------------------------------------------------------------------- - * Finds the image of a point in x. - * On input - * x Contains the value at which the spline is evaluated. - * leftX, leftY - * Coordinates of the left-hand data point used in the - * evaluation of x values. - * rightX, rightY - * Coordinates of the right-hand data point used in the - * evaluation of x values. - * Z1, Z2, Y1, Y2, E2, W2, V2 - * Parameters of the spline. - * ncase Controls the evaluation of the spline by indicating - * whether one or two knots were placed in the interval - * (xtabs,xtabs1). - *--------------------------------------------------------------------------- - */ -static void QuadSpline(Point2d* intp, Point2d* left, Point2d* right, - double param[], int ncase) - -{ - double y; - - if (ncase == 4) { - // Case 4: More than one knot was placed in the interval. - // Determine the location of data point relative to the 1st knot. - if (Y1 > intp->x) - y = QuadGetImage(left->y, V2, Y2, Y1, intp->x, left->x); - else if (Y1 < intp->x) { - // Determine the location of the data point relative to the 2nd knot. - if (Z1 > intp->x) - y = QuadGetImage(Y2, E2, Z2, Z1, intp->x, Y1); - else if (Z1 < intp->x) - y = QuadGetImage(Z2, W2, right->y, right->x, intp->x, Z1); - else - y = Z2; - } - else - y = Y2; - } - else { - // Cases 1, 2, or 3: - // Determine the location of the data point relative to the knot. - if (Z1 < intp->x) - y = QuadGetImage(Z2, W2, right->y, right->x, intp->x, Z1); - else if (Z1 > intp->x) - y = QuadGetImage(left->y, V2, Z2, Z1, intp->x, left->x); - else - y = Z2; - } - - intp->y = y; -} - -/* - *--------------------------------------------------------------------------- - * Calculates the derivative at each of the data points. The - * slopes computed will insure that an osculatory quadratic - * spline will have one additional knot between two adjacent - * points of interpolation. Convexity and monotonicity are - * preserved wherever these conditions are compatible with the - * data. - *--------------------------------------------------------------------------- - */ -static void QuadSlopes(Point2d *points, double *m, int nPoints) -{ - double m1s =0; - double m2s =0; - double m1 =0; - double m2 =0; - int i, n, l; - for (l = 0, i = 1, n = 2; i < (nPoints - 1); l++, i++, n++) { - // Calculate the slopes of the two lines joining three - // consecutive data points. - double ydif1 = points[i].y - points[l].y; - double ydif2 = points[n].y - points[i].y; - m1 = ydif1 / (points[i].x - points[l].x); - m2 = ydif2 / (points[n].x - points[i].x); - if (i == 1) { - // Save slopes of starting point - m1s = m1; - m2s = m2; - } - // If one of the preceding slopes is zero or if they have opposite - // sign, assign the value zero to the derivative at the middle point. - if ((m1 == 0.0) || (m2 == 0.0) || ((m1 * m2) <= 0.0)) - m[i] = 0.0; - else if (fabs(m1) > fabs(m2)) { - // Calculate the slope by extending the line with slope m1. - double xbar = ydif2 / m1 + points[i].x; - double xhat = (xbar + points[n].x) / 2.0; - m[i] = ydif2 / (xhat - points[i].x); - } - else { - // Calculate the slope by extending the line with slope m2. - double xbar = -ydif1 / m2 + points[i].x; - double xhat = (points[l].x + xbar) / 2.0; - m[i] = ydif1 / (points[i].x - xhat); - } - } - - // Calculate the slope at the last point, x(n). - i = nPoints - 2; - n = nPoints - 1; - if ((m1 * m2) < 0.0) - m[n] = m2 * 2.0; - else { - double xmid = (points[i].x + points[n].x) / 2.0; - double yxmid = m[i] * (xmid - points[i].x) + points[i].y; - m[n] = (points[n].y - yxmid) / (points[n].x - xmid); - if ((m[n] * m2) < 0.0) - m[n] = 0.0; - } - - // Calculate the slope at the first point, x(0). - if ((m1s * m2s) < 0.0) - m[0] = m1s * 2.0; - else { - double xmid = (points[0].x + points[1].x) / 2.0; - double yxmid = m[1] * (xmid - points[1].x) + points[1].y; - m[0] = (yxmid - points[0].y) / (xmid - points[0].x); - if ((m[0] * m1s) < 0.0) - m[0] = 0.0; - } -} - -/* - *--------------------------------------------------------------------------- - * - * QuadEval -- - * - * QuadEval controls the evaluation of an osculatory quadratic - * spline. The user may provide his own slopes at the points of - * interpolation or use the subroutine 'QuadSlopes' to calculate - * slopes which are consistent with the shape of the data. - * - * ON INPUT-- - * intpPts must be a nondecreasing vector of points at which the - * spline will be evaluated. - * origPts contains the abscissas of the data points to be - * interpolated. xtab must be increasing. - * y contains the ordinates of the data points to be - * interpolated. - * m contains the slope of the spline at each point of - * interpolation. - * nPoints number of data points (dimension of xtab and y). - * numEval is the number of points of evaluation (dimension of - * xval and yval). - * epsilon is a relative error tolerance used in subroutine - * 'QuadChoose' to distinguish the situation m(i) or - * m(i+1) is relatively close to the slope or twice - * the slope of the linear segment between xtab(i) and - * xtab(i+1). If this situation occurs, roundoff may - * cause a change in convexity or monotonicity of the - * resulting spline and a change in the case number - * provided by 'QuadChoose'. If epsilon is not equal to zero, - * then epsilon should be greater than or equal to machine - * epsilon. - * ON OUTPUT-- - * yval contains the images of the points in xval. - * err is one of the following error codes: - * 0 - QuadEval ran normally. - * 1 - xval(i) is less than xtab(1) for at least one - * i or xval(i) is greater than xtab(num) for at - * least one i. QuadEval will extrapolate to provide - * function values for these abscissas. - * 2 - xval(i+1) < xval(i) for some i. - * - * - * QuadEval calls the following subroutines or functions: - * Search - * QuadCases - * QuadChoose - * QuadSpline - *--------------------------------------------------------------------------- - */ -static int QuadEval(Point2d origPts[], int nOrigPts, Point2d intpPts[], - int nIntpPts, double *m, double epsilon) -{ - double param[10]; - - // Initialize indices and set error result - int error = 0; - int l = nOrigPts - 1; - int p = l - 1; - int ncase = 1; - - // Determine if abscissas of new vector are non-decreasing. - for (int jj=1; jj= origPts[0].x) - break; - } - // Determine if any of the points in xval are GREATER than the - // abscissa of the l data point. - int end; - for (end = nIntpPts - 1; end >= 0; end--) { - if (intpPts[end].x <= origPts[l].x) - break; - } - - if (start > 0) { - // Set error value to indicate that extrapolation has occurred - error = 1; - - // Calculate the images of points of evaluation whose abscissas - // are less than the abscissa of the first data point. - ncase = QuadSelect(origPts, origPts + 1, m[0], m[1], epsilon, param); - for (int jj=0; jj<(start - 1); jj++) - QuadSpline(intpPts + jj, origPts, origPts + 1, param, ncase); - if (nIntpPts == 1) - return error; - } - int ii; - int nn; - if ((nIntpPts == 1) && (end != (nIntpPts - 1))) - goto noExtrapolation; - - // Search locates the interval in which the first in-range - // point of evaluation lies. - int found; - ii = Search(origPts, nOrigPts, intpPts[start].x, &found); - - nn = ii + 1; - if (nn >= nOrigPts) { - nn = nOrigPts - 1; - ii = nOrigPts - 2; - } - /* - * If the first in-range point of evaluation is equal to one - * of the data points, assign the appropriate value from y. - * Continue until a point of evaluation is found which is not - * equal to a data point. - */ - if (found) { - do { - intpPts[start].y = origPts[ii].y; - start++; - if (start >= nIntpPts) { - return error; - } - } while (intpPts[start - 1].x == intpPts[start].x); - - for (;;) { - if (intpPts[start].x < origPts[nn].x) { - break; /* Break out of for-loop */ - } - if (intpPts[start].x == origPts[nn].x) { - do { - intpPts[start].y = origPts[nn].y; - start++; - if (start >= nIntpPts) { - return error; - } - } while (intpPts[start].x == intpPts[start - 1].x); - } - ii++; - nn++; - } - } - /* - * Calculate the images of all the points which lie within - * range of the data. - */ - if ((ii > 0) || (error != 1)) - ncase = QuadSelect(origPts+ii, origPts+nn, m[ii], m[nn], epsilon, param); - - for (int jj=start; jj<=end; jj++) { - // If xx(j) - x(n) is negative, do not recalculate - // the parameters for this section of the spline since - // they are already known. - if (intpPts[jj].x == origPts[nn].x) { - intpPts[jj].y = origPts[nn].y; - continue; - } - else if (intpPts[jj].x > origPts[nn].x) { - double delta; - - // Determine that the routine is in the correct part of the spline - do { - ii++; - nn++; - delta = intpPts[jj].x - origPts[nn].x; - } while (delta > 0.0); - - if (delta < 0.0) - ncase = QuadSelect(origPts+ii, origPts+nn, m[ii], m[nn], - epsilon, param); - else if (delta == 0.0) { - intpPts[jj].y = origPts[nn].y; - continue; - } - } - QuadSpline(intpPts+jj, origPts+ii, origPts+nn, param, ncase); - } - - if (end == (nIntpPts - 1)) - return error; - - if ((nn == l) && (intpPts[end].x != origPts[l].x)) - goto noExtrapolation; - - // Set error value to indicate that extrapolation has occurred - error = 1; - ncase = QuadSelect(origPts + p, origPts + l, m[p], m[l], epsilon, param); - - noExtrapolation: - // Calculate the images of the points of evaluation whose - // abscissas are greater than the abscissa of the last data point. - for (int jj=(end + 1); jj 1) { - return 0; - } - return 1; -} - -/* - *--------------------------------------------------------------------------- - * Reference: - * Numerical Analysis, R. Burden, J. Faires and A. Reynolds. - * Prindle, Weber & Schmidt 1981 pp 112 - *--------------------------------------------------------------------------- - */ -int LineElement::naturalSpline(Point2d *origPts, int nOrigPts, - Point2d *intpPts, int nIntpPts) -{ - Point2d *ip, *iend; - double x, dy, alpha; - int isKnot; - int i, j, n; - - double* dx = new double[nOrigPts]; - /* Calculate vector of differences */ - for (i = 0, j = 1; j < nOrigPts; i++, j++) { - dx[i] = origPts[j].x - origPts[i].x; - if (dx[i] < 0.0) { - return 0; - } - } - n = nOrigPts - 1; /* Number of intervals. */ - TriDiagonalMatrix* A = new TriDiagonalMatrix[nOrigPts]; - if (!A) { - delete [] dx; - return 0; - } - /* Vectors to solve the tridiagonal matrix */ - A[0][0] = A[n][0] = 1.0; - A[0][1] = A[n][1] = 0.0; - A[0][2] = A[n][2] = 0.0; - - /* Calculate the intermediate results */ - for (i = 0, j = 1; j < n; j++, i++) { - alpha = 3.0 * ((origPts[j + 1].y / dx[j]) - (origPts[j].y / dx[i]) - - (origPts[j].y / dx[j]) + (origPts[i].y / dx[i])); - A[j][0] = 2 * (dx[j] + dx[i]) - dx[i] * A[i][1]; - A[j][1] = dx[j] / A[j][0]; - A[j][2] = (alpha - dx[i] * A[i][2]) / A[j][0]; - } - - Cubic2D* eq = new Cubic2D[nOrigPts]; - if (!eq) { - delete [] A; - delete [] dx; - return 0; - } - eq[0].c = eq[n].c = 0.0; - for (j = n, i = n - 1; i >= 0; i--, j--) { - eq[i].c = A[i][2] - A[i][1] * eq[j].c; - dy = origPts[i+1].y - origPts[i].y; - eq[i].b = (dy) / dx[i] - dx[i] * (eq[j].c + 2.0 * eq[i].c) / 3.0; - eq[i].d = (eq[j].c - eq[i].c) / (3.0 * dx[i]); - } - delete [] A; - delete [] dx; - - /* Now calculate the new values */ - for (ip = intpPts, iend = ip + nIntpPts; ip < iend; ip++) { - ip->y = 0.0; - x = ip->x; - - /* Is it outside the interval? */ - if ((x < origPts[0].x) || (x > origPts[n].x)) { - continue; - } - /* Search for the interval containing x in the point array */ - i = Search(origPts, nOrigPts, x, &isKnot); - if (isKnot) { - ip->y = origPts[i].y; - } else { - i--; - x -= origPts[i].x; - ip->y = origPts[i].y + x * (eq[i].b + x * (eq[i].c + x * eq[i].d)); - } - } - delete [] eq; - return 1; -} - -typedef struct { - double t; /* Arc length of interval. */ - double x; /* 2nd derivative of X with respect to T */ - double y; /* 2nd derivative of Y with respect to T */ -} CubicSpline; - -/* - * The following two procedures solve the special linear system which arise - * in cubic spline interpolation. If x is assumed cyclic ( x[i]=x[n+i] ) the - * equations can be written as (i=0,1,...,n-1): - * m[i][0] * x[i-1] + m[i][1] * x[i] + m[i][2] * x[i+1] = b[i] . - * In matrix notation one gets A * x = b, where the matrix A is tridiagonal - * with additional elements in the upper right and lower left position: - * A[i][0] = A_{i,i-1} for i=1,2,...,n-1 and m[0][0] = A_{0,n-1} , - * A[i][1] = A_{i, i } for i=0,1,...,n-1 - * A[i][2] = A_{i,i+1} for i=0,1,...,n-2 and m[n-1][2] = A_{n-1,0}. - * A should be symmetric (A[i+1][0] == A[i][2]) and positive definite. - * The size of the system is given in n (n>=1). - * - * In the first procedure the Cholesky decomposition A = C^T * D * C - * (C is upper triangle with unit diagonal, D is diagonal) is calculated. - * Return TRUE if decomposition exist. - */ -static int SolveCubic1(TriDiagonalMatrix A[], int n) -{ - int i; - double m_ij, m_n, m_nn, d; - - if (n < 1) { - return 0; /* Dimension should be at least 1 */ - } - d = A[0][1]; /* D_{0,0} = A_{0,0} */ - if (d <= 0.0) { - return 0; /* A (or D) should be positive definite */ - } - m_n = A[0][0]; /* A_{0,n-1} */ - m_nn = A[n - 1][1]; /* A_{n-1,n-1} */ - for (i = 0; i < n - 2; i++) { - m_ij = A[i][2]; /* A_{i,1} */ - A[i][2] = m_ij / d; /* C_{i,i+1} */ - A[i][0] = m_n / d; /* C_{i,n-1} */ - m_nn -= A[i][0] * m_n; /* to get C_{n-1,n-1} */ - m_n = -A[i][2] * m_n; /* to get C_{i+1,n-1} */ - d = A[i + 1][1] - A[i][2] * m_ij; /* D_{i+1,i+1} */ - if (d <= 0.0) { - return 0; /* Elements of D should be positive */ - } - A[i + 1][1] = d; - } - if (n >= 2) { /* Complete last column */ - m_n += A[n - 2][2]; /* add A_{n-2,n-1} */ - A[n - 2][0] = m_n / d; /* C_{n-2,n-1} */ - A[n - 1][1] = d = m_nn - A[n - 2][0] * m_n; /* D_{n-1,n-1} */ - if (d <= 0.0) { - return 0; - } - } - return 1; -} - -/* - * The second procedure solves the linear system, with the Cholesky - * decomposition calculated above (in m[][]) and the right side b given - * in x[]. The solution x overwrites the right side in x[]. - */ -static void SolveCubic2(TriDiagonalMatrix A[], CubicSpline spline[], - int nIntervals) -{ - int n = nIntervals - 2; - int m = nIntervals - 1; - - // Division by transpose of C : b = C^{-T} * b - double x = spline[m].x; - double y = spline[m].y; - for (int ii=0; ii= 0) { - // C_{n-2,n-1} * x_{n-1} - spline[m].x = x - A[n][0] * spline[n].x; - spline[m].y = y - A[n][0] * spline[n].y; - } - // Division by D: b = D^{-1} * b - for (int ii=0; ii= 0) { - // C_{n-2,n-1} * x_{n-1} - spline[n].x -= A[n][0] * x; - spline[n].y -= A[n][0] * y; - } - for (int ii=(n - 1); ii>=0; ii--) { - // C_{i,i+1} * x_{i+1} + C_{i,n-1} * x_{n-1} - spline[ii].x -= A[ii][2] * spline[ii + 1].x + A[ii][0] * x; - spline[ii].y -= A[ii][2] * spline[ii + 1].y + A[ii][0] * y; - } -} - -/* - * Find second derivatives (x''(t_i),y''(t_i)) of cubic spline interpolation - * through list of points (x_i,y_i). The parameter t is calculated as the - * length of the linear stroke. The number of points must be at least 3. - * Note: For CLOSED_CONTOURs the first and last point must be equal. - */ -static CubicSpline* CubicSlopes(Point2d points[], int nPoints, - int isClosed, double unitX, double unitY) -{ - CubicSpline *s1, *s2; - int n, i; - double norm, dx, dy; - - CubicSpline* spline = new CubicSpline[nPoints]; - if (!spline) - return NULL; - - TriDiagonalMatrix *A = new TriDiagonalMatrix[nPoints]; - if (!A) { - delete [] spline; - return NULL; - } - /* - * Calculate first differences in (dxdt2[i], y[i]) and interval lengths - * in dist[i]: - */ - s1 = spline; - for (i = 0; i < nPoints - 1; i++) { - s1->x = points[i+1].x - points[i].x; - s1->y = points[i+1].y - points[i].y; - - /* - * The Norm of a linear stroke is calculated in "normal coordinates" - * and used as interval length: - */ - dx = s1->x / unitX; - dy = s1->y / unitY; - s1->t = sqrt(dx * dx + dy * dy); - - s1->x /= s1->t; /* first difference, with unit norm: */ - s1->y /= s1->t; /* || (dxdt2[i], y[i]) || = 1 */ - s1++; - } - - /* - * Setup linear System: Ax = b - */ - n = nPoints - 2; /* Without first and last point */ - if (isClosed) { - /* First and last points must be equal for CLOSED_CONTOURs */ - spline[nPoints - 1].t = spline[0].t; - spline[nPoints - 1].x = spline[0].x; - spline[nPoints - 1].y = spline[0].y; - n++; /* Add last point (= first point) */ - } - s1 = spline, s2 = s1 + 1; - for (i = 0; i < n; i++) { - /* Matrix A, mainly tridiagonal with cyclic second index - ("j = j+n mod n") - */ - A[i][0] = s1->t; /* Off-diagonal element A_{i,i-1} */ - A[i][1] = 2.0 * (s1->t + s2->t); /* A_{i,i} */ - A[i][2] = s2->t; /* Off-diagonal element A_{i,i+1} */ - - /* Right side b_x and b_y */ - s1->x = (s2->x - s1->x) * 6.0; - s1->y = (s2->y - s1->y) * 6.0; - - /* - * If the linear stroke shows a cusp of more than 90 degree, - * the right side is reduced to avoid oscillations in the - * spline: - */ - /* - * The Norm of a linear stroke is calculated in "normal coordinates" - * and used as interval length: - */ - dx = s1->x / unitX; - dy = s1->y / unitY; - norm = sqrt(dx * dx + dy * dy) / 8.5; - if (norm > 1.0) { - /* The first derivative will not be continuous */ - s1->x /= norm; - s1->y /= norm; - } - s1++, s2++; - } - - if (!isClosed) { - /* Third derivative is set to zero at both ends */ - A[0][1] += A[0][0]; /* A_{0,0} */ - A[0][0] = 0.0; /* A_{0,n-1} */ - A[n-1][1] += A[n-1][2]; /* A_{n-1,n-1} */ - A[n-1][2] = 0.0; /* A_{n-1,0} */ - } - /* Solve linear systems for dxdt2[] and y[] */ - - if (SolveCubic1(A, n)) { /* Cholesky decomposition */ - SolveCubic2(A, spline, n); /* A * dxdt2 = b_x */ - } - else { /* Should not happen, but who knows ... */ - delete [] A; - delete [] spline; - return NULL; - } - /* Shift all second derivatives one place right and update the ends. */ - s2 = spline + n, s1 = s2 - 1; - for (/* empty */; s2 > spline; s2--, s1--) { - s2->x = s1->x; - s2->y = s1->y; - } - if (isClosed) { - spline[0].x = spline[n].x; - spline[0].y = spline[n].y; - } else { - /* Third derivative is 0.0 for the first and last interval. */ - spline[0].x = spline[1].x; - spline[0].y = spline[1].y; - spline[n + 1].x = spline[n].x; - spline[n + 1].y = spline[n].y; - } - delete [] A; - return spline; -} - -// Calculate interpolated values of the spline function (defined via p_cntr -// and the second derivatives dxdt2[] and dydt2[]). The number of tabulated -// values is n. On an equidistant grid n_intpol values are calculated. -static int CubicEval(Point2d *origPts, int nOrigPts, Point2d *intpPts, - int nIntpPts, CubicSpline *spline) -{ - double t, tSkip; - Point2d q; - int count; - - /* Sum the lengths of all the segments (intervals). */ - double tMax = 0.0; - for (int ii=0; iiright - extsPtr->left; - double unitY = extsPtr->bottom - extsPtr->top; - if (unitX < FLT_EPSILON) - unitX = FLT_EPSILON; - if (unitY < FLT_EPSILON) - unitY = FLT_EPSILON; - - /* Calculate parameters for cubic spline: - * t = arc length of interval. - * dxdt2 = second derivatives of x with respect to t, - * dydt2 = second derivatives of y with respect to t, - */ - CubicSpline* spline = CubicSlopes(origPts, nOrigPts, isClosed, unitX, unitY); - if (spline == NULL) - return 0; - - int result= CubicEval(origPts, nOrigPts, intpPts, nIntpPts, spline); - - delete [] spline; - return result; -} - -static void CatromCoeffs(Point2d* p, Point2d* a, Point2d* b, - Point2d* c, Point2d* d) -{ - a->x = -p[0].x + 3.0 * p[1].x - 3.0 * p[2].x + p[3].x; - b->x = 2.0 * p[0].x - 5.0 * p[1].x + 4.0 * p[2].x - p[3].x; - c->x = -p[0].x + p[2].x; - d->x = 2.0 * p[1].x; - a->y = -p[0].y + 3.0 * p[1].y - 3.0 * p[2].y + p[3].y; - b->y = 2.0 * p[0].y - 5.0 * p[1].y + 4.0 * p[2].y - p[3].y; - c->y = -p[0].y + p[2].y; - d->y = 2.0 * p[1].y; -} - -int LineElement::catromParametricSpline(Point2d* points, int nPoints, - Point2d* intpPts, int nIntpPts) -{ - // The spline is computed in screen coordinates instead of data points so - // that we can select the abscissas of the interpolated points from each - // pixel horizontally across the plotting area. - - Point2d* origPts = new Point2d[nPoints + 4]; - memcpy(origPts + 1, points, sizeof(Point2d) * nPoints); - - origPts[0] = origPts[1]; - origPts[nPoints + 2] = origPts[nPoints + 1] = origPts[nPoints]; - - for (int ii=0; ii - -#include "tkbltGrBind.h" -#include "tkbltGraph.h" -#include "tkbltGrAxis.h" -#include "tkbltGrElem.h" -#include "tkbltGrElemOp.h" -#include "tkbltGrElemBar.h" -#include "tkbltGrElemLine.h" -#include "tkbltGrLegd.h" - -using namespace Blt; - -static int GetIndex(Tcl_Interp* interp, Element* elemPtr, - Tcl_Obj *objPtr, int *indexPtr); -static Tcl_Obj *DisplayListObj(Graph* graphPtr); - -int Blt::ElementObjConfigure(Element* elemPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = elemPtr->graphPtr_; - Tk_SavedOptions savedOptions; - int mask =0; - int error; - Tcl_Obj* errorResult; - - for (error=0; error<=1; error++) { - if (!error) { - if (Tk_SetOptions(interp, (char*)elemPtr->ops(), elemPtr->optionTable(), - objc, objv, graphPtr->tkwin_, &savedOptions, &mask) - != TCL_OK) - continue; - } - else { - errorResult = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(errorResult); - Tk_RestoreSavedOptions(&savedOptions); - } - - if (elemPtr->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 != 5) { - Tcl_WrongNumArgs(interp, 3, objv, "cget option"); - return TCL_ERROR; - } - - Element* elemPtr; - if (graphPtr->getElement(objv[3], &elemPtr) != TCL_OK) - return TCL_ERROR; - - Tcl_Obj* objPtr = Tk_GetOptionValue(interp, - (char*)elemPtr->ops(), - elemPtr->optionTable(), - objv[4], 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<4) - return TCL_ERROR; - - Element* elemPtr; - if (graphPtr->getElement(objv[3], &elemPtr) != TCL_OK) - return TCL_ERROR; - - if (objc <= 5) { - Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)elemPtr->ops(), - elemPtr->optionTable(), - (objc == 5) ? objv[4] : NULL, - graphPtr->tkwin_); - if (objPtr == NULL) - return TCL_ERROR; - else - Tcl_SetObjResult(interp, objPtr); - return TCL_OK; - } - else - return ElementObjConfigure(elemPtr, interp, objc-4, objv+4); -} - -static int ActivateOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - - // List all the currently active elements - if (objc == 3) { - Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - - Tcl_HashSearch iter; - for (Tcl_HashEntry* hPtr = Tcl_FirstHashEntry(&graphPtr->elements_.table, &iter); hPtr; hPtr = Tcl_NextHashEntry(&iter)) { - Element* elemPtr = (Element*)Tcl_GetHashValue(hPtr); - if (elemPtr->active_) - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewStringObj(elemPtr->name_, -1)); - } - - Tcl_SetObjResult(interp, listObjPtr); - return TCL_OK; - } - - Element* elemPtr; - if (graphPtr->getElement(objv[3], &elemPtr) != TCL_OK) - return TCL_ERROR; - - int* indices = NULL; - int nIndices = -1; - if (objc > 4) { - nIndices = objc - 4; - indices = new int[nIndices]; - - int* activePtr = indices; - for (int ii=4; iiactiveIndices_) - delete [] elemPtr->activeIndices_; - elemPtr->nActiveIndices_ = nIndices; - elemPtr->activeIndices_ = indices; - - elemPtr->active_ = 1; - - graphPtr->flags |= RESET; - graphPtr->eventuallyRedraw(); - - return TCL_OK; -} - -static int BindOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc == 3) { - Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - - Tcl_HashSearch iter; - for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&graphPtr->elements_.tagTable, &iter); hPtr; hPtr = Tcl_NextHashEntry(&iter)) { - char* tagName = - (char*)Tcl_GetHashKey(&graphPtr->elements_.tagTable, hPtr); - Tcl_ListObjAppendElement(interp, listObjPtr,Tcl_NewStringObj(tagName,-1)); - } - - Tcl_SetObjResult(interp, listObjPtr); - return TCL_OK; - } - - return graphPtr->bindTable_->configure(graphPtr->elementTag(Tcl_GetString(objv[3])), objc - 4, objv + 4); -} - -static int ClosestOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<5) - return TCL_ERROR; - - GraphOptions* gops = (GraphOptions*)graphPtr->ops_; - ClosestSearch* searchPtr = &gops->search; - - if (graphPtr->flags & RESET) - graphPtr->resetAxes(); - - int x; - if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) { - Tcl_AppendResult(interp, ": bad window x-coordinate", NULL); - return TCL_ERROR; - } - int y; - if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) { - Tcl_AppendResult(interp, ": bad window y-coordinate", NULL); - return TCL_ERROR; - } - - searchPtr->x = x; - searchPtr->y = y; - searchPtr->index = -1; - searchPtr->dist = (double)(searchPtr->halo + 1); - - if (objc>5) { - for (int ii=5; iigetElement(objv[ii], &elemPtr) != TCL_OK) - return TCL_ERROR; - - ElementOptions* eops = (ElementOptions*)elemPtr->ops(); - if (!eops->hide) - elemPtr->closest(); - } - } - else { - // 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 (ChainLink* link = Chain_LastLink(graphPtr->elements_.displayList); - link; link = Chain_PrevLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - ElementOptions* eops = (ElementOptions*)elemPtr->ops(); - if (!eops->hide) - elemPtr->closest(); - } - } - - if (searchPtr->dist < (double)searchPtr->halo) { - Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("name", -1)); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj(searchPtr->elemPtr->name_, -1)); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("index", -1)); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(searchPtr->index)); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("x", -1)); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(searchPtr->point.x)); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("y", -1)); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(searchPtr->point.y)); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("dist", -1)); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(searchPtr->dist)); - Tcl_SetObjResult(interp, listObjPtr); - } - - return TCL_OK; -} - -static int CreateOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - if (graphPtr->createElement(objc, objv) != TCL_OK) - return TCL_ERROR; - Tcl_SetObjResult(interp, objv[3]); - - graphPtr->flags |= RESET; - graphPtr->eventuallyRedraw(); - - return TCL_OK; -} - -static int DeactivateOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - for (int ii=3; iigetElement(objv[ii], &elemPtr) != TCL_OK) - return TCL_ERROR; - - if (elemPtr->activeIndices_) { - delete [] elemPtr->activeIndices_; - elemPtr->activeIndices_ = NULL; - } - elemPtr->nActiveIndices_ = 0; - elemPtr->active_ = 0; - } - - graphPtr->flags |= RESET; - graphPtr->eventuallyRedraw(); - - return TCL_OK; -} - -static int DeleteOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - for (int ii=3; iigetElement(objv[ii], &elemPtr) != TCL_OK) - return TCL_ERROR; - graphPtr->legend_->removeElement(elemPtr); - delete elemPtr; - } - - graphPtr->flags |= RESET; - graphPtr->eventuallyRedraw(); - - return TCL_OK; -} - -static int ExistsOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Tcl_HashEntry *hPtr = - Tcl_FindHashEntry(&graphPtr->elements_.table, Tcl_GetString(objv[3])); - Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (hPtr != NULL)); - return TCL_OK; -} - -static int LowerOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - - // Move the links of lowered elements out of the display list into - // a temporary list - Chain* chain = new Chain(); - - for (int ii=3; iigetElement(objv[ii], &elemPtr) != TCL_OK) - return TCL_ERROR; - - // look for duplicates - int ok=1; - for (ChainLink* link = Chain_FirstLink(chain); - link; link = Chain_NextLink(link)) { - Element* ptr = (Element*)Chain_GetValue(link); - if (ptr == elemPtr) { - ok=0; - break; - } - } - - if (ok && elemPtr->link) { - graphPtr->elements_.displayList->unlinkLink(elemPtr->link); - chain->linkAfter(elemPtr->link, NULL); - } - } - - // Append the links to end of the display list - ChainLink *next; - for (ChainLink *link = Chain_FirstLink(chain); link; link = next) { - next = Chain_NextLink(link); - chain->unlinkLink(link); - graphPtr->elements_.displayList->linkAfter(link, NULL); - } - delete chain; - - graphPtr->flags |= CACHE; - graphPtr->eventuallyRedraw(); - - Tcl_SetObjResult(interp, DisplayListObj(graphPtr)); - return TCL_OK; -} - -static int NamesOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - - Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - if (objc == 3) { - Tcl_HashSearch iter; - for (Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&graphPtr->elements_.table, &iter); hPtr != NULL; hPtr = Tcl_NextHashEntry(&iter)) { - Element* elemPtr = (Element*)Tcl_GetHashValue(hPtr); - Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); - Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); - } - } - else { - Tcl_HashSearch iter; - for (Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&graphPtr->elements_.table, &iter); hPtr != NULL; hPtr = Tcl_NextHashEntry(&iter)) { - Element* elemPtr = (Element*)Tcl_GetHashValue(hPtr); - - for (int ii=3; iiname_,Tcl_GetString(objv[ii]))) { - Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); - Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); - break; - } - } - } - } - - Tcl_SetObjResult(interp, listObjPtr); - return TCL_OK; -} - -static int RaiseOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - - Chain* chain = new Chain(); - for (int ii=3; iigetElement(objv[ii], &elemPtr) != TCL_OK) - return TCL_ERROR; - - // look for duplicates - int ok=1; - for (ChainLink* link = Chain_FirstLink(chain); - link; link = Chain_NextLink(link)) { - Element* ptr = (Element*)Chain_GetValue(link); - if (ptr == elemPtr) { - ok=0; - break; - } - } - - if (ok && elemPtr->link) { - graphPtr->elements_.displayList->unlinkLink(elemPtr->link); - chain->linkAfter(elemPtr->link, NULL); - } - } - - // Prepend the links to beginning of the display list in reverse order - ChainLink *prev; - for (ChainLink *link = Chain_LastLink(chain); link; link = prev) { - prev = Chain_PrevLink(link); - chain->unlinkLink(link); - graphPtr->elements_.displayList->linkBefore(link, NULL); - } - delete chain; - - graphPtr->flags |= CACHE; - graphPtr->eventuallyRedraw(); - - Tcl_SetObjResult(interp, DisplayListObj(graphPtr)); - return TCL_OK; -} - -static int ShowOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - int elemObjc; - Tcl_Obj** elemObjv; - if (Tcl_ListObjGetElements(interp, objv[3], &elemObjc, &elemObjv) != TCL_OK) - return TCL_ERROR; - - // Collect the named elements into a list - Chain* chain = new Chain(); - for (int ii=0; iigetElement(elemObjv[ii], &elemPtr) != TCL_OK) { - delete chain; - return TCL_ERROR; - } - - // look for duplicates - int ok=1; - for (ChainLink* link = Chain_FirstLink(chain); - link; link = Chain_NextLink(link)) { - Element* ptr = (Element*)Chain_GetValue(link); - if (ptr == elemPtr) { - ok=0; - break; - } - } - - if (ok) - chain->append(elemPtr); - } - - // Clear the links from the currently displayed elements - for (ChainLink* link = Chain_FirstLink(graphPtr->elements_.displayList); - link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - elemPtr->link = NULL; - } - delete graphPtr->elements_.displayList; - graphPtr->elements_.displayList = chain; - - // Set links on all the displayed elements - for (ChainLink* link = Chain_FirstLink(chain); link; - link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - elemPtr->link = link; - } - - graphPtr->flags |= RESET; - graphPtr->eventuallyRedraw(); - - Tcl_SetObjResult(interp, DisplayListObj(graphPtr)); - return TCL_OK; -} - -static int TypeOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Element* elemPtr; - if (graphPtr->getElement(objv[3], &elemPtr) != TCL_OK) - return TCL_ERROR; - - Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->typeName(), -1); - return TCL_OK; -} - -const Ensemble Blt::elementEnsemble[] = { - {"activate", ActivateOp, 0}, - {"bind", BindOp, 0}, - {"cget", CgetOp, 0}, - {"closest", ClosestOp, 0}, - {"configure", ConfigureOp, 0}, - {"create", CreateOp, 0}, - {"deactivate", DeactivateOp, 0}, - {"delete", DeleteOp, 0}, - {"exists", ExistsOp, 0}, - {"lower", LowerOp, 0}, - {"names", NamesOp, 0}, - {"raise", RaiseOp, 0}, - {"show", ShowOp, 0}, - {"type", TypeOp, 0}, - { 0,0,0 } -}; - -// Support - -static Tcl_Obj *DisplayListObj(Graph* graphPtr) -{ - Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - - for (ChainLink* link = Chain_FirstLink(graphPtr->elements_.displayList); - link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); - Tcl_ListObjAppendElement(graphPtr->interp_, listObjPtr, objPtr); - } - - return listObjPtr; -} - -static int GetIndex(Tcl_Interp* interp, Element* elemPtr, - Tcl_Obj *objPtr, int *indexPtr) -{ - ElementOptions* ops = (ElementOptions*)elemPtr->ops(); - - char *string = Tcl_GetString(objPtr); - if ((*string == 'e') && (strcmp("end", string) == 0)) - *indexPtr = NUMBEROFPOINTS(ops); - else if (Tcl_GetIntFromObj(interp, objPtr, indexPtr) != TCL_OK) - return TCL_ERROR; - - return TCL_OK; -} - - diff --git a/src/tkbltGrElemOp.h b/src/tkbltGrElemOp.h deleted file mode 100644 index b596b11..0000000 --- a/src/tkbltGrElemOp.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrElemOp_h__ -#define __BltGrElemOp_h__ - -#include "tkbltGraph.h" - -namespace Blt { - extern const Ensemble elementEnsemble[]; - extern int ElementObjConfigure(Blt::Element* elemPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); -}; - -#endif diff --git a/src/tkbltGrElemOption.C b/src/tkbltGrElemOption.C deleted file mode 100644 index 45591ac..0000000 --- a/src/tkbltGrElemOption.C +++ /dev/null @@ -1,399 +0,0 @@ -/* - * 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 1993-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 -#include -#include - -#include - -#include "tkbltChain.h" - -#include "tkbltGraph.h" -#include "tkbltGrElem.h" -#include "tkbltGrElemOption.h" -#include "tkbltGrPen.h" -#include "tkbltConfig.h" - -using namespace Blt; - -#define SETRANGE(l) ((l).range = ((l).max > (l).min) ? ((l).max - (l).min) : DBL_EPSILON) -#define SETWEIGHT(l, lo, hi) ((l).min = (lo), (l).max = (hi), SETRANGE(l)) - -// Defs - -static int GetPenStyleFromObj(Tcl_Interp* interp, Graph* graphPtr, - Tcl_Obj *objPtr, ClassId classId, - PenStyle *stylePtr); -static int ParseValues(Tcl_Interp* interp, Tcl_Obj *objPtr, int *nValuesPtr, - double **arrayPtr); - -// OptionSpecs - -static Tk_CustomOptionSetProc ValuesSetProc; -static Tk_CustomOptionGetProc ValuesGetProc; -static Tk_CustomOptionFreeProc ValuesFreeProc; -Tk_ObjCustomOption valuesObjOption = - { - "values", ValuesSetProc, ValuesGetProc, RestoreProc, ValuesFreeProc, NULL - }; - -static int ValuesSetProc(ClientData clientData, Tcl_Interp* interp, - Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, - int offset, char* savePtr, int flags) -{ - ElemValues** valuesPtrPtr = (ElemValues**)(widgRec + offset); - *(double*)savePtr = *(double*)valuesPtrPtr; - ElementOptions* ops = (ElementOptions*)widgRec; - Element* elemPtr = ops->elemPtr; - - if (!valuesPtrPtr) - return TCL_OK; - - Tcl_Obj** objv; - int objc; - if (Tcl_ListObjGetElements(interp, *objPtr, &objc, &objv) != TCL_OK) - return TCL_ERROR; - - if (objc == 0) { - *valuesPtrPtr = NULL; - return TCL_OK; - } - - const char *string = Tcl_GetString(objv[0]); - if (objc == 1) { - if (Blt_VectorExists2(interp, string)) { - ElemValuesVector* valuesPtr = new ElemValuesVector(elemPtr, string); - if (valuesPtr->getVector() != TCL_OK) { - delete valuesPtr; - return TCL_ERROR; - } - *valuesPtrPtr = valuesPtr; - } - else - return TCL_ERROR; - } - else { - double* values; - int nValues; - if (ParseValues(interp, *objPtr, &nValues, &values) != TCL_OK) - return TCL_ERROR; - ElemValuesSource* valuesPtr = new ElemValuesSource(nValues, values); - valuesPtr->findRange(); - *valuesPtrPtr = valuesPtr; - } - - return TCL_OK; -} - -static Tcl_Obj* ValuesGetProc(ClientData clientData, Tk_Window tkwin, - char *widgRec, int offset) -{ - ElemValues* valuesPtr = *(ElemValues**)(widgRec + offset); - - if (!valuesPtr) - return Tcl_NewStringObj("", -1); - - int cnt = valuesPtr->nValues(); - if (!cnt) - return Tcl_NewListObj(0, (Tcl_Obj**)NULL); - - Tcl_Obj** ll = new Tcl_Obj*[cnt]; - for (int ii=0; iivalues_[ii]); - Tcl_Obj* listObjPtr = Tcl_NewListObj(cnt, ll); - delete [] ll; - - return listObjPtr; -} - -static void ValuesFreeProc(ClientData clientData, Tk_Window tkwin, char *ptr) -{ - ElemValues* valuesPtr = *(ElemValues**)ptr; - if (valuesPtr) - delete valuesPtr; -} - -static Tk_CustomOptionSetProc PairsSetProc; -static Tk_CustomOptionGetProc PairsGetProc; -static Tk_CustomOptionRestoreProc PairsRestoreProc; -static Tk_CustomOptionFreeProc PairsFreeProc; -Tk_ObjCustomOption pairsObjOption = - { - "pairs", PairsSetProc, PairsGetProc, PairsRestoreProc, PairsFreeProc, NULL - }; - -static int PairsSetProc(ClientData clientData, Tcl_Interp* interp, - Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, - int offset, char* savePtr, int flags) -{ - ElemCoords* coordsPtr = (ElemCoords*)(widgRec + offset); - *(double*)savePtr = (double)NULL; - - double* values; - int nValues; - if (ParseValues(interp, *objPtr, &nValues, &values) != TCL_OK) - return TCL_ERROR; - - if (nValues == 0) - return TCL_OK; - - if (nValues & 1) { - Tcl_AppendResult(interp, "odd number of data points", NULL); - delete [] values; - return TCL_ERROR; - } - - nValues /= 2; - if (coordsPtr->x) - delete coordsPtr->x; - coordsPtr->x = new ElemValuesSource(nValues); - - if (coordsPtr->y) - delete coordsPtr->y; - coordsPtr->y = new ElemValuesSource(nValues); - - int ii=0; - for (double* p = values; iix->values_[ii] = *p++; - coordsPtr->y->values_[ii] = *p++; - } - delete [] values; - - coordsPtr->x->findRange(); - coordsPtr->y->findRange(); - - return TCL_OK; -}; - -static Tcl_Obj* PairsGetProc(ClientData clientData, Tk_Window tkwin, - char *widgRec, int offset) -{ - ElemCoords* coordsPtr = (ElemCoords*)(widgRec + offset); - - if (!coordsPtr || - !coordsPtr->x || !coordsPtr->y || - !coordsPtr->x->nValues() || !coordsPtr->y->nValues()) - return Tcl_NewListObj(0, (Tcl_Obj**)NULL); - - int cnt = MIN(coordsPtr->x->nValues(), coordsPtr->y->nValues()); - Tcl_Obj** ll = new Tcl_Obj*[2*cnt]; - for (int ii=0, jj=0; iix->values_[ii]); - ll[jj++] = Tcl_NewDoubleObj(coordsPtr->y->values_[ii]); - } - Tcl_Obj* listObjPtr = Tcl_NewListObj(2*cnt, ll); - delete [] ll; - - return listObjPtr; -}; - -static void PairsRestoreProc(ClientData clientData, Tk_Window tkwin, - char *ptr, char *savePtr) -{ - // do nothing -} - -static void PairsFreeProc(ClientData clientData, Tk_Window tkwin, char *ptr) -{ - // do nothing -} - -int StyleSetProc(ClientData clientData, Tcl_Interp* interp, - Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, - int offset, char* save, int flags) -{ - Chain* stylePalette = *(Chain**)(widgRec + offset); - ElementOptions* ops = (ElementOptions*)(widgRec); - Element* elemPtr = ops->elemPtr; - size_t size = (size_t)clientData; - - int objc; - Tcl_Obj** objv; - if (Tcl_ListObjGetElements(interp, *objPtr, &objc, &objv) != TCL_OK) - return TCL_ERROR; - - // Reserve the first entry for the "normal" pen. We'll set the style later - elemPtr->freeStylePalette(stylePalette); - ChainLink* link = Chain_FirstLink(stylePalette); - if (!link) { - link = new ChainLink(size); - stylePalette->linkAfter(link, NULL); - } - - PenStyle* stylePtr = (PenStyle*)Chain_GetValue(link); - stylePtr->penPtr = NORMALPEN(ops); - for (int ii = 0; iiweight.min = (double)ii; - stylePtr->weight.max = (double)ii + 1.0; - stylePtr->weight.range = 1.0; - if (GetPenStyleFromObj(interp, elemPtr->graphPtr_, objv[ii], - elemPtr->classId(), - (PenStyle*)stylePtr) != TCL_OK) { - elemPtr->freeStylePalette(stylePalette); - return TCL_ERROR; - } - stylePalette->linkAfter(link, NULL); - } - - return TCL_OK; -} - -Tcl_Obj* StyleGetProc(ClientData clientData, Tk_Window tkwin, - char *widgRec, int offset) -{ - Chain* stylePalette = *(Chain**)(widgRec + offset); - - // count how many - int cnt =0; - for (ChainLink* link = Chain_FirstLink(stylePalette); link; - link = Chain_NextLink(link), cnt++) {} - if (!cnt) - return Tcl_NewListObj(0, (Tcl_Obj**)NULL); - - Tcl_Obj** ll = new Tcl_Obj*[3*cnt]; - int ii=0; - for (ChainLink* link = Chain_FirstLink(stylePalette); link; - link = Chain_NextLink(link)) { - PenStyle *stylePtr = (PenStyle*)Chain_GetValue(link); - ll[ii++] = Tcl_NewStringObj(stylePtr->penPtr->name_, -1); - ll[ii++] = Tcl_NewDoubleObj(stylePtr->weight.min); - ll[ii++] = Tcl_NewDoubleObj(stylePtr->weight.max); - } - Tcl_Obj *listObjPtr = Tcl_NewListObj(3*cnt,ll); - delete [] ll; - - return listObjPtr; -} - -void StyleRestoreProc(ClientData clientData, Tk_Window tkwin, - char *ptr, char *savePtr) -{ - // do nothing -} - -void StyleFreeProc(ClientData clientData, Tk_Window tkwin, char *ptr) -{ - // do nothing -} - -// Support - -static int GetPenStyleFromObj(Tcl_Interp* interp, Graph* graphPtr, - Tcl_Obj *objPtr, ClassId classId, - PenStyle *stylePtr) -{ - int objc; - Tcl_Obj **objv; - if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) - return TCL_ERROR; - - if ((objc != 1) && (objc != 3)) { - Tcl_AppendResult(interp, "bad style entry \"", - Tcl_GetString(objPtr), - "\": should be \"penName\" or \"penName min max\"", - NULL); - return TCL_ERROR; - } - - Pen* penPtr; - if (graphPtr->getPen(objv[0], &penPtr) != TCL_OK) - return TCL_ERROR; - - if (objc == 3) { - double min, max; - if ((Tcl_GetDoubleFromObj(interp, objv[1], &min) != TCL_OK) || - (Tcl_GetDoubleFromObj(interp, objv[2], &max) != TCL_OK)) - return TCL_ERROR; - - SETWEIGHT(stylePtr->weight, min, max); - } - - penPtr->refCount_++; - stylePtr->penPtr = penPtr; - return TCL_OK; -} - -void VectorChangedProc(Tcl_Interp* interp, ClientData clientData, - Blt_VectorNotify notify) -{ - ElemValuesVector* valuesPtr = (ElemValuesVector*)clientData; - if (!valuesPtr) - return; - - if (notify == BLT_VECTOR_NOTIFY_DESTROY) { - valuesPtr->freeSource(); - valuesPtr->reset(); - } - else { - Blt_Vector* vector; - Blt_GetVectorById(interp, valuesPtr->source_, &vector); - if (valuesPtr->fetchValues(vector) != TCL_OK) - return; - } - - Element* elemPtr = valuesPtr->elemPtr_; - Graph* graphPtr = elemPtr->graphPtr_; - - graphPtr->flags |= RESET; - graphPtr->eventuallyRedraw(); -} - -static int ParseValues(Tcl_Interp* interp, Tcl_Obj *objPtr, int *nValuesPtr, - double **arrayPtr) -{ - int objc; - Tcl_Obj **objv; - if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) - return TCL_ERROR; - - *arrayPtr = NULL; - *nValuesPtr = 0; - if (objc > 0) { - double* array = new double[objc]; - if (!array) { - Tcl_AppendResult(interp, "can't allocate new vector", NULL); - return TCL_ERROR; - } - - int i=0; - for (double* p = array; i < objc; i++, p++) { - if (Tcl_GetDoubleFromObj(interp, objv[i], p) != TCL_OK) { - delete [] array; - return TCL_ERROR; - } - } - *arrayPtr = array; - *nValuesPtr = objc; - } - - return TCL_OK; -} diff --git a/src/tkbltGrElemOption.h b/src/tkbltGrElemOption.h deleted file mode 100644 index 4312691..0000000 --- a/src/tkbltGrElemOption.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrElemOption_h__ -#define __BltGrElemOption_h__ - -#include - -extern const char* fillObjOption[]; -extern Tk_CustomOptionSetProc StyleSetProc; -extern Tk_CustomOptionGetProc StyleGetProc; -extern Tk_CustomOptionRestoreProc StyleRestoreProc; -extern Tk_CustomOptionFreeProc StyleFreeProc; - -#endif diff --git a/src/tkbltGrHairs.C b/src/tkbltGrHairs.C deleted file mode 100644 index d7ea3d2..0000000 --- a/src/tkbltGrHairs.C +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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 1993-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 - -#include "tkbltGraph.h" -#include "tkbltGrHairs.h" -#include "tkbltConfig.h" - -using namespace Blt; - -static Tk_OptionSpec optionSpecs[] = { - {TK_OPTION_COLOR, "-color", "color", "Color", - "green", -1, Tk_Offset(CrosshairsOptions, colorPtr), 0, NULL, 0}, - {TK_OPTION_CUSTOM, "-dashes", "dashes", "Dashes", - NULL, -1, Tk_Offset(CrosshairsOptions, dashes), - TK_OPTION_NULL_OK, &dashesObjOption, 0}, - {TK_OPTION_PIXELS, "-linewidth", "lineWidth", "Linewidth", - "1", -1, Tk_Offset(CrosshairsOptions, lineWidth), 0, NULL, 0}, - {TK_OPTION_PIXELS, "-x", "x", "X", - "0", -1, Tk_Offset(CrosshairsOptions, x), 0, NULL, 0}, - {TK_OPTION_PIXELS, "-y", "y", "Y", - "0", -1, Tk_Offset(CrosshairsOptions, y), 0, NULL, 0}, - {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} -}; - -Crosshairs::Crosshairs(Graph* graphPtr) -{ - ops_ = (CrosshairsOptions*)calloc(1, sizeof(CrosshairsOptions)); - - graphPtr_ = graphPtr; - visible_ =0; - gc_ =NULL; - - optionTable_ = Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); - Tk_InitOptions(graphPtr->interp_, (char*)ops_, optionTable_, - graphPtr->tkwin_); -} - -Crosshairs::~Crosshairs() -{ - if (gc_) - graphPtr_->freePrivateGC(gc_); - - Tk_FreeConfigOptions((char*)ops_, optionTable_, graphPtr_->tkwin_); - free(ops_); -} - -// Configure - -int Crosshairs::configure() -{ - CrosshairsOptions* ops = (CrosshairsOptions*)ops_; - - XGCValues gcValues; - gcValues.foreground = ops->colorPtr->pixel; - gcValues.line_width = ops->lineWidth; - unsigned long gcMask = (GCForeground | GCLineWidth); - if (LineIsDashed(ops->dashes)) { - gcValues.line_style = LineOnOffDash; - gcMask |= GCLineStyle; - } - GC newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); - if (LineIsDashed(ops->dashes)) - graphPtr_->setDashes(newGC, &ops->dashes); - - if (gc_) - graphPtr_->freePrivateGC(gc_); - gc_ = newGC; - - // Are the new coordinates on the graph? - map(); - - return TCL_OK; -} - -void Crosshairs::map() -{ - CrosshairsOptions* ops = (CrosshairsOptions*)ops_; - - segArr_[0].x = ops->x; - segArr_[1].x = ops->x; - segArr_[0].y = graphPtr_->bottom_; - segArr_[1].y = graphPtr_->top_; - segArr_[2].y = ops->y; - segArr_[3].y = ops->y; - segArr_[2].x = graphPtr_->left_; - segArr_[3].x = graphPtr_->right_; -} - -void Crosshairs::on() -{ - visible_ =1; -} - -void Crosshairs::off() -{ - visible_ =0; -} - -void Crosshairs::draw(Drawable drawable) -{ - CrosshairsOptions* ops = (CrosshairsOptions*)ops_; - - if (visible_ && Tk_IsMapped(graphPtr_->tkwin_)) { - if (ops->x <= graphPtr_->right_ && - ops->x >= graphPtr_->left_ && - ops->y <= graphPtr_->bottom_ && - ops->y >= graphPtr_->top_) { - XDrawLine(graphPtr_->display_, drawable, gc_, - segArr_[0].x, segArr_[0].y, segArr_[1].x, segArr_[1].y); - XDrawLine(graphPtr_->display_, drawable, gc_, - segArr_[2].x, segArr_[2].y, segArr_[3].x, segArr_[3].y); - } - } -} diff --git a/src/tkbltGrHairs.h b/src/tkbltGrHairs.h deleted file mode 100644 index 825cf2a..0000000 --- a/src/tkbltGrHairs.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrHairs_h__ -#define __BltGrHairs_h__ - -#include - -#include "tkbltGrMisc.h" - -namespace Blt { - class Graph; - - typedef struct { - XColor* colorPtr; - Dashes dashes; - int lineWidth; - int x; - int y; - } CrosshairsOptions; - - class Crosshairs { - protected: - Graph* graphPtr_; - Tk_OptionTable optionTable_; - void* ops_; - - int visible_; - GC gc_; - XPoint segArr_[4]; - - public: - Crosshairs(Graph*); - virtual ~Crosshairs(); - - int configure(); - void map(); - void draw(Drawable); - - void on(); - void off(); - int isOn() {return visible_;} - - Tk_OptionTable optionTable() {return optionTable_;} - void* ops() {return ops_;} - }; -}; - -#endif diff --git a/src/tkbltGrHairsOp.C b/src/tkbltGrHairsOp.C deleted file mode 100644 index 57650ce..0000000 --- a/src/tkbltGrHairsOp.C +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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 1993-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 "tkbltGraph.h" -#include "tkbltGrHairs.h" -#include "tkbltGrHairsOp.h" - -using namespace Blt; - -static int CrosshairsObjConfigure(Graph* graphPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Crosshairs* chPtr = graphPtr->crosshairs_; - Tk_SavedOptions savedOptions; - int mask =0; - int error; - Tcl_Obj* errorResult; - - for (error=0; error<=1; error++) { - if (!error) { - if (Tk_SetOptions(interp, (char*)chPtr->ops(), chPtr->optionTable(), - objc, objv, graphPtr->tkwin_, &savedOptions, &mask) - != TCL_OK) - continue; - } - else { - errorResult = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(errorResult); - Tk_RestoreSavedOptions(&savedOptions); - } - - if (chPtr->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 != 4) { - Tcl_WrongNumArgs(interp, 2, objv, "cget option"); - return TCL_ERROR; - } - - Crosshairs* chPtr = graphPtr->crosshairs_; - Tcl_Obj* objPtr = Tk_GetOptionValue(interp, - (char*)chPtr->ops(), - chPtr->optionTable(), - objv[3], 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; - Crosshairs* chPtr = graphPtr->crosshairs_; - if (objc <= 4) { - Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)chPtr->ops(), - chPtr->optionTable(), - (objc == 4) ? objv[3] : NULL, - graphPtr->tkwin_); - if (objPtr == NULL) - return TCL_ERROR; - else - Tcl_SetObjResult(interp, objPtr); - return TCL_OK; - } - else - return CrosshairsObjConfigure(graphPtr, interp, objc-3, objv+3); -} - -static int OnOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Crosshairs *chPtr = graphPtr->crosshairs_; - - chPtr->on(); - - return TCL_OK; -} - -static int OffOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Crosshairs *chPtr = graphPtr->crosshairs_; - - chPtr->off(); - - return TCL_OK; -} - -static int ToggleOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Crosshairs *chPtr = graphPtr->crosshairs_; - - if (chPtr->isOn()) - chPtr->off(); - else - chPtr->on(); - - return TCL_OK; -} - -const Ensemble Blt::crosshairsEnsemble[] = { - {"cget", CgetOp, 0}, - {"configure", ConfigureOp, 0}, - {"off", OffOp, 0}, - {"on", OnOp, 0}, - {"toggle", ToggleOp, 0}, - { 0,0,0 } -}; diff --git a/src/tkbltGrHairsOp.h b/src/tkbltGrHairsOp.h deleted file mode 100644 index 3f3d009..0000000 --- a/src/tkbltGrHairsOp.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrHairsOp_h__ -#define __BltGrHairsOp_h__ - -#include "tkbltGraph.h" - -namespace Blt { - extern const Ensemble crosshairsEnsemble[]; -}; - -#endif diff --git a/src/tkbltGrLegd.C b/src/tkbltGrLegd.C deleted file mode 100644 index 5242215..0000000 --- a/src/tkbltGrLegd.C +++ /dev/null @@ -1,1070 +0,0 @@ -/* - * 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 1993-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 -#include - -#include "tkbltGrBind.h" -#include "tkbltGraph.h" -#include "tkbltGrLegd.h" -#include "tkbltGrElem.h" -#include "tkbltGrPostscript.h" -#include "tkbltGrMisc.h" -#include "tkbltGrDef.h" -#include "tkbltConfig.h" -#include "tkbltGrPSOutput.h" - -using namespace Blt; - -static void SelectCmdProc(ClientData); -static Tk_SelectionProc SelectionProc; - -// OptionSpecs - -static const char* selectmodeObjOption[] = { - "single", "multiple", NULL -}; -static const char* positionObjOption[] = { - "rightmargin", "leftmargin", "topmargin", "bottommargin", - "plotarea", "xy", NULL -}; - -static Tk_OptionSpec optionSpecs[] = { - {TK_OPTION_BORDER, "-activebackground", "activeBackground", - "ActiveBackground", - STD_ACTIVE_BACKGROUND, -1, Tk_Offset(LegendOptions, activeBg), - 0, NULL, CACHE}, - {TK_OPTION_PIXELS, "-activeborderwidth", "activeBorderWidth", - "ActiveBorderWidth", - STD_BORDERWIDTH, -1, Tk_Offset(LegendOptions, entryBW), 0, NULL, LAYOUT}, - {TK_OPTION_COLOR, "-activeforeground", "activeForeground", "ActiveForeground", - STD_ACTIVE_FOREGROUND, -1, Tk_Offset(LegendOptions, activeFgColor), - 0, NULL, CACHE}, - {TK_OPTION_RELIEF, "-activerelief", "activeRelief", "ActiveRelief", - "flat", -1, Tk_Offset(LegendOptions, activeRelief), 0, NULL, LAYOUT}, - {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor", - "n", -1, Tk_Offset(LegendOptions, anchor), 0, NULL, LAYOUT}, - {TK_OPTION_SYNONYM, "-bg", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-background", 0}, - {TK_OPTION_BORDER, "-background", "background", "Background", - NULL, -1, Tk_Offset(LegendOptions, normalBg), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", - STD_BORDERWIDTH, -1, Tk_Offset(LegendOptions, borderWidth), - 0, NULL, LAYOUT}, - {TK_OPTION_SYNONYM, "-bd", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-borderwidth", 0}, - {TK_OPTION_INT, "-columns", "columns", "columns", - "0", -1, Tk_Offset(LegendOptions, reqColumns), 0, NULL, LAYOUT}, - {TK_OPTION_BOOLEAN, "-exportselection", "exportSelection", "ExportSelection", - "no", -1, Tk_Offset(LegendOptions, exportSelection), 0, NULL, LAYOUT}, - {TK_OPTION_CUSTOM, "-focusdashes", "focusDashes", "FocusDashes", - "dot", -1, Tk_Offset(LegendOptions, focusDashes), - TK_OPTION_NULL_OK, &dashesObjOption, CACHE}, - {TK_OPTION_COLOR, "-focusforeground", "focusForeground", "FocusForeground", - STD_ACTIVE_FOREGROUND, -1, Tk_Offset(LegendOptions, focusColor), - 0, NULL, CACHE}, - {TK_OPTION_FONT, "-font", "font", "Font", - STD_FONT_SMALL, -1, Tk_Offset(LegendOptions, style.font), 0, NULL, LAYOUT}, - {TK_OPTION_SYNONYM, "-fg", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-foreground", 0}, - {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(LegendOptions, fgColor), - 0, NULL, CACHE}, - {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", - "no", -1, Tk_Offset(LegendOptions, hide), 0, NULL, LAYOUT}, - {TK_OPTION_PIXELS, "-ipadx", "iPadX", "Pad", - "1", -1, Tk_Offset(LegendOptions, ixPad), 0, NULL, LAYOUT}, - {TK_OPTION_PIXELS, "-ipady", "iPadY", "Pad", - "1", -1, Tk_Offset(LegendOptions, iyPad), 0, NULL, LAYOUT}, - {TK_OPTION_BORDER, "-nofocusselectbackground", "noFocusSelectBackground", - "NoFocusSelectBackground", - STD_ACTIVE_BACKGROUND, -1, Tk_Offset(LegendOptions, selOutFocusBg), - 0, NULL, CACHE}, - {TK_OPTION_COLOR, "-nofocusselectforeground", "noFocusSelectForeground", - "NoFocusSelectForeground", - STD_ACTIVE_FOREGROUND, -1, Tk_Offset(LegendOptions, selOutFocusFgColor), - 0, NULL, CACHE}, - {TK_OPTION_PIXELS, "-padx", "padX", "Pad", - "1", -1, Tk_Offset(LegendOptions, xPad), 0, NULL, LAYOUT}, - {TK_OPTION_PIXELS, "-pady", "padY", "Pad", - "1", -1, Tk_Offset(LegendOptions, yPad), 0, NULL, LAYOUT}, - {TK_OPTION_STRING_TABLE, "-position", "position", "Position", - "rightmargin", -1, Tk_Offset(LegendOptions, position), - 0, &positionObjOption, LAYOUT}, - {TK_OPTION_BOOLEAN, "-raised", "raised", "Raised", - "no", -1, Tk_Offset(LegendOptions, raised), 0, NULL, LAYOUT}, - {TK_OPTION_RELIEF, "-relief", "relief", "Relief", - "flat", -1, Tk_Offset(LegendOptions, relief), 0, NULL, LAYOUT}, - {TK_OPTION_INT, "-rows", "rows", "rows", - "0", -1, Tk_Offset(LegendOptions, reqRows), 0, NULL, LAYOUT}, - {TK_OPTION_BORDER, "-selectbackground", "selectBackground", - "SelectBackground", - STD_ACTIVE_BACKGROUND, -1, Tk_Offset(LegendOptions, selInFocusBg), - 0, NULL, LAYOUT}, - {TK_OPTION_PIXELS, "-selectborderwidth", "selectBorderWidth", - "SelectBorderWidth", - "1", -1, Tk_Offset(LegendOptions, selBW), 0, NULL, LAYOUT}, - {TK_OPTION_STRING, "-selectcommand", "selectCommand", "SelectCommand", - NULL, -1, Tk_Offset(LegendOptions, selectCmd), TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_COLOR, "-selectforeground", "selectForeground", "SelectForeground", - STD_ACTIVE_FOREGROUND, -1, Tk_Offset(LegendOptions, selInFocusFgColor), - 0, NULL, CACHE}, - {TK_OPTION_STRING_TABLE, "-selectmode", "selectMode", "SelectMode", - "multiple", -1, Tk_Offset(LegendOptions, selectMode), - 0, &selectmodeObjOption, 0}, - {TK_OPTION_RELIEF, "-selectrelief", "selectRelief", "SelectRelief", - "flat", -1, Tk_Offset(LegendOptions, selRelief), 0, NULL, LAYOUT}, - {TK_OPTION_STRING, "-title", "title", "Title", - NULL, -1, Tk_Offset(LegendOptions, title), TK_OPTION_NULL_OK, NULL, LAYOUT}, - {TK_OPTION_COLOR, "-titlecolor", "titleColor", "TitleColor", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(LegendOptions, titleStyle.color), - 0, NULL, CACHE}, - {TK_OPTION_FONT, "-titlefont", "titleFont", "TitleFont", - STD_FONT_SMALL, -1, Tk_Offset(LegendOptions, titleStyle.font), - 0, NULL, LAYOUT}, - {TK_OPTION_PIXELS, "-x", "x", "X", - "0", -1, Tk_Offset(LegendOptions, xReq), 0, NULL, LAYOUT}, - {TK_OPTION_PIXELS, "-y", "y", "Y", - "0", -1, Tk_Offset(LegendOptions, yReq), 0, NULL, LAYOUT}, - {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} -}; - -Legend::Legend(Graph* graphPtr) -{ - ops_ = (void*)calloc(1, sizeof(LegendOptions)); - LegendOptions* ops = (LegendOptions*)ops_; - - graphPtr_ = graphPtr; - flags =0; - nEntries_ =0; - nColumns_ =0; - nRows_ =0; - width_ =0; - height_ =0; - entryWidth_ =0; - entryHeight_ =0; - x_ =0; - y_ =0; - bindTable_ =NULL; - focusGC_ =NULL; - focusPtr_ =NULL; - selAnchorPtr_ =NULL; - selMarkPtr_ =NULL; - selected_ = new Chain(); - titleWidth_ =0; - titleHeight_ =0; - - ops->style.anchor =TK_ANCHOR_NW; - ops->style.color =NULL; - ops->style.font =NULL; - ops->style.angle =0; - ops->style.justify =TK_JUSTIFY_LEFT; - - ops->titleStyle.anchor =TK_ANCHOR_NW; - ops->titleStyle.color =NULL; - ops->titleStyle.font =NULL; - ops->titleStyle.angle =0; - ops->titleStyle.justify =TK_JUSTIFY_LEFT; - - bindTable_ = new BindTable(graphPtr, this); - - Tcl_InitHashTable(&selectTable_, TCL_ONE_WORD_KEYS); - - Tk_CreateSelHandler(graphPtr_->tkwin_, XA_PRIMARY, XA_STRING, - SelectionProc, this, XA_STRING); - - optionTable_ =Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); - Tk_InitOptions(graphPtr->interp_, (char*)ops_, optionTable_, graphPtr->tkwin_); -} - -Legend::~Legend() -{ - // LegendOptions* ops = (LegendOptions*)ops_; - - delete bindTable_; - - if (focusGC_) - graphPtr_->freePrivateGC(focusGC_); - - if (graphPtr_->tkwin_) - Tk_DeleteSelHandler(graphPtr_->tkwin_, XA_PRIMARY, XA_STRING); - - delete selected_; - - Tk_FreeConfigOptions((char*)ops_, optionTable_, graphPtr_->tkwin_); - free(ops_); -} - -int Legend::configure() -{ - LegendOptions* ops = (LegendOptions*)ops_; - - // GC for active label, Dashed outline - unsigned long gcMask = GCForeground | GCLineStyle; - XGCValues gcValues; - gcValues.foreground = ops->focusColor->pixel; - gcValues.line_style = (LineIsDashed(ops->focusDashes)) - ? LineOnOffDash : LineSolid; - GC newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); - if (LineIsDashed(ops->focusDashes)) { - ops->focusDashes.offset = 2; - graphPtr_->setDashes(newGC, &ops->focusDashes); - } - if (focusGC_) - graphPtr_->freePrivateGC(focusGC_); - - focusGC_ = newGC; - - return TCL_OK; -} - -void Legend::map(int plotWidth, int plotHeight) -{ - LegendOptions* ops = (LegendOptions*)ops_; - - entryWidth_ =0; - entryHeight_ = 0; - nRows_ =0; - nColumns_ =0; - nEntries_ =0; - height_ =0; - width_ = 0; - - TextStyle tts(graphPtr_, &ops->titleStyle); - tts.getExtents(ops->title, &titleWidth_, &titleHeight_); - - // Count the number of legend entries and determine the widest and tallest - // label. The number of entries would normally be the number of elements, - // but elements can have no legend entry (-label ""). - int nEntries =0; - int maxWidth =0; - int maxHeight =0; - TextStyle ts(graphPtr_, &ops->style); - for (ChainLink* link = Chain_FirstLink(graphPtr_->elements_.displayList); - link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); - - if (!elemOps->label) - continue; - - int w, h; - ts.getExtents(elemOps->label, &w, &h); - if (maxWidth < (int)w) - maxWidth = w; - - if (maxHeight < (int)h) - maxHeight = h; - - nEntries++; - } - if (nEntries == 0) - return; - - Tk_FontMetrics fontMetrics; - Tk_GetFontMetrics(ops->style.font, &fontMetrics); - int symbolWidth = 2 * fontMetrics.ascent; - - maxWidth += 2 * ops->entryBW + 2*ops->ixPad + - + symbolWidth + 3 * 2; - - maxHeight += 2 * ops->entryBW + 2*ops->iyPad; - - maxWidth |= 0x01; - maxHeight |= 0x01; - - int lw = plotWidth - 2 * ops->borderWidth - 2*ops->xPad; - int lh = plotHeight - 2 * ops->borderWidth - 2*ops->yPad; - - /* - * The number of rows and columns is computed as one of the following: - * - * both options set User defined. - * -rows Compute columns from rows. - * -columns Compute rows from columns. - * neither set Compute rows and columns from - * size of plot. - */ - int nRows =0; - int nColumns =0; - if (ops->reqRows > 0) { - nRows = MIN(ops->reqRows, nEntries); - if (ops->reqColumns > 0) - nColumns = MIN(ops->reqColumns, nEntries); - else - nColumns = ((nEntries - 1) / nRows) + 1; /* Only -rows. */ - } - else if (ops->reqColumns > 0) { /* Only -columns. */ - nColumns = MIN(ops->reqColumns, nEntries); - nRows = ((nEntries - 1) / nColumns) + 1; - } - else { - // Compute # of rows and columns from the legend size - nRows = lh / maxHeight; - nColumns = lw / maxWidth; - if (nRows < 1) { - nRows = nEntries; - } - if (nColumns < 1) { - nColumns = nEntries; - } - if (nRows > nEntries) { - nRows = nEntries; - } - switch ((Position)ops->position) { - case TOP: - case BOTTOM: - nRows = ((nEntries - 1) / nColumns) + 1; - break; - case LEFT: - case RIGHT: - default: - nColumns = ((nEntries - 1) / nRows) + 1; - break; - } - } - if (nColumns < 1) - nColumns = 1; - - if (nRows < 1) - nRows = 1; - - lh = (nRows * maxHeight); - if (titleHeight_ > 0) - lh += titleHeight_ + ops->yPad; - - lw = nColumns * maxWidth; - if (lw < (int)(titleWidth_)) - lw = titleWidth_; - - width_ = lw + 2 * ops->borderWidth + 2*ops->xPad; - height_ = lh + 2 * ops->borderWidth + 2*ops->yPad; - nRows_ = nRows; - nColumns_ = nColumns; - nEntries_ = nEntries; - entryHeight_ = maxHeight; - entryWidth_ = maxWidth; - - int row =0; - int col =0; - int count =0; - for (ChainLink* link = Chain_FirstLink(graphPtr_->elements_.displayList); - link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - count++; - elemPtr->row_ = row; - elemPtr->col_ = col; - row++; - if ((count % nRows) == 0) { - col++; - row = 0; - } - } -} - -void Legend::draw(Drawable drawable) -{ - LegendOptions* ops = (LegendOptions*)ops_; - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - - if ((ops->hide) || (nEntries_ == 0)) - return; - - setOrigin(); - Tk_Window tkwin = graphPtr_->tkwin_; - int w = width_; - int h = height_; - - Pixmap pixmap = Tk_GetPixmap(graphPtr_->display_, Tk_WindowId(tkwin), w, h, - Tk_Depth(tkwin)); - - if (ops->normalBg) - Tk_Fill3DRectangle(tkwin, pixmap, ops->normalBg, 0, 0, - w, h, 0, TK_RELIEF_FLAT); - else { - switch ((Position)ops->position) { - case TOP: - case BOTTOM: - case RIGHT: - case LEFT: - Tk_Fill3DRectangle(tkwin, pixmap, gops->normalBg, 0, 0, - w, h, 0, TK_RELIEF_FLAT); - break; - case PLOT: - case XY: - // Legend background is transparent and is positioned over the the - // plot area. Either copy the part of the background from the backing - // store pixmap or (if no backing store exists) just fill it with the - // background color of the plot. - if (graphPtr_->cache_ != None) - XCopyArea(graphPtr_->display_, graphPtr_->cache_, pixmap, - graphPtr_->drawGC_, x_, y_, w, h, 0, 0); - else - Tk_Fill3DRectangle(tkwin, pixmap, gops->plotBg, 0, 0, - w, h, TK_RELIEF_FLAT, 0); - break; - }; - } - - Tk_FontMetrics fontMetrics; - Tk_GetFontMetrics(ops->style.font, &fontMetrics); - - int symbolSize = fontMetrics.ascent; - int xMid = symbolSize + 1 + ops->entryBW; - int yMid = (symbolSize / 2) + 1 + ops->entryBW; - int xLabel = 2 * symbolSize + ops->entryBW + ops->ixPad + 2 * 2; - int ySymbol = yMid + ops->iyPad; - int xSymbol = xMid + 2; - - int x = ops->xPad + ops->borderWidth; - int y = ops->yPad + ops->borderWidth; - - TextStyle tts(graphPtr_, &ops->titleStyle); - tts.drawText(pixmap, ops->title, x, y); - if (titleHeight_ > 0) - y += titleHeight_ + ops->yPad; - - int count = 0; - int yStart = y; - TextStyle ts(graphPtr_, &ops->style); - - for (ChainLink* link = Chain_FirstLink(graphPtr_->elements_.displayList); - link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); - if (!elemOps->label) - continue; - - int isSelected = entryIsSelected(elemPtr); - if (elemPtr->labelActive_) - Tk_Fill3DRectangle(tkwin, pixmap, ops->activeBg, - x, y, entryWidth_, entryHeight_, - ops->entryBW, ops->activeRelief); - else if (isSelected) { - XColor* fg = (flags & FOCUS) ? - ops->selInFocusFgColor : ops->selOutFocusFgColor; - Tk_3DBorder bg = (flags & FOCUS) ? - ops->selInFocusBg : ops->selOutFocusBg; - ops->style.color = fg; - Tk_Fill3DRectangle(tkwin, pixmap, bg, x, y, - entryWidth_, entryHeight_, - ops->selBW, ops->selRelief); - } - else { - ops->style.color = ops->fgColor; - if (elemOps->legendRelief != TK_RELIEF_FLAT) - Tk_Fill3DRectangle(tkwin, pixmap, gops->normalBg, - x, y, entryWidth_, - entryHeight_, ops->entryBW, - elemOps->legendRelief); - } - elemPtr->drawSymbol(pixmap, x + xSymbol, y + ySymbol, symbolSize); - - ts.drawText(pixmap, elemOps->label, x+xLabel, y+ops->entryBW+ops->iyPad); - count++; - - if (focusPtr_ == elemPtr) { - if (isSelected) { - XColor* color = (flags & FOCUS) ? - ops->selInFocusFgColor : ops->selOutFocusFgColor; - XSetForeground(graphPtr_->display_, focusGC_, color->pixel); - } - XDrawRectangle(graphPtr_->display_, pixmap, focusGC_, - x + 1, y + 1, entryWidth_ - 3, - entryHeight_ - 3); - if (isSelected) - XSetForeground(graphPtr_->display_, focusGC_, ops->focusColor->pixel); - } - - // Check when to move to the next column - if ((count % nRows_) > 0) - y += entryHeight_; - else { - x += entryWidth_; - y = yStart; - } - } - - Tk_3DBorder bg = ops->normalBg; - if (!bg) - bg = gops->normalBg; - - Tk_Draw3DRectangle(tkwin, pixmap, bg, 0, 0, w, h, - ops->borderWidth, ops->relief); - XCopyArea(graphPtr_->display_, pixmap, drawable, graphPtr_->drawGC_, - 0, 0, w, h, x_, y_); - - Tk_FreePixmap(graphPtr_->display_, pixmap); -} - -void Legend::print(PSOutput* psPtr) -{ - LegendOptions* ops = (LegendOptions*)ops_; - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - PostscriptOptions* pops = (PostscriptOptions*)graphPtr_->postscript_->ops_; - - if ((ops->hide) || (nEntries_ == 0)) - return; - - setOrigin(); - - double x = x_; - double y = y_; - int width = width_ - 2*ops->xPad; - int height = height_ - 2*ops->yPad; - - psPtr->append("% Legend\n"); - if (pops->decorations) { - if (ops->normalBg) - psPtr->fill3DRectangle(ops->normalBg, x, y, width, height, - ops->borderWidth, ops->relief); - else - psPtr->print3DRectangle(gops->normalBg, x, y, width, height, - ops->borderWidth, ops->relief); - - } - else { - psPtr->setClearBackground(); - psPtr->fillRectangle(x, y, width, height); - } - - Tk_FontMetrics fontMetrics; - Tk_GetFontMetrics(ops->style.font, &fontMetrics); - int symbolSize = fontMetrics.ascent; - int xMid = symbolSize + 1 + ops->entryBW; - int yMid = (symbolSize / 2) + 1 + ops->entryBW; - int xLabel = 2 * symbolSize + ops->entryBW + ops->ixPad + 5; - int xSymbol = xMid + ops->ixPad; - int ySymbol = yMid + ops->iyPad; - - x += ops->borderWidth; - y += ops->borderWidth; - TextStyle tts(graphPtr_, &ops->titleStyle); - tts.printText(psPtr, ops->title, x, y); - if (titleHeight_ > 0) - y += titleHeight_ + ops->yPad; - - int count = 0; - double yStart = y; - TextStyle ts(graphPtr_, &ops->style); - - for (ChainLink* link = Chain_FirstLink(graphPtr_->elements_.displayList); - link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); - - if (!elemOps->label) - continue; - - if (elemPtr->labelActive_) { - ops->style.color = ops->activeFgColor; - psPtr->fill3DRectangle(ops->activeBg, x, y, entryWidth_, - entryHeight_, ops->entryBW, - ops->activeRelief); - } - else { - ops->style.color = ops->fgColor; - if (elemOps->legendRelief != TK_RELIEF_FLAT) - psPtr->print3DRectangle(gops->normalBg, x, y, entryWidth_, entryHeight_, - ops->entryBW, elemOps->legendRelief); - } - elemPtr->printSymbol(psPtr, x + xSymbol, y + ySymbol, symbolSize); - ts.printText(psPtr, elemOps->label, x + xLabel, - y + ops->entryBW + ops->iyPad); - count++; - - if ((count % nRows_) > 0) - y += entryHeight_; - else { - x += entryWidth_; - y = yStart; - } - } -} - -void Legend::removeElement(Element* elemPtr) -{ - bindTable_->deleteBindings(elemPtr); -} - -void Legend::eventuallyInvokeSelectCmd() -{ - if ((flags & SELECT_PENDING) == 0) { - flags |= SELECT_PENDING; - Tcl_DoWhenIdle(SelectCmdProc, this); - } -} - -void Legend::setOrigin() -{ - LegendOptions* ops = (LegendOptions*)ops_; - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - - int x =0; - int y =0; - int w =0; - int h =0; - switch ((Position)ops->position) { - case RIGHT: - w = gops->rightMargin.width - gops->rightMargin.axesOffset; - h = graphPtr_->bottom_ - graphPtr_->top_; - x = graphPtr_->right_ + gops->rightMargin.axesOffset; - y = graphPtr_->top_; - break; - - case LEFT: - w = gops->leftMargin.width - gops->leftMargin.axesOffset; - h = graphPtr_->bottom_ - graphPtr_->top_; - x = graphPtr_->inset_; - y = graphPtr_->top_; - break; - - case TOP: - w = graphPtr_->right_ - graphPtr_->left_; - h = gops->topMargin.height - gops->topMargin.axesOffset; - if (gops->title) - h -= graphPtr_->titleHeight_; - - x = graphPtr_->left_; - y = graphPtr_->inset_; - if (gops->title) - y += graphPtr_->titleHeight_; - break; - - case BOTTOM: - w = graphPtr_->right_ - graphPtr_->left_; - h = gops->bottomMargin.height - gops->bottomMargin.axesOffset; - x = graphPtr_->left_; - y = graphPtr_->bottom_ + gops->bottomMargin.axesOffset; - break; - - case PLOT: - w = graphPtr_->right_ - graphPtr_->left_; - h = graphPtr_->bottom_ - graphPtr_->top_; - x = graphPtr_->left_; - y = graphPtr_->top_; - break; - - case XY: - w = width_; - h = height_; - x = ops->xReq; - y = ops->yReq; - if (x < 0) - x += graphPtr_->width_; - - if (y < 0) - y += graphPtr_->height_; - break; - } - - switch (ops->anchor) { - case TK_ANCHOR_NW: - break; - case TK_ANCHOR_W: - if (h > height_) - y += (h - height_) / 2; - break; - case TK_ANCHOR_SW: - if (h > height_) - y += (h - height_); - break; - case TK_ANCHOR_N: - if (w > width_) - x += (w - width_) / 2; - break; - case TK_ANCHOR_CENTER: - if (h > height_) - y += (h - height_) / 2; - - if (w > width_) - x += (w - width_) / 2; - break; - case TK_ANCHOR_S: - if (w > width_) - x += (w - width_) / 2; - - if (h > height_) - y += (h - height_); - break; - case TK_ANCHOR_NE: - if (w > width_) - x += w - width_; - break; - case TK_ANCHOR_E: - if (w > width_) - x += w - width_; - - if (h > height_) - y += (h - height_) / 2; - break; - case TK_ANCHOR_SE: - if (w > width_) { - x += w - width_; - } - if (h > height_) { - y += (h - height_); - } - break; - } - - x_ = x + ops->xPad; - y_ = y + ops->yPad; -} - -void Legend::selectEntry(Element* elemPtr) -{ - switch (flags & SELECT_TOGGLE) { - case SELECT_CLEAR: - deselectElement(elemPtr); - break; - case SELECT_SET: - selectElement(elemPtr); - break; - case SELECT_TOGGLE: - Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&selectTable_, (char*)elemPtr); - if (hPtr) - deselectElement(elemPtr); - else - selectElement(elemPtr); - break; - } -} - -void Legend::selectElement(Element* elemPtr) -{ - int isNew; - Tcl_HashEntry* hPtr = - Tcl_CreateHashEntry(&selectTable_, (char*)elemPtr, &isNew); - if (isNew) { - ChainLink* link = selected_->append(elemPtr); - Tcl_SetHashValue(hPtr, link); - } -} - -void Legend::deselectElement(Element* elemPtr) -{ - Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&selectTable_, (char*)elemPtr); - if (hPtr) { - ChainLink* link = (ChainLink*)Tcl_GetHashValue(hPtr); - selected_->deleteLink(link); - Tcl_DeleteHashEntry(hPtr); - } -} - - -int Legend::selectRange(Element *fromPtr, Element *toPtr) -{ - int isBefore=0; - for (ChainLink* linkPtr = fromPtr->link; linkPtr; linkPtr = linkPtr->next()) - if (linkPtr == toPtr->link) - isBefore =1; - - if (isBefore) { - for (ChainLink* link = fromPtr->link; link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - selectEntry(elemPtr); - if (link == toPtr->link) - break; - } - } - else { - for (ChainLink* link = fromPtr->link; link; link = Chain_PrevLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - selectEntry(elemPtr); - if (link == toPtr->link) - break; - } - } - - return TCL_OK; -} - -void Legend::clearSelection() -{ - LegendOptions* ops = (LegendOptions*)ops_; - - Tcl_DeleteHashTable(&selectTable_); - Tcl_InitHashTable(&selectTable_, TCL_ONE_WORD_KEYS); - selected_->reset(); - - if (ops->selectCmd) - eventuallyInvokeSelectCmd(); -} - -int Legend::entryIsSelected(Element* elemPtr) -{ - Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&selectTable_, (char*)elemPtr); - return (hPtr != NULL); -} - -int Legend::getElementFromObj(Tcl_Obj* objPtr, Element** elemPtrPtr) -{ - const char *string = Tcl_GetString(objPtr); - Element* elemPtr = NULL; - - if (!strcmp(string, "anchor")) - elemPtr = selAnchorPtr_; - else if (!strcmp(string, "current")) - elemPtr = (Element*)bindTable_->currentItem(); - else if (!strcmp(string, "first")) - elemPtr = getFirstElement(); - else if (!strcmp(string, "focus")) - elemPtr = focusPtr_; - else if (!strcmp(string, "last")) - elemPtr = getLastElement(); - else if (!strcmp(string, "end")) - elemPtr = getLastElement(); - else if (!strcmp(string, "next.row")) - elemPtr = getNextRow(focusPtr_); - else if (!strcmp(string, "next.column")) - elemPtr = getNextColumn(focusPtr_); - else if (!strcmp(string, "previous.row")) - elemPtr = getPreviousRow(focusPtr_); - else if (!strcmp(string, "previous.column")) - elemPtr = getPreviousColumn(focusPtr_); - else if (string[0] == '@') { - int x, y; - if (graphPtr_->getXY(string, &x, &y) != TCL_OK) - return TCL_ERROR; - - ClassId classId; - elemPtr = (Element*)pickEntry(x, y, &classId); - } - else { - if (graphPtr_->getElement(objPtr, &elemPtr) != TCL_OK) - return TCL_ERROR; - - if (!elemPtr->link) { - Tcl_AppendResult(graphPtr_->interp_, "bad legend index \"", string, "\"", - (char *)NULL); - return TCL_ERROR; - } - ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); - if (!elemOps->label) - elemPtr = NULL; - } - - *elemPtrPtr = elemPtr; - return TCL_OK; -} - -Element* Legend::getNextRow(Element* focusPtr) -{ - int col = focusPtr->col_; - int row = focusPtr->row_ + 1; - for (ChainLink* link = focusPtr->link; link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); - - if (!elemOps->label) - continue; - - if ((elemPtr->col_ == col) && (elemPtr->row_ == row)) - return elemPtr; - } - return NULL; -} - -Element* Legend::getNextColumn(Element* focusPtr) -{ - int col = focusPtr->col_ + 1; - int row = focusPtr->row_; - for (ChainLink* link = focusPtr->link; link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); - - if (!elemOps->label) - continue; - - if ((elemPtr->col_ == col) && (elemPtr->row_ == row)) - return elemPtr; - } - return NULL; -} - -Element* Legend::getPreviousRow(Element* focusPtr) -{ - int col = focusPtr->col_; - int row = focusPtr->row_ - 1; - for (ChainLink* link = focusPtr->link; link; link = Chain_PrevLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); - - if (!elemOps->label) - continue; - - if ((elemPtr->col_ == col) && (elemPtr->row_ == row)) - return elemPtr; - } - return NULL; -} - -Element* Legend::getPreviousColumn(Element* focusPtr) -{ - int col = focusPtr->col_ - 1; - int row = focusPtr->row_; - for (ChainLink* link = focusPtr->link; link; link = Chain_PrevLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); - - if (!elemOps->label) - continue; - - if ((elemPtr->col_ == col) && (elemPtr->row_ == row)) - return elemPtr; - } - return NULL; -} - -Element* Legend::getFirstElement() -{ - for (ChainLink* link = Chain_FirstLink(graphPtr_->elements_.displayList); - link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); - if (elemOps->label) - return elemPtr; - } - return NULL; -} - -Element* Legend::getLastElement() -{ - for (ChainLink* link = Chain_LastLink(graphPtr_->elements_.displayList); - link; link = Chain_PrevLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); - if (elemOps->label) - return elemPtr; - } - return NULL; -} - -ClientData Legend::pickEntry(int xx, int yy, ClassId* classIdPtr) -{ - LegendOptions* ops = (LegendOptions*)ops_; - - int ww = width_; - int hh = height_; - - if (titleHeight_ > 0) - yy -= titleHeight_ + ops->yPad; - - xx -= x_ + ops->borderWidth; - yy -= y_ + ops->borderWidth; - ww -= 2 * ops->borderWidth + 2*ops->xPad; - hh -= 2 * ops->borderWidth + 2*ops->yPad; - - // In the bounding box? if so, compute the index - if (xx >= 0 && xx < ww && yy >= 0 && yy < hh) { - int row = yy / entryHeight_; - int column = xx / entryWidth_; - int nn = (column * nRows_) + row; - - // Legend entries are stored in bottom-to-top - if (nn < nEntries_) { - int count = 0; - for (ChainLink* link = Chain_FirstLink(graphPtr_->elements_.displayList); - link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - ElementOptions* elemOps = (ElementOptions*)elemPtr->ops(); - if (elemOps->label) { - if (count == nn) { - *classIdPtr = elemPtr->classId(); - return elemPtr; - } - count++; - } - } - } - } - - return NULL; -} - -// Support - -static int SelectionProc(ClientData clientData, int offset, char *buffer, - int maxBytes) -{ - Legend* legendPtr = (Legend*)clientData; - Graph* graphPtr = legendPtr->graphPtr_; - LegendOptions* ops = (LegendOptions*)legendPtr->ops(); - - if ((ops->exportSelection) == 0) - return -1; - - // Retrieve the names of the selected entries - Tcl_DString dString; - Tcl_DStringInit(&dString); - if (legendPtr->flags & SELECT_SORTED) { - for (ChainLink* link=Chain_FirstLink(legendPtr->selected_); - link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - Tcl_DStringAppend(&dString, elemPtr->name_, -1); - Tcl_DStringAppend(&dString, "\n", -1); - } - } - else { - for (ChainLink* link=Chain_FirstLink(graphPtr->elements_.displayList); - link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - if (legendPtr->entryIsSelected(elemPtr)) { - Tcl_DStringAppend(&dString, elemPtr->name_, -1); - Tcl_DStringAppend(&dString, "\n", -1); - } - } - } - - int nBytes = Tcl_DStringLength(&dString) - offset; - strncpy(buffer, Tcl_DStringValue(&dString) + offset, maxBytes); - Tcl_DStringFree(&dString); - buffer[maxBytes] = '\0'; - return MIN(nBytes, maxBytes); -} - -static void SelectCmdProc(ClientData clientData) -{ - Legend* legendPtr = (Legend*)clientData; - LegendOptions* ops = (LegendOptions*)legendPtr->ops(); - - Tcl_Preserve(legendPtr); - legendPtr->flags &= ~SELECT_PENDING; - if (ops->selectCmd) { - Tcl_Interp* interp = legendPtr->graphPtr_->interp_; - if (Tcl_GlobalEval(interp, ops->selectCmd) != TCL_OK) - Tcl_BackgroundError(interp); - } - Tcl_Release(legendPtr); -} - - - diff --git a/src/tkbltGrLegd.h b/src/tkbltGrLegd.h deleted file mode 100644 index 66ffbc1..0000000 --- a/src/tkbltGrLegd.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrLegend_h__ -#define __BltGrLegend_h__ - -#include - -#include "tkbltGrMisc.h" -#include "tkbltGrText.h" - -namespace Blt { - class Graph; - class Pick; - class Element; - - /* - * Selection related flags: - * SELECT_PENDING A "selection" command idle task is pending. - * SELECT_CLEAR Clear selection flag of entry. - * SELECT_SET Set selection flag of entry. - * SELECT_TOGGLE Toggle selection flag of entry. - * Mask of selection set/clear/toggle flags. - * SELECT_SORTED Indicates if the entries in the selection - * should be sorted or displayed in the order - * they were selected. - */ - -#define SELECT_CLEAR (1<<24) -#define SELECT_PENDING (1<<25) -#define SELECT_SET (1<<26) -#define SELECT_SORTED (1<<27) -#define SELECT_TOGGLE (SELECT_SET | SELECT_CLEAR) - - typedef enum { - SELECT_MODE_SINGLE, SELECT_MODE_MULTIPLE - } SelectMode; - - typedef struct { - Tk_3DBorder activeBg; - XColor* activeFgColor; - int activeRelief; - Tk_3DBorder normalBg; - XColor* fgColor; - Tk_Anchor anchor; - int borderWidth; - int reqColumns; - int exportSelection; - Dashes focusDashes; - XColor* focusColor; - TextStyleOptions style; - int hide; - int ixPad; - int iyPad; - int xPad; - int yPad; - int raised; - int relief; - int reqRows; - int entryBW; - int selBW; - int xReq; - int yReq; - int position; - const char *selectCmd; - Tk_3DBorder selOutFocusBg; - Tk_3DBorder selInFocusBg; - XColor* selOutFocusFgColor; - XColor* selInFocusFgColor; - SelectMode selectMode; - int selRelief; - const char *title; - TextStyleOptions titleStyle; - } LegendOptions; - - class Legend : public Pick { - public: - enum Position {RIGHT, LEFT, TOP, BOTTOM, PLOT, XY}; - - protected: - Tk_OptionTable optionTable_; - void* ops_; - - GC focusGC_; - Tcl_HashTable selectTable_; - - public: - Graph* graphPtr_; - unsigned int flags; - - int width_; - int height_; - int x_; - int y_; - - int nEntries_; - int nColumns_; - int nRows_; - int entryWidth_; - int entryHeight_; - BindTable* bindTable_; - Element* focusPtr_; - Element* selAnchorPtr_; - Element* selMarkPtr_; - Chain* selected_; - int titleWidth_; - int titleHeight_; - - protected: - void setOrigin(); - Element* getNextRow(Element*); - Element* getNextColumn(Element*); - Element* getPreviousRow(Element*); - Element* getPreviousColumn(Element*); - Element* getFirstElement(); - Element* getLastElement(); - - public: - Legend(Graph*); - virtual ~Legend(); - - int configure(); - void map(int, int); - void draw(Drawable drawable); - void print(PSOutput* ps); - void eventuallyInvokeSelectCmd(); - - void removeElement(Element*); - int getElementFromObj(Tcl_Obj*, Element**); - - void selectEntry(Element*); - void selectElement(Element*); - void deselectElement(Element*); - int selectRange(Element*, Element*); - void clearSelection(); - int entryIsSelected(Element*); - - void* ops() {return ops_;} - Tk_OptionTable optionTable() {return optionTable_;} - - Position position() {return (Position)((LegendOptions*)ops_)->position;} - int isRaised() {return ((LegendOptions*)ops_)->raised;} - int isHidden() {return ((LegendOptions*)ops_)->hide;} - - ClientData pickEntry(int, int, ClassId*); - }; -}; - -#endif diff --git a/src/tkbltGrLegdOp.C b/src/tkbltGrLegdOp.C deleted file mode 100644 index 139d2f1..0000000 --- a/src/tkbltGrLegdOp.C +++ /dev/null @@ -1,496 +0,0 @@ -/* - * 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 1993-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 -#include - -#include "tkbltGrBind.h" -#include "tkbltGraph.h" -#include "tkbltGrLegd.h" -#include "tkbltGrLegdOp.h" -#include "tkbltGrElem.h" - -using namespace Blt; - -static Tk_LostSelProc LostSelectionProc; - -static int LegendObjConfigure(Graph* graphPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Legend* legendPtr = graphPtr->legend_; - Tk_SavedOptions savedOptions; - int mask =0; - int error; - Tcl_Obj* errorResult; - - for (error=0; error<=1; error++) { - if (!error) { - if (Tk_SetOptions(interp, (char*)legendPtr->ops(), - legendPtr->optionTable(), - objc, objv, graphPtr->tkwin_, &savedOptions, &mask) - != TCL_OK) - continue; - } - else { - errorResult = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(errorResult); - Tk_RestoreSavedOptions(&savedOptions); - } - - if (legendPtr->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 != 4) { - Tcl_WrongNumArgs(interp, 2, objv, "cget option"); - return TCL_ERROR; - } - - Legend* legendPtr = graphPtr->legend_; - Tcl_Obj* objPtr = Tk_GetOptionValue(interp, - (char*)legendPtr->ops(), - legendPtr->optionTable(), - objv[3], 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; - Legend* legendPtr = graphPtr->legend_; - if (objc <= 4) { - Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)legendPtr->ops(), - legendPtr->optionTable(), - (objc == 4) ? objv[3] : NULL, - graphPtr->tkwin_); - if (objPtr == NULL) - return TCL_ERROR; - else - Tcl_SetObjResult(interp, objPtr); - return TCL_OK; - } - else - return LegendObjConfigure(graphPtr, interp, objc-3, objv+3); -} - -static int ActivateOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Legend* legendPtr = graphPtr->legend_; - LegendOptions* ops = (LegendOptions*)legendPtr->ops(); - - const char *string = Tcl_GetString(objv[2]); - int active = (string[0] == 'a') ? 1 : 0; - int redraw = 0; - for (int ii=3; iielements_.displayList); - link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - if (Tcl_StringMatch(elemPtr->name_, pattern)) { - if (active) { - if (!elemPtr->labelActive_) { - elemPtr->labelActive_ =1; - redraw = 1; - } - } - else { - if (elemPtr->labelActive_) { - elemPtr->labelActive_ =0; - redraw = 1; - } - } - } - } - } - - if (redraw && !ops->hide) { - graphPtr->flags |= LAYOUT; - graphPtr->eventuallyRedraw(); - } - - // List active elements in stacking order - Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - for (ChainLink* link = Chain_FirstLink(graphPtr->elements_.displayList); - link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - if (elemPtr->labelActive_) { - Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); - Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); - } - } - Tcl_SetObjResult(interp, listObjPtr); - - return TCL_OK; -} - -static int BindOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - - if (objc == 3) { - Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - Tcl_HashSearch iter; - for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&graphPtr->elements_.tagTable, &iter); hPtr; hPtr = Tcl_NextHashEntry(&iter)) { - char* tagName = - (char*)Tcl_GetHashKey(&graphPtr->elements_.tagTable, hPtr); - Tcl_Obj *objPtr = Tcl_NewStringObj(tagName, -1); - Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); - } - - Tcl_SetObjResult(interp, listObjPtr); - return TCL_OK; - } - - return graphPtr->legend_->bindTable_->configure(graphPtr->elementTag(Tcl_GetString(objv[3])), objc - 4, objv + 4); -} - -static int CurselectionOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Legend* legendPtr = graphPtr->legend_; - Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - if (legendPtr->flags & SELECT_SORTED) { - for (ChainLink* link = Chain_FirstLink(legendPtr->selected_); link; - link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); - Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); - } - } - else { - // List of selected entries is in stacking order - for (ChainLink* link = Chain_FirstLink(graphPtr->elements_.displayList); - link; link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - - if (legendPtr->entryIsSelected(elemPtr)) { - Tcl_Obj *objPtr = Tcl_NewStringObj(elemPtr->name_, -1); - Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); - } - } - } - Tcl_SetObjResult(interp, listObjPtr); - return TCL_OK; -} - -static int FocusOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Legend* legendPtr = graphPtr->legend_; - - legendPtr->focusPtr_ = NULL; - if (objc == 4) { - Element* elemPtr; - if (legendPtr->getElementFromObj(objv[3], &elemPtr) != TCL_OK) - return TCL_ERROR; - - if (elemPtr) { - legendPtr->focusPtr_ = elemPtr; - - legendPtr->bindTable_->focusItem_ = (ClientData)elemPtr; - legendPtr->bindTable_->focusContext_ = elemPtr->classId(); - } - } - - graphPtr->flags |= CACHE; - graphPtr->eventuallyRedraw(); - - if (legendPtr->focusPtr_) - Tcl_SetStringObj(Tcl_GetObjResult(interp),legendPtr->focusPtr_->name_,-1); - - return TCL_OK; -} - -static int GetOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<3) - return TCL_ERROR; - - Legend* legendPtr = graphPtr->legend_; - LegendOptions* ops = (LegendOptions*)legendPtr->ops(); - - if (((ops->hide) == 0) && (legendPtr->nEntries_ > 0)) { - Element* elemPtr; - - if (legendPtr->getElementFromObj(objv[3], &elemPtr) != TCL_OK) - return TCL_ERROR; - - if (elemPtr) - Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->name_, -1); - } - return TCL_OK; -} - -const Ensemble Blt::legendEnsemble[] = { - {"activate", ActivateOp, 0}, - {"bind", BindOp, 0}, - {"cget", CgetOp, 0}, - {"configure", ConfigureOp, 0}, - {"curselection", CurselectionOp, 0}, - {"deactivate", ActivateOp, 0}, - {"focus", FocusOp, 0}, - {"get", GetOp, 0}, - {"selection", 0, selectionEnsemble}, - { 0,0,0 } -}; - -// Selection Ops - -static int SelectionAnchorOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Legend* legendPtr = graphPtr->legend_; - Element* elemPtr; - - if (legendPtr->getElementFromObj(objv[4], &elemPtr) != TCL_OK) - return TCL_ERROR; - - // Set both the anchor and the mark. Indicates that a single entry - // is selected - legendPtr->selAnchorPtr_ = elemPtr; - legendPtr->selMarkPtr_ = NULL; - if (elemPtr) - Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->name_, -1); - - graphPtr->flags |= CACHE; - graphPtr->eventuallyRedraw(); - - return TCL_OK; -} - -static int SelectionClearallOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Legend* legendPtr = graphPtr->legend_; - legendPtr->clearSelection(); - - graphPtr->flags |= CACHE; - graphPtr->eventuallyRedraw(); - - return TCL_OK; -} - -static int SelectionIncludesOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Legend* legendPtr = graphPtr->legend_; - Element* elemPtr; - if (legendPtr->getElementFromObj(objv[4], &elemPtr) != TCL_OK) - return TCL_ERROR; - - int boo = legendPtr->entryIsSelected(elemPtr); - Tcl_SetBooleanObj(Tcl_GetObjResult(interp), boo); - return TCL_OK; -} - -static int SelectionMarkOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Legend* legendPtr = graphPtr->legend_; - LegendOptions* ops = (LegendOptions*)legendPtr->ops(); - Element* elemPtr; - - if (legendPtr->getElementFromObj(objv[4], &elemPtr) != TCL_OK) - return TCL_ERROR; - - if (legendPtr->selAnchorPtr_ == NULL) { - Tcl_AppendResult(interp, "selection anchor must be set first", NULL); - return TCL_ERROR; - } - - if (legendPtr->selMarkPtr_ != elemPtr) { - // Deselect entry from the list all the way back to the anchor - ChainLink *link, *next; - for (link = Chain_LastLink(legendPtr->selected_); link; link = next) { - next = Chain_PrevLink(link); - Element *selectPtr = (Element*)Chain_GetValue(link); - if (selectPtr == legendPtr->selAnchorPtr_) - break; - - legendPtr->deselectElement(selectPtr); - } - - legendPtr->flags &= ~SELECT_TOGGLE; - legendPtr->flags |= SELECT_SET; - legendPtr->selectRange(legendPtr->selAnchorPtr_, elemPtr); - Tcl_SetStringObj(Tcl_GetObjResult(interp), elemPtr->name_, -1); - legendPtr->selMarkPtr_ = elemPtr; - - if (ops->selectCmd) - legendPtr->eventuallyInvokeSelectCmd(); - - graphPtr->flags |= CACHE; - graphPtr->eventuallyRedraw(); - } - return TCL_OK; -} - -static int SelectionPresentOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Legend* legendPtr = graphPtr->legend_; - int boo = (Chain_GetLength(legendPtr->selected_) > 0); - Tcl_SetBooleanObj(Tcl_GetObjResult(interp), boo); - return TCL_OK; -} - -static int SelectionSetOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Legend* legendPtr = graphPtr->legend_; - LegendOptions* ops = (LegendOptions*)legendPtr->ops(); - - legendPtr->flags &= ~SELECT_TOGGLE; - const char* string = Tcl_GetString(objv[3]); - switch (string[0]) { - case 's': - legendPtr->flags |= SELECT_SET; - break; - case 'c': - legendPtr->flags |= SELECT_CLEAR; - break; - case 't': - legendPtr->flags |= SELECT_TOGGLE; - break; - } - - Element *firstPtr; - if (legendPtr->getElementFromObj(objv[4], &firstPtr) != TCL_OK) - return TCL_ERROR; - ElementOptions* eops = (ElementOptions*)firstPtr->ops(); - - if ((eops->hide) && ((legendPtr->flags & SELECT_CLEAR)==0)) { - Tcl_AppendResult(interp, "can't select hidden node \"", - Tcl_GetString(objv[4]), "\"", (char *)NULL); - return TCL_ERROR; - } - - Element* lastPtr = firstPtr; - if (objc > 5) { - if (legendPtr->getElementFromObj(objv[5], &lastPtr) != TCL_OK) - return TCL_ERROR; - ElementOptions* eops = (ElementOptions*)firstPtr->ops(); - - if (eops->hide && ((legendPtr->flags & SELECT_CLEAR) == 0)) { - Tcl_AppendResult(interp, "can't select hidden node \"", - Tcl_GetString(objv[5]), "\"", (char *)NULL); - return TCL_ERROR; - } - } - - if (firstPtr == lastPtr) - legendPtr->selectEntry(firstPtr); - else - legendPtr->selectRange(firstPtr, lastPtr); - - // Set both the anchor and the mark. Indicates that a single entry is - // selected - if (legendPtr->selAnchorPtr_ == NULL) - legendPtr->selAnchorPtr_ = firstPtr; - - if (ops->exportSelection) - Tk_OwnSelection(graphPtr->tkwin_, XA_PRIMARY, LostSelectionProc, legendPtr); - - if (ops->selectCmd) - legendPtr->eventuallyInvokeSelectCmd(); - - graphPtr->flags |= CACHE; - graphPtr->eventuallyRedraw(); - - return TCL_OK; -} - -const Ensemble Blt::selectionEnsemble[] = { - {"anchor", SelectionAnchorOp, 0}, - {"clear", SelectionSetOp, 0}, - {"clearall", SelectionClearallOp, 0}, - {"includes", SelectionIncludesOp, 0}, - {"mark", SelectionMarkOp, 0}, - {"present", SelectionPresentOp, 0}, - {"set", SelectionSetOp, 0}, - {"toggle", SelectionSetOp, 0}, - { 0,0,0 } -}; - -// Support - -static void LostSelectionProc(ClientData clientData) -{ - Legend* legendPtr = (Legend*)clientData; - LegendOptions* ops = (LegendOptions*)legendPtr->ops(); - Graph* graphPtr = legendPtr->graphPtr_; - - if (ops->exportSelection) - legendPtr->clearSelection(); - - graphPtr->flags |= CACHE; - graphPtr->eventuallyRedraw(); -} - - - - diff --git a/src/tkbltGrLegdOp.h b/src/tkbltGrLegdOp.h deleted file mode 100644 index 6369a2b..0000000 --- a/src/tkbltGrLegdOp.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrLegdOp_h__ -#define __BltGrLegdOp_h__ - -#include "tkbltGraph.h" - -namespace Blt { - extern const Ensemble legendEnsemble[]; - extern const Ensemble selectionEnsemble[]; -}; - -#endif diff --git a/src/tkbltGrMarker.C b/src/tkbltGrMarker.C deleted file mode 100644 index 6fdcfd6..0000000 --- a/src/tkbltGrMarker.C +++ /dev/null @@ -1,178 +0,0 @@ -/* - * 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 1993-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 -#include - -#include - -#include "tkbltGraph.h" -#include "tkbltGrBind.h" -#include "tkbltGrMarker.h" -#include "tkbltGrAxis.h" -#include "tkbltGrMisc.h" - -using namespace Blt; - -Marker::Marker(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) -{ - optionTable_ =NULL; - ops_ =NULL; - - graphPtr_ =graphPtr; - name_ = dupstr(name); - hashPtr_ = hPtr; - link =NULL; - flags =0; - clipped_ =0; -} - -Marker::~Marker() -{ - graphPtr_->bindTable_->deleteBindings(this); - - if (link) - graphPtr_->markers_.displayList->deleteLink(link); - - if (hashPtr_) - Tcl_DeleteHashEntry(hashPtr_); - - if (name_) - delete [] name_; - - Tk_FreeConfigOptions((char*)ops_, optionTable_, graphPtr_->tkwin_); - free(ops_); -} - -double Marker::HMap(Axis *axisPtr, double x) -{ - AxisOptions* ops = (AxisOptions*)axisPtr->ops(); - - if (x == DBL_MAX) - x = 1.0; - else if (x == -DBL_MAX) - x = 0.0; - else { - if (ops->logScale) { - if (x > 0.0) - x = log10(x); - else if (x < 0.0) - x = 0.0; - } - x = (x - axisPtr->axisRange_.min) * axisPtr->axisRange_.scale; - } - if (ops->descending) - x = 1.0 - x; - - // Horizontal transformation - return (x * axisPtr->screenRange_ + axisPtr->screenMin_); -} - -double Marker::VMap(Axis *axisPtr, double y) -{ - AxisOptions* ops = (AxisOptions*)axisPtr->ops(); - - if (y == DBL_MAX) - y = 1.0; - else if (y == -DBL_MAX) - y = 0.0; - else { - if (ops->logScale) { - if (y > 0.0) - y = log10(y); - else if (y < 0.0) - y = 0.0; - } - y = (y - axisPtr->axisRange_.min) * axisPtr->axisRange_.scale; - } - if (ops->descending) - y = 1.0 - y; - - // Vertical transformation - return (((1.0 - y) * axisPtr->screenRange_) + axisPtr->screenMin_); -} - -Point2d Marker::mapPoint(Point2d* pointPtr, Axis* xAxis, Axis* yAxis) -{ - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - Point2d result; - if (gops->inverted) { - result.x = HMap(yAxis, pointPtr->y); - result.y = VMap(xAxis, pointPtr->x); - } - else { - result.x = HMap(xAxis, pointPtr->x); - result.y = VMap(yAxis, pointPtr->y); - } - - return result; -} - -int Marker::boxesDontOverlap(Graph* graphPtr_, Region2d *extsPtr) -{ - return (((double)graphPtr_->right_ < extsPtr->left) || - ((double)graphPtr_->bottom_ < extsPtr->top) || - (extsPtr->right < (double)graphPtr_->left_) || - (extsPtr->bottom < (double)graphPtr_->top_)); -} - -int Marker::regionInPolygon(Region2d *regionPtr, Point2d *points, int nPoints, - int enclosed) -{ - if (enclosed) { - // All points of the polygon must be inside the rectangle. - for (Point2d *pp = points, *pend = pp + nPoints; pp < pend; pp++) { - if ((pp->x < regionPtr->left) || (pp->x > regionPtr->right) || - (pp->y < regionPtr->top) || (pp->y > regionPtr->bottom)) { - return 0; /* One point is exterior. */ - } - } - return 1; - } - else { - // If any segment of the polygon clips the bounding region, the - // polygon overlaps the rectangle. - points[nPoints] = points[0]; - for (Point2d *pp = points, *pend = pp + nPoints; pp < pend; pp++) { - Point2d p = *pp; - Point2d q = *(pp + 1); - if (lineRectClip(regionPtr, &p, &q)) - return 1; - } - - // Otherwise the polygon and rectangle are either disjoint or - // enclosed. Check if one corner of the rectangle is inside the polygon. - Point2d r; - r.x = regionPtr->left; - r.y = regionPtr->top; - - return pointInPolygon(&r, points, nPoints); - } -} - diff --git a/src/tkbltGrMarker.h b/src/tkbltGrMarker.h deleted file mode 100644 index 573357d..0000000 --- a/src/tkbltGrMarker.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrMarker_h__ -#define __BltGrMarker_h__ - -#include - -#include "tkbltChain.h" - -#include "tkbltGrMisc.h" -#include "tkbltGrPSOutput.h" - -namespace Blt { - class Graph; - class Postscript; - class Axis; - - typedef struct { - Point2d* points; - int num; - } Coords; - - typedef struct { - const char** tags; - Coords* worldPts; - const char* elemName; - Axis* xAxis; - Axis* yAxis; - int hide; - int drawUnder; - int xOffset; - int yOffset; - } MarkerOptions; - - class Marker { - protected: - Tk_OptionTable optionTable_; - void* ops_; - - public: - Graph* graphPtr_; - const char *name_; - Tcl_HashEntry* hashPtr_; - ChainLink* link; - unsigned int flags; - int clipped_; - - protected: - double HMap(Axis*, double); - double VMap(Axis*, double); - Point2d mapPoint(Point2d*, Axis*, Axis*); - int boxesDontOverlap(Graph*, Region2d*); - int regionInPolygon(Region2d *extsPtr, Point2d *points, - int nPoints, int enclosed); - - public: - Marker(Graph*, const char*, Tcl_HashEntry*); - virtual ~Marker(); - - virtual int configure() =0; - virtual void draw(Drawable) =0; - virtual void map() =0; - virtual int pointIn(Point2d*) =0; - virtual int regionIn(Region2d*, int) =0; - virtual void print(PSOutput*) =0; - - virtual ClassId classId() =0; - virtual const char* className() =0; - virtual const char* typeName() =0; - - Tk_OptionTable optionTable() {return optionTable_;} - void* ops() {return ops_;} - }; -}; - -#endif diff --git a/src/tkbltGrMarkerLine.C b/src/tkbltGrMarkerLine.C deleted file mode 100644 index 30ef70e..0000000 --- a/src/tkbltGrMarkerLine.C +++ /dev/null @@ -1,299 +0,0 @@ -/* - * 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 1993-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 -#include - -#include - -#include "tkbltGraph.h" -#include "tkbltGrMarkerLine.h" -#include "tkbltGrMarkerOption.h" -#include "tkbltGrMisc.h" -#include "tkbltGrDef.h" -#include "tkbltConfig.h" -#include "tkbltGrPSOutput.h" - -using namespace Blt; - -#define BOUND(x, lo, hi) (((x) > (hi)) ? (hi) : ((x) < (lo)) ? (lo) : (x)) - -static Tk_OptionSpec optionSpecs[] = { - {TK_OPTION_CUSTOM, "-bindtags", "bindTags", "BindTags", - "Line all", -1, Tk_Offset(LineMarkerOptions, tags), - TK_OPTION_NULL_OK, &listObjOption, 0}, - {TK_OPTION_CUSTOM, "-cap", "cap", "Cap", - "butt", -1, Tk_Offset(LineMarkerOptions, capStyle), - 0, &capStyleObjOption, 0}, - {TK_OPTION_CUSTOM, "-coords", "coords", "Coords", - NULL, -1, Tk_Offset(LineMarkerOptions, worldPts), - TK_OPTION_NULL_OK, &coordsObjOption, 0}, - {TK_OPTION_CUSTOM, "-dashes", "dashes", "Dashes", - NULL, -1, Tk_Offset(LineMarkerOptions, dashes), - TK_OPTION_NULL_OK, &dashesObjOption, 0}, - {TK_OPTION_PIXELS, "-dashoffset", "dashOffset", "DashOffset", - "0", -1, Tk_Offset(LineMarkerOptions, dashes.offset), 0, NULL, 0}, - {TK_OPTION_STRING, "-element", "element", "Element", - NULL, -1, Tk_Offset(LineMarkerOptions, elemName), - TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_COLOR, "-fill", "fill", "Fill", - NULL, -1, Tk_Offset(LineMarkerOptions, fillColor), - TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_CUSTOM, "-join", "join", "Join", - "miter", -1, Tk_Offset(LineMarkerOptions, joinStyle), - 0, &joinStyleObjOption, 0}, - {TK_OPTION_PIXELS, "-linewidth", "lineWidth", "LineWidth", - "1", -1, Tk_Offset(LineMarkerOptions, lineWidth), 0, NULL, 0}, - {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", - "no", -1, Tk_Offset(LineMarkerOptions, hide), 0, NULL, 0}, - {TK_OPTION_CUSTOM, "-mapx", "mapX", "MapX", - "x", -1, Tk_Offset(LineMarkerOptions, xAxis), 0, &xAxisObjOption, 0}, - {TK_OPTION_CUSTOM, "-mapy", "mapY", "MapY", - "y", -1, Tk_Offset(LineMarkerOptions, yAxis), 0, &yAxisObjOption, 0}, - {TK_OPTION_COLOR, "-outline", "outline", "Outline", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(LineMarkerOptions, outlineColor), - TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_BOOLEAN, "-under", "under", "Under", - "no", -1, Tk_Offset(LineMarkerOptions, drawUnder), 0, NULL, CACHE}, - {TK_OPTION_PIXELS, "-xoffset", "xOffset", "XOffset", - "0", -1, Tk_Offset(LineMarkerOptions, xOffset), 0, NULL, 0}, - {TK_OPTION_PIXELS, "-yoffset", "yOffset", "YOffset", - "0", -1, Tk_Offset(LineMarkerOptions, yOffset), 0, NULL, 0}, - {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} -}; - -LineMarker::LineMarker(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) - : Marker(graphPtr, name, hPtr) -{ - ops_ = (LineMarkerOptions*)calloc(1, sizeof(LineMarkerOptions)); - optionTable_ = Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); - - gc_ =NULL; - segments_ =NULL; - nSegments_ =0; -} - -LineMarker::~LineMarker() -{ - if (gc_) - graphPtr_->freePrivateGC(gc_); - if (segments_) - delete [] segments_; -} - -int LineMarker::configure() -{ - LineMarkerOptions* ops = (LineMarkerOptions*)ops_; - - unsigned long gcMask = (GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle); - XGCValues gcValues; - if (ops->outlineColor) { - gcMask |= GCForeground; - gcValues.foreground = ops->outlineColor->pixel; - } - if (ops->fillColor) { - gcMask |= GCBackground; - gcValues.background = ops->fillColor->pixel; - } - gcValues.cap_style = ops->capStyle; - gcValues.join_style = ops->joinStyle; - gcValues.line_width = ops->lineWidth; - gcValues.line_style = LineSolid; - if (LineIsDashed(ops->dashes)) { - gcValues.line_style = - (gcMask & GCBackground) ? LineDoubleDash : LineOnOffDash; - } - - GC newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); - if (gc_) - graphPtr_->freePrivateGC(gc_); - - if (LineIsDashed(ops->dashes)) - graphPtr_->setDashes(newGC, &ops->dashes); - gc_ = newGC; - - return TCL_OK; -} - -void LineMarker::draw(Drawable drawable) -{ - if (nSegments_ > 0) - graphPtr_->drawSegments(drawable, gc_, segments_, nSegments_); -} - -void LineMarker::map() -{ - LineMarkerOptions* ops = (LineMarkerOptions*)ops_; - - nSegments_ = 0; - if (segments_) - delete [] segments_; - - if (!ops->worldPts || (ops->worldPts->num < 2)) - return; - - Region2d extents; - graphPtr_->extents(&extents); - - // Allow twice the number of world coordinates. The line will represented - // as series of line segments, not one continous polyline. This is - // because clipping against the plot area may chop the line into several - // disconnected segments. - - Segment2d* segments = new Segment2d[ops->worldPts->num]; - Point2d* srcPtr = ops->worldPts->points; - Point2d p = mapPoint(srcPtr, ops->xAxis, ops->yAxis); - p.x += ops->xOffset; - p.y += ops->yOffset; - - Segment2d* segPtr = segments; - Point2d* pend; - for (srcPtr++, pend = ops->worldPts->points + ops->worldPts->num; - srcPtr < pend; srcPtr++) { - Point2d next = mapPoint(srcPtr, ops->xAxis, ops->yAxis); - next.x += ops->xOffset; - next.y += ops->yOffset; - Point2d q = next; - - if (lineRectClip(&extents, &p, &q)) { - segPtr->p = p; - segPtr->q = q; - segPtr++; - } - p = next; - } - nSegments_ = segPtr - segments; - segments_ = segments; - clipped_ = (nSegments_ == 0); -} - -int LineMarker::pointIn(Point2d *samplePtr) -{ - GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; - return pointInSegments(samplePtr, segments_, nSegments_, - (double)gops->search.halo); -} - -int LineMarker::pointInSegments(Point2d* samplePtr, Segment2d* segments, - int nSegments, double halo) -{ - double minDist = DBL_MAX; - for (Segment2d *sp = segments, *send = sp + nSegments; sp < send; sp++) { - Point2d t = getProjection((int)samplePtr->x, (int)samplePtr->y, - &sp->p, &sp->q); - double right; - double left; - if (sp->p.x > sp->q.x) { - right = sp->p.x; - left = sp->q.x; - } - else { - right = sp->q.x; - left = sp->p.x; - } - - double top; - double bottom; - if (sp->p.y > sp->q.y) { - bottom = sp->p.y; - top = sp->q.y; - } - else { - bottom = sp->q.y; - top = sp->p.y; - } - - Point2d p; - p.x = BOUND(t.x, left, right); - p.y = BOUND(t.y, top, bottom); - - double dist = hypot(p.x - samplePtr->x, p.y - samplePtr->y); - if (dist < minDist) - minDist = dist; - } - - return (minDist < halo); -} - -int LineMarker::regionIn(Region2d *extsPtr, int enclosed) -{ - LineMarkerOptions* ops = (LineMarkerOptions*)ops_; - - if (!ops->worldPts || ops->worldPts->num < 2) - return 0; - - if (enclosed) { - for (Point2d *pp = ops->worldPts->points, *pend = pp + ops->worldPts->num; - pp < pend; pp++) { - Point2d p = mapPoint(pp, ops->xAxis, ops->yAxis); - if ((p.x < extsPtr->left) && (p.x > extsPtr->right) && - (p.y < extsPtr->top) && (p.y > extsPtr->bottom)) { - return 0; - } - } - return 1; - } - else { - int count = 0; - for (Point2d *pp=ops->worldPts->points, *pend=pp+(ops->worldPts->num - 1); - pp < pend; pp++) { - Point2d p = mapPoint(pp, ops->xAxis, ops->yAxis); - Point2d q = mapPoint(pp + 1, ops->xAxis, ops->yAxis); - if (lineRectClip(extsPtr, &p, &q)) - count++; - } - return (count > 0); /* At least 1 segment passes through - * region. */ - } -} - -void LineMarker::print(PSOutput* psPtr) -{ - LineMarkerOptions* ops = (LineMarkerOptions*)ops_; - - if (nSegments_ > 0) { - psPtr->setLineAttributes(ops->outlineColor, ops->lineWidth, - &ops->dashes, ops->capStyle, ops->joinStyle); - if ((LineIsDashed(ops->dashes)) && (ops->fillColor)) { - psPtr->append("/DashesProc {\n gsave\n "); - psPtr->setBackground(ops->fillColor); - psPtr->append(" "); - psPtr->setDashes(NULL); - psPtr->append("stroke\n"); - psPtr->append("grestore\n"); - psPtr->append("} def\n"); - } - else - psPtr->append("/DashesProc {} def\n"); - - psPtr->printSegments(segments_, nSegments_); - } -} - - diff --git a/src/tkbltGrMarkerLine.h b/src/tkbltGrMarkerLine.h deleted file mode 100644 index 3191951..0000000 --- a/src/tkbltGrMarkerLine.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrMarkerLine_h__ -#define __BltGrMarkerLine_h__ - -#include "tkbltGrMarker.h" - -namespace Blt { - - typedef struct { - const char** tags; - Coords* worldPts; - const char* elemName; - Axis* xAxis; - Axis* yAxis; - int hide; - int drawUnder; - int xOffset; - int yOffset; - - int capStyle; - Dashes dashes; - XColor* fillColor; - int joinStyle; - int lineWidth; - XColor* outlineColor; - } LineMarkerOptions; - - class LineMarker : public Marker { - protected: - GC gc_; - Segment2d* segments_; - int nSegments_; - - protected: - int configure(); - void draw(Drawable); - void map(); - int pointIn(Point2d*); - int regionIn(Region2d*, int); - void print(PSOutput*); - int pointInSegments(Point2d *samplePtr, Segment2d *segments, - int nSegments, double halo); - - public: - LineMarker(Graph*, const char*, Tcl_HashEntry*); - virtual ~LineMarker(); - - ClassId classId() {return CID_MARKER_LINE;} - const char* className() {return "LineMarker";} - const char* typeName() {return "line";} - }; -}; - -#endif diff --git a/src/tkbltGrMarkerOp.C b/src/tkbltGrMarkerOp.C deleted file mode 100644 index 20933ab..0000000 --- a/src/tkbltGrMarkerOp.C +++ /dev/null @@ -1,458 +0,0 @@ -/* - * 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 1993-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 - -#include "tkbltGrBind.h" -#include "tkbltGraph.h" -#include "tkbltGrElem.h" -#include "tkbltGrMarkerOp.h" -#include "tkbltGrMarker.h" -#include "tkbltGrMarkerLine.h" -#include "tkbltGrMarkerPolygon.h" -#include "tkbltGrMarkerText.h" - -using namespace Blt; - -static int GetMarkerFromObj(Tcl_Interp* interp, Graph* graphPtr, - Tcl_Obj* objPtr, Marker** markerPtrPtr); - -#define FIND_ENCLOSED (1<<0) -#define FIND_OVERLAPPING (1<<1) - -static int MarkerObjConfigure( Graph* graphPtr,Marker* markerPtr, - 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*)markerPtr->ops(), - markerPtr->optionTable(), - objc, objv, graphPtr->tkwin_, &savedOptions, &mask) - != TCL_OK) - continue; - } - else { - errorResult = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(errorResult); - Tk_RestoreSavedOptions(&savedOptions); - } - - markerPtr->flags |= MAP_ITEM; - if (markerPtr->configure() != TCL_OK) - return TCL_ERROR; - - MarkerOptions* ops = (MarkerOptions*)markerPtr->ops(); - if (ops->drawUnder) - graphPtr->flags |= CACHE; - 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 CreateMarker(Graph* graphPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - int offset = 5; - const char* name =NULL; - ostringstream str; - if (objc == 4) { - offset = 4; - str << "marker" << graphPtr->nextMarkerId_++ << ends; - name = dupstr(str.str().c_str()); - } - else { - name = dupstr(Tcl_GetString(objv[4])); - if (name[0] == '-') { - delete [] name; - offset = 4; - str << "marker" << graphPtr->nextMarkerId_++ << ends; - name = dupstr(str.str().c_str()); - } - } - - int isNew; - Tcl_HashEntry* hPtr = - Tcl_CreateHashEntry(&graphPtr->markers_.table, name, &isNew); - if (!isNew) { - Tcl_AppendResult(graphPtr->interp_, "marker \"", name, - "\" already exists in \"", Tcl_GetString(objv[0]), - "\"", NULL); - return TCL_ERROR; - } - - const char* type = Tcl_GetString(objv[3]); - Marker* markerPtr; - if (!strcmp(type, "line")) - markerPtr = new LineMarker(graphPtr, name, hPtr); - else if (!strcmp(type, "polygon")) - markerPtr = new PolygonMarker(graphPtr, name, hPtr); - else if (!strcmp(type, "text")) - markerPtr = new TextMarker(graphPtr, name, hPtr); - else { - Tcl_AppendResult(interp, "unknown marker type ", type, NULL); - return TCL_ERROR; - } - - Tcl_SetHashValue(hPtr, markerPtr); - - if ((Tk_InitOptions(graphPtr->interp_, (char*)markerPtr->ops(), markerPtr->optionTable(), graphPtr->tkwin_) != TCL_OK) || (MarkerObjConfigure(graphPtr, markerPtr, interp, objc-offset, objv+offset) != TCL_OK)) { - delete markerPtr; - return TCL_ERROR; - } - - // Unlike elements, new markers are drawn on top of old markers - markerPtr->link = graphPtr->markers_.displayList->prepend(markerPtr); - - Tcl_SetStringObj(Tcl_GetObjResult(interp), name, -1); - - delete [] name; - return TCL_OK; -} - -static int CgetOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Marker* markerPtr; - if (GetMarkerFromObj(interp, graphPtr, objv[3], &markerPtr) != TCL_OK) - return TCL_ERROR; - - Tcl_Obj* objPtr = Tk_GetOptionValue(interp, - (char*)markerPtr->ops(), - markerPtr->optionTable(), - objv[4], 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<4) - return TCL_ERROR; - - Marker* markerPtr; - if (GetMarkerFromObj(interp, graphPtr, objv[3], &markerPtr) != TCL_OK) - return TCL_ERROR; - - if (objc <= 5) { - Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)markerPtr->ops(), - markerPtr->optionTable(), - (objc == 5) ? objv[4] : NULL, - graphPtr->tkwin_); - if (objPtr == NULL) - return TCL_ERROR; - else - Tcl_SetObjResult(interp, objPtr); - return TCL_OK; - } - else - return MarkerObjConfigure(graphPtr, markerPtr, interp, objc-4, objv+4); -} - -static int BindOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc == 3) { - Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - Tcl_HashSearch iter; - for (Tcl_HashEntry* hp = - Tcl_FirstHashEntry(&graphPtr->markers_.tagTable, &iter); - hp; hp = Tcl_NextHashEntry(&iter)) { - - const char* tag = - (const char*)Tcl_GetHashKey(&graphPtr->markers_.tagTable, hp); - Tcl_Obj* objPtr = Tcl_NewStringObj(tag, -1); - Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); - } - Tcl_SetObjResult(interp, listObjPtr); - return TCL_OK; - } - - return graphPtr->bindTable_->configure(graphPtr->markerTag(Tcl_GetString(objv[3])), objc - 4, objv + 4); -} - -static int CreateOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (CreateMarker(graphPtr, interp, objc, objv) != TCL_OK) - return TCL_ERROR; - // set in CreateMarker - // Tcl_SetObjResult(interp, objv[3]); - - graphPtr->flags |= CACHE; - graphPtr->eventuallyRedraw(); - - return TCL_OK; -} - -static int DeleteOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - for (int ii=3; iitkwin_), "\"", NULL); - return TCL_ERROR; - } - delete markerPtr; - } - - graphPtr->flags |= CACHE; - graphPtr->eventuallyRedraw(); - - return TCL_OK; -} - -static int ExistsOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Tcl_HashEntry* hPtr = - Tcl_FindHashEntry(&graphPtr->markers_.table, Tcl_GetString(objv[3])); - Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (hPtr != NULL)); - - return TCL_OK; -} - -static int FindOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - const char* string = Tcl_GetString(objv[3]); - int mode; - if (strcmp(string, "enclosed") == 0) - mode = FIND_ENCLOSED; - else if (strcmp(string, "overlapping") == 0) - mode = FIND_OVERLAPPING; - else { - Tcl_AppendResult(interp, "bad search type \"", string, - ": should be \"enclosed\", or \"overlapping\"", - NULL); - return TCL_ERROR; - } - - int left, right, top, bottom; - if ((Tcl_GetIntFromObj(interp, objv[4], &left) != TCL_OK) || - (Tcl_GetIntFromObj(interp, objv[5], &top) != TCL_OK) || - (Tcl_GetIntFromObj(interp, objv[6], &right) != TCL_OK) || - (Tcl_GetIntFromObj(interp, objv[7], &bottom) != TCL_OK)) { - return TCL_ERROR; - } - - Region2d extents; - if (left < right) { - extents.left = (double)left; - extents.right = (double)right; - } - else { - extents.left = (double)right; - extents.right = (double)left; - } - if (top < bottom) { - extents.top = (double)top; - extents.bottom = (double)bottom; - } - else { - extents.top = (double)bottom; - extents.bottom = (double)top; - } - - int enclosed = (mode == FIND_ENCLOSED); - for (ChainLink* link = Chain_FirstLink(graphPtr->markers_.displayList); - link; link = Chain_NextLink(link)) { - Marker* markerPtr = (Marker*)Chain_GetValue(link); - MarkerOptions* ops = (MarkerOptions*)markerPtr->ops(); - if (ops->hide) - continue; - - if (graphPtr->isElementHidden(markerPtr)) - continue; - - if (markerPtr->regionIn(&extents, enclosed)) { - Tcl_Obj* objPtr = Tcl_GetObjResult(interp); - Tcl_SetStringObj(objPtr, markerPtr->name_, -1); - return TCL_OK; - } - } - - Tcl_SetStringObj(Tcl_GetObjResult(interp), "", -1); - return TCL_OK; -} - -static int NamesOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - if (objc == 3) { - for (ChainLink* link=Chain_FirstLink(graphPtr->markers_.displayList); - link; link = Chain_NextLink(link)) { - Marker* markerPtr = (Marker*)Chain_GetValue(link); - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewStringObj(markerPtr->name_, -1)); - } - } - else { - for (ChainLink* link=Chain_FirstLink(graphPtr->markers_.displayList); - link; link = Chain_NextLink(link)) { - Marker* markerPtr = (Marker*)Chain_GetValue(link); - for (int ii = 3; iiname_, pattern)) { - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewStringObj(markerPtr->name_, -1)); - break; - } - } - } - } - - Tcl_SetObjResult(interp, listObjPtr); - return TCL_OK; -} - -static int RelinkOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Marker* markerPtr; - if (GetMarkerFromObj(interp, graphPtr, objv[3], &markerPtr) != TCL_OK) - return TCL_ERROR; - - Marker* placePtr =NULL; - if (objc == 5) - if (GetMarkerFromObj(interp, graphPtr, objv[4], &placePtr) != TCL_OK) - return TCL_ERROR; - - ChainLink* link = markerPtr->link; - graphPtr->markers_.displayList->unlinkLink(markerPtr->link); - - ChainLink* place = placePtr ? placePtr->link : NULL; - - const char* string = Tcl_GetString(objv[2]); - if (string[0] == 'l') - graphPtr->markers_.displayList->linkAfter(link, place); - else - graphPtr->markers_.displayList->linkBefore(link, place); - - graphPtr->flags |= CACHE; - graphPtr->eventuallyRedraw(); - - return TCL_OK; -} - -static int TypeOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Marker* markerPtr; - if (GetMarkerFromObj(interp, graphPtr, objv[3], &markerPtr) != TCL_OK) - return TCL_ERROR; - - Tcl_SetStringObj(Tcl_GetObjResult(interp), markerPtr->typeName(), -1); - return TCL_OK; -} - -const Ensemble Blt::markerEnsemble[] = { - {"bind", BindOp, 0}, - {"cget", CgetOp, 0}, - {"configure", ConfigureOp, 0}, - {"create", CreateOp, 0}, - {"delete", DeleteOp, 0}, - {"exists", ExistsOp, 0}, - {"find", FindOp, 0}, - {"lower", RelinkOp, 0}, - {"names", NamesOp, 0}, - {"raise", RelinkOp, 0}, - {"type", TypeOp, 0}, - { 0,0,0 } -}; - -// Support - -static int GetMarkerFromObj(Tcl_Interp* interp, Graph* graphPtr, - Tcl_Obj *objPtr, Marker** markerPtrPtr) -{ - const char* string = Tcl_GetString(objPtr); - Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&graphPtr->markers_.table, string); - if (hPtr) { - *markerPtrPtr = (Marker*)Tcl_GetHashValue(hPtr); - return TCL_OK; - } - if (interp) { - Tcl_AppendResult(interp, "can't find marker \"", string, - "\" in \"", Tk_PathName(graphPtr->tkwin_), NULL); - } - - return TCL_ERROR; -} - diff --git a/src/tkbltGrMarkerOp.h b/src/tkbltGrMarkerOp.h deleted file mode 100644 index 6f7c16f..0000000 --- a/src/tkbltGrMarkerOp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __Blt_GrMarkerOp_h__ -#define __Blt_GrMarkerOp_h__ - -#include "tkbltGraph.h" - -namespace Blt { - extern const Ensemble markerEnsemble[]; -}; - -#endif diff --git a/src/tkbltGrMarkerOption.C b/src/tkbltGrMarkerOption.C deleted file mode 100644 index b6eab57..0000000 --- a/src/tkbltGrMarkerOption.C +++ /dev/null @@ -1,210 +0,0 @@ -/* - * 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 1993-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 -#include -#include - -#include "tkbltGrMarker.h" -#include "tkbltGrMarkerOption.h" -#include "tkbltConfig.h" - -using namespace Blt; - -static Tcl_Obj* PrintCoordinate(double x); -static int GetCoordinate(Tcl_Interp* interp, Tcl_Obj *objPtr, double *valuePtr); - -static Tk_CustomOptionSetProc CoordsSetProc; -static Tk_CustomOptionGetProc CoordsGetProc; -static Tk_CustomOptionFreeProc CoordsFreeProc; -Tk_ObjCustomOption coordsObjOption = - { - "coords", CoordsSetProc, CoordsGetProc, RestoreProc, CoordsFreeProc, NULL - }; - -static int CoordsSetProc(ClientData clientData, Tcl_Interp* interp, - Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, - int offset, char* savePtr, int flags) -{ - Coords** coordsPtrPtr = (Coords**)(widgRec + offset); - *(double*)savePtr = *(double*)coordsPtrPtr; - - if (!coordsPtrPtr) - return TCL_OK; - - int objc; - Tcl_Obj** objv; - if (Tcl_ListObjGetElements(interp, *objPtr, &objc, &objv) != TCL_OK) - return TCL_ERROR; - - if (objc == 0) { - *coordsPtrPtr = NULL; - return TCL_OK; - } - - if (objc & 1) { - Tcl_AppendResult(interp, "odd number of marker coordinates specified",NULL); - return TCL_ERROR; - } - - Coords* coordsPtr = new Coords; - coordsPtr->num = objc/2; - coordsPtr->points = new Point2d[coordsPtr->num]; - - Point2d* pp = coordsPtr->points; - for (int ii=0; iix = x; - pp->y = y; - pp++; - } - - *coordsPtrPtr = coordsPtr; - return TCL_OK; -} - -static Tcl_Obj* CoordsGetProc(ClientData clientData, Tk_Window tkwin, - char *widgRec, int offset) -{ - Coords* coordsPtr = *(Coords**)(widgRec + offset); - - if (!coordsPtr) - return Tcl_NewListObj(0, NULL); - - int cnt = coordsPtr->num*2; - Tcl_Obj** ll = new Tcl_Obj*[cnt]; - - Point2d* pp = coordsPtr->points; - for (int ii=0; iix); - ll[ii++] = PrintCoordinate(pp->y); - } - - Tcl_Obj* listObjPtr = Tcl_NewListObj(cnt, ll); - delete [] ll; - return listObjPtr; -} - -static void CoordsFreeProc(ClientData clientData, Tk_Window tkwin, - char *ptr) -{ - Coords* coordsPtr = *(Coords**)ptr; - if (coordsPtr) { - if (coordsPtr->points) - delete [] coordsPtr->points; - delete coordsPtr; - } -} - -static Tk_CustomOptionSetProc CapStyleSetProc; -static Tk_CustomOptionGetProc CapStyleGetProc; -Tk_ObjCustomOption capStyleObjOption = - { - "capStyle", CapStyleSetProc, CapStyleGetProc, NULL, NULL, NULL - }; - -static int CapStyleSetProc(ClientData clientData, Tcl_Interp* interp, - Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, - int offset, char* save, int flags) -{ - int* ptr = (int*)(widgRec + offset); - - Tk_Uid uid = Tk_GetUid(Tcl_GetString(*objPtr)); - int cap; - if (Tk_GetCapStyle(interp, uid, &cap) != TCL_OK) - return TCL_ERROR; - *ptr = cap; - - return TCL_OK; -} - -static Tcl_Obj* CapStyleGetProc(ClientData clientData, Tk_Window tkwin, - char *widgRec, int offset) -{ - int* ptr = (int*)(widgRec + offset); - return Tcl_NewStringObj(Tk_NameOfCapStyle(*ptr), -1); -} - -static Tk_CustomOptionSetProc JoinStyleSetProc; -static Tk_CustomOptionGetProc JoinStyleGetProc; -Tk_ObjCustomOption joinStyleObjOption = - { - "joinStyle", JoinStyleSetProc, JoinStyleGetProc, NULL, NULL, NULL - }; - -static int JoinStyleSetProc(ClientData clientData, Tcl_Interp* interp, - Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, - int offset, char* save, int flags) -{ - int* ptr = (int*)(widgRec + offset); - - Tk_Uid uid = Tk_GetUid(Tcl_GetString(*objPtr)); - int join; - if (Tk_GetJoinStyle(interp, uid, &join) != TCL_OK) - return TCL_ERROR; - *ptr = join; - - return TCL_OK; -} - -static Tcl_Obj* JoinStyleGetProc(ClientData clientData, Tk_Window tkwin, - char *widgRec, int offset) -{ - int* ptr = (int*)(widgRec + offset); - return Tcl_NewStringObj(Tk_NameOfJoinStyle(*ptr), -1); -} - -static Tcl_Obj* PrintCoordinate(double x) -{ - if (x == DBL_MAX) - return Tcl_NewStringObj("+Inf", -1); - else if (x == -DBL_MAX) - return Tcl_NewStringObj("-Inf", -1); - else - return Tcl_NewDoubleObj(x); -} - -static int GetCoordinate(Tcl_Interp* interp, Tcl_Obj *objPtr, double *valuePtr) -{ - const char* expr = Tcl_GetString(objPtr); - char c = expr[0]; - if ((c == 'I') && (strcmp(expr, "Inf") == 0)) - *valuePtr = DBL_MAX; /* Elastic upper bound */ - else if ((c == '-') && (expr[1] == 'I') && (strcmp(expr, "-Inf") == 0)) - *valuePtr = -DBL_MAX; /* Elastic lower bound */ - else if ((c == '+') && (expr[1] == 'I') && (strcmp(expr, "+Inf") == 0)) - *valuePtr = DBL_MAX; /* Elastic upper bound */ - else if (Tcl_GetDoubleFromObj(interp, objPtr, valuePtr) != TCL_OK) - return TCL_ERROR; - - return TCL_OK; -} diff --git a/src/tkbltGrMarkerOption.h b/src/tkbltGrMarkerOption.h deleted file mode 100644 index 143810e..0000000 --- a/src/tkbltGrMarkerOption.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __Blt_GrMarkerOption_h__ -#define __Blt_GrMarkerOption_h__ - -extern Tk_ObjCustomOption coordsObjOption; -extern Tk_ObjCustomOption capStyleObjOption; -extern Tk_ObjCustomOption joinStyleObjOption; -extern Tk_ObjCustomOption xAxisObjOption; -extern Tk_ObjCustomOption yAxisObjOption; - -#endif diff --git a/src/tkbltGrMarkerPolygon.C b/src/tkbltGrMarkerPolygon.C deleted file mode 100644 index ed655aa..0000000 --- a/src/tkbltGrMarkerPolygon.C +++ /dev/null @@ -1,301 +0,0 @@ -/* - * 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 1993-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 - -#include "tkbltGraph.h" -#include "tkbltGrMarkerPolygon.h" -#include "tkbltGrMarkerOption.h" -#include "tkbltGrMisc.h" -#include "tkbltGrDef.h" -#include "tkbltConfig.h" -#include "tkbltGrPSOutput.h" - -using namespace Blt; - -static Tk_OptionSpec optionSpecs[] = { - {TK_OPTION_CUSTOM, "-bindtags", "bindTags", "BindTags", - "Polygon all", -1, Tk_Offset(PolygonMarkerOptions, tags), - TK_OPTION_NULL_OK, &listObjOption, 0}, - {TK_OPTION_CUSTOM, "-cap", "cap", "Cap", - "butt", -1, Tk_Offset(PolygonMarkerOptions, capStyle), - 0, &capStyleObjOption, 0}, - {TK_OPTION_CUSTOM, "-coords", "coords", "Coords", - NULL, -1, Tk_Offset(PolygonMarkerOptions, worldPts), - TK_OPTION_NULL_OK, &coordsObjOption, 0}, - {TK_OPTION_CUSTOM, "-dashes", "dashes", "Dashes", - NULL, -1, Tk_Offset(PolygonMarkerOptions, dashes), - TK_OPTION_NULL_OK, &dashesObjOption, 0}, - {TK_OPTION_STRING, "-element", "element", "Element", - NULL, -1, Tk_Offset(PolygonMarkerOptions, elemName), - TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_COLOR, "-fill", "fill", "Fill", - NULL, -1, Tk_Offset(PolygonMarkerOptions, fill), - TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_CUSTOM, "-join", "join", "Join", - "miter", -1, Tk_Offset(PolygonMarkerOptions, joinStyle), - 0, &joinStyleObjOption, 0}, - {TK_OPTION_PIXELS, "-linewidth", "lineWidth", "LineWidth", - "1", -1, Tk_Offset(PolygonMarkerOptions, lineWidth), 0, NULL, 0}, - {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", - "no", -1, Tk_Offset(PolygonMarkerOptions, hide), 0, NULL, 0}, - {TK_OPTION_CUSTOM, "-mapx", "mapX", "MapX", - "x", -1, Tk_Offset(PolygonMarkerOptions, xAxis), 0, &xAxisObjOption, 0}, - {TK_OPTION_CUSTOM, "-mapy", "mapY", "MapY", - "y", -1, Tk_Offset(PolygonMarkerOptions, yAxis), 0, &yAxisObjOption, 0}, - {TK_OPTION_COLOR, "-outline", "outline", "Outline", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(PolygonMarkerOptions, outline), - TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_BOOLEAN, "-under", "under", "Under", - "no", -1, Tk_Offset(PolygonMarkerOptions, drawUnder), 0, NULL, CACHE}, - {TK_OPTION_PIXELS, "-xoffset", "xOffset", "XOffset", - "0", -1, Tk_Offset(PolygonMarkerOptions, xOffset), 0, NULL, 0}, - {TK_OPTION_PIXELS, "-yoffset", "yOffset", "YOffset", - "0", -1, Tk_Offset(PolygonMarkerOptions, yOffset), 0, NULL, 0}, - {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} -}; - -PolygonMarker::PolygonMarker(Graph* graphPtr, const char* name, - Tcl_HashEntry* hPtr) - : Marker(graphPtr, name, hPtr) -{ - ops_ = (PolygonMarkerOptions*)calloc(1, sizeof(PolygonMarkerOptions)); - optionTable_ = Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); - - screenPts_ =NULL; - outlineGC_ =NULL; - fillGC_ =NULL; - fillPts_ =NULL; - nFillPts_ =0; - outlinePts_ =NULL; - nOutlinePts_ =0; -} - -PolygonMarker::~PolygonMarker() -{ - if (fillGC_) - Tk_FreeGC(graphPtr_->display_, fillGC_); - if (outlineGC_) - graphPtr_->freePrivateGC(outlineGC_); - if (fillPts_) - delete [] fillPts_; - if (outlinePts_) - delete [] outlinePts_; - if (screenPts_) - delete [] screenPts_; -} - -int PolygonMarker::configure() -{ - PolygonMarkerOptions* ops = (PolygonMarkerOptions*)ops_; - - // outlineGC - unsigned long gcMask = (GCLineWidth | GCLineStyle); - XGCValues gcValues; - if (ops->outline) { - gcMask |= GCForeground; - gcValues.foreground = ops->outline->pixel; - } - gcMask |= (GCCapStyle | GCJoinStyle); - gcValues.cap_style = ops->capStyle; - gcValues.join_style = ops->joinStyle; - gcValues.line_style = LineSolid; - gcValues.dash_offset = 0; - gcValues.line_width = ops->lineWidth; - if (LineIsDashed(ops->dashes)) - gcValues.line_style = LineOnOffDash; - - GC newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); - if (LineIsDashed(ops->dashes)) - graphPtr_->setDashes(newGC, &ops->dashes); - if (outlineGC_) - graphPtr_->freePrivateGC(outlineGC_); - outlineGC_ = newGC; - - // fillGC - gcMask = 0; - if (ops->fill) { - gcMask |= GCForeground; - gcValues.foreground = ops->fill->pixel; - } - newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); - if (fillGC_) - Tk_FreeGC(graphPtr_->display_, fillGC_); - fillGC_ = newGC; - - return TCL_OK; -} - -void PolygonMarker::draw(Drawable drawable) -{ - PolygonMarkerOptions* ops = (PolygonMarkerOptions*)ops_; - - // fill region - if ((nFillPts_ > 0) && (ops->fill)) { - XPoint* points = new XPoint[nFillPts_]; - if (!points) - return; - - XPoint* dp = points; - for (Point2d *sp = fillPts_, *send = sp + nFillPts_; sp < send; sp++) { - dp->x = (short int)sp->x; - dp->y = (short int)sp->y; - dp++; - } - - XFillPolygon(graphPtr_->display_, drawable, fillGC_, points, - nFillPts_, Complex, CoordModeOrigin); - delete [] points; - } - - // outline - if ((nOutlinePts_ > 0) && (ops->lineWidth > 0) && (ops->outline)) - graphPtr_->drawSegments(drawable, outlineGC_, outlinePts_, nOutlinePts_); -} - -void PolygonMarker::map() -{ - PolygonMarkerOptions* ops = (PolygonMarkerOptions*)ops_; - - if (outlinePts_) { - delete [] outlinePts_; - outlinePts_ = NULL; - nOutlinePts_ = 0; - } - - if (fillPts_) { - delete [] fillPts_; - fillPts_ = NULL; - nFillPts_ = 0; - } - - if (screenPts_) { - delete [] screenPts_; - screenPts_ = NULL; - } - - if (!ops->worldPts || ops->worldPts->num < 3) - return; - - // Allocate and fill a temporary array to hold the screen coordinates of - // the polygon. - - int nScreenPts = ops->worldPts->num + 1; - Point2d* screenPts = new Point2d[nScreenPts + 1]; - { - Point2d* dp = screenPts; - for (Point2d *sp = ops->worldPts->points, *send = sp + ops->worldPts->num; - sp < send; sp++) { - *dp = mapPoint(sp, ops->xAxis, ops->yAxis); - dp->x += ops->xOffset; - dp->y += ops->yOffset; - dp++; - } - *dp = screenPts[0]; - } - Region2d extents; - graphPtr_->extents(&extents); - - clipped_ = 1; - if (ops->fill) { - Point2d* lfillPts = new Point2d[nScreenPts * 3]; - int n = polyRectClip(&extents, screenPts, ops->worldPts->num,lfillPts); - if (n < 3) - delete [] lfillPts; - else { - nFillPts_ = n; - fillPts_ = lfillPts; - clipped_ = 0; - } - } - if ((ops->outline) && (ops->lineWidth > 0)) { - // Generate line segments representing the polygon outline. The - // resulting outline may or may not be closed from viewport clipping. - Segment2d* outlinePts = new Segment2d[nScreenPts]; - if (!outlinePts) - return; - - // Note that this assumes that the point array contains an extra point - // that closes the polygon. - Segment2d* segPtr = outlinePts; - for (Point2d *sp=screenPts, *send=sp+(nScreenPts - 1); sp < send; sp++) { - segPtr->p = sp[0]; - segPtr->q = sp[1]; - if (lineRectClip(&extents, &segPtr->p, &segPtr->q)) { - segPtr++; - } - } - nOutlinePts_ = segPtr - outlinePts; - outlinePts_ = outlinePts; - if (nOutlinePts_ > 0) - clipped_ = 0; - } - - screenPts_ = screenPts; -} - -int PolygonMarker::pointIn(Point2d *samplePtr) -{ - PolygonMarkerOptions* ops = (PolygonMarkerOptions*)ops_; - - if (ops->worldPts && (ops->worldPts->num >= 3) && screenPts_) - return pointInPolygon(samplePtr, screenPts_, ops->worldPts->num + 1); - - return 0; -} - -int PolygonMarker::regionIn(Region2d *extsPtr, int enclosed) -{ - PolygonMarkerOptions* ops = (PolygonMarkerOptions*)ops_; - - if (ops->worldPts && (ops->worldPts->num >= 3) && screenPts_) - return regionInPolygon(extsPtr, screenPts_, ops->worldPts->num, enclosed); - - return 0; -} - -void PolygonMarker::print(PSOutput* psPtr) -{ - PolygonMarkerOptions* ops = (PolygonMarkerOptions*)ops_; - - if (ops->fill) { - psPtr->printPolyline(fillPts_, nFillPts_); - psPtr->setForeground(ops->fill); - psPtr->append("fill\n"); - } - - if ((ops->lineWidth > 0) && (ops->outline)) { - psPtr->setLineAttributes(ops->outline, ops->lineWidth, &ops->dashes, - ops->capStyle, ops->joinStyle); - psPtr->append("/DashesProc {} def\n"); - - psPtr->printSegments(outlinePts_, nOutlinePts_); - } -} - diff --git a/src/tkbltGrMarkerPolygon.h b/src/tkbltGrMarkerPolygon.h deleted file mode 100644 index 8eb2216..0000000 --- a/src/tkbltGrMarkerPolygon.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrMarkerPolygon_h__ -#define __BltGrMarkerPolygon_h__ - -#include "tkbltGrMarker.h" - -namespace Blt { - - typedef struct { - const char** tags; - Coords* worldPts; - const char* elemName; - Axis* xAxis; - Axis* yAxis; - int hide; - int drawUnder; - int xOffset; - int yOffset; - - int capStyle; - Dashes dashes; - XColor* fill; - int joinStyle; - int lineWidth; - XColor* outline; - } PolygonMarkerOptions; - - class PolygonMarker : public Marker { - protected: - Point2d *screenPts_; - GC outlineGC_; - GC fillGC_; - Point2d *fillPts_; - int nFillPts_; - Segment2d *outlinePts_; - int nOutlinePts_; - - protected: - int configure(); - void draw(Drawable); - void map(); - int pointIn(Point2d*); - int regionIn(Region2d*, int); - void print(PSOutput*); - - public: - PolygonMarker(Graph*, const char*, Tcl_HashEntry*); - virtual ~PolygonMarker(); - - ClassId classId() {return CID_MARKER_POLYGON;} - const char* className() {return "PolygonMarker";} - const char* typeName() {return "polygon";} - }; -}; - -#endif diff --git a/src/tkbltGrMarkerText.C b/src/tkbltGrMarkerText.C deleted file mode 100644 index f6307fb..0000000 --- a/src/tkbltGrMarkerText.C +++ /dev/null @@ -1,276 +0,0 @@ -/* - * 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 1993-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 - -#include - -#include "tkbltGraph.h" -#include "tkbltGrMarkerText.h" -#include "tkbltGrMarkerOption.h" -#include "tkbltGrMisc.h" -#include "tkbltGrDef.h" -#include "tkbltConfig.h" -#include "tkbltGrPSOutput.h" - -using namespace Blt; - -static Tk_OptionSpec optionSpecs[] = { - {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor", - "center", -1, Tk_Offset(TextMarkerOptions, anchor), 0, NULL, 0}, - {TK_OPTION_COLOR, "-background", "background", "Background", - NULL, -1, Tk_Offset(TextMarkerOptions, fillColor), - TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_SYNONYM, "-bg", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-background", 0}, - {TK_OPTION_CUSTOM, "-bindtags", "bindTags", "BindTags", - "Text all", -1, Tk_Offset(TextMarkerOptions, tags), - TK_OPTION_NULL_OK, &listObjOption, 0}, - {TK_OPTION_CUSTOM, "-coords", "coords", "Coords", - NULL, -1, Tk_Offset(TextMarkerOptions, worldPts), - TK_OPTION_NULL_OK, &coordsObjOption, 0}, - {TK_OPTION_STRING, "-element", "element", "Element", - NULL, -1, Tk_Offset(TextMarkerOptions, elemName), - TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_SYNONYM, "-fg", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-foreground", 0}, - {TK_OPTION_SYNONYM, "-fill", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-background", 0}, - {TK_OPTION_FONT, "-font", "font", "Font", - STD_FONT_NORMAL, -1, Tk_Offset(TextMarkerOptions, style.font), 0, NULL, 0}, - {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(TextMarkerOptions, style.color), - 0, NULL, 0}, - {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify", - "left", -1, Tk_Offset(TextMarkerOptions, style.justify), 0, NULL, 0}, - {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", - "no", -1, Tk_Offset(TextMarkerOptions, hide), 0, NULL, 0}, - {TK_OPTION_CUSTOM, "-mapx", "mapX", "MapX", - "x", -1, Tk_Offset(TextMarkerOptions, xAxis), 0, &xAxisObjOption, 0}, - {TK_OPTION_CUSTOM, "-mapy", "mapY", "MapY", - "y", -1, Tk_Offset(TextMarkerOptions, yAxis), 0, &yAxisObjOption, 0}, - {TK_OPTION_SYNONYM, "-outline", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-foreground", 0}, - {TK_OPTION_DOUBLE, "-rotate", "rotate", "Rotate", - "0", -1, Tk_Offset(TextMarkerOptions, style.angle), 0, NULL, 0}, - {TK_OPTION_STRING, "-text", "text", "Text", - NULL, -1, Tk_Offset(TextMarkerOptions, string), TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_BOOLEAN, "-under", "under", "Under", - "no", -1, Tk_Offset(TextMarkerOptions, drawUnder), 0, NULL, CACHE}, - {TK_OPTION_PIXELS, "-xoffset", "xOffset", "XOffset", - "0", -1, Tk_Offset(TextMarkerOptions, xOffset), 0, NULL, 0}, - {TK_OPTION_PIXELS, "-yoffset", "yOffset", "YOffset", - "0", -1, Tk_Offset(TextMarkerOptions, yOffset), 0, NULL, 0}, - {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} -}; - -TextMarker::TextMarker(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) - : Marker(graphPtr, name, hPtr) -{ - ops_ = (TextMarkerOptions*)calloc(1, sizeof(TextMarkerOptions)); - TextMarkerOptions* ops = (TextMarkerOptions*)ops_; - - ops->style.anchor =TK_ANCHOR_NW; - ops->style.color =NULL; - ops->style.font =NULL; - ops->style.angle =0; - ops->style.justify =TK_JUSTIFY_LEFT; - - anchorPt_.x =0; - anchorPt_.y =0; - width_ =0; - height_ =0; - fillGC_ =NULL; - - optionTable_ = Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); -} - -TextMarker::~TextMarker() -{ -} - -int TextMarker::configure() -{ - TextMarkerOptions* ops = (TextMarkerOptions*)ops_; - - ops->style.angle = (float)fmod(ops->style.angle, 360.0); - if (ops->style.angle < 0.0f) - ops->style.angle += 360.0f; - - GC newGC = NULL; - XGCValues gcValues; - unsigned long gcMask; - if (ops->fillColor) { - gcMask = GCForeground; - gcValues.foreground = ops->fillColor->pixel; - newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); - } - if (fillGC_) - Tk_FreeGC(graphPtr_->display_, fillGC_); - fillGC_ = newGC; - - return TCL_OK; -} - -void TextMarker::draw(Drawable drawable) -{ - TextMarkerOptions* ops = (TextMarkerOptions*)ops_; - - if (!ops->string) - return; - - if (fillGC_) { - XPoint points[4]; - for (int ii=0; ii<4; ii++) { - points[ii].x = (short int)(outline_[ii].x + anchorPt_.x); - points[ii].y = (short int)(outline_[ii].y + anchorPt_.y); - } - XFillPolygon(graphPtr_->display_, drawable, fillGC_, points, 4, - Convex, CoordModeOrigin); - } - - TextStyle ts(graphPtr_, &ops->style); - ts.drawText(drawable, ops->string, anchorPt_.x, anchorPt_.y); -} - -void TextMarker::map() -{ - TextMarkerOptions* ops = (TextMarkerOptions*)ops_; - - if (!ops->string) - return; - - if (!ops->worldPts || (ops->worldPts->num < 1)) - return; - - width_ =0; - height_ =0; - - int w, h; - TextStyle ts(graphPtr_, &ops->style); - ts.getExtents(ops->string, &w, &h); - - double rw; - double rh; - graphPtr_->getBoundingBox(w, h, ops->style.angle, &rw, &rh, outline_); - width_ = rw; - height_ = rh; - for (int ii=0; ii<4; ii++) { - outline_[ii].x += rw * 0.5; - outline_[ii].y += rh * 0.5; - } - outline_[4].x = outline_[0].x; - outline_[4].y = outline_[0].y; - - Point2d anchorPtr = mapPoint(ops->worldPts->points, ops->xAxis, ops->yAxis); - anchorPtr = graphPtr_->anchorPoint(anchorPtr.x, anchorPtr.y, - width_, height_, ops->anchor); - anchorPtr.x += ops->xOffset; - anchorPtr.y += ops->yOffset; - - Region2d extents; - extents.left = anchorPtr.x; - extents.top = anchorPtr.y; - extents.right = anchorPtr.x + width_ - 1; - extents.bottom = anchorPtr.y + height_ - 1; - clipped_ = boxesDontOverlap(graphPtr_, &extents); - - anchorPt_ = anchorPtr; -} - -int TextMarker::pointIn(Point2d *samplePtr) -{ - TextMarkerOptions* ops = (TextMarkerOptions*)ops_; - - if (!ops->string) - return 0; - - if (ops->style.angle != 0.0f) { - Point2d points[5]; - - // Figure out the bounding polygon (isolateral) for the text and see - // if the point is inside of it. - for (int ii=0; ii<5; ii++) { - points[ii].x = outline_[ii].x + anchorPt_.x; - points[ii].y = outline_[ii].y + anchorPt_.y; - } - return pointInPolygon(samplePtr, points, 5); - } - - return ((samplePtr->x >= anchorPt_.x) && - (samplePtr->x < (anchorPt_.x + width_)) && - (samplePtr->y >= anchorPt_.y) && - (samplePtr->y < (anchorPt_.y + height_))); -} - -int TextMarker::regionIn(Region2d *extsPtr, int enclosed) -{ - TextMarkerOptions* ops = (TextMarkerOptions*)ops_; - - if (ops->style.angle != 0.0f) { - Point2d points[5]; - for (int ii=0; ii<4; ii++) { - points[ii].x = outline_[ii].x + anchorPt_.x; - points[ii].y = outline_[ii].y + anchorPt_.y; - } - return regionInPolygon(extsPtr, points, 4, enclosed); - } - - if (enclosed) - return ((anchorPt_.x >= extsPtr->left) && - (anchorPt_.y >= extsPtr->top) && - ((anchorPt_.x + width_) <= extsPtr->right) && - ((anchorPt_.y + height_) <= extsPtr->bottom)); - - return !((anchorPt_.x >= extsPtr->right) || - (anchorPt_.y >= extsPtr->bottom) || - ((anchorPt_.x + width_) <= extsPtr->left) || - ((anchorPt_.y + height_) <= extsPtr->top)); -} - -void TextMarker::print(PSOutput* psPtr) -{ - TextMarkerOptions* ops = (TextMarkerOptions*)ops_; - - if (!ops->string) - return; - - if (fillGC_) { - Point2d points[4]; - for (int ii=0; ii<4; ii++) { - points[ii].x = outline_[ii].x + anchorPt_.x; - points[ii].y = outline_[ii].y + anchorPt_.y; - } - psPtr->setBackground(ops->fillColor); - psPtr->fillPolygon(points, 4); - } - - TextStyle ts(graphPtr_, &ops->style); - ts.printText(psPtr, ops->string, anchorPt_.x, anchorPt_.y); -} diff --git a/src/tkbltGrMarkerText.h b/src/tkbltGrMarkerText.h deleted file mode 100644 index 99dc814..0000000 --- a/src/tkbltGrMarkerText.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrMarkerText_h__ -#define __BltGrMarkerText_h__ - -#include - -#include "tkbltGrMarker.h" - -namespace Blt { - - typedef struct { - const char** tags; - Coords* worldPts; - const char* elemName; - Axis* xAxis; - Axis* yAxis; - int hide; - int drawUnder; - int xOffset; - int yOffset; - - Tk_Anchor anchor; - XColor* fillColor; - TextStyleOptions style; - const char* string; - } TextMarkerOptions; - - class TextMarker : public Marker { - protected: - Point2d anchorPt_; - int width_; - int height_; - GC fillGC_; - Point2d outline_[5]; - - protected: - int configure(); - void draw(Drawable); - void map(); - int pointIn(Point2d*); - int regionIn(Region2d*, int); - void print(PSOutput*); - - public: - TextMarker(Graph*, const char*, Tcl_HashEntry*); - virtual ~TextMarker(); - - ClassId classId() {return CID_MARKER_TEXT;} - const char* className() {return "TextMarker";} - const char* typeName() {return "text";} - }; -}; - -#endif diff --git a/src/tkbltGrMisc.C b/src/tkbltGrMisc.C deleted file mode 100644 index d951494..0000000 --- a/src/tkbltGrMisc.C +++ /dev/null @@ -1,335 +0,0 @@ -/* - * 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 1993-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 -#include -#include -#include - -#include - -#include -#include - -#include "tkbltGraph.h" -#include "tkbltGrMisc.h" - -using namespace Blt; - -char* Blt::dupstr(const char* str) -{ - char* copy =NULL; - if (str) { - copy=new char[strlen(str)+1]; - strcpy(copy,str); - } - - return copy; -} - -int Blt::pointInPolygon(Point2d *s, Point2d *points, int nPoints) -{ - int count = 0; - for (Point2d *p=points, *q=p+1, *qend=p + nPoints; q < qend; p++, q++) { - if (((p->y <= s->y) && (s->y < q->y)) || - ((q->y <= s->y) && (s->y < p->y))) { - double b; - - b = (q->x - p->x) * (s->y - p->y) / (q->y - p->y) + p->x; - if (s->x < b) { - count++; /* Count the number of intersections. */ - } - } - } - return (count & 0x01); -} - -static int ClipTest (double ds, double dr, double *t1, double *t2) -{ - double t; - - if (ds < 0.0) { - t = dr / ds; - if (t > *t2) { - return 0; - } - if (t > *t1) { - *t1 = t; - } - } else if (ds > 0.0) { - t = dr / ds; - if (t < *t1) { - return 0; - } - 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 0; - } - } - return 1; -} - -/* - *--------------------------------------------------------------------------- - * 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, Y-D., and B. Barsky, A new concept and method for - * Line Clipping, ACM, TOG,3(1), 1984, pp.1-22. - *--------------------------------------------------------------------------- - */ -int Blt::lineRectClip(Region2d* regionPtr, Point2d *p, Point2d *q) -{ - double t1, t2; - double dx, dy; - - t1 = 0.0, t2 = 1.0; - dx = q->x - p->x; - if ((ClipTest (-dx, p->x - regionPtr->left, &t1, &t2)) && - (ClipTest (dx, regionPtr->right - p->x, &t1, &t2))) { - dy = q->y - p->y; - if ((ClipTest (-dy, p->y - regionPtr->top, &t1, &t2)) && - (ClipTest (dy, regionPtr->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 1; - } - } - return 0; -} - -/* - *--------------------------------------------------------------------------- - * 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 Y. D. and Brian A. Barsky, "Analysis and Algorithm for - * Polygon Clipping", Communications of ACM, Vol. 26, - * p.868-877, 1983 - *--------------------------------------------------------------------------- - */ -#define AddVertex(vx, vy) r->x=(vx), r->y=(vy), r++, count++ -#define LastVertex(vx, vy) r->x=(vx), r->y=(vy), count++ - -int Blt::polyRectClip(Region2d *regionPtr, Point2d *points, int nPoints, - Point2d *clipPts) -{ - Point2d* r = clipPts; - // Counts # of vertices in output polygon. - int count = 0; - - points[nPoints] = points[0]; - for (Point2d *p=points, *q=p+1, *pend=p+nPoints; px - p->x; /* X-direction */ - dy = q->y - p->y; /* Y-direction */ - - if (fabs(dx) < FLT_EPSILON) - dx = (p->x > regionPtr->left) ? -FLT_EPSILON : FLT_EPSILON ; - - if (fabs(dy) < FLT_EPSILON) - dy = (p->y > regionPtr->top) ? -FLT_EPSILON : FLT_EPSILON ; - - if (dx > 0.0) { /* Left */ - xin = regionPtr->left; - xout = regionPtr->right + 1.0; - } - else { /* Right */ - xin = regionPtr->right + 1.0; - xout = regionPtr->left; - } - if (dy > 0.0) { /* Top */ - yin = regionPtr->top; - yout = regionPtr->bottom + 1.0; - } - else { /* Bottom */ - yin = regionPtr->bottom + 1.0; - yout = regionPtr->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 = (xout - p->x) / dx; - double touty = (yout - p->y) / dy; - double 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) { - LastVertex(clipPts[0].x, clipPts[0].y); - } - return count; -} - -/* - *--------------------------------------------------------------------------- - * 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. - *--------------------------------------------------------------------------- - */ -Point2d Blt::getProjection(int x, int y, Point2d *p, Point2d *q) -{ - double dx = p->x - q->x; - double dy = p->y - q->y; - - /* Test for horizontal and vertical lines */ - Point2d t; - 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 { - /* Compute the slope and intercept of PQ. */ - double m1 = (dy / dx); - double 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 PQ. */ - double midX = (p->x + q->x) * 0.5; - double midY = (p->y + q->y) * 0.5; - - /* Rotate the line 90 degrees */ - double ax = midX - (0.5 * dy); - double ay = midY - (0.5 * -dx); - double bx = midX + (0.5 * dy); - double by = midY + (0.5 * -dx); - - double m2 = (ay - by) / (ax - bx); - double 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; -} - -Graph* Blt::getGraphFromWindowData(Tk_Window tkwin) -{ - while (tkwin) { - TkWindow* winPtr = (TkWindow*)tkwin; - if (winPtr->instanceData != NULL) { - Graph* graphPtr = (Graph*)winPtr->instanceData; - if (graphPtr) - return graphPtr; - } - tkwin = Tk_Parent(tkwin); - } - return NULL; -} - diff --git a/src/tkbltGrMisc.h b/src/tkbltGrMisc.h deleted file mode 100644 index b7c521f..0000000 --- a/src/tkbltGrMisc.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrMisc_h__ -#define __BltGrMisc_h__ - -#include -#include -#include -using namespace std; - -#include - -#ifndef MIN -# define MIN(x,y) ((x)<(y)?(x):(y)) -#endif - -#ifndef MAX -# define MAX(x,y) ((x)>(y)?(x):(y)) -#endif - -#define GRAPH_DELETED (1<<1) -#define REDRAW_PENDING (1<<2) -#define FOCUS (1<<3) - -#define MAP_ITEM (1<<4) - -#define RESET (1<<5) -#define LAYOUT (1<<6) -#define MAP_MARKERS (1<<7) -#define CACHE (1<<8) - -#define MARGIN_NONE -1 -#define MARGIN_BOTTOM 0 /* x */ -#define MARGIN_LEFT 1 /* y */ -#define MARGIN_TOP 2 /* x2 */ -#define MARGIN_RIGHT 3 /* y2 */ - -#define LineIsDashed(d) ((d).values[0] != 0) - -namespace Blt { - class Graph; - - typedef struct { - double x; - double y; - } Point2d; - - typedef struct { - Point2d p; - Point2d q; - } Segment2d; - - typedef struct { - double left; - double right; - double top; - double bottom; - } Region2d; - - typedef enum { - CID_NONE, CID_AXIS_X, CID_AXIS_Y, CID_ELEM_BAR, CID_ELEM_LINE, - CID_MARKER_BITMAP, CID_MARKER_IMAGE, CID_MARKER_LINE, CID_MARKER_POLYGON, - CID_MARKER_TEXT - } ClassId; - - typedef struct { - unsigned char values[12]; - int offset; - } Dashes; - - extern char* dupstr(const char*); - extern Graph* getGraphFromWindowData(Tk_Window tkwin); - - extern int pointInPolygon(Point2d *samplePtr, Point2d *screenPts, - int nScreenPts); - extern int polyRectClip(Region2d *extsPtr, Point2d *inputPts, - int nInputPts, Point2d *outputPts); - extern int lineRectClip(Region2d *regionPtr, Point2d *p, Point2d *q); - extern Point2d getProjection (int x, int y, Point2d *p, Point2d *q); -}; - -#endif diff --git a/src/tkbltGrPSOutput.C b/src/tkbltGrPSOutput.C deleted file mode 100644 index 8f02cba..0000000 --- a/src/tkbltGrPSOutput.C +++ /dev/null @@ -1,931 +0,0 @@ -/* - * 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 -#include -#include - -#include - -#include "tk.h" - -// copied from tk3d.h - -typedef struct TkBorder { - Screen *screen; /* Screen on which the border will be used. */ - Visual *visual; /* Visual for all windows and pixmaps using - * the border. */ - int depth; /* Number of bits per pixel of drawables where - * the border will be used. */ - Colormap colormap; /* Colormap out of which pixels are - * allocated. */ - int resourceRefCount; /* Number of active uses of this color (each - * active use corresponds to a call to - * Tk_Alloc3DBorderFromObj or Tk_Get3DBorder). - * If this count is 0, then this structure is - * no longer valid and it isn't present in - * borderTable: it is being kept around only - * because there are objects referring to it. - * The structure is freed when objRefCount and - * resourceRefCount are both 0. */ - int objRefCount; /* The number of Tcl objects that reference - * this structure. */ - XColor *bgColorPtr; /* Background color (intensity between - * lightColorPtr and darkColorPtr). */ - XColor *darkColorPtr; /* Color for darker areas (must free when - * deleting structure). NULL means shadows - * haven't been allocated yet.*/ - XColor *lightColorPtr; /* Color used for lighter areas of border - * (must free this when deleting structure). - * NULL means shadows haven't been allocated - * yet. */ - Pixmap shadow; /* Stipple pattern to use for drawing shadows - * areas. Used for displays with <= 64 colors - * or where colormap has filled up. */ - GC bgGC; /* Used (if necessary) to draw areas in the - * background color. */ - GC darkGC; /* Used to draw darker parts of the border. - * None means the shadow colors haven't been - * allocated yet.*/ - GC lightGC; /* Used to draw lighter parts of the border. - * None means the shadow colors haven't been - * allocated yet. */ - Tcl_HashEntry *hashPtr; /* Entry in borderTable (needed in order to - * delete structure). */ - struct TkBorder *nextPtr; /* Points to the next TkBorder structure with - * the same color name. Borders with the same - * name but different screens or colormaps are - * chained together off a single entry in - * borderTable. */ -} TkBorder; - -#include "tkbltGraph.h" -#include "tkbltGrPostscript.h" -#include "tkbltGrPSOutput.h" - -using namespace Blt; - -PSOutput::PSOutput(Graph* graphPtr) -{ - graphPtr_ = graphPtr; - - Tcl_DStringInit(&dString_); -} - -PSOutput::~PSOutput() -{ - Tcl_DStringFree(&dString_); -} - -void PSOutput::printPolyline(Point2d* screenPts, int nScreenPts) -{ - Point2d* pp = screenPts; - append("newpath\n"); - format(" %g %g moveto\n", pp->x, pp->y); - - Point2d* pend; - for (pp++, pend = screenPts + nScreenPts; pp < pend; pp++) - format(" %g %g lineto\n", pp->x, pp->y); -} - -void PSOutput::printMaxPolyline(Point2d* points, int nPoints) -{ - if (nPoints <= 0) - return; - - for (int nLeft = nPoints; nLeft > 0; nLeft -= 1500) { - int length = MIN(1500, nLeft); - printPolyline(points, length); - append("DashesProc stroke\n"); - points += length; - } -} - -void PSOutput::printSegments(Segment2d* segments, int nSegments) -{ - append("newpath\n"); - - for (Segment2d *sp = segments, *send = sp + nSegments; sp < send; sp++) { - format(" %g %g moveto %g %g lineto\n", sp->p.x, sp->p.y, sp->q.x, sp->q.y); - append("DashesProc stroke\n"); - } -} - -void PSOutput::computeBBox(int width, int height) -{ - Postscript* setupPtr = graphPtr_->postscript_; - PostscriptOptions* pops = (PostscriptOptions*)setupPtr->ops_; - - // scale from points to pica - float pica = 25.4 / 72 * - WidthOfScreen(Tk_Screen(graphPtr_->tkwin_)) / - WidthMMOfScreen(Tk_Screen(graphPtr_->tkwin_)); - - int hBorder = 2*pops->xPad/pica; - int vBorder = 2*pops->yPad/pica; - int hSize = !pops->landscape ? width : height; - int vSize = !pops->landscape ? height : width; - - // If the paper size wasn't specified, set it to the graph size plus the - // paper border. - int paperWidth = pops->reqPaperWidth > 0 ? pops->reqPaperWidth/pica : - hSize + hBorder; - int paperHeight = pops->reqPaperHeight > 0 ? pops->reqPaperHeight/pica : - vSize + vBorder; - - // Scale the plot size if it's bigger than the paper - float hScale = (hSize+hBorder) > paperWidth ? 1.0 : - (float)(paperWidth - hBorder) / hSize; - float vScale = (vSize + vBorder) > paperHeight ? 1.0 : - (float)(paperHeight - vBorder) / vSize; - - float scale = MIN(hScale, vScale); - if (scale != 1.0) { - hSize = hSize*scale + 0.5; - vSize = vSize*scale + 0.5; - } - - int x = (paperWidth > hSize) && pops->center ? - (paperWidth - hSize) / 2 : pops->xPad/pica; - int y = (paperHeight > vSize) && pops->center ? - (paperHeight - vSize) / 2 : pops->yPad/pica; - - setupPtr->left = x; - setupPtr->bottom = y; - setupPtr->right = x + hSize - 1; - setupPtr->top = y + vSize - 1; - setupPtr->scale = scale; - setupPtr->paperHeight = paperHeight; - setupPtr->paperWidth = paperWidth; -} - -const char* PSOutput::getValue(int* lengthPtr) -{ - *lengthPtr = strlen(Tcl_DStringValue(&dString_)); - return Tcl_DStringValue(&dString_); -} - -void PSOutput::append(const char* string) -{ - Tcl_DStringAppend(&dString_, string, -1); -} - -void PSOutput::format(const char* fmt, ...) -{ - va_list argList; - - va_start(argList, fmt); - vsnprintf(scratchArr_, POSTSCRIPT_BUFSIZ, fmt, argList); - va_end(argList); - Tcl_DStringAppend(&dString_, scratchArr_, -1); -} - -void PSOutput::setLineWidth(int lineWidth) -{ - if (lineWidth < 1) - lineWidth = 1; - format("%d setlinewidth\n", lineWidth); -} - -void PSOutput::printRectangle(double x, double y, int width, int height) -{ - append("newpath\n"); - format(" %g %g moveto\n", x, y); - format(" %d %d rlineto\n", width, 0); - format(" %d %d rlineto\n", 0, height); - format(" %d %d rlineto\n", -width, 0); - append("closepath\n"); - append("stroke\n"); -} - -void PSOutput::fillRectangle(double x, double y, int width, int height) -{ - append("newpath\n"); - format(" %g %g moveto\n", x, y); - format(" %d %d rlineto\n", width, 0); - format(" %d %d rlineto\n", 0, height); - format(" %d %d rlineto\n", -width, 0); - append("closepath\n"); - append("fill\n"); -} - -void PSOutput::fillRectangles(XRectangle* rectangles, int nRectangles) -{ - for (XRectangle *rp = rectangles, *rend = rp + nRectangles; rp < rend; rp++) - fillRectangle((double)rp->x, (double)rp->y, (int)rp->width,(int)rp->height); -} - -void PSOutput::setBackground(XColor* colorPtr) -{ - PostscriptOptions* pops = (PostscriptOptions*)graphPtr_->postscript_->ops_; - printXColor(colorPtr); - append(" setrgbcolor\n"); - if (pops->greyscale) - append(" currentgray setgray\n"); -} - -void PSOutput::setForeground(XColor* colorPtr) -{ - PostscriptOptions* pops = (PostscriptOptions*)graphPtr_->postscript_->ops_; - printXColor(colorPtr); - append(" setrgbcolor\n"); - if (pops->greyscale) - append(" currentgray setgray\n"); -} - -void PSOutput::setBackground(Tk_3DBorder border) -{ - TkBorder* borderPtr = (TkBorder*)border; - setBackground(borderPtr->bgColorPtr); -} - -void PSOutput::setFont(Tk_Font font) -{ - Tcl_DString psdstr; - Tcl_DStringInit(&psdstr); - int psSize = Tk_PostscriptFontName(font, &psdstr); - format("%d /%s SetFont\n", psSize, Tcl_DStringValue(&psdstr)); - Tcl_DStringFree(&psdstr); -} - -void PSOutput::setLineAttributes(XColor* colorPtr,int lineWidth, - Dashes* dashesPtr, int capStyle, - int joinStyle) -{ - setJoinStyle(joinStyle); - setCapStyle(capStyle); - setForeground(colorPtr); - setLineWidth(lineWidth); - setDashes(dashesPtr); - append("/DashesProc {} def\n"); -} - -void PSOutput::fill3DRectangle(Tk_3DBorder border, double x, double y, - int width, int height, int borderWidth, - int relief) -{ - TkBorder* borderPtr = (TkBorder*)border; - - setBackground(borderPtr->bgColorPtr); - fillRectangle(x, y, width, height); - print3DRectangle(border, x, y, width, height, borderWidth, relief); -} - -void PSOutput::setClearBackground() -{ - append("1 1 1 setrgbcolor\n"); -} - -void PSOutput::setDashes(Dashes* dashesPtr) -{ - - append("[ "); - if (dashesPtr) { - for (unsigned char* vp = dashesPtr->values; *vp != 0; vp++) - format(" %d", *vp); - } - append("] 0 setdash\n"); -} - -void PSOutput::fillPolygon(Point2d *screenPts, int nScreenPts) -{ - printPolygon(screenPts, nScreenPts); - append("fill\n"); -} - -void PSOutput::setJoinStyle(int joinStyle) -{ - // miter = 0, round = 1, bevel = 2 - format("%d setlinejoin\n", joinStyle); -} - -void PSOutput::setCapStyle(int capStyle) -{ - // X11:not last = 0, butt = 1, round = 2, projecting = 3 - // PS: butt = 0, round = 1, projecting = 2 - if (capStyle > 0) - capStyle--; - - format("%d setlinecap\n", capStyle); -} - -void PSOutput::printPolygon(Point2d *screenPts, int nScreenPts) -{ - Point2d* pp = screenPts; - append("newpath\n"); - format(" %g %g moveto\n", pp->x, pp->y); - - Point2d* pend; - for (pp++, pend = screenPts + nScreenPts; pp < pend; pp++) - format(" %g %g lineto\n", pp->x, pp->y); - - format(" %g %g lineto\n", screenPts[0].x, screenPts[0].y); - append("closepath\n"); -} - -void PSOutput::print3DRectangle(Tk_3DBorder border, double x, double y, - int width, int height, int borderWidth, - int relief) -{ - int twiceWidth = (borderWidth * 2); - if ((width < twiceWidth) || (height < twiceWidth)) - return; - - TkBorder* borderPtr = (TkBorder*)border; - - // Handle grooves and ridges with recursive calls - if ((relief == TK_RELIEF_GROOVE) || (relief == TK_RELIEF_RIDGE)) { - int halfWidth = borderWidth / 2; - int insideOffset = borderWidth - halfWidth; - print3DRectangle(border, (double)x, (double)y, width, height, halfWidth, - (relief == TK_RELIEF_GROOVE) ? - TK_RELIEF_SUNKEN : TK_RELIEF_RAISED); - print3DRectangle(border, (double)(x + insideOffset), - (double)(y + insideOffset), width - insideOffset * 2, - height - insideOffset * 2, halfWidth, - (relief == TK_RELIEF_GROOVE) ? - TK_RELIEF_RAISED : TK_RELIEF_SUNKEN); - return; - } - - XColor* lightPtr = borderPtr->lightColorPtr; - XColor* darkPtr = borderPtr->darkColorPtr; - XColor light; - if (!lightPtr) { - light.red = 0x00; - light.blue = 0x00; - light.green = 0x00; - lightPtr = &light; - } - XColor dark; - if (!darkPtr) { - dark.red = 0x00; - dark.blue = 0x00; - dark.green = 0x00; - darkPtr = &dark; - } - - XColor* topPtr, *bottomPtr; - if (relief == TK_RELIEF_RAISED) { - topPtr = lightPtr; - bottomPtr = darkPtr; - } - else if (relief == TK_RELIEF_SUNKEN) { - topPtr = darkPtr; - bottomPtr = lightPtr; - } - else if (relief == TK_RELIEF_SOLID) { - topPtr = lightPtr; - bottomPtr = lightPtr; - } - else { - topPtr = borderPtr->bgColorPtr; - bottomPtr = borderPtr->bgColorPtr; - } - - setBackground(bottomPtr); - fillRectangle(x, y + height - borderWidth, width, borderWidth); - fillRectangle(x + width - borderWidth, y, borderWidth, height); - - Point2d points[7]; - points[0].x = points[1].x = points[6].x = x; - points[0].y = points[6].y = y + height; - points[1].y = points[2].y = y; - points[2].x = x + width; - points[3].x = x + width - borderWidth; - points[3].y = points[4].y = y + borderWidth; - points[4].x = points[5].x = x + borderWidth; - points[5].y = y + height - borderWidth; - if (relief != TK_RELIEF_FLAT) - setBackground(topPtr); - - fillPolygon(points, 7); -} - -void PSOutput::printXColor(XColor* colorPtr) -{ - format("%g %g %g", - ((double)(colorPtr->red >> 8) / 255.0), - ((double)(colorPtr->green >> 8) / 255.0), - ((double)(colorPtr->blue >> 8) / 255.0)); -} - -int PSOutput::preamble(const char* fileName) -{ - Postscript* setupPtr = graphPtr_->postscript_; - PostscriptOptions* ops = (PostscriptOptions*)setupPtr->ops_; - - if (!fileName) - fileName = Tk_PathName(graphPtr_->tkwin_); - - // Comments - append("%!PS-Adobe-3.0 EPSF-3.0\n"); - - // The "BoundingBox" comment is required for EPS files. The box - // coordinates are integers, so we need round away from the center of the - // box. - format("%%%%BoundingBox: %d %d %d %d\n", - setupPtr->left, setupPtr->paperHeight - setupPtr->top, - setupPtr->right, setupPtr->paperHeight - setupPtr->bottom); - - append("%%Pages: 0\n"); - - format("%%%%Creator: (%s %s %s)\n", PACKAGE_NAME, PACKAGE_VERSION, - Tk_Class(graphPtr_->tkwin_)); - - time_t ticks = time((time_t *) NULL); - char date[200]; - strcpy(date, ctime(&ticks)); - char* newline = date + strlen(date) - 1; - if (*newline == '\n') - *newline = '\0'; - - format("%%%%CreationDate: (%s)\n", date); - format("%%%%Title: (%s)\n", fileName); - append("%%DocumentData: Clean7Bit\n"); - if (ops->landscape) - append("%%Orientation: Landscape\n"); - else - append("%%Orientation: Portrait\n"); - - append("%%DocumentNeededResources: font Helvetica Courier\n"); - addComments(ops->comments); - append("%%EndComments\n\n"); - - // Prolog - prolog(); - - // Setup - append("%%BeginSetup\n"); - append("gsave\n"); - append("1 setlinewidth\n"); - append("1 setlinejoin\n"); - append("0 setlinecap\n"); - append("[] 0 setdash\n"); - append("0 0 0 setrgbcolor\n"); - - if (ops->footer) { - const char* who = getenv("LOGNAME"); - if (!who) - who = "???"; - - append("8 /Helvetica SetFont\n"); - append("10 30 moveto\n"); - format("(Date: %s) show\n", date); - append("10 20 moveto\n"); - format("(File: %s) show\n", fileName); - append("10 10 moveto\n"); - format("(Created by: %s@%s) show\n", who, Tcl_GetHostName()); - append("0 0 moveto\n"); - } - - // Set the conversion from postscript to X11 coordinates. Scale pica to - // pixels and flip the y-axis (the origin is the upperleft corner). - // Papersize is in pixels. Translate the new origin *after* changing the scale - append("% Transform coordinate system to use X11 coordinates\n"); - append("% 1. Flip y-axis over by reversing the scale,\n"); - append("% 2. Translate the origin to the other side of the page,\n"); - append("% making the origin the upper left corner\n"); - append("1 -1 scale\n"); - format("0 %d translate\n", -setupPtr->paperHeight); - - // Set Origin - format("%% Set origin\n%d %d translate\n\n", setupPtr->left,setupPtr->bottom); - if (ops->landscape) - format("%% Landscape orientation\n0 %g translate\n-90 rotate\n", - ((double)graphPtr_->width_ * setupPtr->scale)); - - append("\n%%EndSetup\n\n"); - - return TCL_OK; -} - -void PSOutput::addComments(const char** comments) -{ - if (!comments) - return; - - for (const char** pp = comments; *pp; pp+=2) { - if (*(pp+1) == NULL) - break; - format("%% %s: %s\n", *pp, *(pp+1)); - } -} - -unsigned char PSOutput::reverseBits(unsigned char byte) -{ - byte = ((byte >> 1) & 0x55) | ((byte << 1) & 0xaa); - byte = ((byte >> 2) & 0x33) | ((byte << 2) & 0xcc); - byte = ((byte >> 4) & 0x0f) | ((byte << 4) & 0xf0); - return byte; -} - -void PSOutput::byteToHex(unsigned char byte, char* string) -{ - static char hexDigits[] = "0123456789ABCDEF"; - - string[0] = hexDigits[byte >> 4]; - string[1] = hexDigits[byte & 0x0F]; -} - -void PSOutput::prolog() -{ - append( -"%%BeginProlog\n" -"%\n" -"% PostScript prolog file of the BLT graph widget.\n" -"%\n" -"% Copyright 1989-1992 Regents of the University of California.\n" -"% Permission to use, copy, modify, and distribute this\n" -"% software and its documentation for any purpose and without\n" -"% fee is hereby granted, provided that the above copyright\n" -"% notice appear in all copies. The University of California\n" -"% makes no representations about the suitability of this\n" -"% software for any purpose. It is provided 'as is' without\n" -"% express or implied warranty.\n" -"%\n" -"% Copyright 1991-1997 Bell Labs Innovations for Lucent Technologies.\n" -"%\n" -"% Permission to use, copy, modify, and distribute this software and its\n" -"% documentation for any purpose and without fee is hereby granted, provided\n" -"% that the above copyright notice appear in all copies and that both that the\n" -"% copyright notice and warranty disclaimer appear in supporting documentation,\n" -"% and that the names of Lucent Technologies any of their entities not be used\n" -"% in advertising or publicity pertaining to distribution of the software\n" -"% without specific, written prior permission.\n" -"%\n" -"% Lucent Technologies disclaims all warranties with regard to this software,\n" -"% including all implied warranties of merchantability and fitness. In no event\n" -"% shall Lucent Technologies be liable for any special, indirect or\n" -"% consequential damages or any damages whatsoever resulting from loss of use,\n" -"% data or profits, whether in an action of contract, negligence or other\n" -"% tortuous action, arising out of or in connection with the use or performance\n" -"% of this software.\n" -"%\n" -"\n" -"200 dict begin\n" -"\n" -"/BaseRatio 1.3467736870885982 def % Ratio triangle base / symbol size\n" -"/DrawSymbolProc 0 def % Routine to draw symbol outline/fill\n" -"/DashesProc 0 def % Dashes routine (line segments)\n" -"\n" -"% Define the array ISOLatin1Encoding (which specifies how characters are \n" -"% encoded for ISO-8859-1 fonts), if it isn't already present (Postscript \n" -"% level 2 is supposed to define it, but level 1 doesn't). \n" -"\n" -"systemdict /ISOLatin1Encoding known not { \n" -" /ISOLatin1Encoding [ \n" -" /space /space /space /space /space /space /space /space \n" -" /space /space /space /space /space /space /space /space \n" -" /space /space /space /space /space /space /space /space \n" -" /space /space /space /space /space /space /space /space \n" -" /space /exclam /quotedbl /numbersign /dollar /percent /ampersand \n" -" /quoteright \n" -" /parenleft /parenright /asterisk /plus /comma /minus /period /slash \n" -" /zero /one /two /three /four /five /six /seven \n" -" /eight /nine /colon /semicolon /less /equal /greater /question \n" -" /at /A /B /C /D /E /F /G \n" -" /H /I /J /K /L /M /N /O \n" -" /P /Q /R /S /T /U /V /W \n" -" /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore \n" -" /quoteleft /a /b /c /d /e /f /g \n" -" /h /i /j /k /l /m /n /o \n" -" /p /q /r /s /t /u /v /w \n" -" /x /y /z /braceleft /bar /braceright /asciitilde /space \n" -" /space /space /space /space /space /space /space /space \n" -" /space /space /space /space /space /space /space /space \n" -" /dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent \n" -" /dieresis /space /ring /cedilla /space /hungarumlaut /ogonek /caron \n" -" /space /exclamdown /cent /sterling /currency /yen /brokenbar /section \n" -" /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen \n" -" /registered /macron \n" -" /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph \n" -" /periodcentered \n" -" /cedillar /onesuperior /ordmasculine /guillemotright /onequarter \n" -" /onehalf /threequarters /questiondown \n" -" /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla \n" -" /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex \n" -" /Idieresis \n" -" /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply \n" -" /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn \n" -" /germandbls \n" -" /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla \n" -" /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex \n" -" /idieresis \n" -" /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide \n" -" /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn \n" -" /ydieresis \n" -" ] def \n" -"} if \n" -"\n" -"% font ISOEncode font \n" -"% This procedure changes the encoding of a font from the default \n" -"% Postscript encoding to ISOLatin1. It is typically invoked just \n" -"% before invoking 'setfont'. The body of this procedure comes from \n" -"% Section 5.6.1 of the Postscript book. \n" -"\n" -"/ISOEncode { \n" -" dup length dict\n" -" begin \n" -" {1 index /FID ne {def} {pop pop} ifelse} forall \n" -" /Encoding ISOLatin1Encoding def \n" -" currentdict \n" -" end \n" -"\n" -" % I'm not sure why it's necessary to use 'definefont' on this new \n" -" % font, but it seems to be important; just use the name 'Temporary' \n" -" % for the font. \n" -"\n" -" /Temporary exch definefont \n" -"} bind def \n" -"\n" -"/Stroke {\n" -" gsave\n" -" stroke\n" -" grestore\n" -"} def\n" -"\n" -"/Fill {\n" -" gsave\n" -" fill\n" -" grestore\n" -"} def\n" -"\n" -"/SetFont { \n" -" % Stack: pointSize fontName\n" -" findfont exch scalefont ISOEncode setfont\n" -"} def\n" -"\n" -"/Box {\n" -" % Stack: x y width height\n" -" newpath\n" -" exch 4 2 roll moveto\n" -" dup 0 rlineto\n" -" exch 0 exch rlineto\n" -" neg 0 rlineto\n" -" closepath\n" -"} def\n" -"\n" -"/LS { % Stack: x1 y1 x2 y2\n" -" newpath \n" -" 4 2 roll moveto \n" -" lineto \n" -" closepath\n" -" stroke\n" -"} def\n" -"\n" -"/baselineSampler ( TXygqPZ) def\n" -"% Put an extra-tall character in; done this way to avoid encoding trouble\n" -"baselineSampler 0 196 put\n" -"\n" -"/cstringshow {\n" -" {\n" -" dup type /stringtype eq\n" -" { show } { glyphshow }\n" -" ifelse\n" -" } forall\n" -"} bind def\n" -"\n" -"/cstringwidth {\n" -" 0 exch 0 exch\n" -" {\n" -" dup type /stringtype eq\n" -" { stringwidth } {\n" -" currentfont /Encoding get exch 1 exch put (\001)\n" -" stringwidth\n" -" }\n" -" ifelse\n" -" exch 3 1 roll add 3 1 roll add exch\n" -" } forall\n" -"} bind def\n" -"\n" -"/DrawText {\n" -" gsave\n" -" /justify exch def\n" -" /yoffset exch def\n" -" /xoffset exch def\n" -" /strings exch def\n" -" % Compute the baseline offset and the actual font height.\n" -" 0 0 moveto baselineSampler false charpath\n" -" pathbbox dup /baseline exch def\n" -" exch pop exch sub /height exch def pop\n" -" newpath\n" -" % overall width\n" -" /ww 0 def\n" -" strings {\n" -" cstringwidth pop\n" -" dup ww gt {/ww exch def} {pop} ifelse\n" -" newpath\n" -" } forall\n" -" % overall height\n" -" /hh 0 def\n" -" strings length height mul /hh exch def\n" -" newpath\n" -" % Translate to x,y\n" -" translate\n" -" % Translate to offset\n" -" ww xoffset mul hh yoffset mul translate\n" -" % rotate\n" -" ww 2 div hh 2 div translate\n" -" neg rotate\n" -" ww -2 div hh -2 div translate\n" -" % Translate to justify and baseline\n" -" justify ww mul baseline translate\n" -" % For each line, justify and display\n" -" strings {\n" -" dup cstringwidth pop\n" -" justify neg mul 0 moveto\n" -" gsave\n" -" 1 -1 scale\n" -" cstringshow\n" -" grestore\n" -" 0 height translate\n" -" } forall\n" -" grestore\n" -"} bind def \n" -"\n" -"% Symbols:\n" -"\n" -"% Skinny-cross\n" -"/Sc {\n" -" % Stack: x y symbolSize\n" -" gsave\n" -" 3 -2 roll translate 45 rotate\n" -" 0 0 3 -1 roll Sp\n" -" grestore\n" -"} def\n" -"\n" -"% Skinny-plus\n" -"/Sp {\n" -" % Stack: x y symbolSize\n" -" gsave\n" -" 3 -2 roll translate\n" -" 2 div\n" -" dup 2 copy\n" -" newpath \n" -" neg 0 \n" -" moveto 0 \n" -" lineto\n" -" DrawSymbolProc\n" -" newpath \n" -" neg 0 \n" -" exch moveto 0 \n" -" exch lineto\n" -" DrawSymbolProc\n" -" grestore\n" -"} def\n" -"\n" -"% Cross\n" -"/Cr {\n" -" % Stack: x y symbolSize\n" -" gsave\n" -" 3 -2 roll translate 45 rotate\n" -" 0 0 3 -1 roll Pl\n" -" grestore\n" -"} def\n" -"\n" -"% Plus\n" -"/Pl {\n" -" % Stack: x y symbolSize\n" -" gsave\n" -" 3 -2 roll translate\n" -" dup 2 div\n" -" exch 6 div\n" -"\n" -" %\n" -" % 2 3 The plus/cross symbol is a\n" -" % closed polygon of 12 points.\n" -" % 0 1 4 5 The diagram to the left\n" -" % x,y represents the positions of\n" -" % 11 10 7 6 the points which are computed\n" -" % below.\n" -" % 9 8\n" -" %\n" -"\n" -" newpath\n" -" 2 copy exch neg exch neg moveto \n" -" dup neg dup lineto\n" -" 2 copy neg exch neg lineto\n" -" 2 copy exch neg lineto\n" -" dup dup neg lineto \n" -" 2 copy neg lineto 2 copy lineto\n" -" dup dup lineto \n" -" 2 copy exch lineto \n" -" 2 copy neg exch lineto\n" -" dup dup neg exch lineto \n" -" exch neg exch lineto\n" -" closepath\n" -" DrawSymbolProc\n" -" grestore\n" -"} def\n" -"\n" -"% Circle\n" -"/Ci {\n" -" % Stack: x y symbolSize\n" -" gsave\n" -" 3 copy pop moveto \n" -" newpath\n" -" 2 div 0 360 arc\n" -" closepath \n" -" DrawSymbolProc\n" -" grestore\n" -"} def\n" -"\n" -"% Square\n" -"/Sq {\n" -" % Stack: x y symbolSize\n" -" gsave\n" -" dup dup 2 div dup\n" -" 6 -1 roll exch sub exch\n" -" 5 -1 roll exch sub 4 -2 roll Box\n" -" DrawSymbolProc\n" -" grestore\n" -"} def\n" -"\n" -"% Line\n" -"/Li {\n" -" % Stack: x y symbolSize\n" -" gsave\n" -" 3 1 roll exch 3 -1 roll 2 div 3 copy\n" -" newpath\n" -" sub exch moveto \n" -" add exch lineto\n" -" closepath\n" -" stroke\n" -" grestore\n" -"} def\n" -"\n" -"% Diamond\n" -"/Di {\n" -" % Stack: x y symbolSize\n" -" gsave\n" -" 3 1 roll translate 45 rotate 0 0 3 -1 roll Sq\n" -" grestore\n" -"} def\n" -" \n" -"% Triangle\n" -"/Tr {\n" -" % Stack: x y symbolSize\n" -" gsave\n" -" 3 -2 roll translate\n" -" BaseRatio mul 0.5 mul % Calculate 1/2 base\n" -" dup 0 exch 30 cos mul % h1 = height above center point\n" -" neg % b2 0 -h1\n" -" newpath \n" -" moveto % point 1; b2\n" -" dup 30 sin 30 cos div mul % h2 = height below center point\n" -" 2 copy lineto % point 2; b2 h2\n" -" exch neg exch lineto % \n" -" closepath\n" -" DrawSymbolProc\n" -" grestore\n" -"} def\n" -"\n" -"% Arrow\n" -"/Ar {\n" -" % Stack: x y symbolSize\n" -" gsave\n" -" 3 -2 roll translate\n" -" BaseRatio mul 0.5 mul % Calculate 1/2 base\n" -" dup 0 exch 30 cos mul % h1 = height above center point\n" -" % b2 0 h1\n" -" newpath moveto % point 1; b2\n" -" dup 30 sin 30 cos div mul % h2 = height below center point\n" -" neg % -h2 b2\n" -" 2 copy lineto % point 2; b2 h2\n" -" exch neg exch lineto % \n" -" closepath\n" -" DrawSymbolProc\n" -" grestore\n" -"} def\n" -"\n" -"%%EndProlog\n" -); -} diff --git a/src/tkbltGrPSOutput.h b/src/tkbltGrPSOutput.h deleted file mode 100644 index c54e771..0000000 --- a/src/tkbltGrPSOutput.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __Blt_GrPSOutput_h__ -#define __Blt_GrPSOutput_h__ - -#include - -#define POSTSCRIPT_BUFSIZ ((BUFSIZ*2)-1) - -namespace Blt { - class Graph; - class Postscript; - - class PSOutput { - protected: - Graph* graphPtr_; - Tcl_DString dString_; - char scratchArr_[POSTSCRIPT_BUFSIZ+1]; - - protected: - void addComments(const char**); - void printXColor(XColor*); - unsigned char reverseBits(unsigned char); - void byteToHex(unsigned char, char*); - void setJoinStyle(int); - void setCapStyle(int); - void prolog(); - - public: - PSOutput(Graph*); - virtual ~PSOutput(); - - void printPolyline(Point2d*, int); - void printMaxPolyline(Point2d*, int); - void printSegments(Segment2d*, int); - void printRectangle(double, double, int, int); - void printPolygon(Point2d*, int); - void print3DRectangle(Tk_3DBorder, double, double, int, int, int, int); - - void fillRectangle(double, double, int, int); - void fillRectangles(XRectangle*, int); - void fill3DRectangle(Tk_3DBorder, double, double, int, int, int, int); - void fillPolygon(Point2d*, int); - - void setFont(Tk_Font); - void setLineWidth(int); - void setBackground(XColor*); - void setForeground(XColor*); - void setBackground(Tk_3DBorder); - void setLineAttributes(XColor*,int, Dashes*, int, int); - void setClearBackground(); - void setDashes(Dashes*); - - int preamble(const char*); - void computeBBox(int, int); - const char* getValue(int*); - void append(const char*); - void format(const char*, ...); - void varAppend(const char*, ...); - }; -}; - -#endif diff --git a/src/tkbltGrPen.C b/src/tkbltGrPen.C deleted file mode 100644 index 42851d8..0000000 --- a/src/tkbltGrPen.C +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 1993-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 - -#include "tkbltGrPen.h" -#include "tkbltGraph.h" - -using namespace Blt; - -Pen::Pen(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) -{ - optionTable_ = NULL; - ops_ = NULL; - graphPtr_ = graphPtr; - name_ = dupstr(name); - hashPtr_ = hPtr; - refCount_ =0; - flags =0; - manageOptions_ =0; -} - -Pen::~Pen() -{ - if (name_) - delete [] name_; - - if (hashPtr_) - Tcl_DeleteHashEntry(hashPtr_); - - // PenOptions* ops = (PenOptions*)ops_; - - Tk_FreeConfigOptions((char*)ops_, optionTable_, graphPtr_->tkwin_); - - if (manageOptions_) - free(ops_); -} diff --git a/src/tkbltGrPen.h b/src/tkbltGrPen.h deleted file mode 100644 index 003e8dd..0000000 --- a/src/tkbltGrPen.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrPen_h__ -#define __BltGrPen_h__ - -#include - -#include "tkbltGrText.h" - -namespace Blt { - class Graph; - - typedef struct { - int errorBarShow; - int errorBarLineWidth; - int errorBarCapWidth; - XColor* errorBarColor; - int valueShow; - const char* valueFormat; - TextStyleOptions valueStyle; - } PenOptions; - - class Pen { - protected: - Tk_OptionTable optionTable_; - void* ops_; - - public: - Graph* graphPtr_; - const char *name_; - Tcl_HashEntry *hashPtr_; - int refCount_; - unsigned int flags; - int manageOptions_; - - public: - Pen(); - Pen(Graph*, const char*, Tcl_HashEntry*); - virtual ~Pen(); - - virtual ClassId classId() =0; - virtual const char* className() =0; - virtual const char* typeName() =0; - - Tk_OptionTable optionTable() {return optionTable_;} - void* ops() {return ops_;} - - virtual int configure() =0; - }; -}; - -#endif diff --git a/src/tkbltGrPenBar.C b/src/tkbltGrPenBar.C deleted file mode 100644 index 8f6e59a..0000000 --- a/src/tkbltGrPenBar.C +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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 1993-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 - -#include "tkbltGrPenBar.h" -#include "tkbltGraph.h" -#include "tkbltGrDef.h" -#include "tkbltConfig.h" - -using namespace Blt; - -static Tk_OptionSpec barPenOptionSpecs[] = { - {TK_OPTION_SYNONYM, "-background", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-color", 0}, - {TK_OPTION_SYNONYM, "-bd", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-borderwidth", 0}, - {TK_OPTION_SYNONYM, "-bg", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-color", 0}, - {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", - STD_BORDERWIDTH, -1, Tk_Offset(BarPenOptions, borderWidth), 0, NULL, CACHE}, - {TK_OPTION_BORDER, "-color", "color", "Color", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(BarPenOptions, fill), 0, NULL, CACHE}, - {TK_OPTION_COLOR, "-errorbarcolor", "errorBarColor", "ErrorBarColor", - NULL, -1, Tk_Offset(BarPenOptions, errorBarColor), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_PIXELS, "-errorbarwidth", "errorBarWidth","ErrorBarWidth", - "1", -1, Tk_Offset(BarPenOptions, errorBarLineWidth), 0, NULL, CACHE}, - {TK_OPTION_PIXELS, "-errorbarcap", "errorBarCap", "ErrorBarCap", - "0", -1, Tk_Offset(BarPenOptions, errorBarCapWidth), 0, NULL, LAYOUT}, - {TK_OPTION_SYNONYM, "-fg", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-outline", 0}, - {TK_OPTION_SYNONYM, "-fill", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-color", 0}, - {TK_OPTION_SYNONYM, "-foreground", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-outline", 0}, - {TK_OPTION_COLOR, "-outline", "outline", "Outline", - NULL, -1, Tk_Offset(BarPenOptions, outlineColor), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_RELIEF, "-relief", "relief", "Relief", - "raised", -1, Tk_Offset(BarPenOptions, relief), 0, NULL, LAYOUT}, - {TK_OPTION_STRING_TABLE, "-showerrorbars", "showErrorBars", "ShowErrorBars", - "both", -1, Tk_Offset(BarPenOptions, errorBarShow), - 0, &fillObjOption, LAYOUT}, - {TK_OPTION_STRING_TABLE, "-showvalues", "showValues", "ShowValues", - "none", -1, Tk_Offset(BarPenOptions, valueShow), 0, &fillObjOption, CACHE}, - {TK_OPTION_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor", - "s", -1, Tk_Offset(BarPenOptions, valueStyle.anchor), 0, NULL, CACHE}, - {TK_OPTION_COLOR, "-valuecolor", "valueColor", "ValueColor", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(BarPenOptions, valueStyle.color), - 0, NULL, CACHE}, - {TK_OPTION_FONT, "-valuefont", "valueFont", "ValueFont", - STD_FONT_SMALL, -1, Tk_Offset(BarPenOptions, valueStyle.font), - 0, NULL, CACHE}, - {TK_OPTION_STRING, "-valueformat", "valueFormat", "ValueFormat", - "%g", -1, Tk_Offset(BarPenOptions, valueFormat), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate", - "0", -1, Tk_Offset(BarPenOptions, valueStyle.angle), 0, NULL, CACHE}, - {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} -}; - -BarPen::BarPen(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) - : Pen(graphPtr, name, hPtr) -{ - ops_ = calloc(1, sizeof(BarPenOptions)); - BarPenOptions* ops = (BarPenOptions*)ops_; - manageOptions_ =1; - - outlineGC_ =NULL; - errorBarGC_ =NULL; - - ops->valueStyle.anchor =TK_ANCHOR_NW; - ops->valueStyle.color =NULL; - ops->valueStyle.font =NULL; - ops->valueStyle.angle =0; - ops->valueStyle.justify =TK_JUSTIFY_LEFT; - - optionTable_ = Tk_CreateOptionTable(graphPtr_->interp_, barPenOptionSpecs); -} - -BarPen::BarPen(Graph* graphPtr, const char* name, void* options) - : Pen(graphPtr, name, NULL) -{ - ops_ = options; - BarPenOptions* ops = (BarPenOptions*)ops_; - manageOptions_ =0; - - outlineGC_ =NULL; - errorBarGC_ =NULL; - - ops->valueStyle.anchor =TK_ANCHOR_NW; - ops->valueStyle.color =NULL; - ops->valueStyle.font =NULL; - ops->valueStyle.angle =0; - ops->valueStyle.justify =TK_JUSTIFY_LEFT; - - optionTable_ = Tk_CreateOptionTable(graphPtr_->interp_, barPenOptionSpecs); -} - -BarPen::~BarPen() -{ - if (outlineGC_) - Tk_FreeGC(graphPtr_->display_, outlineGC_); - if (errorBarGC_) - Tk_FreeGC(graphPtr_->display_, errorBarGC_); -} - -int BarPen::configure() -{ - BarPenOptions* ops = (BarPenOptions*)ops_; - - // outlineGC - { - unsigned long gcMask = GCForeground | GCLineWidth; - XGCValues gcValues; - gcValues.line_width = ops->borderWidth; - if (ops->outlineColor) - gcValues.foreground = ops->outlineColor->pixel; - else if (ops->fill) - gcValues.foreground = Tk_3DBorderColor(ops->fill)->pixel; - GC newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); - if (outlineGC_) - Tk_FreeGC(graphPtr_->display_, outlineGC_); - outlineGC_ = newGC; - } - - // errorBarGC - { - unsigned long gcMask = GCForeground | GCLineWidth; - XGCValues gcValues; - if (ops->errorBarColor) - gcValues.foreground = ops->errorBarColor->pixel; - else if (ops->outlineColor) - gcValues.foreground = ops->outlineColor->pixel; - else if (ops->fill) - gcValues.foreground = Tk_3DBorderColor(ops->fill)->pixel; - - gcValues.line_width = ops->errorBarLineWidth; - GC newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); - if (errorBarGC_) - Tk_FreeGC(graphPtr_->display_, errorBarGC_); - errorBarGC_ = newGC; - } - - return TCL_OK; -} - diff --git a/src/tkbltGrPenBar.h b/src/tkbltGrPenBar.h deleted file mode 100644 index f39db94..0000000 --- a/src/tkbltGrPenBar.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrPenBar_h__ -#define __BltGrPenBar_h__ - -#include - -#include "tkbltGrPen.h" - -namespace Blt { - - typedef struct { - int errorBarShow; - int errorBarLineWidth; - int errorBarCapWidth; - XColor* errorBarColor; - int valueShow; - const char *valueFormat; - TextStyleOptions valueStyle; - - XColor* outlineColor; - Tk_3DBorder fill; - int borderWidth; - int relief; - } BarPenOptions; - - class BarPen : public Pen { - public: - GC fillGC_; - GC outlineGC_; - GC errorBarGC_; - - public: - BarPen(Graph*, const char*, Tcl_HashEntry*); - BarPen(Graph*, const char*, void*); - virtual ~BarPen(); - - ClassId classId() {return CID_ELEM_BAR;} - const char* className() {return "BarElement";} - const char* typeName() {return "bar";} - - int configure(); - }; -}; - -#endif diff --git a/src/tkbltGrPenLine.C b/src/tkbltGrPenLine.C deleted file mode 100644 index 5f15ce8..0000000 --- a/src/tkbltGrPenLine.C +++ /dev/null @@ -1,243 +0,0 @@ -/* - * 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 1993-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 -#include - -#include "tkbltGrPenLine.h" -#include "tkbltGraph.h" -#include "tkbltGrMisc.h" -#include "tkbltGrDef.h" -#include "tkbltConfig.h" - -using namespace Blt; - -const char* symbolObjOption[] = - {"none", "square", "circle", "diamond", "plus", "cross", "splus", "scross", "triangle", "arrow", NULL}; - -// Defs - -static Tk_OptionSpec linePenOptionSpecs[] = { - {TK_OPTION_COLOR, "-color", "color", "Color", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(LinePenOptions, traceColor), - 0, NULL, CACHE}, - {TK_OPTION_CUSTOM, "-dashes", "dashes", "Dashes", - NULL, -1, Tk_Offset(LinePenOptions, traceDashes), - TK_OPTION_NULL_OK, &dashesObjOption, CACHE}, - {TK_OPTION_COLOR, "-errorbarcolor", "errorBarColor", "ErrorBarColor", - NULL, -1, Tk_Offset(LinePenOptions, errorBarColor), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_PIXELS, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth", - "1", -1, Tk_Offset(LinePenOptions, errorBarLineWidth), 0, NULL, CACHE}, - {TK_OPTION_PIXELS, "-errorbarcap", "errorBarCap", "ErrorBarCap", - "0", -1, Tk_Offset(LinePenOptions, errorBarCapWidth), 0, NULL, LAYOUT}, - {TK_OPTION_COLOR, "-fill", "fill", "Fill", - NULL, -1, Tk_Offset(LinePenOptions, symbol.fillColor), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_PIXELS, "-linewidth", "lineWidth", "LineWidth", - "1", -1, Tk_Offset(LinePenOptions, traceWidth), 0, NULL, CACHE}, - {TK_OPTION_COLOR, "-offdash", "offDash", "OffDash", - NULL, -1, Tk_Offset(LinePenOptions, traceOffColor), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_COLOR, "-outline", "outline", "Outline", - NULL, -1, Tk_Offset(LinePenOptions, symbol.outlineColor), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_PIXELS, "-outlinewidth", "outlineWidth", "OutlineWidth", - "1", -1, Tk_Offset(LinePenOptions, symbol.outlineWidth), 0, NULL, CACHE}, - {TK_OPTION_PIXELS, "-pixels", "pixels", "Pixels", - "0.1i", -1, Tk_Offset(LinePenOptions, symbol.size), 0, NULL, LAYOUT}, - {TK_OPTION_STRING_TABLE, "-showerrorbars", "showErrorBars", "ShowErrorBars", - "both", -1, Tk_Offset(LinePenOptions, errorBarShow), - 0, &fillObjOption, LAYOUT}, - {TK_OPTION_STRING_TABLE, "-showvalues", "showValues", "ShowValues", - "none", -1, Tk_Offset(LinePenOptions, valueShow), 0, &fillObjOption, CACHE}, - {TK_OPTION_STRING_TABLE, "-symbol", "symbol", "Symbol", - "none", -1, Tk_Offset(LinePenOptions, symbol), 0, &symbolObjOption, CACHE}, - {TK_OPTION_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor", - "s", -1, Tk_Offset(LinePenOptions, valueStyle.anchor), 0, NULL, CACHE}, - {TK_OPTION_COLOR, "-valuecolor", "valueColor", "ValueColor", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(LinePenOptions, valueStyle.color), - 0, NULL, CACHE}, - {TK_OPTION_FONT, "-valuefont", "valueFont", "ValueFont", - STD_FONT_SMALL, -1, Tk_Offset(LinePenOptions, valueStyle.font), - 0, NULL, CACHE}, - {TK_OPTION_STRING, "-valueformat", "valueFormat", "ValueFormat", - "%g", -1, Tk_Offset(LinePenOptions, valueFormat), - TK_OPTION_NULL_OK, NULL, CACHE}, - {TK_OPTION_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate", - "0", -1, Tk_Offset(LinePenOptions, valueStyle.angle), 0, NULL, CACHE}, - {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} -}; - -LinePen::LinePen(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) - : Pen(graphPtr, name, hPtr) -{ - ops_ = calloc(1, sizeof(LinePenOptions)); - LinePenOptions* ops = (LinePenOptions*)ops_; - manageOptions_ =1; - - traceGC_ =NULL; - errorBarGC_ =NULL; - - ops->symbol.type = SYMBOL_NONE; - - ops->valueStyle.anchor =TK_ANCHOR_NW; - ops->valueStyle.color =NULL; - ops->valueStyle.font =NULL; - ops->valueStyle.angle =0; - ops->valueStyle.justify =TK_JUSTIFY_LEFT; - - optionTable_ = Tk_CreateOptionTable(graphPtr_->interp_, linePenOptionSpecs); -} - -LinePen::LinePen(Graph* graphPtr, const char* name, void* options) - : Pen(graphPtr, name, NULL) -{ - ops_ = options; - LinePenOptions* ops = (LinePenOptions*)ops_; - manageOptions_ =0; - - traceGC_ =NULL; - errorBarGC_ =NULL; - - ops->symbol.type = SYMBOL_NONE; - - ops->valueStyle.anchor =TK_ANCHOR_NW; - ops->valueStyle.color =NULL; - ops->valueStyle.font =NULL; - ops->valueStyle.angle =0; - ops->valueStyle.justify =TK_JUSTIFY_LEFT; - - optionTable_ = Tk_CreateOptionTable(graphPtr_->interp_, linePenOptionSpecs); -} - -LinePen::~LinePen() -{ - LinePenOptions* ops = (LinePenOptions*)ops_; - - if (errorBarGC_) - Tk_FreeGC(graphPtr_->display_, errorBarGC_); - - if (traceGC_) - graphPtr_->freePrivateGC(traceGC_); - - if (ops->symbol.outlineGC) - Tk_FreeGC(graphPtr_->display_, ops->symbol.outlineGC); - - if (ops->symbol.fillGC) - Tk_FreeGC(graphPtr_->display_, ops->symbol.fillGC); -} - -int LinePen::configure() -{ - LinePenOptions* ops = (LinePenOptions*)ops_; - - // symbol outline - { - unsigned long gcMask = (GCLineWidth | GCForeground); - XColor* colorPtr = ops->symbol.outlineColor; - if (!colorPtr) - colorPtr = ops->traceColor; - XGCValues gcValues; - gcValues.foreground = colorPtr->pixel; - gcValues.line_width = ops->symbol.outlineWidth; - GC newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); - if (ops->symbol.outlineGC) - Tk_FreeGC(graphPtr_->display_, ops->symbol.outlineGC); - ops->symbol.outlineGC = newGC; - } - - // symbol fill - { - unsigned long gcMask = (GCLineWidth | GCForeground); - XColor* colorPtr = ops->symbol.fillColor; - if (!colorPtr) - colorPtr = ops->traceColor; - GC newGC = NULL; - XGCValues gcValues; - if (colorPtr) { - gcValues.foreground = colorPtr->pixel; - newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); - } - if (ops->symbol.fillGC) - Tk_FreeGC(graphPtr_->display_, ops->symbol.fillGC); - ops->symbol.fillGC = newGC; - } - - // trace - { - unsigned long gcMask = - (GCLineWidth | GCForeground | GCLineStyle | GCCapStyle | GCJoinStyle); - XGCValues gcValues; - gcValues.cap_style = CapButt; - gcValues.join_style = JoinRound; - gcValues.line_style = LineSolid; - gcValues.line_width = ops->traceWidth; - - gcValues.foreground = ops->traceColor->pixel; - XColor* colorPtr = ops->traceOffColor; - if (colorPtr) { - gcMask |= GCBackground; - gcValues.background = colorPtr->pixel; - } - if (LineIsDashed(ops->traceDashes)) { - gcValues.line_width = ops->traceWidth; - gcValues.line_style = !colorPtr ? LineOnOffDash : LineDoubleDash; - } - GC newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); - if (traceGC_) - graphPtr_->freePrivateGC(traceGC_); - - if (LineIsDashed(ops->traceDashes)) { - ops->traceDashes.offset = ops->traceDashes.values[0] / 2; - graphPtr_->setDashes(newGC, &ops->traceDashes); - } - traceGC_ = newGC; - } - - // errorbar - { - unsigned long gcMask = (GCLineWidth | GCForeground); - XColor* colorPtr = ops->errorBarColor; - if (!colorPtr) - colorPtr = ops->traceColor; - XGCValues gcValues; - gcValues.line_width = ops->errorBarLineWidth; - gcValues.foreground = colorPtr->pixel; - GC newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); - if (errorBarGC_) { - Tk_FreeGC(graphPtr_->display_, errorBarGC_); - } - errorBarGC_ = newGC; - } - - return TCL_OK; -} - - diff --git a/src/tkbltGrPenLine.h b/src/tkbltGrPenLine.h deleted file mode 100644 index 63eeeb8..0000000 --- a/src/tkbltGrPenLine.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrPenLine_h__ -#define __BltGrPenLine_h__ - -#include "tkbltGrPen.h" - -namespace Blt { - - typedef enum { - SYMBOL_NONE, SYMBOL_SQUARE, SYMBOL_CIRCLE, SYMBOL_DIAMOND, SYMBOL_PLUS, - SYMBOL_CROSS, SYMBOL_SPLUS, SYMBOL_SCROSS, SYMBOL_TRIANGLE, SYMBOL_ARROW - } SymbolType; - - typedef struct { - SymbolType type; - int size; - XColor* outlineColor; - int outlineWidth; - GC outlineGC; - XColor* fillColor; - GC fillGC; - } Symbol; - - typedef struct { - int errorBarShow; - int errorBarLineWidth; - int errorBarCapWidth; - XColor* errorBarColor; - int valueShow; - const char* valueFormat; - TextStyleOptions valueStyle; - - Symbol symbol; - int traceWidth; - Dashes traceDashes; - XColor* traceColor; - XColor* traceOffColor; - } LinePenOptions; - - class LinePen : public Pen { - public: - GC traceGC_; - GC errorBarGC_; - - public: - LinePen(Graph*, const char*, Tcl_HashEntry*); - LinePen(Graph*, const char*, void*); - virtual ~LinePen(); - - ClassId classId() {return CID_ELEM_LINE;} - const char* className() {return "LineElement";} - const char* typeName() {return "line";} - - int configure(); - }; -}; - -extern const char* symbolObjOption[]; - -#endif diff --git a/src/tkbltGrPenOp.C b/src/tkbltGrPenOp.C deleted file mode 100644 index 8c5669d..0000000 --- a/src/tkbltGrPenOp.C +++ /dev/null @@ -1,217 +0,0 @@ -/* - * 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 1996-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 "tkbltGraph.h" -#include "tkbltGrPen.h" -#include "tkbltGrPenOp.h" -#include "tkbltGrPenLine.h" -#include "tkbltGrPenBar.h" - -using namespace Blt; - -int Blt::PenObjConfigure(Graph* graphPtr, Pen* penPtr, - 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*)penPtr->ops(), penPtr->optionTable(), - objc, objv, graphPtr->tkwin_, &savedOptions, &mask) - != TCL_OK) - continue; - } - else { - errorResult = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(errorResult); - Tk_RestoreSavedOptions(&savedOptions); - } - - if (penPtr->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 != 5) { - Tcl_WrongNumArgs(interp, 3, objv, "cget option"); - return TCL_ERROR; - } - - Pen* penPtr; - if (graphPtr->getPen(objv[3], &penPtr) != TCL_OK) - return TCL_ERROR; - - Tcl_Obj* objPtr = Tk_GetOptionValue(interp, - (char*)penPtr->ops(), - penPtr->optionTable(), - objv[4], 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<4) - return TCL_ERROR; - - Pen* penPtr; - if (graphPtr->getPen(objv[3], &penPtr) != TCL_OK) - return TCL_ERROR; - - if (objc <= 5) { - Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)penPtr->ops(), - penPtr->optionTable(), - (objc == 5) ? objv[4] : NULL, - graphPtr->tkwin_); - if (objPtr == NULL) - return TCL_ERROR; - else - Tcl_SetObjResult(interp, objPtr); - return TCL_OK; - } - else - return PenObjConfigure(graphPtr, penPtr, interp, objc-4, objv+4); -} - -static int CreateOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - if (graphPtr->createPen(Tcl_GetString(objv[3]), objc, objv) != TCL_OK) - return TCL_ERROR; - Tcl_SetObjResult(interp, objv[3]); - - return TCL_OK; -} - -static int DeleteOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Pen* penPtr; - if (graphPtr->getPen(objv[3], &penPtr) != TCL_OK) - return TCL_ERROR; - - if (penPtr->refCount_ == 0) - delete penPtr; - - return TCL_OK; -} - -static int NamesOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - if (objc == 3) { - Tcl_HashSearch iter; - for (Tcl_HashEntry *hPtr=Tcl_FirstHashEntry(&graphPtr->penTable_, &iter); - hPtr; hPtr=Tcl_NextHashEntry(&iter)) { - Pen* penPtr = (Pen*)Tcl_GetHashValue(hPtr); - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewStringObj(penPtr->name_, -1)); - } - } - else { - Tcl_HashSearch iter; - for (Tcl_HashEntry *hPtr=Tcl_FirstHashEntry(&graphPtr->penTable_, &iter); - hPtr; hPtr=Tcl_NextHashEntry(&iter)) { - Pen* penPtr = (Pen*)Tcl_GetHashValue(hPtr); - for (int ii=3; iiname_, pattern)) { - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewStringObj(penPtr->name_, -1)); - break; - } - } - } - } - Tcl_SetObjResult(interp, listObjPtr); - return TCL_OK; -} - -static int TypeOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - if (objc<4) - return TCL_ERROR; - - Pen* penPtr; - if (graphPtr->getPen(objv[3], &penPtr) != TCL_OK) - return TCL_ERROR; - - Tcl_SetStringObj(Tcl_GetObjResult(interp), penPtr->typeName(), -1); - return TCL_OK; -} - -const Ensemble Blt::penEnsemble[] = { - {"cget", CgetOp, 0}, - {"configure", ConfigureOp, 0}, - {"create", CreateOp, 0}, - {"delete", DeleteOp, 0}, - {"names", NamesOp, 0}, - {"type", TypeOp, 0}, - { 0,0,0 } -}; - diff --git a/src/tkbltGrPenOp.h b/src/tkbltGrPenOp.h deleted file mode 100644 index 5dab592..0000000 --- a/src/tkbltGrPenOp.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrPenOp_h__ -#define __BltGrPenOp_h__ - -#include "tkbltGraph.h" - -namespace Blt { - extern const Ensemble penEnsemble[]; - extern int PenObjConfigure(Blt::Graph* graphPtr, Blt::Pen* penPtr, - Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); -}; - -#endif diff --git a/src/tkbltGrPenOption.C b/src/tkbltGrPenOption.C deleted file mode 100644 index b1da1b6..0000000 --- a/src/tkbltGrPenOption.C +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 1996-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 "tkbltGraph.h" -#include "tkbltGrPen.h" -#include "tkbltConfig.h" - -using namespace Blt; - -static Tk_CustomOptionSetProc PenSetProc; -static Tk_CustomOptionGetProc PenGetProc; -static Tk_CustomOptionFreeProc PenFreeProc; -Tk_ObjCustomOption penObjOption = - { - "pen", PenSetProc, PenGetProc, RestoreProc, PenFreeProc, NULL - }; - -static int PenSetProc(ClientData clientData, Tcl_Interp* interp, - Tk_Window tkwin, Tcl_Obj** objPtr, char* widgRec, - int offset, char* savePtr, int flags) -{ - Pen** penPtrPtr = (Pen**)(widgRec + offset); - *(double*)savePtr = *(double*)penPtrPtr; - - if (!penPtrPtr) - return TCL_OK; - - const char* string = Tcl_GetString(*objPtr); - if (!string || !string[0]) { - *penPtrPtr = NULL; - return TCL_OK; - } - - Graph* graphPtr = getGraphFromWindowData(tkwin); - Pen* penPtr; - if (graphPtr->getPen(*objPtr, &penPtr) != TCL_OK) - return TCL_ERROR; - - penPtr->refCount_++; - *penPtrPtr = penPtr; - - return TCL_OK; -}; - -static Tcl_Obj* PenGetProc(ClientData clientData, Tk_Window tkwin, - char *widgRec, int offset) -{ - Pen* penPtr = *(Pen**)(widgRec + offset); - if (!penPtr) - return Tcl_NewStringObj("", -1); - - return Tcl_NewStringObj(penPtr->name_, -1); -}; - -static void PenFreeProc(ClientData clientData, Tk_Window tkwin, char *ptr) -{ - Pen* penPtr = *(Pen**)ptr; - if (penPtr) - if (penPtr->refCount_ > 0) - penPtr->refCount_--; -} - - diff --git a/src/tkbltGrPostscript.C b/src/tkbltGrPostscript.C deleted file mode 100644 index 4bbf504..0000000 --- a/src/tkbltGrPostscript.C +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 - -#include "tkbltGraph.h" -#include "tkbltGrPostscript.h" -#include "tkbltConfig.h" - -using namespace Blt; - -static Tk_OptionSpec optionSpecs[] = { - {TK_OPTION_BOOLEAN, "-center", "center", "Center", - "yes", -1, Tk_Offset(PostscriptOptions, center), 0, NULL, 0}, - {TK_OPTION_CUSTOM, "-comments", "comments", "Comments", - NULL, -1, Tk_Offset(PostscriptOptions, comments), - TK_OPTION_NULL_OK, &listObjOption, 0}, - {TK_OPTION_BOOLEAN, "-decorations", "decorations", "Decorations", - "yes", -1, Tk_Offset(PostscriptOptions, decorations), 0, NULL, 0}, - {TK_OPTION_BOOLEAN, "-footer", "footer", "Footer", - "no", -1, Tk_Offset(PostscriptOptions, footer), 0, NULL, 0}, - {TK_OPTION_BOOLEAN, "-greyscale", "greyscale", "Greyscale", - "no", -1, Tk_Offset(PostscriptOptions, greyscale), 0, NULL, 0}, - {TK_OPTION_PIXELS, "-height", "height", "Height", - "0", -1, Tk_Offset(PostscriptOptions, reqHeight), 0, NULL, 0}, - {TK_OPTION_BOOLEAN, "-landscape", "landscape", "Landscape", - "no", -1, Tk_Offset(PostscriptOptions, landscape), 0, NULL, 0}, - {TK_OPTION_INT, "-level", "level", "Level", - "2", -1, Tk_Offset(PostscriptOptions, level), 0, NULL, 0}, - {TK_OPTION_PIXELS, "-padx", "padX", "PadX", - "1.0i", -1, Tk_Offset(PostscriptOptions, xPad), 0, NULL, 0}, - {TK_OPTION_PIXELS, "-pady", "padY", "PadY", - "1.0i", -1, Tk_Offset(PostscriptOptions, yPad), 0, NULL, 0}, - {TK_OPTION_PIXELS, "-paperheight", "paperHeight", "PaperHeight", - "11.0i", -1, Tk_Offset(PostscriptOptions, reqPaperHeight), 0, NULL, 0}, - {TK_OPTION_PIXELS, "-paperwidth", "paperWidth", "PaperWidth", - "8.5i", -1, Tk_Offset(PostscriptOptions, reqPaperWidth), 0, NULL, 0}, - {TK_OPTION_PIXELS, "-width", "width", "Width", - "0", -1, Tk_Offset(PostscriptOptions, reqWidth), 0, NULL, 0}, - {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} -}; - -Postscript::Postscript(Graph* graphPtr) -{ - ops_ = (PostscriptOptions*)calloc(1, sizeof(PostscriptOptions)); - graphPtr_ = graphPtr; - - optionTable_ =Tk_CreateOptionTable(graphPtr_->interp_, optionSpecs); - Tk_InitOptions(graphPtr_->interp_, (char*)ops_, optionTable_, - graphPtr_->tkwin_); -} - -Postscript::~Postscript() -{ - Tk_FreeConfigOptions((char*)ops_, optionTable_, graphPtr_->tkwin_); - free(ops_); -} - diff --git a/src/tkbltGrPostscript.h b/src/tkbltGrPostscript.h deleted file mode 100644 index 7ab54a3..0000000 --- a/src/tkbltGrPostscript.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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. - */ - -#ifndef __BltGrPostscript_h__ -#define __BltGrPostscript_h__ - -#include - -namespace Blt { - - typedef struct { - int center; - const char **comments; - int decorations; - int footer; - int greyscale; - int landscape; - int level; - int xPad; - int yPad; - int reqPaperWidth; - int reqPaperHeight; - int reqWidth; - int reqHeight; - } PostscriptOptions; - - class Postscript { - public: - Tk_OptionTable optionTable_; - void* ops_; - Graph* graphPtr_; - - short int left; - short int bottom; - short int right; - short int top; - float scale; - int paperHeight; - int paperWidth; - - public: - Postscript(Graph*); - virtual ~Postscript(); - }; -}; - -#endif diff --git a/src/tkbltGrPostscriptOp.C b/src/tkbltGrPostscriptOp.C deleted file mode 100644 index 931feb9..0000000 --- a/src/tkbltGrPostscriptOp.C +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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 - -#include "tkbltGraph.h" -#include "tkbltGrPostscript.h" -#include "tkbltGrPostscriptOp.h" -#include "tkbltGrPSOutput.h" - -using namespace Blt; - -int Blt::PostscriptObjConfigure(Graph* graphPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Postscript* setupPtr = graphPtr->postscript_; - Tk_SavedOptions savedOptions; - int mask =0; - int error; - Tcl_Obj* errorResult; - - for (error=0; error<=1; error++) { - if (!error) { - if (Tk_SetOptions(interp, (char*)setupPtr->ops_, setupPtr->optionTable_, - objc, objv, graphPtr->tkwin_, &savedOptions, &mask) - != TCL_OK) - continue; - } - else { - errorResult = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(errorResult); - Tk_RestoreSavedOptions(&savedOptions); - } - - 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 != 4) { - Tcl_WrongNumArgs(interp, 2, objv, "cget option"); - return TCL_ERROR; - } - - Postscript *setupPtr = graphPtr->postscript_; - Tcl_Obj* objPtr = Tk_GetOptionValue(interp, - (char*)setupPtr->ops_, - setupPtr->optionTable_, - objv[3], 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; - Postscript* setupPtr = graphPtr->postscript_; - if (objc <= 4) { - Tcl_Obj* objPtr = Tk_GetOptionInfo(interp, (char*)setupPtr->ops_, - setupPtr->optionTable_, - (objc == 4) ? objv[3] : NULL, - graphPtr->tkwin_); - if (objPtr == NULL) - return TCL_ERROR; - else - Tcl_SetObjResult(interp, objPtr); - return TCL_OK; - } - else - return PostscriptObjConfigure(graphPtr, interp, objc-3, objv+3); -} - -static int OutputOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - - const char *fileName = NULL; - Tcl_Channel channel = NULL; - if (objc > 3) { - fileName = Tcl_GetString(objv[3]); - if (fileName[0] != '-') { - // First argument is the file name - objv++, objc--; - - channel = Tcl_OpenFileChannel(interp, fileName, "w", 0666); - if (!channel) - return TCL_ERROR; - - if (Tcl_SetChannelOption(interp, channel, "-translation", "binary") - != TCL_OK) - return TCL_ERROR; - } - } - - PSOutput* psPtr = new PSOutput(graphPtr); - - if (PostscriptObjConfigure(graphPtr, interp, objc-3, objv+3) != TCL_OK) { - if (channel) - Tcl_Close(interp, channel); - delete psPtr; - return TCL_ERROR; - } - - if (graphPtr->print(fileName, psPtr) != TCL_OK) { - if (channel) - Tcl_Close(interp, channel); - delete psPtr; - return TCL_ERROR; - } - - int length; - const char* buffer = psPtr->getValue(&length); - if (channel) { - int nBytes = Tcl_Write(channel, buffer, length); - if (nBytes < 0) { - Tcl_AppendResult(interp, "error writing file \"", fileName, "\": ", - Tcl_PosixError(interp), (char *)NULL); - if (channel) - Tcl_Close(interp, channel); - delete psPtr; - return TCL_ERROR; - } - Tcl_Close(interp, channel); - } - else - Tcl_SetStringObj(Tcl_GetObjResult(interp), buffer, length); - - delete psPtr; - - return TCL_OK; -} - -const Ensemble Blt::postscriptEnsemble[] = { - {"cget", CgetOp, 0}, - {"configure", ConfigureOp, 0}, - {"output", OutputOp, 0}, - { 0,0,0 } -}; diff --git a/src/tkbltGrPostscriptOp.h b/src/tkbltGrPostscriptOp.h deleted file mode 100644 index 9a81266..0000000 --- a/src/tkbltGrPostscriptOp.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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. - */ - -#ifndef __BltGrPostscriptOp_h__ -#define __BltGrPostscriptOp_h__ - -#include "tkbltGraph.h" - -namespace Blt { - extern const Ensemble postscriptEnsemble[]; - extern int PostscriptObjConfigure(Blt::Graph* graphPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); -}; - -#endif diff --git a/src/tkbltGrText.C b/src/tkbltGrText.C deleted file mode 100644 index 20709f1..0000000 --- a/src/tkbltGrText.C +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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 1993-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 - -#include -#include - -#include "tkbltGrText.h" -#include "tkbltGraph.h" -#include "tkbltGrPSOutput.h" - -using namespace Blt; - -TextStyle::TextStyle(Graph* graphPtr) -{ - ops_ = (TextStyleOptions*)calloc(1, sizeof(TextStyleOptions)); - TextStyleOptions* ops = (TextStyleOptions*)ops_; - graphPtr_ = graphPtr; - manageOptions_ = 1; - - ops->anchor =TK_ANCHOR_NW; - ops->color =NULL; - ops->font =NULL; - ops->angle =0; - ops->justify =TK_JUSTIFY_LEFT; - - xPad_ = 0; - yPad_ = 0; - gc_ = NULL; -} - -TextStyle::TextStyle(Graph* graphPtr, TextStyleOptions* ops) -{ - ops_ = (TextStyleOptions*)ops; - graphPtr_ = graphPtr; - manageOptions_ = 0; - - xPad_ = 0; - yPad_ = 0; - gc_ = NULL; -} - -TextStyle::~TextStyle() -{ - // TextStyleOptions* ops = (TextStyleOptions*)ops_; - - if (gc_) - Tk_FreeGC(graphPtr_->display_, gc_); - - if (manageOptions_) - free(ops_); -} - -void TextStyle::drawText(Drawable drawable, const char *text, int x, int y) -{ - TextStyleOptions* ops = (TextStyleOptions*)ops_; - - if (!text || !(*text)) - return; - - if (!gc_) - resetStyle(); - - int w1, h1; - Tk_TextLayout layout = Tk_ComputeTextLayout(ops->font, text, -1, -1, - ops->justify, 0, &w1, &h1); - Point2d rr = rotateText(x, y, w1, h1); -#if (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 6) - TkDrawAngledTextLayout(graphPtr_->display_, drawable, gc_, layout, - rr.x, rr.y, ops->angle, 0, -1); -#else - Tk_DrawTextLayout(graphPtr_->display_, drawable, gc_, layout, - rr.x, rr.y, 0, -1); -#endif -} - -void TextStyle::drawText2(Drawable drawable, const char *text, - int x, int y, int* ww, int* hh) -{ - TextStyleOptions* ops = (TextStyleOptions*)ops_; - - if (!text || !(*text)) - return; - - if (!gc_) - resetStyle(); - - int w1, h1; - Tk_TextLayout layout = Tk_ComputeTextLayout(ops->font, text, -1, -1, - ops->justify, 0, &w1, &h1); - Point2d rr = rotateText(x, y, w1, h1); -#if (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 6) - TkDrawAngledTextLayout(graphPtr_->display_, drawable, gc_, layout, - rr.x, rr.y, ops->angle, 0, -1); -#else - Tk_DrawTextLayout(graphPtr_->display_, drawable, gc_, layout, - rr.x, rr.y, 0, -1); -#endif - - float angle = fmod(ops->angle, 360.0); - if (angle < 0.0) - angle += 360.0; - - if (angle != 0.0) { - double rotWidth, rotHeight; - graphPtr_->getBoundingBox(w1, h1, angle, &rotWidth, &rotHeight, NULL); - w1 = rotWidth; - h1 = rotHeight; - } - - *ww = w1; - *hh = h1; -} - -void TextStyle::printText(PSOutput* psPtr, const char *text, int x, int y) -{ - TextStyleOptions* ops = (TextStyleOptions*)ops_; - - if (!text || !(*text)) - return; - - int w1, h1; - Tk_TextLayout layout = Tk_ComputeTextLayout(ops->font, text, -1, -1, - ops->justify, 0, &w1, &h1); - - int xx =0; - int yy =0; - switch (ops->anchor) { - case TK_ANCHOR_NW: xx = 0; yy = 0; break; - case TK_ANCHOR_N: xx = 1; yy = 0; break; - case TK_ANCHOR_NE: xx = 2; yy = 0; break; - case TK_ANCHOR_E: xx = 2; yy = 1; break; - case TK_ANCHOR_SE: xx = 2; yy = 2; break; - case TK_ANCHOR_S: xx = 1; yy = 2; break; - case TK_ANCHOR_SW: xx = 0; yy = 2; break; - case TK_ANCHOR_W: xx = 0; yy = 1; break; - case TK_ANCHOR_CENTER: xx = 1; yy = 1; break; - } - - const char* justify =NULL; - switch (ops->justify) { - case TK_JUSTIFY_LEFT: justify = "0"; break; - case TK_JUSTIFY_CENTER: justify = "0.5"; break; - case TK_JUSTIFY_RIGHT: justify = "1"; break; - } - - psPtr->setFont(ops->font); - psPtr->setForeground(ops->color); - - psPtr->format("%g %d %d [\n", ops->angle, x, y); - Tcl_ResetResult(graphPtr_->interp_); - Tk_TextLayoutToPostscript(graphPtr_->interp_, layout); - psPtr->append(Tcl_GetStringResult(graphPtr_->interp_)); - Tcl_ResetResult(graphPtr_->interp_); - psPtr->format("] %g %g %s DrawText\n", xx/-2.0, yy/-2.0, justify); -} - -void TextStyle::resetStyle() -{ - TextStyleOptions* ops = (TextStyleOptions*)ops_; - - unsigned long gcMask; - gcMask = GCFont; - - XGCValues gcValues; - gcValues.font = Tk_FontId(ops->font); - if (ops->color) { - gcMask |= GCForeground; - gcValues.foreground = ops->color->pixel; - } - GC newGC = Tk_GetGC(graphPtr_->tkwin_, gcMask, &gcValues); - if (gc_) - Tk_FreeGC(graphPtr_->display_, gc_); - - gc_ = newGC; -} - -Point2d TextStyle::rotateText(int x, int y, int w1, int h1) -{ - TextStyleOptions* ops = (TextStyleOptions*)ops_; - - // Matrix t0 = Translate(-x,-y); - // Matrix t1 = Translate(-w1/2,-h1/2); - // Matrix rr = Rotate(angle); - // Matrix t2 = Translate(w2/2,h2/2); - // Matrix t3 = Translate(x,y); - - double angle = ops->angle; - double ccos = cos(M_PI*angle/180.); - double ssin = sin(M_PI*angle/180.); - double w2, h2; - graphPtr_->getBoundingBox(w1, h1, angle, &w2, &h2, NULL); - - double x1 = x+w1/2.; - double y1 = y+h1/2.; - double x2 = w2/2.+x; - double y2 = h2/2.+y; - - double rx = x*ccos + y*ssin + (-x1*ccos -y1*ssin +x2); - double ry = -x*ssin + y*ccos + ( x1*ssin -y1*ccos +y2); - - return graphPtr_->anchorPoint(rx, ry, w2, h2, ops->anchor); -} - -void TextStyle::getExtents(const char *text, int* ww, int* hh) -{ - TextStyleOptions* ops = (TextStyleOptions*)ops_; - - int w, h; - graphPtr_->getTextExtents(ops->font, text, -1, &w, &h); - *ww = w + 2*xPad_; - *hh = h + 2*yPad_; -} diff --git a/src/tkbltGrText.h b/src/tkbltGrText.h deleted file mode 100644 index 770e99e..0000000 --- a/src/tkbltGrText.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltText_h__ -#define __BltText_h__ - -#include - -#include "tkbltGrMisc.h" - -namespace Blt { - class Graph; - class PSOutput; - - typedef struct { - Tk_Anchor anchor; - XColor* color; - Tk_Font font; - double angle; - Tk_Justify justify; - } TextStyleOptions; - - class TextStyle { - protected: - Graph* graphPtr_; - void* ops_; - GC gc_; - int manageOptions_; - - public: - int xPad_; - int yPad_; - - protected: - void resetStyle(); - Point2d rotateText(int, int, int, int); - - public: - TextStyle(Graph*); - TextStyle(Graph*, TextStyleOptions*); - virtual ~TextStyle(); - - void* ops() {return ops_;} - void drawText(Drawable, const char*, int, int); - void drawText2(Drawable, const char*, int, int, int*, int*); - void printText(PSOutput*, const char*, int, int); - void getExtents(const char*, int*, int*); - }; -}; - -#endif diff --git a/src/tkbltGrXAxisOp.C b/src/tkbltGrXAxisOp.C deleted file mode 100644 index b431a43..0000000 --- a/src/tkbltGrXAxisOp.C +++ /dev/null @@ -1,220 +0,0 @@ -/* - * 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 1993-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 - -#include "tkbltGraph.h" -#include "tkbltGrBind.h" -#include "tkbltGrXAxisOp.h" -#include "tkbltGrAxis.h" -#include "tkbltGrAxisOp.h" - -using namespace Blt; - -static Axis* GetAxisFromCmd(ClientData clientData, Tcl_Obj* obj) -{ - Graph* graphPtr = (Graph*)clientData; - GraphOptions* ops = (GraphOptions*)graphPtr->ops_; - - int margin; - const char* name = Tcl_GetString(obj); - if (!strcmp(name,"xaxis")) - margin = (ops->inverted) ? MARGIN_LEFT : MARGIN_BOTTOM; - else if (!strcmp(name,"yaxis")) - margin = (ops->inverted) ? MARGIN_BOTTOM : MARGIN_LEFT; - else if (!strcmp(name,"x2axis")) - margin = (ops->inverted) ? MARGIN_RIGHT : MARGIN_TOP; - else if (!strcmp(name,"y2axis")) - margin = (ops->inverted) ? MARGIN_TOP : MARGIN_RIGHT; - else - return NULL; - - ChainLink* link = Chain_FirstLink(ops->margins[margin].axes); - return (Axis*)Chain_GetValue(link); -} - -static int CgetOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); - return AxisCgetOp(axisPtr, interp, objc, objv); -} - -static int ConfigureOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); - return AxisConfigureOp(axisPtr, interp, objc, objv); -} - -static int ActivateOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); - return AxisActivateOp(axisPtr, interp, objc, objv); -} - -static int BindOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); - return graphPtr->bindTable_->configure(graphPtr->axisTag(axisPtr->name_), objc-3, objv+3); -} - -static int InvTransformOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); - return AxisInvTransformOp(axisPtr, interp, objc, objv); -} - -static int LimitsOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); - return AxisLimitsOp(axisPtr, interp, objc, objv); -} - -static int TransformOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); - return AxisTransformOp(axisPtr, interp, objc, objv); -} - -static int UseOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Graph* graphPtr = (Graph*)clientData; - GraphOptions* ops = (GraphOptions*)graphPtr->ops_; - - int margin; - ClassId classId; - const char* name = Tcl_GetString(objv[1]); - if (!strcmp(name,"xaxis")) { - classId = CID_AXIS_X; - margin = (ops->inverted) ? MARGIN_LEFT : MARGIN_BOTTOM; - } - else if (!strcmp(name,"yaxis")) { - classId = CID_AXIS_Y; - margin = (ops->inverted) ? MARGIN_BOTTOM : MARGIN_LEFT; - } - else if (!strcmp(name,"x2axis")) { - classId = CID_AXIS_X; - margin = (ops->inverted) ? MARGIN_RIGHT : MARGIN_TOP; - } - else if (!strcmp(name,"y2axis")) { - classId = CID_AXIS_Y; - margin = (ops->inverted) ? MARGIN_TOP : MARGIN_RIGHT; - } - else - return TCL_ERROR; - - Chain* chain = ops->margins[margin].axes; - - if (objc == 3) { - Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - for (ChainLink* link = Chain_FirstLink(chain); link; - link = Chain_NextLink(link)) { - Axis* axisPtr = (Axis*)Chain_GetValue(link); - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewStringObj(axisPtr->name_, -1)); - } - Tcl_SetObjResult(interp, listObjPtr); - return TCL_OK; - } - - int axisObjc; - Tcl_Obj **axisObjv; - if (Tcl_ListObjGetElements(interp, objv[3], &axisObjc, &axisObjv) != TCL_OK) - return TCL_ERROR; - - for (ChainLink* link = Chain_FirstLink(chain); link; - link = Chain_NextLink(link)) { - Axis* axisPtr = (Axis*)Chain_GetValue(link); - axisPtr->link = NULL; - axisPtr->use_ =0; - // Clear the axis type if it's not currently used - if (axisPtr->refCount_ == 0) - axisPtr->setClass(CID_NONE); - } - - chain->reset(); - for (int ii=0; iigetAxis(axisObjv[ii], &axisPtr) != TCL_OK) - return TCL_ERROR; - - if (axisPtr->classId_ == CID_NONE) - axisPtr->setClass(classId); - else if (axisPtr->classId_ != classId) { - Tcl_AppendResult(interp, "wrong type axis \"", - axisPtr->name_, "\": can't use ", - axisPtr->className_, " type axis.", NULL); - return TCL_ERROR; - } - if (axisPtr->link) { - // Move the axis from the old margin's "use" list to the new - axisPtr->chain->unlinkLink(axisPtr->link); - chain->linkAfter(axisPtr->link, NULL); - } - else - axisPtr->link = chain->append(axisPtr); - - axisPtr->chain = chain; - axisPtr->use_ =1; - } - - graphPtr->flags |= RESET; - graphPtr->eventuallyRedraw(); - - return TCL_OK; -} - -static int ViewOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Axis* axisPtr = GetAxisFromCmd(clientData, objv[1]); - return AxisViewOp(axisPtr, interp, objc, objv); -} - -const Ensemble Blt::xaxisEnsemble[] = { - {"activate", ActivateOp, 0}, - {"bind", BindOp, 0}, - {"cget", CgetOp, 0}, - {"configure", ConfigureOp, 0}, - {"deactivate", ActivateOp, 0}, - {"invtransform", InvTransformOp, 0}, - {"limits", LimitsOp, 0}, - {"transform", TransformOp, 0}, - {"use", UseOp, 0}, - {"view", ViewOp, 0}, - { 0,0,0 } -}; diff --git a/src/tkbltGrXAxisOp.h b/src/tkbltGrXAxisOp.h deleted file mode 100644 index b813c83..0000000 --- a/src/tkbltGrXAxisOp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGrXAxisOp_h__ -#define __BltGrXAxisOp_h__ - -#include "tkbltGraph.h" - -namespace Blt { - extern const Ensemble xaxisEnsemble[]; -}; - -#endif diff --git a/src/tkbltGraph.C b/src/tkbltGraph.C deleted file mode 100644 index bb1707d..0000000 --- a/src/tkbltGraph.C +++ /dev/null @@ -1,1457 +0,0 @@ -/* - * 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 - -#include - -#include "tkbltGraph.h" -#include "tkbltGraphOp.h" - -#include "tkbltGrBind.h" -#include "tkbltGrAxis.h" -#include "tkbltGrAxisOp.h" -#include "tkbltGrXAxisOp.h" -#include "tkbltGrPen.h" -#include "tkbltGrPenBar.h" -#include "tkbltGrPenLine.h" -#include "tkbltGrElem.h" -#include "tkbltGrElemBar.h" -#include "tkbltGrElemLine.h" -#include "tkbltGrMarker.h" -#include "tkbltGrLegd.h" -#include "tkbltGrHairs.h" -#include "tkbltGrDef.h" -#include "tkbltGrPostscript.h" -#include "tkbltGrPSOutput.h" - -using namespace Blt; - -#define MARKER_ABOVE 0 -#define MARKER_UNDER 1 - -// OptionSpecs - -Graph::Graph(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - valid_ =1; - interp_ = interp; - tkwin_ = Tk_CreateWindowFromPath(interp_, Tk_MainWindow(interp_), - Tcl_GetString(objv[1]), NULL); - if (!tkwin_) { - valid_ =0; - return; - } - display_ = Tk_Display(tkwin_); - ((TkWindow*)tkwin_)->instanceData = this; - - cmdToken_ = Tcl_CreateObjCommand(interp_, Tk_PathName(tkwin_), - GraphInstCmdProc, this, - GraphInstCmdDeleteProc); - - flags = RESET; - nextMarkerId_ = 1; - - inset_ =0; - titleX_ =0; - titleY_ =0; - titleWidth_ =0; - titleHeight_ =0; - width_ =0; - height_ =0; - left_ =0; - right_ =0; - top_ =0; - bottom_ =0; - focusPtr_ =NULL; - halo_ =0; - drawGC_ =NULL; - vRange_ =0; - hRange_ =0; - vOffset_ =0; - hOffset_ =0; - vScale_ =0; - hScale_ =0; - cache_ =None; - cacheWidth_ =0; - cacheHeight_ =0; - - Tcl_InitHashTable(&axes_.table, TCL_STRING_KEYS); - Tcl_InitHashTable(&axes_.tagTable, TCL_STRING_KEYS); - Tcl_InitHashTable(&elements_.table, TCL_STRING_KEYS); - Tcl_InitHashTable(&elements_.tagTable, TCL_STRING_KEYS); - Tcl_InitHashTable(&markers_.table, TCL_STRING_KEYS); - Tcl_InitHashTable(&markers_.tagTable, TCL_STRING_KEYS); - Tcl_InitHashTable(&penTable_, TCL_STRING_KEYS); - - axes_.displayList = new Chain(); - elements_.displayList = new Chain(); - markers_.displayList = new Chain(); - bindTable_ = new BindTable(this, this); - - // Keep a hold of the associated tkwin until we destroy the graph, - // otherwise Tk might free it while we still need it. - Tcl_Preserve(tkwin_); - - Tk_CreateEventHandler(tkwin_, - ExposureMask|StructureNotifyMask|FocusChangeMask, - GraphEventProc, this); -} - -Graph::~Graph() -{ - // GraphOptions* ops = (GraphOptions*)ops_; - - destroyMarkers(); - destroyElements(); // must come before legend and others - - delete crosshairs_; - delete legend_; - delete postscript_; - - destroyAxes(); - destroyPens(); - - if (bindTable_) - delete bindTable_; - - if (drawGC_) - Tk_FreeGC(display_, drawGC_); - - if (cache_ != None) - Tk_FreePixmap(display_, cache_); - - Tk_FreeConfigOptions((char*)ops_, optionTable_, tkwin_); - Tcl_Release(tkwin_); - tkwin_ = NULL; - - free (ops_); -} - -int Graph::configure() -{ - GraphOptions* ops = (GraphOptions*)ops_; - - inset_ = ops->borderWidth + ops->highlightWidth; - if ((ops->reqHeight != Tk_ReqHeight(tkwin_)) || - (ops->reqWidth != Tk_ReqWidth(tkwin_))) - Tk_GeometryRequest(tkwin_, ops->reqWidth, ops->reqHeight); - - Tk_SetInternalBorder(tkwin_, ops->borderWidth); - XColor* colorPtr = Tk_3DBorderColor(ops->normalBg); - - titleWidth_ =0; - titleHeight_ =0; - if (ops->title != NULL) { - int w, h; - TextStyle ts(this, &ops->titleTextStyle); - ts.getExtents(ops->title, &w, &h); - titleHeight_ = h; - } - - // Create GCs for interior and exterior regions, and a background GC for - // clearing the margins with XFillRectangle - // Margin - XGCValues gcValues; - gcValues.foreground = ops->titleTextStyle.color->pixel; - gcValues.background = colorPtr->pixel; - unsigned long gcMask = (GCForeground | GCBackground); - GC newGC = Tk_GetGC(tkwin_, gcMask, &gcValues); - if (drawGC_ != NULL) - Tk_FreeGC(display_, drawGC_); - drawGC_ = newGC; - - // If the -inverted option changed, we need to readjust the pointers - // to the axes and recompute the their scales. - adjustAxes(); - - // Free the pixmap if we're not buffering the display of elements anymore. - if (cache_ != None) { - Tk_FreePixmap(display_, cache_); - cache_ = None; - } - - return TCL_OK; -} - -void Graph::map() -{ - if (flags & RESET) { - resetAxes(); - flags &= ~RESET; - flags |= LAYOUT; - } - - if (flags & LAYOUT) { - layoutGraph(); - crosshairs_->map(); - mapAxes(); - mapElements(); - flags &= ~LAYOUT; - flags |= MAP_MARKERS | CACHE; - } - - mapMarkers(); -} - -void Graph::draw() -{ - GraphOptions* ops = (GraphOptions*)ops_; - - flags &= ~REDRAW_PENDING; - if ((flags & GRAPH_DELETED) || !Tk_IsMapped(tkwin_)) - return; - - // Don't bother computing the layout until the size of the window is - // something reasonable. - if ((Tk_Width(tkwin_) <= 1) || (Tk_Height(tkwin_) <= 1)) - return; - - width_ = Tk_Width(tkwin_); - height_ = Tk_Height(tkwin_); - - map(); - - // Create a pixmap the size of the window for double buffering - Pixmap drawable = Tk_GetPixmap(display_, Tk_WindowId(tkwin_), - width_, height_, Tk_Depth(tkwin_)); - - if (cache_ == None || cacheWidth_ != width_ || cacheHeight_ != height_) { - if (cache_ != None) - Tk_FreePixmap(display_, cache_); - cache_ = Tk_GetPixmap(display_, Tk_WindowId(tkwin_), width_, height_, - Tk_Depth(tkwin_)); - cacheWidth_ = width_; - cacheHeight_ = height_; - flags |= CACHE; - } - - // Update cache if needed - if (flags & CACHE) { - drawMargins(cache_); - - switch (legend_->position()) { - case Legend::TOP: - case Legend::BOTTOM: - case Legend::RIGHT: - case Legend::LEFT: - legend_->draw(cache_); - break; - default: - break; - } - - // Draw the background of the plotting area with 3D border - Tk_Fill3DRectangle(tkwin_, cache_, ops->plotBg, - left_-ops->plotBW, - top_-ops->plotBW, - right_-left_+1+2*ops->plotBW, - bottom_-top_+1+2*ops->plotBW, - ops->plotBW, ops->plotRelief); - - drawAxesGrids(cache_); - drawAxes(cache_); - drawAxesLimits(cache_); - - if (!legend_->isRaised()) { - switch (legend_->position()) { - case Legend::PLOT: - case Legend::XY: - legend_->draw(cache_); - break; - default: - break; - } - } - - drawMarkers(cache_, MARKER_UNDER); - drawElements(cache_); - drawActiveElements(cache_); - - if (legend_->isRaised()) { - switch (legend_->position()) { - case Legend::PLOT: - case Legend::XY: - legend_->draw(cache_); - break; - default: - break; - } - } - - flags &= ~CACHE; - } - - XCopyArea(display_, cache_, drawable, drawGC_, 0, 0, Tk_Width(tkwin_), - Tk_Height(tkwin_), 0, 0); - - drawMarkers(drawable, MARKER_ABOVE); - - // Draw 3D border just inside of the focus highlight ring - if ((ops->borderWidth > 0) && (ops->relief != TK_RELIEF_FLAT)) - Tk_Draw3DRectangle(tkwin_, drawable, ops->normalBg, - ops->highlightWidth, ops->highlightWidth, - width_ - 2*ops->highlightWidth, - height_ - 2*ops->highlightWidth, - ops->borderWidth, ops->relief); - - // Draw focus highlight ring - if ((ops->highlightWidth > 0) && (flags & FOCUS)) { - GC gc = Tk_GCForColor(ops->highlightColor, drawable); - Tk_DrawFocusHighlight(tkwin_, gc, ops->highlightWidth, drawable); - } - - // crosshairs - crosshairs_->draw(drawable); - - XCopyArea(display_, drawable, Tk_WindowId(tkwin_), drawGC_, - 0, 0, width_, height_, 0, 0); - - Tk_FreePixmap(display_, drawable); -} - -int Graph::print(const char* ident, PSOutput* psPtr) -{ - GraphOptions* ops = (GraphOptions*)ops_; - PostscriptOptions* pops = (PostscriptOptions*)postscript_->ops_; - - // be sure the window is realized so that relief colors are available - if (flags & REDRAW_PENDING) { - flags |= REDRAW_PENDING; - DisplayGraph(this); - } - - // We need to know how big a graph to print. If the graph hasn't been drawn - // yet, the width and height will be 1. Instead use the requested size of - // the widget. The user can still override this with the -width and -height - // postscript options. - if (pops->reqWidth > 0) - width_ = pops->reqWidth; - else if (width_ < 2) - width_ = Tk_ReqWidth(tkwin_); - - if (pops->reqHeight > 0) - height_ = pops->reqHeight; - else if (height_ < 2) - height_ = Tk_ReqHeight(tkwin_); - - psPtr->computeBBox(width_, height_); - flags |= RESET; - - // Turn on PostScript measurements when computing the graph's layout. - reconfigure(); - - map(); - - int x = left_ - ops->plotBW; - int y = top_ - ops->plotBW; - - int w = (right_ - left_ + 1) + (2*ops->plotBW); - int h = (bottom_ - top_ + 1) + (2*ops->plotBW); - - int result = psPtr->preamble(ident); - if (result != TCL_OK) - goto error; - - psPtr->setFont(ops->titleTextStyle.font); - if (pops->decorations) - psPtr->setBackground(Tk_3DBorderColor(ops->plotBg)); - else - psPtr->setClearBackground(); - - psPtr->fillRectangle(x, y, w, h); - psPtr->append("gsave\n\n"); - - // Start - printMargins(psPtr); - - switch (legend_->position()) { - case Legend::TOP: - case Legend::BOTTOM: - case Legend::RIGHT: - case Legend::LEFT: - legend_->print(psPtr); - break; - default: - break; - } - - printAxesGrids(psPtr); - printAxes(psPtr); - printAxesLimits(psPtr); - - if (!legend_->isRaised()) { - switch (legend_->position()) { - case Legend::PLOT: - case Legend::XY: - legend_->print(psPtr); - break; - default: - break; - } - } - - printMarkers(psPtr, MARKER_UNDER); - printElements(psPtr); - printActiveElements(psPtr); - - if (legend_->isRaised()) { - switch (legend_->position()) { - case Legend::PLOT: - case Legend::XY: - legend_->print(psPtr); - break; - default: - break; - } - } - printMarkers(psPtr, MARKER_ABOVE); - - psPtr->append("\n"); - psPtr->append("% Unset clipping\n"); - psPtr->append("grestore\n\n"); - psPtr->append("showpage\n"); - psPtr->append("%Trailer\n"); - psPtr->append("grestore\n"); - psPtr->append("end\n"); - psPtr->append("%EOF\n"); - - error: - width_ = Tk_Width(tkwin_); - height_ = Tk_Height(tkwin_); - reconfigure(); - - // Redraw the graph in order to re-calculate the layout as soon as - // possible. This is in the case the crosshairs are active. - flags |= LAYOUT; - eventuallyRedraw(); - - return result; -} - -void Graph::eventuallyRedraw() -{ - if (flags & GRAPH_DELETED) - return; - - if (!(flags & REDRAW_PENDING)) { - flags |= REDRAW_PENDING; - Tcl_DoWhenIdle(DisplayGraph, this); - } -} - -void Graph::extents(Region2d* regionPtr) -{ - GraphOptions* ops = (GraphOptions*)ops_; - - regionPtr->left = (double)(hOffset_ - ops->xPad); - regionPtr->top = (double)(vOffset_ - ops->yPad); - regionPtr->right = (double)(hOffset_ + hRange_ + ops->xPad); - regionPtr->bottom = (double)(vOffset_ + vRange_ + ops->yPad); -} - -int Graph::invoke(const Ensemble* ensemble, int cmdIndex, - int objc, Tcl_Obj* const objv[]) -{ - while (cmdIndex < objc) { - int index; - if (Tcl_GetIndexFromObjStruct(interp_, objv[cmdIndex], ensemble, sizeof(ensemble[0]), "command", 0, &index) != TCL_OK) - return TCL_ERROR; - - if (ensemble[index].proc) - return ensemble[index].proc(this, interp_, objc, objv); - - ensemble = ensemble[index].subensemble; - ++cmdIndex; - } - - Tcl_WrongNumArgs(interp_, cmdIndex, objv, "option ?arg ...?"); - return TCL_ERROR; -} - -void Graph::reconfigure() -{ - configure(); - legend_->configure(); - configureElements(); - configureAxes(); - configureMarkers(); -} - -// Margins - -void Graph::drawMargins(Drawable drawable) -{ - GraphOptions* ops = (GraphOptions*)ops_; - XRectangle rects[4]; - - // Draw the four outer rectangles which encompass the plotting - // surface. This clears the surrounding area and clips the plot. - rects[0].x = rects[0].y = rects[3].x = rects[1].x = 0; - rects[0].width = rects[3].width = (short int)width_; - rects[0].height = (short int)top_; - rects[3].y = bottom_; - rects[3].height = height_ - bottom_; - rects[2].y = rects[1].y = top_; - rects[1].width = left_; - rects[2].height = rects[1].height = bottom_ - top_; - rects[2].x = right_; - rects[2].width = width_ - right_; - - Tk_Fill3DRectangle(tkwin_, drawable, ops->normalBg, - rects[0].x, rects[0].y, rects[0].width, rects[0].height, - 0, TK_RELIEF_FLAT); - Tk_Fill3DRectangle(tkwin_, drawable, ops->normalBg, - rects[1].x, rects[1].y, rects[1].width, rects[1].height, - 0, TK_RELIEF_FLAT); - Tk_Fill3DRectangle(tkwin_, drawable, ops->normalBg, - rects[2].x, rects[2].y, rects[2].width, rects[2].height, - 0, TK_RELIEF_FLAT); - Tk_Fill3DRectangle(tkwin_, drawable, ops->normalBg, - rects[3].x, rects[3].y, rects[3].width, rects[3].height, - 0, TK_RELIEF_FLAT); - - // Draw 3D border around the plotting area - if (ops->plotBW > 0) { - int x = left_ - ops->plotBW; - int y = top_ - ops->plotBW; - int w = (right_ - left_) + (2*ops->plotBW); - int h = (bottom_ - top_) + (2*ops->plotBW); - Tk_Draw3DRectangle(tkwin_, drawable, ops->normalBg, - x, y, w, h, ops->plotBW, ops->plotRelief); - } - - if (ops->title) { - TextStyle ts(this, &ops->titleTextStyle); - ts.drawText(drawable, ops->title, titleX_, titleY_); - } -} - -void Graph::printMargins(PSOutput* psPtr) -{ - GraphOptions* ops = (GraphOptions*)ops_; - PostscriptOptions* pops = (PostscriptOptions*)postscript_->ops_; - XRectangle margin[4]; - - margin[0].x = margin[0].y = margin[3].x = margin[1].x = 0; - margin[0].width = margin[3].width = width_; - margin[0].height = top_; - margin[3].y = bottom_; - margin[3].height = height_ - bottom_; - margin[2].y = margin[1].y = top_; - margin[1].width = left_; - margin[2].height = margin[1].height = bottom_ - top_; - margin[2].x = right_; - margin[2].width = width_ - right_; - - // Clear the surrounding margins and clip the plotting surface - if (pops->decorations) - psPtr->setBackground(Tk_3DBorderColor(ops->normalBg)); - else - psPtr->setClearBackground(); - - psPtr->append("% Margins\n"); - psPtr->fillRectangles(margin, 4); - - if (pops->decorations) { - psPtr->append("% Interior 3D border\n"); - if (ops->plotBW > 0) { - int x = left_ - ops->plotBW; - int y = top_ - ops->plotBW; - int w = (right_ - left_) + (2*ops->plotBW); - int h = (bottom_ - top_) + (2*ops->plotBW); - psPtr->print3DRectangle(ops->normalBg, (double)x, (double)y, w, h, - ops->plotBW, ops->plotRelief); - } - } - - if (ops->title) { - psPtr->append("% Graph title\n"); - TextStyle ts(this, &ops->titleTextStyle); - ts.printText(psPtr, ops->title, (double)titleX_, (double)titleY_); - } -} - -// Pens - -void Graph::destroyPens() -{ - Tcl_HashSearch iter; - for (Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&penTable_, &iter); - hPtr; hPtr = Tcl_NextHashEntry(&iter)) { - Pen* penPtr = (Pen*)Tcl_GetHashValue(hPtr); - delete penPtr; - } - Tcl_DeleteHashTable(&penTable_); -} - -int Graph::getPen(Tcl_Obj* objPtr, Pen** penPtrPtr) -{ - *penPtrPtr = NULL; - const char *name = Tcl_GetString(objPtr); - if (!name || !name[0]) - return TCL_ERROR; - - Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&penTable_, name); - if (!hPtr) { - Tcl_AppendResult(interp_, "can't find pen \"", name, "\" in \"", - Tk_PathName(tkwin_), "\"", NULL); - return TCL_ERROR; - } - - *penPtrPtr = (Pen*)Tcl_GetHashValue(hPtr); - - return TCL_OK; -} - -// Elements - -void Graph::destroyElements() -{ - Tcl_HashSearch iter; - for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&elements_.table, &iter); - hPtr; hPtr = Tcl_NextHashEntry(&iter)) { - Element* elemPtr = (Element*)Tcl_GetHashValue(hPtr); - legend_->removeElement(elemPtr); - delete elemPtr; - } - - Tcl_DeleteHashTable(&elements_.table); - Tcl_DeleteHashTable(&elements_.tagTable); - delete elements_.displayList; -} - -void Graph::configureElements() -{ - for (ChainLink* link = Chain_FirstLink(elements_.displayList); link; - link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - elemPtr->configure(); - } -} - -void Graph::mapElements() -{ - for (ChainLink* link = Chain_FirstLink(elements_.displayList); link; - link = Chain_NextLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - elemPtr->map(); - } -} - -void Graph::drawElements(Drawable drawable) -{ - // Draw with respect to the stacking order - for (ChainLink* link=Chain_LastLink(elements_.displayList); link; - link = Chain_PrevLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - elemPtr->draw(drawable); - } -} - -void Graph::drawActiveElements(Drawable drawable) -{ - for (ChainLink* link = Chain_LastLink(elements_.displayList); link; - link = Chain_PrevLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - elemPtr->drawActive(drawable); - } -} - -void Graph::printElements(PSOutput* psPtr) -{ - for (ChainLink* link = Chain_LastLink(elements_.displayList); link; - link = Chain_PrevLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - elemPtr->print(psPtr); - } -} - -void Graph::printActiveElements(PSOutput* psPtr) -{ - for (ChainLink* link = Chain_LastLink(elements_.displayList); link; - link = Chain_PrevLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - elemPtr->printActive(psPtr); - } -} - -int Graph::getElement(Tcl_Obj *objPtr, Element **elemPtrPtr) -{ - *elemPtrPtr =NULL; - const char* name = Tcl_GetString(objPtr); - if (!name || !name[0]) - return TCL_ERROR; - - Tcl_HashEntry*hPtr = Tcl_FindHashEntry(&elements_.table, name); - if (!hPtr) { - Tcl_AppendResult(interp_, "can't find element \"", name, "\" in \"", - Tk_PathName(tkwin_), "\"", NULL); - return TCL_ERROR; - } - - *elemPtrPtr = (Element*)Tcl_GetHashValue(hPtr); - return TCL_OK; -} - -ClientData Graph::elementTag(const char *tagName) -{ - int isNew; - Tcl_HashEntry* hPtr = - Tcl_CreateHashEntry(&elements_.tagTable, tagName, &isNew); - return Tcl_GetHashKey(&elements_.tagTable, hPtr); -} - -// Markers - -void Graph::destroyMarkers() -{ - Tcl_HashSearch iter; - for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&markers_.table, &iter); - hPtr; hPtr=Tcl_NextHashEntry(&iter)) { - Marker* markerPtr = (Marker*)Tcl_GetHashValue(hPtr); - delete markerPtr; - } - Tcl_DeleteHashTable(&markers_.table); - Tcl_DeleteHashTable(&markers_.tagTable); - delete markers_.displayList; -} - - -void Graph::configureMarkers() -{ - for (ChainLink* link = Chain_FirstLink(markers_.displayList); link; - link = Chain_NextLink(link)) { - Marker* markerPtr = (Marker*)Chain_GetValue(link); - markerPtr->configure(); - } -} - -void Graph::mapMarkers() -{ - for (ChainLink* link = Chain_FirstLink(markers_.displayList); link; - link = Chain_NextLink(link)) { - Marker* markerPtr = (Marker*)Chain_GetValue(link); - MarkerOptions* mops = (MarkerOptions*)markerPtr->ops(); - - if (mops->hide) - continue; - - if ((flags & MAP_MARKERS) || (markerPtr->flags & MAP_ITEM)) { - markerPtr->map(); - markerPtr->flags &= ~MAP_ITEM; - } - } - - flags &= ~MAP_MARKERS; -} - -void Graph::drawMarkers(Drawable drawable, int under) -{ - for (ChainLink* link = Chain_LastLink(markers_.displayList); link; - link = Chain_PrevLink(link)) { - Marker* markerPtr = (Marker*)Chain_GetValue(link); - MarkerOptions* mops = (MarkerOptions*)markerPtr->ops(); - - if ((mops->drawUnder != under) || markerPtr->clipped_ || mops->hide) - continue; - - if (isElementHidden(markerPtr)) - continue; - - markerPtr->draw(drawable); - } -} - -void Graph::printMarkers(PSOutput* psPtr, int under) -{ - for (ChainLink* link = Chain_LastLink(markers_.displayList); link; - link = Chain_PrevLink(link)) { - Marker* markerPtr = (Marker*)Chain_GetValue(link); - MarkerOptions* mops = (MarkerOptions*)markerPtr->ops(); - if (mops->drawUnder != under) - continue; - - if (mops->hide) - continue; - - if (isElementHidden(markerPtr)) - continue; - - psPtr->format("%% Marker \"%s\" is a %s.\n", - markerPtr->name_, markerPtr->className()); - markerPtr->print(psPtr); - } -} - -ClientData Graph::markerTag(const char* tagName) -{ - int isNew; - Tcl_HashEntry* hPtr = Tcl_CreateHashEntry(&markers_.tagTable, tagName,&isNew); - return Tcl_GetHashKey(&markers_.tagTable, hPtr); -} - -Marker* Graph::nearestMarker(int x, int y, int under) -{ - Point2d point; - point.x = (double)x; - point.y = (double)y; - for (ChainLink* link = Chain_FirstLink(markers_.displayList); link; - link = Chain_NextLink(link)) { - Marker* markerPtr = (Marker*)Chain_GetValue(link); - MarkerOptions* mops = (MarkerOptions*)markerPtr->ops(); - - if ((markerPtr->flags & MAP_ITEM) || mops->hide) - continue; - - if (isElementHidden(markerPtr)) - continue; - - if (mops->drawUnder == under) - if (markerPtr->pointIn(&point)) - return markerPtr; - } - return NULL; -} - -int Graph::isElementHidden(Marker* markerPtr) -{ - MarkerOptions* mops = (MarkerOptions*)markerPtr->ops(); - - if (mops->elemName) { - Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&elements_.table, mops->elemName); - if (hPtr) { - Element* elemPtr = (Element*)Tcl_GetHashValue(hPtr); - ElementOptions* eops = (ElementOptions*)elemPtr->ops(); - if (!elemPtr->link || eops->hide) - return 1; - } - } - return 0; -} - -// Axis - -int Graph::createAxes() -{ - for (int ii=0; ii<4; ii++) { - int isNew; - Tcl_HashEntry* hPtr = - Tcl_CreateHashEntry(&axes_.table, axisNames[ii].name, &isNew); - Chain* chain = new Chain(); - - Axis* axisPtr = new Axis(this, axisNames[ii].name, ii, hPtr); - if (!axisPtr) - return TCL_ERROR; - AxisOptions* ops = (AxisOptions*)axisPtr->ops(); - - Tcl_SetHashValue(hPtr, axisPtr); - - axisPtr->refCount_ = 1; - axisPtr->use_ =1; - - axisPtr->setClass(!(ii&1) ? CID_AXIS_X : CID_AXIS_Y); - - if (Tk_InitOptions(interp_, (char*)axisPtr->ops(), - axisPtr->optionTable(), tkwin_) != TCL_OK) - return TCL_ERROR; - - if (axisPtr->configure() != TCL_OK) - return TCL_ERROR; - - if ((axisPtr->margin_ == MARGIN_RIGHT) || (axisPtr->margin_ == MARGIN_TOP)) - ops->hide = 1; - - axisChain_[ii] = chain; - axisPtr->link = chain->append(axisPtr); - axisPtr->chain = chain; - } - return TCL_OK; -} - -int Graph::createAxis(int objc, Tcl_Obj* const objv[]) -{ - char *string = Tcl_GetString(objv[3]); - if (string[0] == '-') { - Tcl_AppendResult(interp_, "name of axis \"", string, - "\" can't start with a '-'", NULL); - return TCL_ERROR; - } - - int isNew; - Tcl_HashEntry* hPtr = Tcl_CreateHashEntry(&axes_.table, string, &isNew); - if (!isNew) { - Tcl_AppendResult(interp_, "axis \"", string, "\" already exists in \"", - Tcl_GetString(objv[0]), "\"", NULL); - return TCL_ERROR; - } - - Axis* axisPtr = new Axis(this, Tcl_GetString(objv[3]), MARGIN_NONE, hPtr); - if (!axisPtr) - return TCL_ERROR; - - Tcl_SetHashValue(hPtr, axisPtr); - - if ((Tk_InitOptions(interp_, (char*)axisPtr->ops(), axisPtr->optionTable(), tkwin_) != TCL_OK) || (AxisObjConfigure(axisPtr, interp_, objc-4, objv+4) != TCL_OK)) { - delete axisPtr; - return TCL_ERROR; - } - - return TCL_OK; -} - -void Graph::destroyAxes() -{ - Tcl_HashSearch cursor; - for (Tcl_HashEntry *hPtr=Tcl_FirstHashEntry(&axes_.table, &cursor); - hPtr; hPtr=Tcl_NextHashEntry(&cursor)) { - Axis *axisPtr = (Axis*)Tcl_GetHashValue(hPtr); - delete axisPtr; - } - Tcl_DeleteHashTable(&axes_.table); - - for (int ii=0; ii<4; ii++) - delete axisChain_[ii]; - - Tcl_DeleteHashTable(&axes_.tagTable); - delete axes_.displayList; -} - -void Graph::configureAxes() -{ - Tcl_HashSearch cursor; - for (Tcl_HashEntry *hPtr=Tcl_FirstHashEntry(&axes_.table, &cursor); - hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { - Axis *axisPtr = (Axis*)Tcl_GetHashValue(hPtr); - axisPtr->configure(); - } -} - -void Graph::mapAxes() -{ - GraphOptions* ops = (GraphOptions*)ops_; - - for (int ii=0; ii<4; ii++) { - int count =0; - int offset =0; - - Chain* chain = ops->margins[ii].axes; - for (ChainLink* link=Chain_FirstLink(chain); link; - link = Chain_NextLink(link)) { - Axis *axisPtr = (Axis*)Chain_GetValue(link); - AxisOptions* aops = (AxisOptions*)axisPtr->ops(); - if (!axisPtr->use_) - continue; - - if (aops->reqNumMajorTicks <= 0) - aops->reqNumMajorTicks = 4; - - if (ops->stackAxes) - axisPtr->mapStacked(count, ii); - else - axisPtr->map(offset, ii); - - if (aops->showGrid) - axisPtr->mapGridlines(); - - offset += axisPtr->isHorizontal() ? axisPtr->height_ : axisPtr->width_; - count++; - } - } -} - -void Graph::drawAxes(Drawable drawable) -{ - GraphOptions* ops = (GraphOptions*)ops_; - - for (int ii=0; ii<4; ii++) { - for (ChainLink* link = Chain_LastLink(ops->margins[ii].axes); link; - link = Chain_PrevLink(link)) { - Axis *axisPtr = (Axis*)Chain_GetValue(link); - axisPtr->draw(drawable); - } - } -} - -void Graph::drawAxesLimits(Drawable drawable) -{ - Tcl_HashSearch cursor; - for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&axes_.table, &cursor); - hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { - Axis *axisPtr = (Axis*)Tcl_GetHashValue(hPtr); - axisPtr->drawLimits(drawable); - } -} - -void Graph::drawAxesGrids(Drawable drawable) -{ - GraphOptions* ops = (GraphOptions*)ops_; - - for (int ii=0; ii<4; ii++) { - for (ChainLink* link = Chain_FirstLink(ops->margins[ii].axes); link; - link = Chain_NextLink(link)) { - Axis *axisPtr = (Axis*)Chain_GetValue(link); - axisPtr->drawGrids(drawable); - } - } -} - -void Graph::printAxes(PSOutput* psPtr) -{ - GraphOptions* ops = (GraphOptions*)ops_; - - for (Margin *mp = ops->margins, *mend = mp + 4; mp < mend; mp++) { - for (ChainLink* link = Chain_FirstLink(mp->axes); link; - link = Chain_NextLink(link)) { - Axis *axisPtr = (Axis*)Chain_GetValue(link); - axisPtr->print(psPtr); - } - } -} - -void Graph::printAxesGrids(PSOutput* psPtr) -{ - GraphOptions* ops = (GraphOptions*)ops_; - - for (int ii=0; ii<4; ii++) { - for (ChainLink* link = Chain_FirstLink(ops->margins[ii].axes); link; - link = Chain_NextLink(link)) { - Axis *axisPtr = (Axis*)Chain_GetValue(link); - axisPtr->printGrids(psPtr); - } - } -} - -void Graph::printAxesLimits(PSOutput* psPtr) -{ - Tcl_HashSearch cursor; - for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&axes_.table, &cursor); - hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { - Axis *axisPtr = (Axis*)Tcl_GetHashValue(hPtr); - axisPtr->printLimits(psPtr); - } -} - -int Graph::getAxis(Tcl_Obj *objPtr, Axis **axisPtrPtr) -{ - *axisPtrPtr = NULL; - const char* name = Tcl_GetString(objPtr); - if (!name || !name[0]) - return TCL_ERROR; - - Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&axes_.table, name); - if (!hPtr) { - Tcl_AppendResult(interp_, "can't find axis \"", name, "\" in \"", - Tk_PathName(tkwin_), "\"", NULL); - return TCL_ERROR; - } - - *axisPtrPtr = (Axis*)Tcl_GetHashValue(hPtr); - return TCL_OK; -} - -ClientData Graph::axisTag(const char *tagName) -{ - int isNew; - Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&axes_.tagTable, tagName, &isNew); - return Tcl_GetHashKey(&axes_.tagTable, hPtr); -} - -void Graph::adjustAxes() -{ - GraphOptions* ops = (GraphOptions*)ops_; - - if (ops->inverted) { - ops->leftMargin.axes = axisChain_[0]; - ops->bottomMargin.axes = axisChain_[1]; - ops->rightMargin.axes = axisChain_[2]; - ops->topMargin.axes = axisChain_[3]; - } - else { - ops->leftMargin.axes = axisChain_[1]; - ops->bottomMargin.axes = axisChain_[0]; - ops->rightMargin.axes = axisChain_[3]; - ops->topMargin.axes = axisChain_[2]; - } -} - -Point2d Graph::map2D(double x, double y, Axis* xAxis, Axis* yAxis) -{ - GraphOptions* ops = (GraphOptions*)ops_; - - Point2d point; - if (ops->inverted) { - point.x = yAxis->hMap(y); - point.y = xAxis->vMap(x); - } - else { - point.x = xAxis->hMap(x); - point.y = yAxis->vMap(y); - } - return point; -} - -Point2d Graph::invMap2D(double x, double y, Axis* xAxis, Axis* yAxis) -{ - GraphOptions* ops = (GraphOptions*)ops_; - - Point2d point; - if (ops->inverted) { - point.x = xAxis->invVMap(y); - point.y = yAxis->invHMap(x); - } - else { - point.x = xAxis->invHMap(x); - point.y = yAxis->invVMap(y); - } - return point; -} - -void Graph::resetAxes() -{ - // Step 1: Reset all axes. Initialize the data limits of the axis to - // impossible values. - Tcl_HashSearch cursor; - for (Tcl_HashEntry* hPtr = Tcl_FirstHashEntry(&axes_.table, &cursor); - hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { - Axis *axisPtr = (Axis*)Tcl_GetHashValue(hPtr); - axisPtr->min_ = axisPtr->valueRange_.min = DBL_MAX; - axisPtr->max_ = axisPtr->valueRange_.max = -DBL_MAX; - } - - // Step 2: For each element that's to be displayed, get the smallest - // and largest data values mapped to each X and Y-axis. This - // will be the axis limits if the user doesn't override them - // with -min and -max options. - for (ChainLink* link = Chain_FirstLink(elements_.displayList); link; - link = Chain_NextLink(link)) { - Region2d exts; - - Element* elemPtr = (Element*)Chain_GetValue(link); - ElementOptions* elemops = (ElementOptions*)elemPtr->ops(); - elemPtr->extents(&exts); - elemops->xAxis->getDataLimits(exts.left, exts.right); - elemops->yAxis->getDataLimits(exts.top, exts.bottom); - } - - // Step 3: Now that we know the range of data values for each axis, - // set axis limits and compute a sweep to generate tick values. - for (Tcl_HashEntry* hPtr = Tcl_FirstHashEntry(&axes_.table, &cursor); - hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { - Axis *axisPtr = (Axis*)Tcl_GetHashValue(hPtr); - AxisOptions* ops = (AxisOptions*)axisPtr->ops(); - axisPtr->fixRange(); - - double min = axisPtr->min_; - double max = axisPtr->max_; - if ((!isnan(axisPtr->scrollMin_)) && (min < axisPtr->scrollMin_)) - min = axisPtr->scrollMin_; - - if ((!isnan(axisPtr->scrollMax_)) && (max > axisPtr->scrollMax_)) - max = axisPtr->scrollMax_; - - if (ops->logScale) - axisPtr->logScale(min, max); - else - axisPtr->linearScale(min, max); - } -} - -Axis* Graph::nearestAxis(int x, int y) -{ - Tcl_HashSearch cursor; - for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&axes_.table, &cursor); - hPtr; hPtr = Tcl_NextHashEntry(&cursor)) { - Axis *axisPtr = (Axis*)Tcl_GetHashValue(hPtr); - AxisOptions* ops = (AxisOptions*)axisPtr->ops(); - if (ops->hide || !axisPtr->use_) - continue; - - if (ops->showTicks) { - for (ChainLink* link = Chain_FirstLink(axisPtr->tickLabels_); link; - link = Chain_NextLink(link)) { - TickLabel *labelPtr = (TickLabel*)Chain_GetValue(link); - double rw, rh; - Point2d bbox[5]; - getBoundingBox(labelPtr->width, labelPtr->height, ops->tickAngle, - &rw, &rh, bbox); - Point2d t; - t = anchorPoint(labelPtr->anchorPos.x, labelPtr->anchorPos.y, - rw, rh, axisPtr->tickAnchor_); - t.x = x - t.x - (rw * 0.5); - t.y = y - t.y - (rh * 0.5); - - bbox[4] = bbox[0]; - if (pointInPolygon(&t, bbox, 5)) { - return axisPtr; - } - } - } - - if (ops->title) { - int w, h; - double rw, rh; - Point2d bbox[5]; - getTextExtents(ops->titleFont, ops->title, -1, &w, &h); - getBoundingBox(w, h, axisPtr->titleAngle_, &rw, &rh, bbox); - Point2d t = anchorPoint(axisPtr->titlePos_.x, axisPtr->titlePos_.y, - rw, rh, axisPtr->titleAnchor_); - // Translate the point so that the 0,0 is the upper left - // corner of the bounding box - t.x = x - t.x - (rw * 0.5); - t.y = y - t.y - (rh * 0.5); - - bbox[4] = bbox[0]; - if (pointInPolygon(&t, bbox, 5)) { - return axisPtr; - } - } - if (ops->lineWidth > 0) { - if ((x <= axisPtr->right_) && (x >= axisPtr->left_) && - (y <= axisPtr->bottom_) && (y >= axisPtr->top_)) { - return axisPtr; - } - } - } - - return NULL; -} - -// Bind - -const char** Graph::getTags(ClientData object, ClassId classId, int* num) -{ - const char** tags =NULL; - - switch (classId) { - case CID_ELEM_BAR: - case CID_ELEM_LINE: - { - Element* ptr = (Element*)object; - ElementOptions* ops = (ElementOptions*)ptr->ops(); - int cnt =0; - for (const char** pp=ops->tags; *pp; pp++) - cnt++; - cnt +=2; - - tags = new const char*[cnt]; - tags[0] = (const char*)elementTag(ptr->name_); - tags[1] = (const char*)elementTag(ptr->className()); - int ii=2; - for (const char** pp = ops->tags; *pp; pp++, ii++) - tags[ii] = (const char*)elementTag(*pp); - - *num = cnt; - return tags; - } - break; - case CID_AXIS_X: - case CID_AXIS_Y: - { - Axis* ptr = (Axis*)object; - AxisOptions* ops = (AxisOptions*)ptr->ops(); - int cnt =0; - for (const char** pp=ops->tags; *pp; pp++) - cnt++; - cnt +=2; - - tags = new const char*[cnt]; - tags[0] = (const char*)axisTag(ptr->name_); - tags[1] = (const char*)axisTag(ptr->className()); - int ii=2; - for (const char** pp = ops->tags; *pp; pp++, ii++) - tags[ii] = (const char*)axisTag(*pp); - - *num = cnt; - return tags; - } - break; - case CID_MARKER_BITMAP: - case CID_MARKER_LINE: - case CID_MARKER_POLYGON: - case CID_MARKER_TEXT: - { - Marker* ptr = (Marker*)object; - MarkerOptions* ops = (MarkerOptions*)ptr->ops(); - int cnt =0; - for (const char** pp=ops->tags; *pp; pp++) - cnt++; - cnt +=2; - - tags = new const char*[cnt]; - tags[0] = (const char*)markerTag(ptr->name_); - tags[1] = (const char*)markerTag(ptr->className()); - int ii=2; - for (const char** pp = ops->tags; *pp; pp++, ii++) - tags[ii] = (const char*)markerTag(*pp); - - *num = cnt; - return tags; - } - break; - default: - break; - } - - return NULL; -} - -ClientData Graph::pickEntry(int xx, int yy, ClassId* classIdPtr) -{ - if (flags & (LAYOUT | MAP_MARKERS)) { - *classIdPtr = CID_NONE; - return NULL; - } - - // Sample coordinate is in one of the graph margins. Can only pick an axis. - Region2d exts; - extents(&exts); - if (xx>=exts.right || xx=exts.bottom || yyclassId(); - return axisPtr; - } - } - - // From top-to-bottom check: - // 1. markers drawn on top (-under false). - // 2. elements using its display list back to front. - // 3. markers drawn under element (-under true). - Marker* markerPtr = nearestMarker(xx, yy, 0); - if (markerPtr) { - *classIdPtr = markerPtr->classId(); - return markerPtr; - } - - GraphOptions* ops = (GraphOptions*)ops_; - ClosestSearch* searchPtr = &ops->search; - searchPtr->index = -1; - searchPtr->x = xx; - searchPtr->y = yy; - searchPtr->dist = (double)(searchPtr->halo + 1); - - for (ChainLink* link = Chain_LastLink(elements_.displayList); link; - link = Chain_PrevLink(link)) { - Element* elemPtr = (Element*)Chain_GetValue(link); - ElementOptions* eops = (ElementOptions*)elemPtr->ops(); - if (eops->hide) - continue; - elemPtr->closest(); - } - - // Found an element within the minimum halo distance. - if (searchPtr->dist <= (double)searchPtr->halo) { - *classIdPtr = searchPtr->elemPtr->classId(); - return searchPtr->elemPtr; - } - - markerPtr = nearestMarker(xx, yy, 1); - if (markerPtr) { - *classIdPtr = markerPtr->classId(); - return markerPtr; - } - - *classIdPtr = CID_NONE; - return NULL; -} - -int Graph::getXY(const char* string, int* xPtr, int* yPtr) -{ - if (!string || !*string) { - *xPtr = -SHRT_MAX; - *yPtr = -SHRT_MAX; - return TCL_OK; - } - - if (*string != '@') { - Tcl_AppendResult(interp_, "bad position \"", string, - "\": should be \"@x,y\"", (char *)NULL); - return TCL_ERROR; - } - - char* comma = (char*)strchr(string + 1, ','); - if (!comma) { - Tcl_AppendResult(interp_, "bad position \"", string, - "\": should be \"@x,y\"", (char *)NULL); - return TCL_ERROR; - } - - *comma = '\0'; - int x, y; - int 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; -} - -// Graphics - -void Graph::drawSegments(Drawable drawable, GC gc, - Segment2d* segments, int nSegments) -{ - for (Segment2d *sp = segments, *send = sp + nSegments; sp < send; sp++) - XDrawLine(display_, drawable, gc, sp->p.x, sp->p.y, sp->q.x, sp->q.y); -} - -GC Graph::getPrivateGC(unsigned long gcMask, XGCValues *valuePtr) -{ - Pixmap pixmap = None; - Drawable drawable = Tk_WindowId(tkwin_); - Display* display = Tk_Display(tkwin_); - if (drawable == None) - drawable = RootWindow(Tk_Display(tkwin_),Tk_ScreenNumber(tkwin_)); - - GC gc = XCreateGC(display, drawable, gcMask, valuePtr); - if (pixmap != None) - Tk_FreePixmap(display, pixmap); - - return gc; -} - -void Graph::freePrivateGC(GC gc) -{ - Tk_FreeXId(display_, (XID)XGContextFromGC(gc)); - XFreeGC(display_, gc); -} - -void Graph::setDashes(GC gc, Dashes* dashesPtr) -{ - XSetDashes(display_, gc, dashesPtr->offset, (const char*)dashesPtr->values, - (int)strlen((char*)dashesPtr->values)); -} diff --git a/src/tkbltGraph.h b/src/tkbltGraph.h deleted file mode 100644 index 8aece0f..0000000 --- a/src/tkbltGraph.h +++ /dev/null @@ -1,256 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGraph_h__ -#define __BltGraph_h__ - -#include - -#include "tkbltChain.h" -#include "tkbltGrMisc.h" -#include "tkbltGrText.h" - -typedef struct Ensemble { - const char *name; - Tcl_ObjCmdProc *proc; - const struct Ensemble *subensemble; -} Ensemble; - -namespace Blt { - class Axis; - class BindTable; - class Crosshairs; - class Element; - class Marker; - class Legend; - class Pen; - class Postscript; - class PSOutput; - - class Pick { - public: - virtual ClientData pickEntry(int, int, ClassId*) =0; - }; - - typedef struct { - int halo; - int mode; - int x; - int y; - int along; - - Element* elemPtr; - Point2d point; - int index; - double dist; - } ClosestSearch; - - typedef struct { - short int width; - short int height; - short int axesOffset; - short int axesTitleLength; - short int maxTickWidth; - short int maxTickHeight; - unsigned int nAxes; - Chain* axes; - int reqSize; - int site; - } Margin; - - typedef struct { - Tcl_HashTable table; - Chain* displayList; - Tcl_HashTable tagTable; - } Component; - -#define rightMargin margins[MARGIN_RIGHT] -#define leftMargin margins[MARGIN_LEFT] -#define topMargin margins[MARGIN_TOP] -#define bottomMargin margins[MARGIN_BOTTOM] - - typedef struct { - double aspect; - Tk_3DBorder normalBg; - int borderWidth; - Margin margins[4]; - Tk_Cursor cursor; - TextStyleOptions titleTextStyle; - int reqHeight; - XColor* highlightBgColor; - XColor* highlightColor; - int highlightWidth; - int inverted; - Tk_3DBorder plotBg; - int plotBW; - int xPad; - int yPad; - int plotRelief; - int relief; - ClosestSearch search; - int stackAxes; - const char *takeFocus; // nor used in C code - const char *title; - int reqWidth; - int reqPlotWidth; - int reqPlotHeight; - } GraphOptions; - - class Graph : public Pick { - public: - Tcl_Interp* interp_; - Tk_Window tkwin_; - Display *display_; - Tcl_Command cmdToken_; - Tk_OptionTable optionTable_; - void* ops_; - int valid_; - - unsigned int flags; - int nextMarkerId_; - - Component axes_; - Component elements_; - Component markers_; - Tcl_HashTable penTable_; - BindTable* bindTable_; - Chain* axisChain_[4]; - - Legend* legend_; - Crosshairs* crosshairs_; - Postscript* postscript_; - - int inset_; - short int titleX_; - short int titleY_; - short int titleWidth_; - short int titleHeight_; - int width_; - int height_; - short int left_; - short int right_; - short int top_; - short int bottom_; - Axis* focusPtr_; - int halo_; - GC drawGC_; - int vRange_; - int hRange_; - int vOffset_; - int hOffset_; - float vScale_; - float hScale_; - Pixmap cache_; - short int cacheWidth_; - short int cacheHeight_; - - protected: - void layoutGraph(); - - void drawMargins(Drawable); - void printMargins(PSOutput*); - int getMarginGeometry(Margin*); - - void destroyPens(); - - void destroyElements(); - void configureElements(); - virtual void mapElements(); - void drawElements(Drawable); - void drawActiveElements(Drawable); - void printElements(PSOutput*); - void printActiveElements(PSOutput*); - - void destroyMarkers(); - void configureMarkers(); - void mapMarkers(); - void drawMarkers(Drawable, int); - void printMarkers(PSOutput*, int); - - int createAxes(); - void destroyAxes(); - void configureAxes(); - void mapAxes(); - void drawAxes(Drawable); - void drawAxesLimits(Drawable); - void drawAxesGrids(Drawable); - void adjustAxes(); - - public: - Graph(ClientData, Tcl_Interp*, int, Tcl_Obj* const []); - virtual ~Graph(); - - virtual int configure(); - void map(); - void draw(); - void eventuallyRedraw(); - int print(const char*, PSOutput*); - void extents(Region2d*); - int invoke(const Ensemble*, int, int, Tcl_Obj* const []); - void reconfigure(); - - int createAxis(int, Tcl_Obj* const []); - void printAxes(PSOutput*); - void printAxesGrids(PSOutput*); - void printAxesLimits(PSOutput*); - int getAxis(Tcl_Obj*, Axis**); - ClientData axisTag(const char*); - Point2d map2D(double, double, Axis*, Axis*); - Point2d invMap2D(double, double, Axis*, Axis*); - virtual void resetAxes(); - Axis* nearestAxis(int, int); - - ClientData markerTag(const char*); - Marker* nearestMarker(int, int, int); - int isElementHidden(Marker*); - - virtual int createElement(int, Tcl_Obj* const []) =0; - int getElement(Tcl_Obj*, Element**); - ClientData elementTag(const char*); - - virtual int createPen(const char*, int, Tcl_Obj* const []) =0; - int getPen(Tcl_Obj*, Pen**); - - int getXY(const char*, int*, int*); - void getTextExtents(Tk_Font, const char*, int, int*, int*); - void getBoundingBox(int, int, float, double*, double*, Point2d*); - Point2d anchorPoint(double, double, double, double, Tk_Anchor); - - const char** getTags(ClientData, ClassId, int*); - ClientData pickEntry(int, int, ClassId*); - - void drawSegments(Drawable, GC, Segment2d*, int); - void setDashes(GC, Dashes*); - - GC getPrivateGC(unsigned long, XGCValues*); - void freePrivateGC(GC); - }; -}; - -#endif diff --git a/src/tkbltGraphBar.C b/src/tkbltGraphBar.C deleted file mode 100644 index dc8664c..0000000 --- a/src/tkbltGraphBar.C +++ /dev/null @@ -1,518 +0,0 @@ -/* - * 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 - -#include "tkbltGraphBar.h" -#include "tkbltGraphOp.h" - -#include "tkbltGrAxis.h" -#include "tkbltGrXAxisOp.h" -#include "tkbltGrPen.h" -#include "tkbltGrPenOp.h" -#include "tkbltGrPenBar.h" -#include "tkbltGrPenLine.h" -#include "tkbltGrElem.h" -#include "tkbltGrElemOp.h" -#include "tkbltGrElemBar.h" -#include "tkbltGrElemLine.h" -#include "tkbltGrMarker.h" -#include "tkbltGrLegd.h" -#include "tkbltGrHairs.h" -#include "tkbltGrPostscript.h" -#include "tkbltGrDef.h" - -using namespace Blt; - -// BarGroup - -BarGroup::BarGroup() -{ - nSegments =0; - xAxis =NULL; - yAxis =NULL; - sum =0; - count =0; - lastY =0; - index =0; -} - -// BarGraph - -static const char* barmodeObjOption[] = - {"normal", "stacked", "aligned", "overlap", NULL}; -static const char* searchModeObjOption[] = {"points", "traces", "auto", NULL}; -static const char* searchAlongObjOption[] = {"x", "y", "both", NULL}; - -static Tk_OptionSpec optionSpecs[] = { - {TK_OPTION_DOUBLE, "-aspect", "aspect", "Aspect", - "0", -1, Tk_Offset(BarGraphOptions, aspect), 0, NULL, RESET}, - {TK_OPTION_BORDER, "-background", "background", "Background", - STD_NORMAL_BACKGROUND, -1, Tk_Offset(BarGraphOptions, normalBg), - 0, NULL, CACHE}, - {TK_OPTION_STRING_TABLE, "-barmode", "barMode", "BarMode", - "normal", -1, Tk_Offset(BarGraphOptions, barMode), - 0, &barmodeObjOption, RESET}, - {TK_OPTION_DOUBLE, "-barwidth", "barWidth", "BarWidth", - ".9", -1, Tk_Offset(BarGraphOptions, barWidth), 0, NULL, RESET}, - {TK_OPTION_DOUBLE, "-baseline", "baseline", "Baseline", - "0", -1, Tk_Offset(BarGraphOptions, baseline), 0, NULL, RESET}, - {TK_OPTION_SYNONYM, "-bd", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-borderwidth", 0}, - {TK_OPTION_SYNONYM, "-bg", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-background", 0}, - {TK_OPTION_SYNONYM, "-bm", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-bottommargin", 0}, - {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", - STD_BORDERWIDTH, -1, Tk_Offset(BarGraphOptions, borderWidth), - 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-bottommargin", "bottomMargin", "BottomMargin", - "0", -1, Tk_Offset(BarGraphOptions, bottomMargin.reqSize), 0, NULL, RESET}, - {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", - "crosshair", -1, Tk_Offset(BarGraphOptions, cursor), - TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_SYNONYM, "-fg", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-foreground", 0}, - {TK_OPTION_FONT, "-font", "font", "Font", - STD_FONT_MEDIUM, -1, Tk_Offset(BarGraphOptions, titleTextStyle.font), - 0, NULL, RESET}, - {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(BarGraphOptions, titleTextStyle.color), - 0, NULL, CACHE}, - {TK_OPTION_SYNONYM, "-halo", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-searchhalo", 0}, - {TK_OPTION_PIXELS, "-height", "height", "Height", - "4i", -1, Tk_Offset(BarGraphOptions, reqHeight), 0, NULL, RESET}, - {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground", - "HighlightBackground", - STD_NORMAL_BACKGROUND, -1, Tk_Offset(BarGraphOptions, highlightBgColor), - 0, NULL, CACHE}, - {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(BarGraphOptions, highlightColor), - 0, NULL, CACHE}, - {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness", - "HighlightThickness", - "2", -1, Tk_Offset(BarGraphOptions, highlightWidth), 0, NULL, RESET}, - {TK_OPTION_BOOLEAN, "-invertxy", "invertXY", "InvertXY", - "no", -1, Tk_Offset(BarGraphOptions, inverted), 0, NULL, RESET}, - {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify", - "center", -1, Tk_Offset(BarGraphOptions, titleTextStyle.justify), - 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-leftmargin", "leftMargin", "Margin", - "0", -1, Tk_Offset(BarGraphOptions, leftMargin.reqSize), 0, NULL, RESET}, - {TK_OPTION_SYNONYM, "-lm", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-leftmargin", 0}, - {TK_OPTION_BORDER, "-plotbackground", "plotbackground", "PlotBackground", - STD_NORMAL_BACKGROUND, -1, Tk_Offset(BarGraphOptions, plotBg), - 0, NULL, CACHE}, - {TK_OPTION_PIXELS, "-plotborderwidth", "plotBorderWidth", "PlotBorderWidth", - STD_BORDERWIDTH, -1, Tk_Offset(BarGraphOptions, plotBW), 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-plotpadx", "plotPadX", "PlotPad", - "0", -1, Tk_Offset(BarGraphOptions, xPad), 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-plotpady", "plotPadY", "PlotPad", - "0", -1, Tk_Offset(BarGraphOptions, yPad), 0, NULL, RESET}, - {TK_OPTION_RELIEF, "-plotrelief", "plotRelief", "Relief", - "flat", -1, Tk_Offset(BarGraphOptions, plotRelief), 0, NULL, RESET}, - {TK_OPTION_RELIEF, "-relief", "relief", "Relief", - "flat", -1, Tk_Offset(BarGraphOptions, relief), 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-rightmargin", "rightMargin", "Margin", - "0", -1, Tk_Offset(BarGraphOptions, rightMargin.reqSize), 0, NULL, RESET}, - {TK_OPTION_SYNONYM, "-rm", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-rightmargin", 0}, - {TK_OPTION_PIXELS, "-searchhalo", "searchhalo", "SearchHalo", - "2m", -1, Tk_Offset(BarGraphOptions, search.halo), 0, NULL, 0}, - {TK_OPTION_STRING_TABLE, "-searchmode", "searchMode", "SearchMode", - "points", -1, Tk_Offset(BarGraphOptions, search.mode), - 0, &searchModeObjOption, 0}, - {TK_OPTION_STRING_TABLE, "-searchalong", "searchAlong", "SearchAlong", - "both", -1, Tk_Offset(BarGraphOptions, search.along), - 0, &searchAlongObjOption, 0}, - {TK_OPTION_BOOLEAN, "-stackaxes", "stackAxes", "StackAxes", - "no", -1, Tk_Offset(BarGraphOptions, stackAxes), 0, NULL, RESET}, - {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus", - NULL, -1, Tk_Offset(BarGraphOptions, takeFocus), - TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_STRING, "-title", "title", "Title", - NULL, -1, Tk_Offset(BarGraphOptions, title), TK_OPTION_NULL_OK, NULL, RESET}, - {TK_OPTION_SYNONYM, "-tm", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-topmargin", 0}, - {TK_OPTION_PIXELS, "-topmargin", "topMargin", "TopMargin", - "0", -1, Tk_Offset(BarGraphOptions, topMargin.reqSize), 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-width", "width", "Width", - "5i", -1, Tk_Offset(BarGraphOptions, reqWidth), 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-plotwidth", "plotWidth", "PlotWidth", - "0", -1, Tk_Offset(BarGraphOptions, reqPlotWidth), 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-plotheight", "plotHeight", "PlotHeight", - "0", -1, Tk_Offset(BarGraphOptions, reqPlotHeight), 0, NULL, RESET}, - {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} -}; - -// Create - -BarGraph::BarGraph(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) - : Graph(clientData, interp, objc, objv) -{ - // problems so far? - if (!valid_) - return; - - ops_ = (BarGraphOptions*)calloc(1, sizeof(BarGraphOptions)); - BarGraphOptions* ops = (BarGraphOptions*)ops_; - - Tk_SetClass(tkwin_, "Barchart"); - - barGroups_ =NULL; - nBarGroups_ =0; - maxBarSetSize_ =0; - Tcl_InitHashTable(&setTable_, sizeof(BarSetKey)/sizeof(int)); - - ops->bottomMargin.site = MARGIN_BOTTOM; - ops->leftMargin.site = MARGIN_LEFT; - ops->topMargin.site = MARGIN_TOP; - ops->rightMargin.site = MARGIN_RIGHT; - - ops->titleTextStyle.anchor = TK_ANCHOR_N; - ops->titleTextStyle.color =NULL; - ops->titleTextStyle.font =NULL; - ops->titleTextStyle.angle =0; - ops->titleTextStyle.justify =TK_JUSTIFY_LEFT; - - optionTable_ = Tk_CreateOptionTable(interp_, optionSpecs); - if ((Tk_InitOptions(interp_, (char*)ops_, optionTable_, tkwin_) != TCL_OK) || (GraphObjConfigure(this, interp_, objc-2, objv+2) != TCL_OK)) { - valid_ =0; - return; - } - - // do this last after Tk_SetClass set - legend_ = new Legend(this); - crosshairs_ = new Crosshairs(this); - postscript_ = new Postscript(this); - - if (createPen("active", 0, NULL) != TCL_OK) { - valid_ =0; - return; - } - - if (createAxes() != TCL_OK) { - valid_ =0; - return; - } - - adjustAxes(); - - Tcl_SetStringObj(Tcl_GetObjResult(interp_), Tk_PathName(tkwin_), -1); -} - -BarGraph::~BarGraph() -{ - destroyBarSets(); -} - -int BarGraph::configure() -{ - BarGraphOptions* ops = (BarGraphOptions*)ops_; - // Don't allow negative bar widths. Reset to an arbitrary value (0.1) - if (ops->barWidth <= 0.0f) - ops->barWidth = 0.9f; - - return Graph::configure(); -} - -int BarGraph::createPen(const char* penName, int objc, Tcl_Obj* const objv[]) -{ - int isNew; - Tcl_HashEntry *hPtr = - Tcl_CreateHashEntry(&penTable_, penName, &isNew); - if (!isNew) { - Tcl_AppendResult(interp_, "pen \"", penName, "\" already exists in \"", - Tk_PathName(tkwin_), "\"", (char *)NULL); - return TCL_ERROR; - } - - Pen* penPtr = new BarPen(this, penName, hPtr); - if (!penPtr) - return TCL_ERROR; - - Tcl_SetHashValue(hPtr, penPtr); - - if ((Tk_InitOptions(interp_, (char*)penPtr->ops(), penPtr->optionTable(), tkwin_) != TCL_OK) || (PenObjConfigure(this, penPtr, interp_, objc-4, objv+4) != TCL_OK)) { - delete penPtr; - return TCL_ERROR; - } - - flags |= RESET; - eventuallyRedraw(); - - return TCL_OK; -} - -int BarGraph::createElement(int objc, Tcl_Obj* const objv[]) -{ - char *name = Tcl_GetString(objv[3]); - if (name[0] == '-') { - Tcl_AppendResult(interp_, "name of element \"", name, - "\" can't start with a '-'", NULL); - return TCL_ERROR; - } - - int isNew; - Tcl_HashEntry* hPtr = - Tcl_CreateHashEntry(&elements_.table, name, &isNew); - if (!isNew) { - Tcl_AppendResult(interp_, "element \"", name, - "\" already exists in \"", Tcl_GetString(objv[0]), - "\"", NULL); - return TCL_ERROR; - } - - Element* elemPtr = new BarElement(this, name, hPtr); - if (!elemPtr) - return TCL_ERROR; - - Tcl_SetHashValue(hPtr, elemPtr); - - if ((Tk_InitOptions(interp_, (char*)elemPtr->ops(), elemPtr->optionTable(), tkwin_) != TCL_OK) || (ElementObjConfigure(elemPtr, interp_, objc-4, objv+4) != TCL_OK)) { - delete elemPtr; - return TCL_ERROR; - } - - elemPtr->link = elements_.displayList->append(elemPtr); - - return TCL_OK; -} - -void BarGraph::mapElements() -{ - BarGraphOptions* ops = (BarGraphOptions*)ops_; - if ((BarMode)ops->barMode != INFRONT) - resetBarSets(); - - Graph::mapElements(); -} - -void BarGraph::resetAxes() -{ - BarGraphOptions* ops = (BarGraphOptions*)ops_; - - /* FIXME: This should be called whenever the display list of - * elements change. Maybe yet another flag INIT_STACKS to - * indicate that the element display list has changed. - * Needs to be done before the axis limits are set. - */ - initBarSets(); - if (((BarMode)ops->barMode == STACKED) && (nBarGroups_ > 0)) - computeBarStacks(); - - Graph::resetAxes(); -} - -void BarGraph::initBarSets() -{ - BarGraphOptions* ops = (BarGraphOptions*)ops_; - - // Free resources associated with a previous frequency table. This includes - // the array of frequency information and the table itself - destroyBarSets(); - if ((BarMode)ops->barMode == INFRONT) - return; - - // Initialize a hash table and fill it with unique abscissas. Keep track - // of the frequency of each x-coordinate and how many abscissas have - // duplicate mappings. - Tcl_HashTable setTable; - Tcl_InitHashTable(&setTable, sizeof(BarSetKey)/sizeof(int)); - int nSegs =0; - - for (ChainLink* link = Chain_FirstLink(elements_.displayList); - link; link = Chain_NextLink(link)) { - BarElement* bePtr = (BarElement*)Chain_GetValue(link); - BarElementOptions* ops = (BarElementOptions*)bePtr->ops(); - if (ops->hide) - continue; - - nSegs++; - if (ops->coords.x) { - int nPoints = ops->coords.x->nValues(); - for (double *x=ops->coords.x->values_, *xend=x+nPoints; xxAxis; - key.yAxis =NULL; - - int isNew; - Tcl_HashEntry* hhPtr = - Tcl_CreateHashEntry(&setTable, (char*)&key, &isNew); - Tcl_HashTable* tablePtr; - if (isNew) { - tablePtr = (Tcl_HashTable*)malloc(sizeof(Tcl_HashTable)); - Tcl_InitHashTable(tablePtr, TCL_STRING_KEYS); - Tcl_SetHashValue(hhPtr, tablePtr); - } - else - tablePtr = (Tcl_HashTable*)Tcl_GetHashValue(hhPtr); - - const char* name = ops->groupName ? ops->groupName : ops->yAxis->name_; - Tcl_HashEntry* hhPtr2 = Tcl_CreateHashEntry(tablePtr, name, &isNew); - size_t count =1; - if (!isNew) { - count = (size_t)Tcl_GetHashValue(hhPtr2); - count++; - } - Tcl_SetHashValue(hhPtr2, count); - } - } - } - - // no bar elements to be displayed - if (setTable.numEntries == 0) - return; - - int sum =0; - int max =0; - Tcl_HashSearch iter; - for (Tcl_HashEntry *hhPtr = Tcl_FirstHashEntry(&setTable, &iter); hhPtr; - hhPtr = Tcl_NextHashEntry(&iter)) { - BarSetKey* keyPtr = (BarSetKey*)Tcl_GetHashKey(&setTable, hhPtr); - Tcl_HashTable* tablePtr = (Tcl_HashTable*)Tcl_GetHashValue(hhPtr); - - int isNew; - Tcl_HashEntry* hPtr = - Tcl_CreateHashEntry(&setTable_, (char*)keyPtr, &isNew); - Tcl_SetHashValue(hPtr, tablePtr); - - if (max < tablePtr->numEntries) - max = tablePtr->numEntries; // # of stacks in group - sum += tablePtr->numEntries; - } - - Tcl_DeleteHashTable(&setTable); - - if (sum > 0) { - barGroups_ = new BarGroup[sum]; - BarGroup* groupPtr = barGroups_; - Tcl_HashSearch iter; - for (Tcl_HashEntry* hPtr = Tcl_FirstHashEntry(&setTable_, &iter); - hPtr; hPtr = Tcl_NextHashEntry(&iter)) { - BarSetKey* keyPtr = (BarSetKey*)Tcl_GetHashKey(&setTable_, hPtr); - Tcl_HashTable* tablePtr = (Tcl_HashTable*)Tcl_GetHashValue(hPtr); - - size_t xcount = 0; - Tcl_HashSearch iter2; - for (Tcl_HashEntry *hPtr2 = Tcl_FirstHashEntry(tablePtr, &iter2); - hPtr2; hPtr2 = Tcl_NextHashEntry(&iter2)) { - size_t count = (size_t)Tcl_GetHashValue(hPtr2); - groupPtr->nSegments = count; - groupPtr->xAxis = keyPtr->xAxis; - groupPtr->yAxis = keyPtr->yAxis; - groupPtr->index = xcount++; - Tcl_SetHashValue(hPtr2, groupPtr); - - groupPtr++; - } - } - } - - maxBarSetSize_ = max; - nBarGroups_ = sum; -} - -void BarGraph::destroyBarSets() -{ - if (barGroups_) { - delete [] barGroups_; - barGroups_ = NULL; - } - - nBarGroups_ = 0; - Tcl_HashSearch iter; - for (Tcl_HashEntry* hPtr=Tcl_FirstHashEntry(&setTable_, &iter); hPtr; - hPtr=Tcl_NextHashEntry(&iter)) { - Tcl_HashTable* tablePtr = (Tcl_HashTable*)Tcl_GetHashValue(hPtr); - Tcl_DeleteHashTable(tablePtr); - free(tablePtr); - } - - Tcl_DeleteHashTable(&setTable_); - Tcl_InitHashTable(&setTable_, sizeof(BarSetKey)/sizeof(int)); -} - -void BarGraph::resetBarSets() -{ - for (BarGroup *gp = barGroups_, *gend = gp + nBarGroups_; gp < gend; gp++) { - gp->lastY = 0.0; - gp->count = 0; - } -} - -void BarGraph::computeBarStacks() -{ - BarGraphOptions* ops = (BarGraphOptions*)ops_; - - if (((BarMode)ops->barMode != STACKED) || (nBarGroups_ == 0)) - return; - - // Initialize the stack sums to zero - for (BarGroup *gp = barGroups_, *gend = gp + nBarGroups_; gp < gend; gp++) - gp->sum = 0.0; - - // Consider each bar x-y coordinate. Add the ordinates of duplicate - // abscissas - - for (ChainLink* link = Chain_FirstLink(elements_.displayList); link; - link = Chain_NextLink(link)) { - BarElement* bePtr = (BarElement*)Chain_GetValue(link); - BarElementOptions* ops = (BarElementOptions*)bePtr->ops(); - if (ops->hide) - continue; - - if (ops->coords.x && ops->coords.y) { - for (double *x=ops->coords.x->values_, *y=ops->coords.y->values_, - *xend=x+ops->coords.x->nValues(); xxAxis; - key.yAxis =NULL; - Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&setTable_, (char*)&key); - if (!hPtr) - continue; - - Tcl_HashTable *tablePtr = (Tcl_HashTable*)Tcl_GetHashValue(hPtr); - const char *name = ops->groupName ? ops->groupName : ops->yAxis->name_; - hPtr = Tcl_FindHashEntry(tablePtr, name); - if (!hPtr) - continue; - - BarGroup *groupPtr = (BarGroup*)Tcl_GetHashValue(hPtr); - groupPtr->sum += *y; - } - } - } -} - diff --git a/src/tkbltGraphBar.h b/src/tkbltGraphBar.h deleted file mode 100644 index 252c8d9..0000000 --- a/src/tkbltGraphBar.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGraphBar_h__ -#define __BltGraphBar_h__ - -#include - -#include "tkbltGraph.h" - -namespace Blt { - - typedef struct { - double value; - Axis* xAxis; - Axis* yAxis; - } BarSetKey; - - class BarGroup { - public: - int nSegments; - Axis* xAxis; - Axis* yAxis; - float sum; - int count; - float lastY; - size_t index; - - public: - BarGroup(); - }; - - typedef struct { - double aspect; - Tk_3DBorder normalBg; - int borderWidth; - Margin margins[4]; - Tk_Cursor cursor; - TextStyleOptions titleTextStyle; - int reqHeight; - XColor* highlightBgColor; - XColor* highlightColor; - int highlightWidth; - int inverted; - Tk_3DBorder plotBg; - int plotBW; - int xPad; - int yPad; - int plotRelief; - int relief; - ClosestSearch search; - int stackAxes; - const char *takeFocus; // nor used in C code - const char *title; - int reqWidth; - int reqPlotWidth; - int reqPlotHeight; - - // bar graph - int barMode; - double barWidth; - double baseline; - } BarGraphOptions; - - class BarGraph : public Graph { - public: - enum BarMode {INFRONT, STACKED, ALIGNED, OVERLAP}; - - public: - BarGroup* barGroups_; - int nBarGroups_; - Tcl_HashTable setTable_; - int maxBarSetSize_; - - protected: - void resetAxes(); - void mapElements(); - void initBarSets(); - void destroyBarSets(); - void resetBarSets(); - void computeBarStacks(); - - public: - BarGraph(ClientData, Tcl_Interp*, int, Tcl_Obj* const []); - virtual ~BarGraph(); - - int configure(); - int createPen(const char*, int, Tcl_Obj* const []); - int createElement(int, Tcl_Obj* const []); - }; -}; - -#endif diff --git a/src/tkbltGraphLine.C b/src/tkbltGraphLine.C deleted file mode 100644 index fe3f5d0..0000000 --- a/src/tkbltGraphLine.C +++ /dev/null @@ -1,267 +0,0 @@ -/* - * 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 - -#include "tkbltGraphLine.h" -#include "tkbltGraphOp.h" - -#include "tkbltGrAxis.h" -#include "tkbltGrXAxisOp.h" -#include "tkbltGrPen.h" -#include "tkbltGrPenOp.h" -#include "tkbltGrPenBar.h" -#include "tkbltGrPenLine.h" -#include "tkbltGrElem.h" -#include "tkbltGrElemOp.h" -#include "tkbltGrElemBar.h" -#include "tkbltGrElemLine.h" -#include "tkbltGrMarker.h" -#include "tkbltGrLegd.h" -#include "tkbltGrHairs.h" -#include "tkbltGrPostscript.h" -#include "tkbltGrDef.h" - -using namespace Blt; - -static const char* searchModeObjOption[] = {"points", "traces", "auto", NULL}; -static const char* searchAlongObjOption[] = {"x", "y", "both", NULL}; - -static Tk_OptionSpec optionSpecs[] = { - {TK_OPTION_DOUBLE, "-aspect", "aspect", "Aspect", - "0", -1, Tk_Offset(LineGraphOptions, aspect), 0, NULL, RESET}, - {TK_OPTION_BORDER, "-background", "background", "Background", - STD_NORMAL_BACKGROUND, -1, Tk_Offset(LineGraphOptions, normalBg), - 0, NULL, CACHE}, - {TK_OPTION_SYNONYM, "-bd", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-borderwidth", 0}, - {TK_OPTION_SYNONYM, "-bg", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-background", 0}, - {TK_OPTION_SYNONYM, "-bm", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-bottommargin", 0}, - {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", - STD_BORDERWIDTH, -1, Tk_Offset(LineGraphOptions, borderWidth), - 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-bottommargin", "bottomMargin", "BottomMargin", - "0", -1, Tk_Offset(LineGraphOptions, bottomMargin.reqSize), 0, NULL, RESET}, - {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", - "crosshair", -1, Tk_Offset(LineGraphOptions, cursor), - TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_SYNONYM, "-fg", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-foreground", 0}, - {TK_OPTION_FONT, "-font", "font", "Font", - STD_FONT_MEDIUM, -1, Tk_Offset(LineGraphOptions, titleTextStyle.font), - 0, NULL, RESET}, - {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", - STD_NORMAL_FOREGROUND, -1, - Tk_Offset(LineGraphOptions, titleTextStyle.color), 0, NULL, CACHE}, - {TK_OPTION_SYNONYM, "-halo", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-searchhalo", 0}, - {TK_OPTION_PIXELS, "-height", "height", "Height", - "4i", -1, Tk_Offset(LineGraphOptions, reqHeight), 0, NULL, RESET}, - {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground", - "HighlightBackground", - STD_NORMAL_BACKGROUND, -1, Tk_Offset(LineGraphOptions, highlightBgColor), - 0, NULL, CACHE}, - {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", - STD_NORMAL_FOREGROUND, -1, Tk_Offset(LineGraphOptions, highlightColor), - 0, NULL, CACHE}, - {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness", - "HighlightThickness", - "2", -1, Tk_Offset(LineGraphOptions, highlightWidth), 0, NULL, RESET}, - {TK_OPTION_BOOLEAN, "-invertxy", "invertXY", "InvertXY", - "no", -1, Tk_Offset(LineGraphOptions, inverted), 0, NULL, RESET}, - {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify", - "center", -1, Tk_Offset(LineGraphOptions, titleTextStyle.justify), - 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-leftmargin", "leftMargin", "Margin", - "0", -1, Tk_Offset(LineGraphOptions, leftMargin.reqSize), 0, NULL, RESET}, - {TK_OPTION_SYNONYM, "-lm", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-leftmargin", 0}, - {TK_OPTION_BORDER, "-plotbackground", "plotbackground", "PlotBackground", - STD_NORMAL_BACKGROUND, -1, Tk_Offset(LineGraphOptions, plotBg), - 0, NULL, CACHE}, - {TK_OPTION_PIXELS, "-plotborderwidth", "plotBorderWidth", "PlotBorderWidth", - STD_BORDERWIDTH, -1, Tk_Offset(LineGraphOptions, plotBW), 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-plotpadx", "plotPadX", "PlotPad", - "0", -1, Tk_Offset(LineGraphOptions, xPad), 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-plotpady", "plotPadY", "PlotPad", - "0", -1, Tk_Offset(LineGraphOptions, yPad), 0, NULL, RESET}, - {TK_OPTION_RELIEF, "-plotrelief", "plotRelief", "Relief", - "flat", -1, Tk_Offset(LineGraphOptions, plotRelief), 0, NULL, RESET}, - {TK_OPTION_RELIEF, "-relief", "relief", "Relief", - "flat", -1, Tk_Offset(LineGraphOptions, relief), 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-rightmargin", "rightMargin", "Margin", - "0", -1, Tk_Offset(LineGraphOptions, rightMargin.reqSize), 0, NULL, RESET}, - {TK_OPTION_SYNONYM, "-rm", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-rightmargin", 0}, - {TK_OPTION_PIXELS, "-searchhalo", "searchhalo", "SearchHalo", - "2m", -1, Tk_Offset(LineGraphOptions, search.halo), 0, NULL, 0}, - {TK_OPTION_STRING_TABLE, "-searchmode", "searchMode", "SearchMode", - "points", -1, Tk_Offset(LineGraphOptions, search.mode), - 0, &searchModeObjOption, 0}, - {TK_OPTION_STRING_TABLE, "-searchalong", "searchAlong", "SearchAlong", - "both", -1, Tk_Offset(LineGraphOptions, search.along), - 0, &searchAlongObjOption, 0}, - {TK_OPTION_BOOLEAN, "-stackaxes", "stackAxes", "StackAxes", - "no", -1, Tk_Offset(LineGraphOptions, stackAxes), 0, NULL, RESET}, - {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus", - NULL, -1, Tk_Offset(LineGraphOptions, takeFocus), - TK_OPTION_NULL_OK, NULL, 0}, - {TK_OPTION_STRING, "-title", "title", "Title", - NULL, -1, Tk_Offset(LineGraphOptions, title), - TK_OPTION_NULL_OK, NULL, RESET}, - {TK_OPTION_SYNONYM, "-tm", NULL, NULL, - NULL, 0, -1, 0, (ClientData)"-topmargin", 0}, - {TK_OPTION_PIXELS, "-topmargin", "topMargin", "TopMargin", - "0", -1, Tk_Offset(LineGraphOptions, topMargin.reqSize), 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-width", "width", "Width", - "5i", -1, Tk_Offset(LineGraphOptions, reqWidth), 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-plotwidth", "plotWidth", "PlotWidth", - "0", -1, Tk_Offset(LineGraphOptions, reqPlotWidth), 0, NULL, RESET}, - {TK_OPTION_PIXELS, "-plotheight", "plotHeight", "PlotHeight", - "0", -1, Tk_Offset(LineGraphOptions, reqPlotHeight), 0, NULL, RESET}, - {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} -}; - -// Create - -LineGraph::LineGraph(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) - : Graph(clientData, interp, objc, objv) -{ - // problems so far? - if (!valid_) - return; - - ops_ = (LineGraphOptions*)calloc(1, sizeof(LineGraphOptions)); - LineGraphOptions* ops = (LineGraphOptions*)ops_; - - Tk_SetClass(tkwin_, "Graph"); - - ops->bottomMargin.site = MARGIN_BOTTOM; - ops->leftMargin.site = MARGIN_LEFT; - ops->topMargin.site = MARGIN_TOP; - ops->rightMargin.site = MARGIN_RIGHT; - - ops->titleTextStyle.anchor = TK_ANCHOR_N; - ops->titleTextStyle.color =NULL; - ops->titleTextStyle.font =NULL; - ops->titleTextStyle.angle =0; - ops->titleTextStyle.justify =TK_JUSTIFY_LEFT; - - optionTable_ = Tk_CreateOptionTable(interp_, optionSpecs); - if ((Tk_InitOptions(interp_, (char*)ops_, optionTable_, tkwin_) != TCL_OK) || (GraphObjConfigure(this, interp_, objc-2, objv+2) != TCL_OK)) { - valid_ =0; - return; - } - - // do this last after Tk_SetClass set - legend_ = new Legend(this); - crosshairs_ = new Crosshairs(this); - postscript_ = new Postscript(this); - - if (createPen("active", 0, NULL) != TCL_OK) { - valid_ =0; - return; - } - - if (createAxes() != TCL_OK) { - valid_ =0; - return; - } - - adjustAxes(); - - Tcl_SetStringObj(Tcl_GetObjResult(interp_), Tk_PathName(tkwin_), -1); -} - -LineGraph::~LineGraph() -{ -} - -int LineGraph::createPen(const char* penName, int objc, Tcl_Obj* const objv[]) -{ - int isNew; - Tcl_HashEntry *hPtr = - Tcl_CreateHashEntry(&penTable_, penName, &isNew); - if (!isNew) { - Tcl_AppendResult(interp_, "pen \"", penName, "\" already exists in \"", - Tk_PathName(tkwin_), "\"", (char *)NULL); - return TCL_ERROR; - } - - Pen* penPtr = new LinePen(this, penName, hPtr); - if (!penPtr) - return TCL_ERROR; - - Tcl_SetHashValue(hPtr, penPtr); - - if ((Tk_InitOptions(interp_, (char*)penPtr->ops(), penPtr->optionTable(), tkwin_) != TCL_OK) || (PenObjConfigure(this, penPtr, interp_, objc-4, objv+4) != TCL_OK)) { - delete penPtr; - return TCL_ERROR; - } - - return TCL_OK; -} - -int LineGraph::createElement(int objc, Tcl_Obj* const objv[]) -{ - char *name = Tcl_GetString(objv[3]); - if (name[0] == '-') { - Tcl_AppendResult(interp_, "name of element \"", name, - "\" can't start with a '-'", NULL); - return TCL_ERROR; - } - - int isNew; - Tcl_HashEntry* hPtr = - Tcl_CreateHashEntry(&elements_.table, name, &isNew); - if (!isNew) { - Tcl_AppendResult(interp_, "element \"", name, - "\" already exists in \"", Tcl_GetString(objv[0]), - "\"", NULL); - return TCL_ERROR; - } - - Element* elemPtr = new LineElement(this, name, hPtr); - if (!elemPtr) - return TCL_ERROR; - - Tcl_SetHashValue(hPtr, elemPtr); - - if ((Tk_InitOptions(interp_, (char*)elemPtr->ops(), elemPtr->optionTable(), tkwin_) != TCL_OK) || (ElementObjConfigure(elemPtr, interp_, objc-4, objv+4) != TCL_OK)) { - delete elemPtr; - return TCL_ERROR; - } - - elemPtr->link = elements_.displayList->append(elemPtr); - - return TCL_OK; -} diff --git a/src/tkbltGraphLine.h b/src/tkbltGraphLine.h deleted file mode 100644 index ea8d2a0..0000000 --- a/src/tkbltGraphLine.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltGraphLine_h__ -#define __BltGraphLine_h__ - -#include - -#include "tkbltGraph.h" - -namespace Blt { - - typedef struct { - double aspect; - Tk_3DBorder normalBg; - int borderWidth; - Margin margins[4]; - Tk_Cursor cursor; - Blt::TextStyleOptions titleTextStyle; - int reqHeight; - XColor* highlightBgColor; - XColor* highlightColor; - int highlightWidth; - int inverted; - Tk_3DBorder plotBg; - int plotBW; - int xPad; - int yPad; - int plotRelief; - int relief; - ClosestSearch search; - int stackAxes; - const char *takeFocus; // nor used in C code - const char *title; - int reqWidth; - int reqPlotWidth; - int reqPlotHeight; - } LineGraphOptions; - - class LineGraph : public Graph { - public: - LineGraph(ClientData, Tcl_Interp*, int objc, Tcl_Obj* const []); - virtual ~LineGraph(); - - int createElement(int, Tcl_Obj* const []); - int createPen(const char*, int, Tcl_Obj* const []); - }; -}; - -#endif diff --git a/src/tkbltGraphOp.C b/src/tkbltGraphOp.C deleted file mode 100644 index 9d4f45b..0000000 --- a/src/tkbltGraphOp.C +++ /dev/null @@ -1,456 +0,0 @@ -/* - * 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 - -#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[]) -{ - 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(point.x)); - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(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; -} - diff --git a/src/tkbltGraphOp.h b/src/tkbltGraphOp.h deleted file mode 100644 index ff3f6ef..0000000 --- a/src/tkbltGraphOp.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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. - */ - -#ifndef __BltGraphOp_h__ -#define __BltGraphOp_h__ - -#include - -extern int GraphObjConfigure(Blt::Graph* graphPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); - -extern Tcl_ObjCmdProc GraphInstCmdProc; -extern Tcl_CmdDeleteProc GraphInstCmdDeleteProc; -extern Tk_EventProc GraphEventProc; -extern Tcl_IdleProc DisplayGraph; -extern Tcl_FreeProc DestroyGraph; - -#endif diff --git a/src/tkbltGraphSup.C b/src/tkbltGraphSup.C deleted file mode 100644 index 005f10c..0000000 --- a/src/tkbltGraphSup.C +++ /dev/null @@ -1,686 +0,0 @@ -/* - * 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 1993-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 -#include - -#include - -#include "tkbltGraph.h" -#include "tkbltGrAxis.h" -#include "tkbltGrElem.h" -#include "tkbltGrLegd.h" -#include "tkbltGrMisc.h" - -using namespace Blt; - -#define AXIS_PAD_TITLE 2 -#define ROTATE_0 0 -#define ROTATE_90 1 -#define ROTATE_180 2 -#define ROTATE_270 3 - -/* - *--------------------------------------------------------------------------- - * - * layoutGraph -- - * - * Calculate the layout of the graph. Based upon the data, axis limits, - * X and Y titles, and title height, determine the cavity left which is - * the plotting surface. The first step get the data and axis limits for - * calculating the space needed for the top, bottom, left, and right - * margins. - * - * 1) The LEFT margin is the area from the left border to the Y axis - * (not including ticks). It composes the border width, the width an - * optional Y axis label and its padding, and the tick numeric labels. - * The Y axis label is rotated 90 degrees so that the width is the - * font height. - * - * 2) The RIGHT margin is the area from the end of the graph - * to the right window border. It composes the border width, - * some padding, the font height (this may be dubious. It - * appears to provide a more even border), the max of the - * legend width and 1/2 max X tick number. This last part is - * so that the last tick label is not clipped. - * - * Window Width - * ___________________________________________________________ - * | | | | - * | | TOP height of title | | - * | | | | - * | | x2 title | | - * | | | | - * | | height of x2-axis | | - * |__________|_______________________________|_______________| W - * | | -plotpady | | i - * |__________|_______________________________|_______________| n - * | | top right | | d - * | | | | o - * | LEFT | | RIGHT | w - * | | | | - * | y | Free area = 104% | y2 | H - * | | Plotting surface = 100% | | e - * | t | Tick length = 2 + 2% | t | i - * | i | | i | g - * | t | | t legend| h - * | l | | l width| t - * | e | | e | - * | height| |height | - * | of | | of | - * | y-axis| |y2-axis | - * | | | | - * | |origin 0,0 | | - * |__________|_left_________________bottom___|_______________| - * | |-plotpady | | - * |__________|_______________________________|_______________| - * | | (xoffset, yoffset) | | - * | | | | - * | | height of x-axis | | - * | | | | - * | | BOTTOM x title | | - * |__________|_______________________________|_______________| - * - * 3) The TOP margin is the area from the top window border to the top - * of the graph. It composes the border width, twice the height of - * the title font (if one is given) and some padding between the - * title. - * - * 4) The BOTTOM margin is area from the bottom window border to the - * X axis (not including ticks). It composes the border width, the height - * an optional X axis label and its padding, the height of the font - * of the tick labels. - * - * The plotting area is between the margins which includes the X and Y axes - * including the ticks but not the tick numeric labels. The length of the - * ticks and its padding is 5% of the entire plotting area. Hence the entire - * plotting area is scaled as 105% of the width and height of the area. - * - * The axis labels, ticks labels, title, and legend may or may not be - * displayed which must be taken into account. - * - * if reqWidth > 0 : set outer size - * if reqPlotWidth > 0 : set plot size - *--------------------------------------------------------------------------- - */ - -void Graph::layoutGraph() -{ - GraphOptions* ops = (GraphOptions*)ops_; - - int width = width_; - int height = height_; - - // Step 1 - // Compute the amount of space needed to display the axes - // associated with each margin. They can be overridden by - // -leftmargin, -rightmargin, -bottommargin, and -topmargin - // graph options, respectively. - int left = getMarginGeometry(&ops->leftMargin); - int right = getMarginGeometry(&ops->rightMargin); - int top = getMarginGeometry(&ops->topMargin); - int bottom = getMarginGeometry(&ops->bottomMargin); - - int pad = ops->bottomMargin.maxTickWidth; - if (pad < ops->topMargin.maxTickWidth) - pad = ops->topMargin.maxTickWidth; - - pad = pad / 2 + 3; - if (right < pad) - right = pad; - - if (left < pad) - left = pad; - - pad = ops->leftMargin.maxTickHeight; - if (pad < ops->rightMargin.maxTickHeight) - pad = ops->rightMargin.maxTickHeight; - - pad = pad / 2; - if (top < pad) - top = pad; - - if (bottom < pad) - bottom = pad; - - if (ops->leftMargin.reqSize > 0) - left = ops->leftMargin.reqSize; - - if (ops->rightMargin.reqSize > 0) - right = ops->rightMargin.reqSize; - - if (ops->topMargin.reqSize > 0) - top = ops->topMargin.reqSize; - - if (ops->bottomMargin.reqSize > 0) - bottom = ops->bottomMargin.reqSize; - - // Step 2 - // Add the graph title height to the top margin. - if (ops->title) - top += titleHeight_ + 6; - - int inset = (inset_ + ops->plotBW); - int inset2 = 2 * inset; - - // Step 3 - // Estimate the size of the plot area from the remaining - // space. This may be overridden by the -plotwidth and - // -plotheight graph options. We use this to compute the - // size of the legend. - if (width == 0) - width = 400; - - if (height == 0) - height = 400; - - int plotWidth = (ops->reqPlotWidth > 0) ? ops->reqPlotWidth : - width - (inset2 + left + right); - int plotHeight = (ops->reqPlotHeight > 0) ? ops->reqPlotHeight : - height - (inset2 + top + bottom); - legend_->map(plotWidth, plotHeight); - - // Step 4 - // Add the legend to the appropiate margin. - if (!legend_->isHidden()) { - switch (legend_->position()) { - case Legend::RIGHT: - right += legend_->width_ + 2; - break; - case Legend::LEFT: - left += legend_->width_ + 2; - break; - case Legend::TOP: - top += legend_->height_ + 2; - break; - case Legend::BOTTOM: - bottom += legend_->height_ + 2; - break; - case Legend::XY: - case Legend::PLOT: - break; - } - } - - // Recompute the plotarea or graph size, now accounting for the legend. - if (ops->reqPlotWidth == 0) { - plotWidth = width - (inset2 + left + right); - if (plotWidth < 1) - plotWidth = 1; - } - if (ops->reqPlotHeight == 0) { - plotHeight = height - (inset2 + top + bottom); - if (plotHeight < 1) - plotHeight = 1; - } - - // Step 5 - // If necessary, correct for the requested plot area aspect ratio. - if ((ops->reqPlotWidth == 0) && (ops->reqPlotHeight == 0) && - (ops->aspect > 0.0f)) { - float ratio; - - // Shrink one dimension of the plotarea to fit the requested - // width/height aspect ratio. - ratio = (float)plotWidth / (float)plotHeight; - if (ratio > ops->aspect) { - // Shrink the width - int scaledWidth = (int)(plotHeight * ops->aspect); - if (scaledWidth < 1) - scaledWidth = 1; - - // Add the difference to the right margin. - // CHECK THIS: w = scaledWidth - right += (plotWidth - scaledWidth); - } - else { - // Shrink the height - int scaledHeight = (int)(plotWidth / ops->aspect); - if (scaledHeight < 1) - scaledHeight = 1; - - // Add the difference to the top margin - // CHECK THIS: h = scaledHeight; - top += (plotHeight - scaledHeight); - } - } - - // Step 6 - // If there's multiple axes in a margin, the axis titles will be - // displayed in the adjoining margins. Make sure there's room - // for the longest axis titles. - if (top < ops->leftMargin.axesTitleLength) - top = ops->leftMargin.axesTitleLength; - - if (right < ops->bottomMargin.axesTitleLength) - right = ops->bottomMargin.axesTitleLength; - - if (top < ops->rightMargin.axesTitleLength) - top = ops->rightMargin.axesTitleLength; - - if (right < ops->topMargin.axesTitleLength) - right = ops->topMargin.axesTitleLength; - - // Step 7 - // Override calculated values with requested margin sizes. - if (ops->leftMargin.reqSize > 0) - left = ops->leftMargin.reqSize; - - if (ops->rightMargin.reqSize > 0) - right = ops->rightMargin.reqSize; - - if (ops->topMargin.reqSize > 0) - top = ops->topMargin.reqSize; - - if (ops->bottomMargin.reqSize > 0) - bottom = ops->bottomMargin.reqSize; - - if (ops->reqPlotWidth > 0) { - // Width of plotarea is constained. If there's extra space, add it to - // the left and/or right margins. If there's too little, grow the - // graph width to accomodate it. - int w = plotWidth + inset2 + left + right; - - // Extra space in window - if (width > w) { - int extra = (width - w) / 2; - if (ops->leftMargin.reqSize == 0) { - left += extra; - if (ops->rightMargin.reqSize == 0) - right += extra; - else - left += extra; - } - else if (ops->rightMargin.reqSize == 0) - right += extra + extra; - } - else if (width < w) - width = w; - } - - // Constrain the plotarea height - if (ops->reqPlotHeight > 0) { - - // Height of plotarea is constained. If there's extra space, - // add it to th top and/or bottom margins. If there's too little, - // grow the graph height to accomodate it. - int h = plotHeight + inset2 + top + bottom; - - // Extra space in window - if (height > h) { - int extra = (height - h) / 2; - if (ops->topMargin.reqSize == 0) { - top += extra; - if (ops->bottomMargin.reqSize == 0) - bottom += extra; - else - top += extra; - } - else if (ops->bottomMargin.reqSize == 0) - bottom += extra + extra; - } - else if (height < h) - height = h; - } - - width_ = width; - height_ = height; - left_ = left + inset; - top_ = top + inset; - right_ = width - right - inset; - bottom_ = height - bottom - inset; - - ops->leftMargin.width = left + inset_; - ops->rightMargin.width = right + inset_; - ops->topMargin.height = top + inset_; - ops->bottomMargin.height = bottom + inset_; - - vOffset_ = top_ + ops->yPad; - vRange_ = plotHeight - 2*ops->yPad; - hOffset_ = left_ + ops->xPad; - hRange_ = plotWidth - 2*ops->xPad; - - if (vRange_ < 1) - vRange_ = 1; - - if (hRange_ < 1) - hRange_ = 1; - - hScale_ = 1.0f / (float)hRange_; - vScale_ = 1.0f / (float)vRange_; - - // Calculate the placement of the graph title so it is centered within the - // space provided for it in the top margin - titleY_ = 3 + inset_; - titleX_ = (right_ + left_) / 2; -} - -int Graph::getMarginGeometry(Margin *marginPtr) -{ - GraphOptions* ops = (GraphOptions*)ops_; - int isHoriz = !(marginPtr->site & 0x1); /* Even sites are horizontal */ - - // Count the visible axes. - unsigned int nVisible = 0; - unsigned int l =0; - int w =0; - int h =0; - - marginPtr->maxTickWidth =0; - marginPtr->maxTickHeight =0; - - if (ops->stackAxes) { - for (ChainLink* link = Chain_FirstLink(marginPtr->axes); link; - link = Chain_NextLink(link)) { - Axis* axisPtr = (Axis*)Chain_GetValue(link); - AxisOptions* ops = (AxisOptions*)axisPtr->ops(); - if (!ops->hide && axisPtr->use_) { - nVisible++; - axisPtr->getGeometry(); - - if (isHoriz) { - if (h < axisPtr->height_) - h = axisPtr->height_; - } - else { - if (w < axisPtr->width_) - w = axisPtr->width_; - } - if (axisPtr->maxTickWidth_ > marginPtr->maxTickWidth) - marginPtr->maxTickWidth = axisPtr->maxTickWidth_; - - if (axisPtr->maxTickHeight_ > marginPtr->maxTickHeight) - marginPtr->maxTickHeight = axisPtr->maxTickHeight_; - } - } - } - else { - for (ChainLink* link = Chain_FirstLink(marginPtr->axes); link; - link = Chain_NextLink(link)) { - Axis* axisPtr = (Axis*)Chain_GetValue(link); - AxisOptions* ops = (AxisOptions*)axisPtr->ops(); - if (!ops->hide && axisPtr->use_) { - nVisible++; - axisPtr->getGeometry(); - - if ((ops->titleAlternate) && (l < axisPtr->titleWidth_)) - l = axisPtr->titleWidth_; - - if (isHoriz) - h += axisPtr->height_; - else - w += axisPtr->width_; - - if (axisPtr->maxTickWidth_ > marginPtr->maxTickWidth) - marginPtr->maxTickWidth = axisPtr->maxTickWidth_; - - if (axisPtr->maxTickHeight_ > marginPtr->maxTickHeight) - marginPtr->maxTickHeight = axisPtr->maxTickHeight_; - } - } - } - // Enforce a minimum size for margins. - if (w < 3) - w = 3; - - if (h < 3) - h = 3; - - marginPtr->nAxes = nVisible; - marginPtr->axesTitleLength = l; - marginPtr->width = w; - marginPtr->height = h; - marginPtr->axesOffset = (isHoriz) ? h : w; - return marginPtr->axesOffset; -} - -void Graph::getTextExtents(Tk_Font font, const char *text, int textLen, - int* ww, int* hh) -{ - if (!text) { - *ww =0; - *hh =0; - return; - } - - Tk_FontMetrics fm; - Tk_GetFontMetrics(font, &fm); - int lineHeight = fm.linespace; - - if (textLen < 0) - textLen = strlen(text); - - int maxWidth =0; - int maxHeight =0; - int lineLen =0; - const char *line =NULL; - const char *p, *pend; - for (p =line=text, pend=text+textLen; p 0) { - int lineWidth = Tk_TextWidth(font, line, lineLen); - if (lineWidth > maxWidth) - maxWidth = lineWidth; - } - maxHeight += lineHeight; - line = p + 1; /* Point to the start of the next line. */ - lineLen = 0; /* Reset counter to indicate the start of a - * new line. */ - continue; - } - lineLen++; - } - - if ((lineLen > 0) && (*(p - 1) != '\n')) { - maxHeight += lineHeight; - int lineWidth = Tk_TextWidth(font, line, lineLen); - if (lineWidth > maxWidth) - maxWidth = lineWidth; - } - - *ww = maxWidth; - *hh = maxHeight; -} - -/* - *--------------------------------------------------------------------------- - * - * Computes the dimensions of the bounding box surrounding a rectangle - * rotated about its center. If pointArr isn't NULL, the coordinates of - * the rotated rectangle are also returned. - * - * The dimensions are determined by rotating the rectangle, and doubling - * the maximum x-coordinate and y-coordinate. - * - * w = 2 * maxX, h = 2 * maxY - * - * Since the rectangle is centered at 0,0, the coordinates of the - * bounding box are (-w/2,-h/2 w/2,-h/2, w/2,h/2 -w/2,h/2). - * - * 0 ------- 1 - * | | - * | x | - * | | - * 3 ------- 2 - * - * Results: - * The width and height of the bounding box containing the rotated - * rectangle are returned. - * - *--------------------------------------------------------------------------- - */ -void Graph::getBoundingBox(int width, int height, float angle, - double *rotWidthPtr, double *rotHeightPtr, - Point2d *bbox) -{ - angle = fmod(angle, 360.0); - if (fmod(angle, (double)90.0) == 0.0) { - int ll, ur, ul, lr; - double rotWidth, rotHeight; - - // Handle right-angle rotations specially - int quadrant = (int)(angle / 90.0); - switch (quadrant) { - case ROTATE_270: - ul = 3, ur = 0, lr = 1, ll = 2; - rotWidth = (double)height; - rotHeight = (double)width; - break; - case ROTATE_90: - ul = 1, ur = 2, lr = 3, ll = 0; - rotWidth = (double)height; - rotHeight = (double)width; - break; - case ROTATE_180: - ul = 2, ur = 3, lr = 0, ll = 1; - rotWidth = (double)width; - rotHeight = (double)height; - break; - default: - case ROTATE_0: - ul = 0, ur = 1, lr = 2, ll = 3; - rotWidth = (double)width; - rotHeight = (double)height; - break; - } - if (bbox) { - double x = rotWidth * 0.5; - double y = rotHeight * 0.5; - bbox[ll].x = bbox[ul].x = -x; - bbox[ur].y = bbox[ul].y = -y; - bbox[lr].x = bbox[ur].x = x; - bbox[ll].y = bbox[lr].y = y; - } - *rotWidthPtr = rotWidth; - *rotHeightPtr = rotHeight; - return; - } - - // Set the four corners of the rectangle whose center is the origin - Point2d corner[4]; - corner[1].x = corner[2].x = (double)width * 0.5; - corner[0].x = corner[3].x = -corner[1].x; - corner[2].y = corner[3].y = (double)height * 0.5; - corner[0].y = corner[1].y = -corner[2].y; - - double radians = (-angle / 180.0) * M_PI; - double sinTheta = sin(radians); - double cosTheta = cos(radians); - double xMax =0; - double yMax =0; - - // Rotate the four corners and find the maximum X and Y coordinates - for (int ii=0; ii<4; ii++) { - double x = (corner[ii].x * cosTheta) - (corner[ii].y * sinTheta); - double y = (corner[ii].x * sinTheta) + (corner[ii].y * cosTheta); - if (x > xMax) - xMax = x; - - if (y > yMax) - yMax = y; - - if (bbox) { - bbox[ii].x = x; - bbox[ii].y = y; - } - } - - // By symmetry, the width and height of the bounding box are twice the - // maximum x and y coordinates. - *rotWidthPtr = xMax + xMax; - *rotHeightPtr = yMax + yMax; -} - -/* - *--------------------------------------------------------------------------- - * - * Blt_AnchorPoint -- - * - * Translates a position, using both the dimensions of the bounding box, - * and the anchor direction, returning the coordinates of the upper-left - * corner of the box. The anchor indicates where the given x-y position - * is in relation to the bounding box. - * - * 7 nw --- 0 n --- 1 ne - * | | - * 6 w 8 center 2 e - * | | - * 5 sw --- 4 s --- 3 se - * - * The coordinates returned are translated to the origin of the bounding - * box (suitable for giving to XCopyArea, XCopyPlane, etc.) - * - * Results: - * The translated coordinates of the bounding box are returned. - * - *--------------------------------------------------------------------------- - */ -Point2d Graph::anchorPoint(double x, double y, double w, double h, - Tk_Anchor anchor) -{ - Point2d t; - - switch (anchor) { - case TK_ANCHOR_NW: /* 7 Upper left corner */ - break; - case TK_ANCHOR_W: /* 6 Left center */ - y -= (h * 0.5); - break; - case TK_ANCHOR_SW: /* 5 Lower left corner */ - y -= h; - break; - case TK_ANCHOR_N: /* 0 Top center */ - x -= (w * 0.5); - break; - case TK_ANCHOR_CENTER: /* 8 Center */ - x -= (w * 0.5); - y -= (h * 0.5); - break; - case TK_ANCHOR_S: /* 4 Bottom center */ - x -= (w * 0.5); - y -= h; - break; - case TK_ANCHOR_NE: /* 1 Upper right corner */ - x -= w; - break; - case TK_ANCHOR_E: /* 2 Right center */ - x -= w; - y -= (h * 0.5); - break; - case TK_ANCHOR_SE: /* 3 Lower right corner */ - x -= w; - y -= h; - break; - } - - t.x = x; - t.y = y; - return t; -} - diff --git a/src/tkbltInt.C b/src/tkbltInt.C deleted file mode 100644 index 5e9dde7..0000000 --- a/src/tkbltInt.C +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 -#include -using namespace std; - -extern "C" { -Tcl_AppInitProc Tkblt_Init; -Tcl_AppInitProc Tkblt_SafeInit; -}; - -Tcl_AppInitProc Blt_VectorCmdInitProc; -Tcl_AppInitProc Blt_GraphCmdInitProc; - -#include "tkbltStubInit.c" - -int Tkblt_Init(Tcl_Interp* interp) -{ - Tcl_Namespace *nsPtr; - - if (Tcl_InitStubs(interp, TCL_PATCH_LEVEL, 0) == NULL) - return TCL_ERROR; - if (Tk_InitStubs(interp, TK_PATCH_LEVEL, 0) == NULL) - return TCL_ERROR; - - nsPtr = Tcl_FindNamespace(interp, "::blt", (Tcl_Namespace *)NULL, 0); - if (nsPtr == NULL) { - nsPtr = Tcl_CreateNamespace(interp, "::blt", NULL, NULL); - if (nsPtr == NULL) - return TCL_ERROR; - } - - if (Blt_VectorCmdInitProc(interp) != TCL_OK) - return TCL_ERROR; - if (Blt_GraphCmdInitProc(interp) != TCL_OK) - return TCL_ERROR; - - if (Tcl_PkgProvideEx(interp, PACKAGE_NAME, PACKAGE_VERSION, (ClientData)&tkbltStubs) != TCL_OK) - return TCL_ERROR; - - return TCL_OK; -} - -int Tkblt_SafeInit(Tcl_Interp* interp) -{ - return Tkblt_Init(interp); -} diff --git a/src/tkbltNsUtil.C b/src/tkbltNsUtil.C deleted file mode 100644 index f2ecfa3..0000000 --- a/src/tkbltNsUtil.C +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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 1997-2008 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. - */ - -#ifdef __CYGWIN__ -extern "C" { -#include -} -#else -#include -#endif - -#include "tkbltNsUtil.h" - -using namespace Blt; - -Tcl_Namespace* Blt::GetCommandNamespace(Tcl_Command cmdToken) -{ - Command* cmdPtr = (Command*)cmdToken; - return (Tcl_Namespace *)cmdPtr->nsPtr; -} - -int Blt::ParseObjectName(Tcl_Interp* interp, const char *path, - Blt_ObjectName *namePtr, unsigned int flags) -{ - namePtr->nsPtr = NULL; - namePtr->name = NULL; - char* colon = NULL; - - /* Find the last namespace separator in the qualified name. */ - char* last = (char *)(path + strlen(path)); - while (--last > path) { - if ((*last == ':') && (*(last - 1) == ':')) { - last++; /* just after the last "::" */ - colon = last - 2; - break; - } - } - if (colon == NULL) { - namePtr->name = path; - if ((flags & BLT_NO_DEFAULT_NS) == 0) { - namePtr->nsPtr = Tcl_GetCurrentNamespace(interp); - } - return 1; /* No namespace designated in name. */ - } - - /* Separate the namespace and the object name. */ - *colon = '\0'; - if (path[0] == '\0') { - namePtr->nsPtr = Tcl_GetGlobalNamespace(interp); - } else { - namePtr->nsPtr = Tcl_FindNamespace(interp, (char *)path, NULL, - (flags & BLT_NO_ERROR_MSG) ? 0 : TCL_LEAVE_ERR_MSG); - } - /* Repair the string. */ *colon = ':'; - - if (namePtr->nsPtr == NULL) { - return 0; /* Namespace doesn't exist. */ - } - namePtr->name =last; - return 1; -} - -char* Blt::MakeQualifiedName(Blt_ObjectName *namePtr, Tcl_DString *resultPtr) -{ - Tcl_DStringInit(resultPtr); - if ((namePtr->nsPtr->fullName[0] != ':') || - (namePtr->nsPtr->fullName[1] != ':') || - (namePtr->nsPtr->fullName[2] != '\0')) { - Tcl_DStringAppend(resultPtr, namePtr->nsPtr->fullName, -1); - } - Tcl_DStringAppend(resultPtr, "::", -1); - Tcl_DStringAppend(resultPtr, (char *)namePtr->name, -1); - return Tcl_DStringValue(resultPtr); -} - -static Tcl_Namespace* NamespaceOfVariable(Var *varPtr) -{ - if (varPtr->flags & VAR_IN_HASHTABLE) { - VarInHash *vhashPtr = (VarInHash *)varPtr; - TclVarHashTable *vtablePtr; - - vtablePtr = (TclVarHashTable *)vhashPtr->entry.tablePtr; - return (Tcl_Namespace*)(vtablePtr->nsPtr); - } - return NULL; -} - -Tcl_Namespace* Blt::GetVariableNamespace(Tcl_Interp* interp, const char *path) -{ - Blt_ObjectName objName; - if (!ParseObjectName(interp, path, &objName, BLT_NO_DEFAULT_NS)) - return NULL; - - if (objName.nsPtr == NULL) { - Var*varPtr = (Var*)Tcl_FindNamespaceVar(interp, (char *)path, - (Tcl_Namespace *)NULL, - TCL_GLOBAL_ONLY); - if (varPtr) - return NamespaceOfVariable(varPtr); - - } - return objName.nsPtr; -} diff --git a/src/tkbltNsUtil.h b/src/tkbltNsUtil.h deleted file mode 100644 index 950a48a..0000000 --- a/src/tkbltNsUtil.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef BLT_NS_UTIL_H -#define BLT_NS_UTIL_H 1 - -#define NS_SEARCH_NONE (0) -#define NS_SEARCH_CURRENT (1<<0) -#define NS_SEARCH_GLOBAL (1<<1) -#define NS_SEARCH_BOTH (NS_SEARCH_GLOBAL | NS_SEARCH_CURRENT) - -#define BLT_NO_DEFAULT_NS (1<<0) -#define BLT_NO_ERROR_MSG (1<<1) - -namespace Blt { - - typedef struct { - const char *name; - Tcl_Namespace *nsPtr; - } Blt_ObjectName; - - extern Tcl_Namespace* GetVariableNamespace(Tcl_Interp* interp, - const char *varName); - - extern Tcl_Namespace* GetCommandNamespace(Tcl_Command cmdToken); - - extern int ParseObjectName(Tcl_Interp* interp, const char *name, - Blt_ObjectName *objNamePtr, - unsigned int flags); - - extern char* MakeQualifiedName(Blt_ObjectName *objNamePtr, - Tcl_DString *resultPtr); -}; - -#endif /* BLT_NS_UTIL_H */ diff --git a/src/tkbltOp.C b/src/tkbltOp.C deleted file mode 100644 index 4199a44..0000000 --- a/src/tkbltOp.C +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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 - -#include "tkbltOp.h" - -using namespace Blt; - -static int BinaryOpSearch(Blt_OpSpec *specs, int nSpecs, const char *string, - int length) -{ - int low = 0; - int high = nSpecs - 1; - char c = string[0]; - while (low <= high) { - int median = (low + high) >> 1; - Blt_OpSpec *specPtr = specs + median; - - /* Test the first character */ - int compare = c - specPtr->name[0]; - if (compare == 0) { - /* Now test the entire string */ - compare = strncmp(string, specPtr->name, length); - if (compare == 0) { - if ((int)length < specPtr->minChars) { - return -2; /* Ambiguous operation name */ - } - } - } - if (compare < 0) { - high = median - 1; - } else if (compare > 0) { - low = median + 1; - } else { - return median; /* Op found. */ - } - } - return -1; /* Can't find operation */ -} - -static int LinearOpSearch(Blt_OpSpec *specs, int nSpecs, const char *string, - int length) -{ - char c = string[0]; - int nMatches = 0; - int last = -1; - int i =0; - for (Blt_OpSpec *specPtr = specs; iname[0]) && - (strncmp(string, specPtr->name, length) == 0)) { - last = i; - nMatches++; - if ((int)length == specPtr->minChars) { - break; - } - } - } - if (nMatches > 1) - return -2; /* Ambiguous operation name */ - - if (nMatches == 0) - return -1; /* Can't find operation */ - - return last; /* Op found. */ -} - -void* Blt::GetOpFromObj(Tcl_Interp* interp, int nSpecs, Blt_OpSpec *specs, - int operPos, int objc, Tcl_Obj* const objv[], - int flags) -{ - Blt_OpSpec *specPtr; - int n; - - if (objc <= operPos) { /* No operation argument */ - Tcl_AppendResult(interp, "wrong # args: ", (char *)NULL); - usage: - Tcl_AppendResult(interp, "should be one of...", (char *)NULL); - for (n = 0; n < nSpecs; n++) { - Tcl_AppendResult(interp, "\n ", (char *)NULL); - for (int ii = 0; ii < operPos; ii++) { - Tcl_AppendResult(interp, Tcl_GetString(objv[ii]), " ", - (char *)NULL); - } - specPtr = specs + n; - Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage, - (char *)NULL); - } - return NULL; - } - - int length; - const char* string = Tcl_GetStringFromObj(objv[operPos], &length); - if (flags & BLT_OP_LINEAR_SEARCH) - n = LinearOpSearch(specs, nSpecs, string, length); - else - n = BinaryOpSearch(specs, nSpecs, string, length); - - if (n == -2) { - char c; - - Tcl_AppendResult(interp, "ambiguous", (char *)NULL); - if (operPos > 2) { - Tcl_AppendResult(interp, " ", Tcl_GetString(objv[operPos - 1]), - (char *)NULL); - } - Tcl_AppendResult(interp, " operation \"", string, "\" matches: ", - (char *)NULL); - - c = string[0]; - for (n = 0; n < nSpecs; n++) { - specPtr = specs + n; - if ((c == specPtr->name[0]) && - (strncmp(string, specPtr->name, length) == 0)) { - Tcl_AppendResult(interp, " ", specPtr->name, (char *)NULL); - } - } - return NULL; - - } else if (n == -1) { /* Can't find operation, display help */ - Tcl_AppendResult(interp, "bad", (char *)NULL); - if (operPos > 2) { - Tcl_AppendResult(interp, " ", Tcl_GetString(objv[operPos - 1]), - (char *)NULL); - } - Tcl_AppendResult(interp, " operation \"", string, "\": ", (char *)NULL); - goto usage; - } - specPtr = specs + n; - if ((objc < specPtr->minArgs) || - ((specPtr->maxArgs > 0) && (objc > specPtr->maxArgs))) { - int i; - - Tcl_AppendResult(interp, "wrong # args: should be \"", (char *)NULL); - for (i = 0; i < operPos; i++) { - Tcl_AppendResult(interp, Tcl_GetString(objv[i]), " ", - (char *)NULL); - } - Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage, "\"", - (char *)NULL); - return NULL; - } - return specPtr->proc; -} - diff --git a/src/tkbltOp.h b/src/tkbltOp.h deleted file mode 100644 index fc9ffb7..0000000 --- a/src/tkbltOp.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef __BltOp_h__ -#define __BltOp_h__ - -#include - -#define BLT_OP_BINARY_SEARCH 0 -#define BLT_OP_LINEAR_SEARCH 1 - -namespace Blt { - - typedef struct { - const char *name; /* Name of operation */ - int minChars; /* Minimum # characters to disambiguate */ - void *proc; - int minArgs; /* Minimum # args required */ - int maxArgs; /* Maximum # args required */ - const char *usage; /* Usage message */ - } Blt_OpSpec; - - typedef enum { - BLT_OP_ARG0, /* Op is the first argument. */ - BLT_OP_ARG1, /* Op is the second argument. */ - BLT_OP_ARG2, /* Op is the third argument. */ - BLT_OP_ARG3, /* Op is the fourth argument. */ - BLT_OP_ARG4 /* Op is the fifth argument. */ - - } Blt_OpIndex; - - void *GetOpFromObj(Tcl_Interp* interp, int nSpecs, - Blt_OpSpec *specs, int operPos, int objc, - Tcl_Obj* const objv[], int flags); -}; - -#endif - diff --git a/src/tkbltParse.C b/src/tkbltParse.C deleted file mode 100644 index 095b16a..0000000 --- a/src/tkbltParse.C +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Smithsonian Astrophysical Observatory, Cambridge, MA, USA - * This code has been modified under the terms listed below and is made - * available under the same terms. - */ - -/* - * - * This file is copied from tclParse.c in the TCL library distribution. - * - * Copyright (c) 1987-1993 The Regents of the University of - * California. - * - * Copyright (c) 1994-1998 Sun Microsystems, Inc. - * - */ - -/* - * Since TCL 8.1.0 these routines have been replaced by ones that - * generate byte-codes. But since these routines are used in vector - * expressions, where no such byte-compilation is necessary, I now - * include them. In fact, the byte-compiled versions would be slower - * since the compiled code typically runs only one time. - */ - -#include -#include - -#include -#include -#include -using namespace std; - -#include - -#include "tkbltParse.h" - -using namespace Blt; - -/* - * A table used to classify input characters to assist in parsing - * TCL commands. The table should be indexed with a signed character - * using the CHAR_TYPE macro. The character may have a negative - * value. The CHAR_TYPE macro takes a pointer to a signed character - * and a pointer to the last character in the source string. If the - * src pointer is pointing at the terminating null of the string, - * CHAR_TYPE returns TCL_COMMAND_END. - */ - -#define STATIC_STRING_SPACE 150 -#define TCL_NORMAL 0x01 -#define TCL_SPACE 0x02 -#define TCL_COMMAND_END 0x04 -#define TCL_QUOTE 0x08 -#define TCL_OPEN_BRACKET 0x10 -#define TCL_OPEN_BRACE 0x20 -#define TCL_CLOSE_BRACE 0x40 -#define TCL_BACKSLASH 0x80 -#define TCL_DOLLAR 0x00 - -/* - * The following table assigns a type to each character. Only types - * meaningful to TCL parsing are represented here. The table is - * designed to be referenced with either signed or unsigned characters, - * so it has 384 entries. The first 128 entries correspond to negative - * character values, the next 256 correspond to positive character - * values. The last 128 entries are identical to the first 128. The - * table is always indexed with a 128-byte offset (the 128th entry - * corresponds to a 0 character value). - */ - -static unsigned char tclTypeTable[] = -{ - /* - * Negative character values, from -128 to -1: - */ - - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - - /* - * Positive character values, from 0-127: - */ - - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_SPACE, TCL_COMMAND_END, TCL_SPACE, - TCL_SPACE, TCL_SPACE, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_SPACE, TCL_NORMAL, TCL_QUOTE, TCL_NORMAL, - TCL_DOLLAR, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_COMMAND_END, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_OPEN_BRACKET, - TCL_BACKSLASH, TCL_COMMAND_END, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_OPEN_BRACE, - TCL_NORMAL, TCL_CLOSE_BRACE, TCL_NORMAL, TCL_NORMAL, - - /* - * Large unsigned character values, from 128-255: - */ - - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, - TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, -}; - -#define CHAR_TYPE(src,last) \ - (((src)==(last))?TCL_COMMAND_END:(tclTypeTable+128)[(int)*(src)]) - -int Blt::ParseNestedCmd(Tcl_Interp* interp, const char *string, - int flags, const char **termPtr, ParseValue *parsePtr) - -{ - return TCL_ERROR; -} - -int Blt::ParseBraces(Tcl_Interp* interp, const char *string, - const char **termPtr, ParseValue *parsePtr) -{ - int level; - const char *src; - char *dest, *end; - char c; - const char *lastChar = string + strlen(string); - - src = string; - dest = parsePtr->next; - end = parsePtr->end; - level = 1; - - /* - * Copy the characters one at a time to the result area, stopping - * when the matching close-brace is found. - */ - - for (;;) { - c = *src; - src++; - - if (dest == end) { - parsePtr->next = dest; - (*parsePtr->expandProc) (parsePtr, 20); - dest = parsePtr->next; - end = parsePtr->end; - } - *dest = c; - dest++; - - if (CHAR_TYPE(src - 1, lastChar) == TCL_NORMAL) { - continue; - } else if (c == '{') { - level++; - } else if (c == '}') { - level--; - if (level == 0) { - dest--; /* Don't copy the last close brace. */ - break; - } - } else if (c == '\\') { - int count; - - /* - * Must always squish out backslash-newlines, even when in - * braces. This is needed so that this sequence can appear - * anywhere in a command, such as the middle of an expression. - */ - - if (*src == '\n') { - dest[-1] = Tcl_Backslash(src - 1, &count); - src += count - 1; - } else { - Tcl_Backslash(src - 1, &count); - while (count > 1) { - if (dest == end) { - parsePtr->next = dest; - (*parsePtr->expandProc) (parsePtr, 20); - dest = parsePtr->next; - end = parsePtr->end; - } - *dest = *src; - dest++; - src++; - count--; - } - } - } else if (c == '\0') { - Tcl_AppendResult(interp, "missing close-brace", (char *)NULL); - *termPtr = string - 1; - return TCL_ERROR; - } - } - - *dest = '\0'; - parsePtr->next = dest; - *termPtr = src; - return TCL_OK; -} - -void Blt::ExpandParseValue(ParseValue *parsePtr, int needed) - -{ - /* - * Either double the size of the buffer or add enough new space - * to meet the demand, whichever produces a larger new buffer. - */ - int size = (parsePtr->end - parsePtr->buffer) + 1; - if (size < needed) - size += needed; - else - size += size; - - char* buffer = (char*)malloc((unsigned int)size); - - /* - * Copy from old buffer to new, free old buffer if needed, and - * mark new buffer as malloc-ed. - */ - memcpy((VOID *) buffer, (VOID *) parsePtr->buffer, - (size_t) (parsePtr->next - parsePtr->buffer)); - parsePtr->next = buffer + (parsePtr->next - parsePtr->buffer); - if (parsePtr->clientData != 0) { - free(parsePtr->buffer); - } - parsePtr->buffer = buffer; - parsePtr->end = buffer + size - 1; - parsePtr->clientData = (ClientData)1; -} - -int Blt::ParseQuotes(Tcl_Interp* interp, const char *string, int termChar, - int flags, const char **termPtr, ParseValue *parsePtr) -{ - const char *src; - char *dest, c; - const char *lastChar = string + strlen(string); - - src = string; - dest = parsePtr->next; - - for (;;) { - if (dest == parsePtr->end) { - /* - * Target buffer space is about to run out. Make more space. - */ - parsePtr->next = dest; - (*parsePtr->expandProc) (parsePtr, 1); - dest = parsePtr->next; - } - c = *src; - src++; - if (c == termChar) { - *dest = '\0'; - parsePtr->next = dest; - *termPtr = src; - return TCL_OK; - } else if (CHAR_TYPE(src - 1, lastChar) == TCL_NORMAL) { - copy: - *dest = c; - dest++; - continue; - } else if (c == '$') { - int length; - const char *value; - - value = Tcl_ParseVar(interp, src - 1, termPtr); - if (value == NULL) { - return TCL_ERROR; - } - src = *termPtr; - length = strlen(value); - if ((parsePtr->end - dest) <= length) { - parsePtr->next = dest; - (*parsePtr->expandProc) (parsePtr, length); - dest = parsePtr->next; - } - strcpy(dest, value); - dest += length; - continue; - } else if (c == '[') { - int result; - - parsePtr->next = dest; - result = ParseNestedCmd(interp, src, flags, termPtr, parsePtr); - if (result != TCL_OK) { - return result; - } - src = *termPtr; - dest = parsePtr->next; - continue; - } else if (c == '\\') { - int nRead; - - src--; - *dest = Tcl_Backslash(src, &nRead); - dest++; - src += nRead; - continue; - } else if (c == '\0') { - Tcl_ResetResult(interp); - ostringstream str; - str << "missing " << termChar << ends; - Tcl_SetStringObj(Tcl_GetObjResult(interp), str.str().c_str(), 9); - *termPtr = string - 1; - return TCL_ERROR; - } else { - goto copy; - } - } -} - diff --git a/src/tkbltParse.h b/src/tkbltParse.h deleted file mode 100644 index ee215a5..0000000 --- a/src/tkbltParse.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef _BLT_PARSE_H -#define _BLT_PARSE_H - -namespace Blt { - - typedef struct _ParseValue ParseValue; - struct _ParseValue { - char *buffer; - char *next; - char *end; - void (*expandProc)(ParseValue *pvPtr, int needed); - ClientData clientData; - }; - - extern int ParseBraces(Tcl_Interp* interp, const char *string, - const char **termPtr, ParseValue *pvPtr); - extern int ParseNestedCmd(Tcl_Interp* interp, const char *string, - int flags, const char **termPtr, - ParseValue *pvPtr); - extern int ParseQuotes(Tcl_Interp* interp, const char *string, - int termChar, int flags, const char **termPtr, - ParseValue * pvPtr); - extern void ExpandParseValue(ParseValue *pvPtr, int needed); -} - -#endif diff --git a/src/tkbltStubInit.c b/src/tkbltStubInit.c deleted file mode 100644 index 8d28789..0000000 --- a/src/tkbltStubInit.c +++ /dev/null @@ -1,30 +0,0 @@ -#include - -/* !BEGIN!: Do not edit below this line. */ - -const TkbltStubs tkbltStubs = { - TCL_STUB_MAGIC, - 0, - Blt_CreateVector, /* 0 */ - Blt_CreateVector2, /* 1 */ - Blt_DeleteVectorByName, /* 2 */ - Blt_DeleteVector, /* 3 */ - Blt_GetVector, /* 4 */ - Blt_GetVectorFromObj, /* 5 */ - Blt_ResetVector, /* 6 */ - Blt_ResizeVector, /* 7 */ - Blt_VectorExists, /* 8 */ - Blt_VectorExists2, /* 9 */ - Blt_AllocVectorId, /* 10 */ - Blt_GetVectorById, /* 11 */ - Blt_SetVectorChangedProc, /* 12 */ - Blt_FreeVectorId, /* 13 */ - Blt_NameOfVectorId, /* 14 */ - Blt_NameOfVector, /* 15 */ - Blt_ExprVector, /* 16 */ - Blt_InstallIndexProc, /* 17 */ - Blt_VecMin, /* 18 */ - Blt_VecMax, /* 19 */ -}; - -/* !END!: Do not edit above this line. */ diff --git a/src/tkbltStubLib.C b/src/tkbltStubLib.C deleted file mode 100644 index e973063..0000000 --- a/src/tkbltStubLib.C +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef USE_TCL_STUBS -#define USE_TCL_STUBS -#endif - -#include - -ClientData tkbltStubsPtr =NULL; - -const char* Tkblt_InitStubs(Tcl_Interp* interp, const char* version, int exact) -{ - const char* actualVersion = - Tcl_PkgRequireEx(interp, "tkblt", version, exact, &tkbltStubsPtr); - - return (actualVersion && tkbltStubsPtr) ? actualVersion : NULL; -} diff --git a/src/tkbltSwitch.C b/src/tkbltSwitch.C deleted file mode 100644 index bb80663..0000000 --- a/src/tkbltSwitch.C +++ /dev/null @@ -1,407 +0,0 @@ -/* - * 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 -#include - -#include -#include -#include -using namespace std; - -#include - -#include "tkbltSwitch.h" - -using namespace Blt; - -#define COUNT_NNEG 0 -#define COUNT_POS 1 -#define COUNT_ANY 2 - -static char* Blt_Strdup(const char *string) -{ - size_t size = strlen(string) + 1; - char* ptr = (char*)malloc(size * sizeof(char)); - if (ptr != NULL) { - strcpy(ptr, string); - } - return ptr; -} - -static int Blt_GetCountFromObj(Tcl_Interp* interp, Tcl_Obj *objPtr, int check, - long *valuePtr) -{ - long count; - if (Tcl_GetLongFromObj(interp, objPtr, &count) != TCL_OK) - return TCL_ERROR; - - switch (check) { - case COUNT_NNEG: - if (count < 0) { - Tcl_AppendResult(interp, "bad value \"", Tcl_GetString(objPtr), - "\": can't be negative", (char *)NULL); - return TCL_ERROR; - } - break; - case COUNT_POS: - if (count <= 0) { - Tcl_AppendResult(interp, "bad value \"", Tcl_GetString(objPtr), - "\": must be positive", (char *)NULL); - return TCL_ERROR; - } - break; - case COUNT_ANY: - break; - } - *valuePtr = count; - return TCL_OK; -} - -static void DoHelp(Tcl_Interp* interp, Blt_SwitchSpec *specs) -{ - Tcl_DString ds; - Tcl_DStringInit(&ds); - Tcl_DStringAppend(&ds, "following switches are available:", -1); - for (Blt_SwitchSpec *sp = specs; sp->type != BLT_SWITCH_END; sp++) { - Tcl_DStringAppend(&ds, "\n ", 4); - Tcl_DStringAppend(&ds, sp->switchName, -1); - Tcl_DStringAppend(&ds, " ", 1); - Tcl_DStringAppend(&ds, sp->help, -1); - } - Tcl_AppendResult(interp, Tcl_DStringValue(&ds), (char *)NULL); - Tcl_DStringFree(&ds); -} - -static Blt_SwitchSpec *FindSwitchSpec(Tcl_Interp* interp, Blt_SwitchSpec *specs, - const char *name, int length, - int needFlags, int hateFlags) -{ - char c = name[1]; - Blt_SwitchSpec *matchPtr = NULL; - for (Blt_SwitchSpec *sp = specs; sp->type != BLT_SWITCH_END; sp++) { - if (sp->switchName == NULL) - continue; - - if (((sp->flags & needFlags) != needFlags) || (sp->flags & hateFlags)) - continue; - - if ((sp->switchName[1] != c) || (strncmp(sp->switchName,name,length)!=0)) - continue; - - if (sp->switchName[length] == '\0') - return sp; /* Stop on a perfect match. */ - - if (matchPtr != NULL) { - Tcl_AppendResult(interp, "ambiguous switch \"", name, "\"\n", - (char *) NULL); - DoHelp(interp, specs); - return NULL; - } - matchPtr = sp; - } - - if (strcmp(name, "-help") == 0) { - DoHelp(interp, specs); - return NULL; - } - - if (matchPtr == NULL) { - Tcl_AppendResult(interp, "unknown switch \"", name, "\"\n", - (char *)NULL); - DoHelp(interp, specs); - return NULL; - } - - return matchPtr; -} - -static int DoSwitch(Tcl_Interp* interp, Blt_SwitchSpec *sp, - Tcl_Obj *objPtr, void *record) -{ - do { - char *ptr = (char *)record + sp->offset; - switch (sp->type) { - case BLT_SWITCH_BOOLEAN: - { - int boo; - - if (Tcl_GetBooleanFromObj(interp, objPtr, &boo) != TCL_OK) { - return TCL_ERROR; - } - if (sp->mask > 0) { - if (boo) { - *((int *)ptr) |= sp->mask; - } else { - *((int *)ptr) &= ~sp->mask; - } - } else { - *((int *)ptr) = boo; - } - } - break; - - case BLT_SWITCH_DOUBLE: - if (Tcl_GetDoubleFromObj(interp, objPtr, (double *)ptr) != TCL_OK) { - return TCL_ERROR; - } - break; - - case BLT_SWITCH_OBJ: - Tcl_IncrRefCount(objPtr); - *(Tcl_Obj **)ptr = objPtr; - break; - - case BLT_SWITCH_FLOAT: - { - double value; - - if (Tcl_GetDoubleFromObj(interp, objPtr, &value) != TCL_OK) { - return TCL_ERROR; - } - *(float *)ptr = (float)value; - } - break; - - case BLT_SWITCH_INT: - if (Tcl_GetIntFromObj(interp, objPtr, (int *)ptr) != TCL_OK) { - return TCL_ERROR; - } - break; - - case BLT_SWITCH_INT_NNEG: - { - long value; - - if (Blt_GetCountFromObj(interp, objPtr, COUNT_NNEG, - &value) != TCL_OK) { - return TCL_ERROR; - } - *(int *)ptr = (int)value; - } - break; - - case BLT_SWITCH_INT_POS: - { - long value; - - if (Blt_GetCountFromObj(interp, objPtr, COUNT_POS, - &value) != TCL_OK) { - return TCL_ERROR; - } - *(int *)ptr = (int)value; - } - break; - - case BLT_SWITCH_LIST: - { - int argc; - - if (Tcl_SplitList(interp, Tcl_GetString(objPtr), &argc, - (const char ***)ptr) != TCL_OK) { - return TCL_ERROR; - } - } - break; - - case BLT_SWITCH_LONG: - if (Tcl_GetLongFromObj(interp, objPtr, (long *)ptr) != TCL_OK) { - return TCL_ERROR; - } - break; - - case BLT_SWITCH_LONG_NNEG: - { - long value; - - if (Blt_GetCountFromObj(interp, objPtr, COUNT_NNEG, - &value) != TCL_OK) { - return TCL_ERROR; - } - *(long *)ptr = value; - } - break; - - case BLT_SWITCH_LONG_POS: - { - long value; - - if (Blt_GetCountFromObj(interp, objPtr, COUNT_POS, &value) - != TCL_OK) { - return TCL_ERROR; - } - *(long *)ptr = value; - } - break; - - case BLT_SWITCH_STRING: - { - char *value; - - value = Tcl_GetString(objPtr); - value = (*value == '\0') ? NULL : Blt_Strdup(value); - if (*(char **)ptr != NULL) { - free(*(char **)ptr); - } - *(char **)ptr = value; - } - break; - - case BLT_SWITCH_CUSTOM: - if ((*sp->customPtr->parseProc)(sp->customPtr->clientData, interp, - sp->switchName, objPtr, (char *)record, sp->offset, sp->flags) - != TCL_OK) { - return TCL_ERROR; - } - break; - - default: - ostringstream str; - str << sp->type << ends; - Tcl_AppendResult(interp, "bad switch table: unknown type \"", - str.str().c_str(), "\"", NULL); - return TCL_ERROR; - } - sp++; - } while ((sp->switchName == NULL) && (sp->type != BLT_SWITCH_END)); - return TCL_OK; -} - -int Blt::ParseSwitches(Tcl_Interp* interp, Blt_SwitchSpec *specs, - int objc, Tcl_Obj* const objv[], void *record, - int flags) -{ - Blt_SwitchSpec *sp; - int needFlags = flags & ~(BLT_SWITCH_USER_BIT - 1); - int hateFlags = 0; - - /* - * Pass 1: Clear the change flags on all the specs so that we - * can check it later. - */ - for (sp = specs; sp->type != BLT_SWITCH_END; sp++) - sp->flags &= ~BLT_SWITCH_SPECIFIED; - - /* - * Pass 2: Process the arguments that match entries in the specs. - * It's an error if the argument doesn't match anything. - */ - int count; - for (count = 0; count < objc; count++) { - char *arg; - int length; - - arg = Tcl_GetStringFromObj(objv[count], &length); - if (flags & BLT_SWITCH_OBJV_PARTIAL) { - /* - * If the argument doesn't start with a '-' (not a switch) or is - * '--', stop processing and return the number of arguments - * comsumed. - */ - if (arg[0] != '-') { - return count; - } - if ((arg[1] == '-') && (arg[2] == '\0')) { - return count + 1; /* include the "--" in the count. */ - } - } - sp = FindSwitchSpec(interp, specs, arg, length, needFlags, hateFlags); - if (sp == NULL) { - return -1; - } - if (sp->type == BLT_SWITCH_BITMASK) { - char *ptr; - - ptr = (char *)record + sp->offset; - *((int *)ptr) |= sp->mask; - } else if (sp->type == BLT_SWITCH_BITMASK_INVERT) { - char *ptr; - - ptr = (char *)record + sp->offset; - *((int *)ptr) &= ~sp->mask; - } else if (sp->type == BLT_SWITCH_VALUE) { - char *ptr; - - ptr = (char *)record + sp->offset; - *((int *)ptr) = sp->mask; - } else { - count++; - if (count == objc) { - Tcl_AppendResult(interp, "value for \"", arg, "\" missing", - (char *) NULL); - return -1; - } - if (DoSwitch(interp, sp, objv[count], record) != TCL_OK) { - ostringstream str; - str << "\n (processing \"" << sp->switchName << "\" switch)" << ends; - Tcl_AddErrorInfo(interp, str.str().c_str()); - return -1; - } - } - sp->flags |= BLT_SWITCH_SPECIFIED; - } - - return count; -} - -void Blt::FreeSwitches(Blt_SwitchSpec *specs, void *record, int needFlags) -{ - for (Blt_SwitchSpec *sp = specs; sp->type != BLT_SWITCH_END; sp++) { - if ((sp->flags & needFlags) == needFlags) { - char *ptr = (char *)record + sp->offset; - switch (sp->type) { - case BLT_SWITCH_STRING: - case BLT_SWITCH_LIST: - if (*((char **) ptr) != NULL) { - free(*((char **) ptr)); - *((char **) ptr) = NULL; - } - break; - - case BLT_SWITCH_OBJ: - if (*((Tcl_Obj **) ptr) != NULL) { - Tcl_DecrRefCount(*((Tcl_Obj **)ptr)); - *((Tcl_Obj **) ptr) = NULL; - } - break; - - case BLT_SWITCH_CUSTOM: - if ((*(char **)ptr != NULL) && - (sp->customPtr->freeProc != NULL)) { - (*sp->customPtr->freeProc)((char *)record, sp->offset, - sp->flags); - } - break; - - default: - break; - } - } - } -} diff --git a/src/tkbltSwitch.h b/src/tkbltSwitch.h deleted file mode 100644 index eed7b31..0000000 --- a/src/tkbltSwitch.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef BLT_SWITCH_H -#define BLT_SWITCH_H - -#include - -#define BLT_SWITCH_DEFAULTS (0) -#define BLT_SWITCH_ARGV_PARTIAL (1<<1) -#define BLT_SWITCH_OBJV_PARTIAL (1<<1) - - /* - * Possible flag values for Blt_SwitchSpec structures. Any bits at or - * above BLT_SWITCH_USER_BIT may be used by clients for selecting - * certain entries. - */ -#define BLT_SWITCH_NULL_OK (1<<0) -#define BLT_SWITCH_DONT_SET_DEFAULT (1<<3) -#define BLT_SWITCH_SPECIFIED (1<<4) -#define BLT_SWITCH_USER_BIT (1<<8) - -namespace Blt { - - typedef int (Blt_SwitchParseProc)(ClientData clientData, Tcl_Interp* interp, - const char *switchName, - Tcl_Obj *valueObjPtr, char *record, - int offset, int flags); - typedef void (Blt_SwitchFreeProc)(char *record, int offset, int flags); - - typedef struct { - Blt_SwitchParseProc *parseProc; /* Procedure to parse a switch - * value and store it in its * - * converted form in the data * - * record. */ - - Blt_SwitchFreeProc *freeProc; /* Procedure to free a switch. */ - - ClientData clientData; /* Arbitrary one-word value used by - * switch parser, passed to - * parseProc. */ - } Blt_SwitchCustom; - - /* - * Type values for Blt_SwitchSpec structures. See the user - * documentation for details. - */ - typedef enum { - BLT_SWITCH_BOOLEAN, - BLT_SWITCH_DOUBLE, - BLT_SWITCH_BITMASK, - BLT_SWITCH_BITMASK_INVERT, - BLT_SWITCH_FLOAT, - BLT_SWITCH_INT, - BLT_SWITCH_INT_NNEG, - BLT_SWITCH_INT_POS, - BLT_SWITCH_LIST, - BLT_SWITCH_LONG, - BLT_SWITCH_LONG_NNEG, - BLT_SWITCH_LONG_POS, - BLT_SWITCH_OBJ, - BLT_SWITCH_STRING, - BLT_SWITCH_VALUE, - BLT_SWITCH_CUSTOM, - BLT_SWITCH_END - } Blt_SwitchTypes; - - typedef struct { - Blt_SwitchTypes type; /* Type of option, such as - * BLT_SWITCH_COLOR; see definitions - * below. Last option in table must - * have type BLT_SWITCH_END. */ - - const char *switchName; /* Switch used to specify option in - * argv. NULL means this spec is part - * of a group. */ - - const char *help; /* Help string. */ - int offset; /* Where in widget record to store - * value; use Blt_Offset macro to - * generate values for this. */ - - int flags; /* Any combination of the values - * defined below. */ - - unsigned int mask; - - Blt_SwitchCustom *customPtr; /* If type is BLT_SWITCH_CUSTOM then - * this is a pointer to info about how - * to parse and print the option. - * Otherwise it is irrelevant. */ - } Blt_SwitchSpec; - - extern int ParseSwitches(Tcl_Interp* interp, Blt_SwitchSpec *specPtr, - int objc, Tcl_Obj *const *objv, void *rec, - int flags); - extern void FreeSwitches(Blt_SwitchSpec *specs, void *rec, int flags); -}; - -#endif /* BLT_SWITCH_H */ diff --git a/src/tkbltVecCmd.C b/src/tkbltVecCmd.C deleted file mode 100644 index d42dcda..0000000 --- a/src/tkbltVecCmd.C +++ /dev/null @@ -1,1811 +0,0 @@ -/* - * 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 1995-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. - * - * Code for binary data read operation was donated by Harold Kirsch. - * - */ - -/* - * TODO: - * o Add H. Kirsch's vector binary read operation - * x binread file0 - * x binread -file file0 - * - * o Add ASCII/binary file reader - * x read fileName - * - * o Allow Tcl-based client notifications. - * vector x - * x notify call Display - * x notify delete Display - * x notify reorder #1 #2 - */ - -#include -#include -#include -#include - -#include - -#include "tkbltVecInt.h" -#include "tkbltOp.h" -#include "tkbltNsUtil.h" -#include "tkbltSwitch.h" - -using namespace Blt; - -extern int Blt_SimplifyLine (Point2d *origPts, int low, int high, - double tolerance, int *indices); - -typedef int (VectorCmdProc)(Vector *vPtr, Tcl_Interp* interp, int objc, - Tcl_Obj* const objv[]); -typedef int (QSortCompareProc) (const void *, const void *); - -static Blt_SwitchParseProc ObjToFFTVector; -static Blt_SwitchCustom fftVectorSwitch = { - ObjToFFTVector, NULL, (ClientData)0, -}; - -static Blt_SwitchParseProc ObjToIndex; -static Blt_SwitchCustom indexSwitch = { - ObjToIndex, NULL, (ClientData)0, -}; - -typedef struct { - Tcl_Obj *formatObjPtr; - int from, to; -} PrintSwitches; - -static Blt_SwitchSpec printSwitches[] = - { - {BLT_SWITCH_OBJ, "-format", "string", - Tk_Offset(PrintSwitches, formatObjPtr), 0}, - {BLT_SWITCH_CUSTOM, "-from", "index", - Tk_Offset(PrintSwitches, from), 0, 0, &indexSwitch}, - {BLT_SWITCH_CUSTOM, "-to", "index", - Tk_Offset(PrintSwitches, to), 0, 0, &indexSwitch}, - {BLT_SWITCH_END} - }; - - -typedef struct { - int flags; -} SortSwitches; - -#define SORT_DECREASING (1<<0) -#define SORT_UNIQUE (1<<1) - -static Blt_SwitchSpec sortSwitches[] = - { - {BLT_SWITCH_BITMASK, "-decreasing", "", - Tk_Offset(SortSwitches, flags), 0, SORT_DECREASING}, - {BLT_SWITCH_BITMASK, "-reverse", "", - Tk_Offset(SortSwitches, flags), 0, SORT_DECREASING}, - {BLT_SWITCH_BITMASK, "-uniq", "", - Tk_Offset(SortSwitches, flags), 0, SORT_UNIQUE}, - {BLT_SWITCH_END} - }; - -typedef struct { - double delta; - Vector *imagPtr; /* Vector containing imaginary part. */ - Vector *freqPtr; /* Vector containing frequencies. */ - VectorInterpData *dataPtr; - int mask; /* Flags controlling FFT. */ -} FFTData; - - -static Blt_SwitchSpec fftSwitches[] = { - {BLT_SWITCH_CUSTOM, "-imagpart", "vector", - Tk_Offset(FFTData, imagPtr), 0, 0, &fftVectorSwitch}, - {BLT_SWITCH_BITMASK, "-noconstant", "", - Tk_Offset(FFTData, mask), 0, FFT_NO_CONSTANT}, - {BLT_SWITCH_BITMASK, "-spectrum", "", - Tk_Offset(FFTData, mask), 0, FFT_SPECTRUM}, - {BLT_SWITCH_BITMASK, "-bartlett", "", - Tk_Offset(FFTData, mask), 0, FFT_BARTLETT}, - {BLT_SWITCH_DOUBLE, "-delta", "float", - Tk_Offset(FFTData, mask), 0, 0, }, - {BLT_SWITCH_CUSTOM, "-frequencies", "vector", - Tk_Offset(FFTData, freqPtr), 0, 0, &fftVectorSwitch}, - {BLT_SWITCH_END} -}; - -static int Blt_ExprIntFromObj(Tcl_Interp* interp, Tcl_Obj *objPtr, - int *valuePtr) -{ - // First try to extract the value as a simple integer. - if (Tcl_GetIntFromObj((Tcl_Interp *)NULL, objPtr, valuePtr) == TCL_OK) - return TCL_OK; - - // Otherwise try to parse it as an expression. - long lvalue; - if (Tcl_ExprLong(interp, Tcl_GetString(objPtr), &lvalue) == TCL_OK) { - *valuePtr = lvalue; - return TCL_OK; - } - - return TCL_ERROR; -} - -static int Blt_ExprDoubleFromObj(Tcl_Interp* interp, Tcl_Obj *objPtr, - double *valuePtr) -{ - // First try to extract the value as a double precision number. - if (Tcl_GetDoubleFromObj((Tcl_Interp *)NULL, objPtr, valuePtr) == TCL_OK) - return TCL_OK; - - // Then try to parse it as an expression. - if (Tcl_ExprDouble(interp, Tcl_GetString(objPtr), valuePtr) == TCL_OK) - return TCL_OK; - - return TCL_ERROR; -} - -static int ObjToFFTVector(ClientData clientData, Tcl_Interp* interp, - const char *switchName, Tcl_Obj *objPtr, - char *record, int offset, int flags) -{ - FFTData *dataPtr = (FFTData *)record; - Vector *vPtr; - Vector **vPtrPtr = (Vector **)(record + offset); - int isNew; /* Not used. */ - char *string; - - string = Tcl_GetString(objPtr); - vPtr = Vec_Create(dataPtr->dataPtr, string, string, string, &isNew); - if (vPtr == NULL) { - return TCL_ERROR; - } - *vPtrPtr = vPtr; - - return TCL_OK; -} - -static int ObjToIndex(ClientData clientData, Tcl_Interp* interp, - const char *switchName, Tcl_Obj *objPtr, char *record, - int offset, int flags) -{ - Vector *vPtr = (Vector*)clientData; - int *indexPtr = (int *)(record + offset); - int index; - - if (Vec_GetIndex(interp, vPtr, Tcl_GetString(objPtr), &index, - INDEX_CHECK, (Blt_VectorIndexProc **)NULL) != TCL_OK) { - return TCL_ERROR; - } - *indexPtr = index; - - return TCL_OK; -} - -static Tcl_Obj* GetValues(Vector *vPtr, int first, int last) -{ - Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - for (double *vp=vPtr->valueArr+first, *vend=vPtr->valueArr+last; - vp <= vend; vp++) - Tcl_ListObjAppendElement(vPtr->interp, listObjPtr, Tcl_NewDoubleObj(*vp)); - - return listObjPtr; -} - -static void ReplicateValue(Vector *vPtr, int first, int last, double value) -{ - for (double *vp=vPtr->valueArr+first, *vend=vPtr->valueArr+last; - vp <= vend; vp++) - *vp = value; - - vPtr->notifyFlags |= UPDATE_RANGE; -} - -static int CopyList(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - if (Vec_SetLength(interp, vPtr, objc) != TCL_OK) - return TCL_ERROR; - - for (int ii = 0; ii < objc; ii++) { - double value; - if (Blt_ExprDoubleFromObj(interp, objv[ii], &value) != TCL_OK) { - Vec_SetLength(interp, vPtr, ii); - return TCL_ERROR; - } - vPtr->valueArr[ii] = value; - } - - return TCL_OK; -} - -static int AppendVector(Vector *destPtr, Vector *srcPtr) -{ - size_t oldSize = destPtr->length; - size_t newSize = oldSize + srcPtr->last - srcPtr->first + 1; - if (Vec_ChangeLength(destPtr->interp, destPtr, newSize) != TCL_OK) { - return TCL_ERROR; - } - size_t nBytes = (newSize - oldSize) * sizeof(double); - memcpy((char *)(destPtr->valueArr + oldSize), - (srcPtr->valueArr + srcPtr->first), nBytes); - destPtr->notifyFlags |= UPDATE_RANGE; - return TCL_OK; -} - -static int AppendList(Vector *vPtr, int objc, Tcl_Obj* const objv[]) -{ - Tcl_Interp* interp = vPtr->interp; - - int oldSize = vPtr->length; - if (Vec_ChangeLength(interp, vPtr, vPtr->length + objc) != TCL_OK) - return TCL_ERROR; - - int count = oldSize; - for (int i = 0; i < objc; i++) { - double value; - if (Blt_ExprDoubleFromObj(interp, objv[i], &value) != TCL_OK) { - Vec_ChangeLength(interp, vPtr, count); - return TCL_ERROR; - } - vPtr->valueArr[count++] = value; - } - vPtr->notifyFlags |= UPDATE_RANGE; - - return TCL_OK; -} - -// Vector instance option commands - -static int AppendOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - for (int i = 2; i < objc; i++) { - Vector* v2Ptr = Vec_ParseElement((Tcl_Interp *)NULL, vPtr->dataPtr, - Tcl_GetString(objv[i]), - (const char **)NULL, NS_SEARCH_BOTH); - int result; - if (v2Ptr != NULL) - result = AppendVector(vPtr, v2Ptr); - else { - int nElem; - Tcl_Obj **elemObjArr; - - if (Tcl_ListObjGetElements(interp, objv[i], &nElem, &elemObjArr) - != TCL_OK) { - return TCL_ERROR; - } - result = AppendList(vPtr, nElem, elemObjArr); - } - - if (result != TCL_OK) - return TCL_ERROR; - } - - if (objc > 2) { - if (vPtr->flush) - Vec_FlushCache(vPtr); - Vec_UpdateClients(vPtr); - } - - return TCL_OK; -} - -static int ClearOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Vec_FlushCache(vPtr); - return TCL_OK; -} - -static int DeleteOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - // FIXME: Don't delete vector with no indices - if (objc == 2) { - Vec_Free(vPtr); - return TCL_OK; - } - - // Allocate an "unset" bitmap the size of the vector - unsigned char* unsetArr = - (unsigned char*)calloc(sizeof(unsigned char), (vPtr->length + 7) / 8); -#define SetBit(i) (unsetArr[(i) >> 3] |= (1 << ((i) & 0x07))) -#define GetBit(i) (unsetArr[(i) >> 3] & (1 << ((i) & 0x07))) - - for (int i = 2; i < objc; i++) { - char* string = Tcl_GetString(objv[i]); - if (Vec_GetIndexRange(interp, vPtr, string, (INDEX_COLON | INDEX_CHECK), - (Blt_VectorIndexProc **) NULL) != TCL_OK) { - free(unsetArr); - return TCL_ERROR; - } - - // Mark the range of elements for deletion - for (int j = vPtr->first; j <= vPtr->last; j++) - SetBit(j); - } - - int count = 0; - for (int i = 0; i < vPtr->length; i++) { - // Skip elements marked for deletion - if (GetBit(i)) - continue; - - if (count < i) { - vPtr->valueArr[count] = vPtr->valueArr[i]; - } - count++; - } - free(unsetArr); - vPtr->length = count; - - if (vPtr->flush) - Vec_FlushCache(vPtr); - Vec_UpdateClients(vPtr); - - return TCL_OK; -} - -static int DupOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - for (int i = 2; i < objc; i++) { - char* name = Tcl_GetString(objv[i]); - int isNew; - Vector* v2Ptr = Vec_Create(vPtr->dataPtr, name, name, name, &isNew); - if (v2Ptr == NULL) - return TCL_ERROR; - - if (v2Ptr == vPtr) - continue; - - if (Vec_Duplicate(v2Ptr, vPtr) != TCL_OK) - return TCL_ERROR; - - if (!isNew) { - if (v2Ptr->flush) - Vec_FlushCache(v2Ptr); - Vec_UpdateClients(v2Ptr); - } - } - - return TCL_OK; -} - -static int FFTOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - FFTData data; - memset(&data, 0, sizeof(data)); - data.delta = 1.0; - - char* realVecName = Tcl_GetString(objv[2]); - int isNew; - Vector* v2Ptr = Vec_Create(vPtr->dataPtr, realVecName, realVecName, - realVecName, &isNew); - if (v2Ptr == NULL) - return TCL_ERROR; - - if (v2Ptr == vPtr) { - Tcl_AppendResult(interp, "real vector \"", realVecName, "\"", - " can't be the same as the source", (char *)NULL); - return TCL_ERROR; - } - - if (ParseSwitches(interp, fftSwitches, objc - 3, objv + 3, &data, - BLT_SWITCH_DEFAULTS) < 0) - return TCL_ERROR; - - if (Vec_FFT(interp, v2Ptr, data.imagPtr, data.freqPtr, data.delta, - data.mask, vPtr) != TCL_OK) - return TCL_ERROR; - - // Update bookkeeping - if (!isNew) { - if (v2Ptr->flush) - Vec_FlushCache(v2Ptr); - Vec_UpdateClients(v2Ptr); - } - - if (data.imagPtr != NULL) { - if (data.imagPtr->flush) - Vec_FlushCache(data.imagPtr); - Vec_UpdateClients(data.imagPtr); - } - - if (data.freqPtr != NULL) { - if (data.freqPtr->flush) - Vec_FlushCache(data.freqPtr); - Vec_UpdateClients(data.freqPtr); - } - - return TCL_OK; -} - -static int InverseFFTOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - char* name = Tcl_GetString(objv[2]); - Vector *srcImagPtr; - if (Vec_LookupName(vPtr->dataPtr, name, &srcImagPtr) != TCL_OK ) - return TCL_ERROR; - - name = Tcl_GetString(objv[3]); - int isNew; - Vector* destRealPtr = Vec_Create(vPtr->dataPtr, name, name, name, &isNew); - name = Tcl_GetString(objv[4]); - Vector* destImagPtr = Vec_Create(vPtr->dataPtr, name, name, name, &isNew); - - if (Vec_InverseFFT(interp, srcImagPtr, destRealPtr, destImagPtr, vPtr) - != TCL_OK ) - return TCL_ERROR; - - if (destRealPtr->flush) - Vec_FlushCache(destRealPtr); - Vec_UpdateClients(destRealPtr); - - if (destImagPtr->flush) - Vec_FlushCache(destImagPtr); - Vec_UpdateClients(destImagPtr); - - return TCL_OK; -} - -static int IndexOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - char* string = Tcl_GetString(objv[2]); - if (Vec_GetIndexRange(interp, vPtr, string, INDEX_ALL_FLAGS, - (Blt_VectorIndexProc **) NULL) != TCL_OK) - return TCL_ERROR; - - int first = vPtr->first; - int last = vPtr->last; - if (objc == 3) { - Tcl_Obj *listObjPtr; - - if (first == vPtr->length) { - Tcl_AppendResult(interp, "can't get index \"", string, "\"", - (char *)NULL); - return TCL_ERROR; /* Can't read from index "++end" */ - } - listObjPtr = GetValues(vPtr, first, last); - Tcl_SetObjResult(interp, listObjPtr); - } - else { - // FIXME: huh? Why set values here? - if (first == SPECIAL_INDEX) { - Tcl_AppendResult(interp, "can't set index \"", string, "\"", - (char *)NULL); - // Tried to set "min" or "max" - return TCL_ERROR; - } - - double value; - if (Blt_ExprDoubleFromObj(interp, objv[3], &value) != TCL_OK) - return TCL_ERROR; - - if (first == vPtr->length) { - if (Vec_ChangeLength(interp, vPtr, vPtr->length + 1) != TCL_OK) - return TCL_ERROR; - } - - ReplicateValue(vPtr, first, last, value); - Tcl_SetObjResult(interp, objv[3]); - if (vPtr->flush) - Vec_FlushCache(vPtr); - Vec_UpdateClients(vPtr); - } - - return TCL_OK; -} - -static int LengthOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - if (objc == 3) { - int nElem; - if (Tcl_GetIntFromObj(interp, objv[2], &nElem) != TCL_OK) - return TCL_ERROR; - - if (nElem < 0) { - Tcl_AppendResult(interp, "bad vector size \"", - Tcl_GetString(objv[2]), "\"", (char *)NULL); - return TCL_ERROR; - } - - if ((Vec_SetSize(interp, vPtr, nElem) != TCL_OK) || - (Vec_SetLength(interp, vPtr, nElem) != TCL_OK)) - return TCL_ERROR; - - if (vPtr->flush) - Vec_FlushCache(vPtr); - Vec_UpdateClients(vPtr); - } - Tcl_SetIntObj(Tcl_GetObjResult(interp), vPtr->length); - - return TCL_OK; -} - -static int MapOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - if (objc > 2) { - if (Vec_MapVariable(interp, vPtr, Tcl_GetString(objv[2])) - != TCL_OK) - return TCL_ERROR; - } - - if (vPtr->arrayName != NULL) - Tcl_SetStringObj(Tcl_GetObjResult(interp), vPtr->arrayName, -1); - - return TCL_OK; -} - -static int MaxOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Tcl_SetDoubleObj(Tcl_GetObjResult(interp), Vec_Max(vPtr)); - return TCL_OK; -} - -static int MergeOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - // Allocate an array of vector pointers of each vector to be - // merged in the current vector. - Vector** vecArr = (Vector**)malloc(sizeof(Vector *) * objc); - Vector** vPtrPtr = vecArr; - - int refSize = -1; - int nElem = 0; - for (int i = 2; i < objc; i++) { - Vector *v2Ptr; - if (Vec_LookupName(vPtr->dataPtr, Tcl_GetString(objv[i]), &v2Ptr) - != TCL_OK) { - free(vecArr); - return TCL_ERROR; - } - - // Check that all the vectors are the same length - int length = v2Ptr->last - v2Ptr->first + 1; - if (refSize < 0) - refSize = length; - else if (length != refSize) { - Tcl_AppendResult(vPtr->interp, "vectors \"", vPtr->name, - "\" and \"", v2Ptr->name, "\" differ in length", - (char *)NULL); - free(vecArr); - return TCL_ERROR; - } - *vPtrPtr++ = v2Ptr; - nElem += refSize; - } - *vPtrPtr = NULL; - - double* valueArr = (double*)malloc(sizeof(double) * nElem); - if (valueArr == NULL) { - Tcl_AppendResult(vPtr->interp, "not enough memory to allocate ", - Itoa(nElem), " vector elements", (char *)NULL); - return TCL_ERROR; - } - - // Merge the values from each of the vectors into the current vector - double* valuePtr = valueArr; - for (int i = 0; i < refSize; i++) { - for (Vector** vpp = vecArr; *vpp != NULL; vpp++) { - *valuePtr++ = (*vpp)->valueArr[i + (*vpp)->first]; - } - } - free(vecArr); - Vec_Reset(vPtr, valueArr, nElem, nElem, TCL_DYNAMIC); - - return TCL_OK; -} - -static int MinOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Tcl_SetDoubleObj(Tcl_GetObjResult(interp), Vec_Min(vPtr)); - return TCL_OK; -} - -static int NormalizeOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Vec_UpdateRange(vPtr); - double range = vPtr->max - vPtr->min; - if (objc > 2) { - char* string = Tcl_GetString(objv[2]); - int isNew; - Vector* v2Ptr = Vec_Create(vPtr->dataPtr, string, string, string, &isNew); - if (v2Ptr == NULL) - return TCL_ERROR; - - if (Vec_SetLength(interp, v2Ptr, vPtr->length) != TCL_OK) - return TCL_ERROR; - - for (int i = 0; i < vPtr->length; i++) - v2Ptr->valueArr[i] = (vPtr->valueArr[i] - vPtr->min) / range; - - Vec_UpdateRange(v2Ptr); - if (!isNew) { - if (v2Ptr->flush) { - Vec_FlushCache(v2Ptr); - } - Vec_UpdateClients(v2Ptr); - } - } - else { - Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - for (int i = 0; i < vPtr->length; i++) { - double norm = (vPtr->valueArr[i] - vPtr->min) / range; - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewDoubleObj(norm)); - } - Tcl_SetObjResult(interp, listObjPtr); - } - - return TCL_OK; -} - -static int NotifyOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - enum optionIndices { - OPTION_ALWAYS, OPTION_NEVER, OPTION_WHENIDLE, - OPTION_NOW, OPTION_CANCEL, OPTION_PENDING - }; - static const char *optionArr[] = { - "always", "never", "whenidle", "now", "cancel", "pending", NULL - }; - - int option; - if (Tcl_GetIndexFromObj(interp, objv[2], optionArr, "qualifier", TCL_EXACT, - &option) != TCL_OK) - return TCL_OK; - - switch (option) { - case OPTION_ALWAYS: - vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK; - vPtr->notifyFlags |= NOTIFY_ALWAYS; - break; - case OPTION_NEVER: - vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK; - vPtr->notifyFlags |= NOTIFY_NEVER; - break; - case OPTION_WHENIDLE: - vPtr->notifyFlags &= ~NOTIFY_WHEN_MASK; - vPtr->notifyFlags |= NOTIFY_WHENIDLE; - break; - case OPTION_NOW: - // FIXME: How does this play when an update is pending? - Blt_Vec_NotifyClients(vPtr); - break; - case OPTION_CANCEL: - if (vPtr->notifyFlags & NOTIFY_PENDING) { - vPtr->notifyFlags &= ~NOTIFY_PENDING; - Tcl_CancelIdleCall(Blt_Vec_NotifyClients, (ClientData)vPtr); - } - break; - case OPTION_PENDING: - int boll = (vPtr->notifyFlags & NOTIFY_PENDING); - Tcl_SetBooleanObj(Tcl_GetObjResult(interp), boll); - break; - } - - return TCL_OK; -} - -static int PopulateOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - char* string = Tcl_GetString(objv[2]); - int isNew; - Vector* v2Ptr = Vec_Create(vPtr->dataPtr, string, string, string, &isNew); - if (v2Ptr == NULL) - return TCL_ERROR; - - // Source vector is empty - if (vPtr->length == 0) - return TCL_OK; - - int density; - if (Tcl_GetIntFromObj(interp, objv[3], &density) != TCL_OK) - return TCL_ERROR; - - if (density < 1) { - Tcl_AppendResult(interp, "bad density \"", Tcl_GetString(objv[3]), - "\"", (char *)NULL); - return TCL_ERROR; - } - int size = (vPtr->length - 1) * (density + 1) + 1; - if (Vec_SetLength(interp, v2Ptr, size) != TCL_OK) - return TCL_ERROR; - - int count = 0; - double* valuePtr = v2Ptr->valueArr; - int i; - for (i = 0; i < (vPtr->length - 1); i++) { - double range = vPtr->valueArr[i + 1] - vPtr->valueArr[i]; - double slice = range / (double)(density + 1); - for (int j = 0; j <= density; j++) { - *valuePtr = vPtr->valueArr[i] + (slice * (double)j); - valuePtr++; - count++; - } - } - count++; - *valuePtr = vPtr->valueArr[i]; - if (!isNew) { - if (v2Ptr->flush) - Vec_FlushCache(v2Ptr); - Vec_UpdateClients(v2Ptr); - } - - return TCL_OK; -} - -static int ValuesOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - PrintSwitches switches; - switches.formatObjPtr = NULL; - switches.from = 0; - switches.to = vPtr->length - 1; - indexSwitch.clientData = vPtr; - if (ParseSwitches(interp, printSwitches, objc - 2, objv + 2, &switches, - BLT_SWITCH_DEFAULTS) < 0) - return TCL_ERROR; - - if (switches.from > switches.to) { - // swap positions - int tmp = switches.to; - switches.to = switches.from; - switches.from = tmp; - } - - if (switches.formatObjPtr == NULL) { - Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - for (int i = switches.from; i <= switches.to; i++) - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewDoubleObj(vPtr->valueArr[i])); - - Tcl_SetObjResult(interp, listObjPtr); - } - else { - Tcl_DString ds; - Tcl_DStringInit(&ds); - const char* fmt = Tcl_GetString(switches.formatObjPtr); - for (int i = switches.from; i <= switches.to; i++) { - char buffer[200]; - sprintf(buffer, fmt, vPtr->valueArr[i]); - Tcl_DStringAppend(&ds, buffer, -1); - } - Tcl_DStringResult(interp, &ds); - Tcl_DStringFree(&ds); - } - - return TCL_OK; -} - -static int RangeOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - int first; - int last; - - if (objc == 2) { - first = 0; - last = vPtr->length - 1; - } - else if (objc == 4) { - if ((Vec_GetIndex(interp, vPtr, Tcl_GetString(objv[2]), &first, - INDEX_CHECK, (Blt_VectorIndexProc **) NULL) != TCL_OK) || - (Vec_GetIndex(interp, vPtr, Tcl_GetString(objv[3]), &last, - INDEX_CHECK, (Blt_VectorIndexProc **) NULL) != TCL_OK)) - return TCL_ERROR; - - } - else { - Tcl_AppendResult(interp, "wrong # args: should be \"", - Tcl_GetString(objv[0]), " range ?first last?", - (char *)NULL); - return TCL_ERROR; - } - - Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - if (first > last) { - // Return the list reversed - for (int i=last; i<=first; i++) - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewDoubleObj(vPtr->valueArr[i])); - } - else { - for (int i=first; i<=last; i++) - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewDoubleObj(vPtr->valueArr[i])); - } - - Tcl_SetObjResult(interp, listObjPtr); - - return TCL_OK; -} - -static int InRange(double value, double min, double max) -{ - double range = max - min; - if (range < DBL_EPSILON) - return (fabs(max - value) < DBL_EPSILON); - - double norm = (value - min) / range; - return ((norm >= -DBL_EPSILON) && ((norm - 1.0) < DBL_EPSILON)); -} - -enum NativeFormats { - FMT_UNKNOWN = -1, - FMT_UCHAR, FMT_CHAR, - FMT_USHORT, FMT_SHORT, - FMT_UINT, FMT_INT, - FMT_ULONG, FMT_LONG, - FMT_FLOAT, FMT_DOUBLE -}; - -/* - *--------------------------------------------------------------------------- - * - * GetBinaryFormat - * - * Translates a format string into a native type. Valid formats are - * - * signed i1, i2, i4, i8 - * unsigned u1, u2, u4, u8 - * real r4, r8, r16 - * - * There must be a corresponding native type. For example, this for - * reading 2-byte binary integers from an instrument and converting them - * to unsigned shorts or ints. - * - *--------------------------------------------------------------------------- - */ -static enum NativeFormats GetBinaryFormat(Tcl_Interp* interp, char *string, - int *sizePtr) -{ - char c = tolower(string[0]); - if (Tcl_GetInt(interp, string + 1, sizePtr) != TCL_OK) { - Tcl_AppendResult(interp, "unknown binary format \"", string, - "\": incorrect byte size", (char *)NULL); - return FMT_UNKNOWN; - } - - switch (c) { - case 'r': - if (*sizePtr == sizeof(double)) - return FMT_DOUBLE; - else if (*sizePtr == sizeof(float)) - return FMT_FLOAT; - - break; - - case 'i': - if (*sizePtr == sizeof(char)) - return FMT_CHAR; - else if (*sizePtr == sizeof(int)) - return FMT_INT; - else if (*sizePtr == sizeof(long)) - return FMT_LONG; - else if (*sizePtr == sizeof(short)) - return FMT_SHORT; - - break; - - case 'u': - if (*sizePtr == sizeof(unsigned char)) - return FMT_UCHAR; - else if (*sizePtr == sizeof(unsigned int)) - return FMT_UINT; - else if (*sizePtr == sizeof(unsigned long)) - return FMT_ULONG; - else if (*sizePtr == sizeof(unsigned short)) - return FMT_USHORT; - - break; - - default: - Tcl_AppendResult(interp, "unknown binary format \"", string, - "\": should be either i#, r#, u# (where # is size in bytes)", - (char *)NULL); - return FMT_UNKNOWN; - } - Tcl_AppendResult(interp, "can't handle format \"", string, "\"", - (char *)NULL); - - return FMT_UNKNOWN; -} - -static int CopyValues(Vector *vPtr, char *byteArr, enum NativeFormats fmt, - int size, int length, int swap, int *indexPtr) -{ - if ((swap) && (size > 1)) { - int nBytes = size * length; - for (int i = 0; i < nBytes; i += size) { - unsigned char* p = (unsigned char *)(byteArr + i); - int left, right; - for (left = 0, right = size - 1; left < right; left++, right--) { - p[left] ^= p[right]; - p[right] ^= p[left]; - p[left] ^= p[right]; - } - } - } - - int newSize = *indexPtr + length; - if (newSize > vPtr->length) { - if (Vec_ChangeLength(vPtr->interp, vPtr, newSize) != TCL_OK) - return TCL_ERROR; - } - -#define CopyArrayToVector(vPtr, arr) \ - for (int i = 0, n = *indexPtr; i < length; i++, n++) { \ - (vPtr)->valueArr[n] = (double)(arr)[i]; \ - } - - switch (fmt) { - case FMT_CHAR: - CopyArrayToVector(vPtr, (char *)byteArr); - break; - - case FMT_UCHAR: - CopyArrayToVector(vPtr, (unsigned char *)byteArr); - break; - - case FMT_INT: - CopyArrayToVector(vPtr, (int *)byteArr); - break; - - case FMT_UINT: - CopyArrayToVector(vPtr, (unsigned int *)byteArr); - break; - - case FMT_LONG: - CopyArrayToVector(vPtr, (long *)byteArr); - break; - - case FMT_ULONG: - CopyArrayToVector(vPtr, (unsigned long *)byteArr); - break; - - case FMT_SHORT: - CopyArrayToVector(vPtr, (short int *)byteArr); - break; - - case FMT_USHORT: - CopyArrayToVector(vPtr, (unsigned short int *)byteArr); - break; - - case FMT_FLOAT: - CopyArrayToVector(vPtr, (float *)byteArr); - break; - - case FMT_DOUBLE: - CopyArrayToVector(vPtr, (double *)byteArr); - break; - - case FMT_UNKNOWN: - break; - } - *indexPtr += length; - return TCL_OK; -} - -/* - *--------------------------------------------------------------------------- - * - * BinreadOp -- - * - * Reads binary values from a TCL channel. Values are either appended to - * the end of the vector or placed at a given index (using the "-at" - * option), overwriting existing values. Data is read until EOF is found - * on the channel or a specified number of values are read. (note that - * this is not necessarily the same as the number of bytes). - * - * The following flags are supported: - * -swap Swap bytes - * -at index Start writing data at the index. - * -format fmt Specifies the format of the data. - * - * This binary reader was created and graciously donated by Harald Kirsch - * (kir@iitb.fhg.de). Anything that's wrong is due to my (gah) munging - * of the code. - * - * Results: - * Returns a standard TCL result. The interpreter result will contain the - * number of values (not the number of bytes) read. - * - * Caveats: - * Channel reads must end on an element boundary. - * - *--------------------------------------------------------------------------- - */ - -static int BinreadOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - enum NativeFormats fmt; - - char* string = Tcl_GetString(objv[2]); - int mode; - Tcl_Channel channel = Tcl_GetChannel(interp, string, &mode); - if (channel == NULL) - return TCL_ERROR; - - if ((mode & TCL_READABLE) == 0) { - Tcl_AppendResult(interp, "channel \"", string, - "\" wasn't opened for reading", (char *)NULL); - return TCL_ERROR; - } - int first = vPtr->length; - fmt = FMT_DOUBLE; - int size = sizeof(double); - int swap = 0; - int count = 0; - - if (objc > 3) { - string = Tcl_GetString(objv[3]); - if (string[0] != '-') { - long int value; - // Get the number of values to read. - if (Tcl_GetLongFromObj(interp, objv[3], &value) != TCL_OK) - return TCL_ERROR; - - if (value < 0) { - Tcl_AppendResult(interp, "count can't be negative", (char *)NULL); - return TCL_ERROR; - } - count = (size_t)value; - objc--, objv++; - } - } - - // Process any option-value pairs that remain. - for (int i = 3; i < objc; i++) { - string = Tcl_GetString(objv[i]); - if (strcmp(string, "-swap") == 0) - swap = 1; - else if (strcmp(string, "-format") == 0) { - i++; - if (i >= objc) { - Tcl_AppendResult(interp, "missing arg after \"", string, - "\"", (char *)NULL); - return TCL_ERROR; - } - - string = Tcl_GetString(objv[i]); - fmt = GetBinaryFormat(interp, string, &size); - if (fmt == FMT_UNKNOWN) - return TCL_ERROR; - } - else if (strcmp(string, "-at") == 0) { - i++; - if (i >= objc) { - Tcl_AppendResult(interp, "missing arg after \"", string, - "\"", (char *)NULL); - return TCL_ERROR; - } - - string = Tcl_GetString(objv[i]); - if (Vec_GetIndex(interp, vPtr, string, &first, 0, - (Blt_VectorIndexProc **)NULL) != TCL_OK) - return TCL_ERROR; - - if (first > vPtr->length) { - Tcl_AppendResult(interp, "index \"", string, - "\" is out of range", (char *)NULL); - return TCL_ERROR; - } - } - } - -#define BUFFER_SIZE 1024 - int arraySize = (count == 0) ? BUFFER_SIZE*size : count*size; - - char* byteArr = (char*)malloc(arraySize); - // FIXME: restore old channel translation later? - if (Tcl_SetChannelOption(interp, channel, "-translation","binary") != TCL_OK) - return TCL_ERROR; - - int total = 0; - while (!Tcl_Eof(channel)) { - int bytesRead = Tcl_Read(channel, byteArr, arraySize); - if (bytesRead < 0) { - Tcl_AppendResult(interp, "error reading channel: ", - Tcl_PosixError(interp), (char *)NULL); - return TCL_ERROR; - } - - if ((bytesRead % size) != 0) { - Tcl_AppendResult(interp, "error reading channel: short read", - (char *)NULL); - return TCL_ERROR; - } - - int length = bytesRead / size; - if (CopyValues(vPtr, byteArr, fmt, size, length, swap, &first) != TCL_OK) - return TCL_ERROR; - - total += length; - if (count > 0) - break; - } - free(byteArr); - - if (vPtr->flush) - Vec_FlushCache(vPtr); - Vec_UpdateClients(vPtr); - - // Set the result as the number of values read - Tcl_SetIntObj(Tcl_GetObjResult(interp), total); - - return TCL_OK; -} - -static int SearchOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - int wantValue = 0; - char* string = Tcl_GetString(objv[2]); - if ((string[0] == '-') && (strcmp(string, "-value") == 0)) { - wantValue = 1; - objv++, objc--; - } - double min; - if (Blt_ExprDoubleFromObj(interp, objv[2], &min) != TCL_OK) - return TCL_ERROR; - - double max = min; - if (objc > 4) { - Tcl_AppendResult(interp, "wrong # arguments: should be \"", - Tcl_GetString(objv[0]), " search ?-value? min ?max?", - (char *)NULL); - return TCL_ERROR; - } - - if ((objc > 3) && (Blt_ExprDoubleFromObj(interp, objv[3], &max) != TCL_OK)) - return TCL_ERROR; - - // Bogus range. Don't bother looking - if ((min - max) >= DBL_EPSILON) - return TCL_OK; - - Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - if (wantValue) { - for (int i = 0; i < vPtr->length; i++) { - if (InRange(vPtr->valueArr[i], min, max)) - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewDoubleObj(vPtr->valueArr[i])); - } - } - else { - for (int i = 0; i < vPtr->length; i++) { - if (InRange(vPtr->valueArr[i], min, max)) - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewIntObj(i + vPtr->offset)); - } - } - Tcl_SetObjResult(interp, listObjPtr); - - return TCL_OK; -} - -static int OffsetOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - if (objc == 3) { - int newOffset; - if (Tcl_GetIntFromObj(interp, objv[2], &newOffset) != TCL_OK) - return TCL_ERROR; - - vPtr->offset = newOffset; - } - Tcl_SetIntObj(Tcl_GetObjResult(interp), vPtr->offset); - - return TCL_OK; -} - -static int RandomOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - for (int i = 0; i < vPtr->length; i++) - vPtr->valueArr[i] = drand48(); - - if (vPtr->flush) - Vec_FlushCache(vPtr); - Vec_UpdateClients(vPtr); - - return TCL_OK; -} - -static int SeqOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - double start; - if (Blt_ExprDoubleFromObj(interp, objv[2], &start) != TCL_OK) - return TCL_ERROR; - - double stop; - if (Blt_ExprDoubleFromObj(interp, objv[3], &stop) != TCL_OK) - return TCL_ERROR; - - int n = vPtr->length; - if ((objc > 4) && (Blt_ExprIntFromObj(interp, objv[4], &n) != TCL_OK)) - return TCL_ERROR; - - if (n > 1) { - if (Vec_SetLength(interp, vPtr, n) != TCL_OK) - return TCL_ERROR; - - double step = (stop - start) / (double)(n - 1); - for (int i = 0; i < n; i++) - vPtr->valueArr[i] = start + (step * i); - - if (vPtr->flush) - Vec_FlushCache(vPtr); - - Vec_UpdateClients(vPtr); - } - return TCL_OK; -} - -static int SetOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - int nElem; - Tcl_Obj **elemObjArr; - - // The source can be either a list of numbers or another vector. - - Vector* v2Ptr = Vec_ParseElement((Tcl_Interp *)NULL, vPtr->dataPtr, - Tcl_GetString(objv[2]), NULL, - NS_SEARCH_BOTH); - int result; - if (v2Ptr != NULL) { - if (vPtr == v2Ptr) { - // Source and destination vectors are the same. Copy the source - // first into a temporary vector to avoid memory overlaps. - Vector* tmpPtr = Vec_New(vPtr->dataPtr); - result = Vec_Duplicate(tmpPtr, v2Ptr); - if (result == TCL_OK) { - result = Vec_Duplicate(vPtr, tmpPtr); - } - Vec_Free(tmpPtr); - } - else - result = Vec_Duplicate(vPtr, v2Ptr); - } - else if (Tcl_ListObjGetElements(interp, objv[2], &nElem, &elemObjArr) - == TCL_OK) - result = CopyList(vPtr, interp, nElem, elemObjArr); - else - return TCL_ERROR; - - if (result == TCL_OK) { - // The vector has changed; so flush the array indices (they're wrong - // now), find the new range of the data, and notify the vector's - //clients that it's been modified. - if (vPtr->flush) - Vec_FlushCache(vPtr); - Vec_UpdateClients(vPtr); - } - - return result; -} - -static int SimplifyOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - double tolerance = 10.0; - - int nPoints = vPtr->length / 2; - int* simple = (int*)malloc(nPoints * sizeof(int)); - Point2d* reduced = (Point2d*)malloc(nPoints * sizeof(Point2d)); - Point2d* orig = (Point2d *)vPtr->valueArr; - int n = Blt_SimplifyLine(orig, 0, nPoints - 1, tolerance, simple); - for (int i = 0; i < n; i++) - reduced[i] = orig[simple[i]]; - - free(simple); - Vec_Reset(vPtr, (double *)reduced, n * 2, vPtr->length, TCL_DYNAMIC); - // The vector has changed; so flush the array indices (they're wrong - // now), find the new range of the data, and notify the vector's - // clients that it's been modified. - if (vPtr->flush) - Vec_FlushCache(vPtr); - Vec_UpdateClients(vPtr); - - return TCL_OK; -} - -static int SplitOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - int nVectors = objc - 2; - if ((vPtr->length % nVectors) != 0) { - Tcl_AppendResult(interp, "can't split vector \"", vPtr->name, - "\" into ", Itoa(nVectors), " even parts.", (char *)NULL); - return TCL_ERROR; - } - - if (nVectors > 0) { - int extra = vPtr->length / nVectors; - for (int i = 0; i < nVectors; i++) { - char* string = Tcl_GetString(objv[i+2]); - int isNew; - Vector* v2Ptr = Vec_Create(vPtr->dataPtr, string, string, string, &isNew); - int oldSize = v2Ptr->length; - int newSize = oldSize + extra; - if (Vec_SetLength(interp, v2Ptr, newSize) != TCL_OK) - return TCL_ERROR; - - int j,k; - for (j = i, k = oldSize; j < vPtr->length; j += nVectors, k++) - v2Ptr->valueArr[k] = vPtr->valueArr[j]; - - Vec_UpdateClients(v2Ptr); - if (v2Ptr->flush) { - Vec_FlushCache(v2Ptr); - } - } - } - return TCL_OK; -} - - -// Pointer to the array of values currently being sorted. -static Vector **sortVectors; -// Indicates the ordering of the sort. If non-zero, the vectors are sorted in -// decreasing order -static int sortDecreasing; -static int nSortVectors; - -static int CompareVectors(void *a, void *b) -{ - int sign = (sortDecreasing) ? -1 : 1; - for (int i = 0; i < nSortVectors; i++) { - Vector* vPtr = sortVectors[i]; - double delta = vPtr->valueArr[*(int *)a] - vPtr->valueArr[*(int *)b]; - if (delta < 0.0) - return (-1 * sign); - else if (delta > 0.0) - return (1 * sign); - } - - return 0; -} - -size_t* Blt::Vec_SortMap(Vector **vectors, int nVectors) -{ - Vector *vPtr = *vectors; - int length = vPtr->last - vPtr->first + 1; - size_t* map = (size_t*)malloc(sizeof(size_t) * length); - for (int i = vPtr->first; i <= vPtr->last; i++) - map[i] = i; - - // Set global variables for sorting routine - sortVectors = vectors; - nSortVectors = nVectors; - qsort((char *)map, length, sizeof(size_t),(QSortCompareProc *)CompareVectors); - - return map; -} - -static size_t* SortVectors(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - - Vector** vectors = (Vector**)malloc(sizeof(Vector *) * (objc + 1)); - vectors[0] = vPtr; - size_t* map = NULL; - for (int i = 0; i < objc; i++) { - Vector* v2Ptr; - if (Vec_LookupName(vPtr->dataPtr, Tcl_GetString(objv[i]), - &v2Ptr) != TCL_OK) - goto error; - - if (v2Ptr->length != vPtr->length) { - Tcl_AppendResult(interp, "vector \"", v2Ptr->name, - "\" is not the same size as \"", vPtr->name, "\"", - (char *)NULL); - goto error; - } - vectors[i + 1] = v2Ptr; - } - map = Vec_SortMap(vectors, objc + 1); - - error: - free(vectors); - - return map; -} - -static int SortOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - sortDecreasing = 0; - SortSwitches switches; - switches.flags = 0; - int i = ParseSwitches(interp, sortSwitches, objc - 2, objv + 2, &switches, - BLT_SWITCH_OBJV_PARTIAL); - if (i < 0) - return TCL_ERROR; - - objc -= i, objv += i; - sortDecreasing = (switches.flags & SORT_DECREASING); - - size_t *map = (objc > 2) ? SortVectors(vPtr, interp, objc - 2, objv + 2) : - Vec_SortMap(&vPtr, 1); - - if (map == NULL) - return TCL_ERROR; - - int sortLength = vPtr->length; - - // Create an array to store a copy of the current values of the - // vector. We'll merge the values back into the vector based upon the - // indices found in the index array. - size_t nBytes = sizeof(double) * sortLength; - double* copy = (double*)malloc(nBytes); - memcpy((char *)copy, (char *)vPtr->valueArr, nBytes); - if (switches.flags & SORT_UNIQUE) { - int count =1; - for (int n = 1; n < sortLength; n++) { - size_t next = map[n]; - size_t prev = map[n - 1]; - if (copy[next] != copy[prev]) { - map[count] = next; - count++; - } - } - sortLength = count; - nBytes = sortLength * sizeof(double); - } - - if (sortLength != vPtr->length) - Vec_SetLength(interp, vPtr, sortLength); - - for (int n = 0; n < sortLength; n++) - vPtr->valueArr[n] = copy[map[n]]; - - if (vPtr->flush) - Vec_FlushCache(vPtr); - Vec_UpdateClients(vPtr); - - // Now sort any other vectors in the same fashion. The vectors must be - // the same size as the map though - int result = TCL_ERROR; - for (int i = 2; i < objc; i++) { - Vector *v2Ptr; - if (Vec_LookupName(vPtr->dataPtr, Tcl_GetString(objv[i]), &v2Ptr) != TCL_OK) - goto error; - - if (sortLength != v2Ptr->length) - Vec_SetLength(interp, v2Ptr, sortLength); - - memcpy((char *)copy, (char *)v2Ptr->valueArr, nBytes); - for (int n = 0; n < sortLength; n++) - v2Ptr->valueArr[n] = copy[map[n]]; - - Vec_UpdateClients(v2Ptr); - if (v2Ptr->flush) - Vec_FlushCache(v2Ptr); - } - result = TCL_OK; - - error: - free(copy); - free(map); - - return result; -} - -static int InstExprOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - if (Blt_ExprVector(interp, Tcl_GetString(objv[2]), (Blt_Vector *)vPtr) != TCL_OK) - return TCL_ERROR; - - if (vPtr->flush) - Vec_FlushCache(vPtr); - Vec_UpdateClients(vPtr); - - return TCL_OK; -} - -static int ArithOp(Vector *vPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - double value; - double scalar; - - Vector* v2Ptr = Vec_ParseElement((Tcl_Interp *)NULL, vPtr->dataPtr, - Tcl_GetString(objv[2]), NULL, - NS_SEARCH_BOTH); - if (v2Ptr != NULL) { - int length = v2Ptr->last - v2Ptr->first + 1; - if (length != vPtr->length) { - Tcl_AppendResult(interp, "vectors \"", Tcl_GetString(objv[0]), - "\" and \"", Tcl_GetString(objv[2]), - "\" are not the same length", (char *)NULL); - return TCL_ERROR; - } - - char* string = Tcl_GetString(objv[1]); - Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - switch (string[0]) { - case '*': - for (int i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) { - value = vPtr->valueArr[i] * v2Ptr->valueArr[j]; - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); - } - break; - - case '/': - for (int i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) { - value = vPtr->valueArr[i] / v2Ptr->valueArr[j]; - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); - } - break; - - case '-': - for (int i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) { - value = vPtr->valueArr[i] - v2Ptr->valueArr[j]; - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); - } - break; - - case '+': - for (int i = 0, j = v2Ptr->first; i < vPtr->length; i++, j++) { - value = vPtr->valueArr[i] + v2Ptr->valueArr[j]; - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); - } - break; - } - Tcl_SetObjResult(interp, listObjPtr); - - } - else if (Blt_ExprDoubleFromObj(interp, objv[2], &scalar) == TCL_OK) { - Tcl_Obj* listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); - char* string = Tcl_GetString(objv[1]); - switch (string[0]) { - case '*': - for (int i = 0; i < vPtr->length; i++) { - value = vPtr->valueArr[i] * scalar; - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); - } - break; - - case '/': - for (int i = 0; i < vPtr->length; i++) { - value = vPtr->valueArr[i] / scalar; - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); - } - break; - - case '-': - for (int i = 0; i < vPtr->length; i++) { - value = vPtr->valueArr[i] - scalar; - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); - } - break; - - case '+': - for (int i = 0; i < vPtr->length; i++) { - value = vPtr->valueArr[i] + scalar; - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(value)); - } - break; - } - Tcl_SetObjResult(interp, listObjPtr); - } - else - return TCL_ERROR; - - return TCL_OK; -} - -static Blt_OpSpec vectorInstOps[] = - { - {"*", 1, (void*)ArithOp, 3, 3, "item",}, /*Deprecated*/ - {"+", 1, (void*)ArithOp, 3, 3, "item",}, /*Deprecated*/ - {"-", 1, (void*)ArithOp, 3, 3, "item",}, /*Deprecated*/ - {"/", 1, (void*)ArithOp, 3, 3, "item",}, /*Deprecated*/ - {"append", 1, (void*)AppendOp, 3, 0, "item ?item...?",}, - {"binread", 1, (void*)BinreadOp, 3, 0, "channel ?numValues? ?flags?",}, - {"clear", 1, (void*)ClearOp, 2, 2, "",}, - {"delete", 2, (void*)DeleteOp, 2, 0, "index ?index...?",}, - {"dup", 2, (void*)DupOp, 3, 0, "vecName",}, - {"expr", 1, (void*)InstExprOp, 3, 3, "expression",}, - {"fft", 1, (void*)FFTOp, 3, 0, "vecName ?switches?",}, - {"index", 3, (void*)IndexOp, 3, 4, "index ?value?",}, - {"inversefft",3, (void*)InverseFFTOp,4, 4, "vecName vecName",}, - {"length", 1, (void*)LengthOp, 2, 3, "?newSize?",}, - {"max", 2, (void*)MaxOp, 2, 2, "",}, - {"merge", 2, (void*)MergeOp, 3, 0, "vecName ?vecName...?",}, - {"min", 2, (void*)MinOp, 2, 2, "",}, - {"normalize", 3, (void*)NormalizeOp, 2, 3, "?vecName?",}, /*Deprecated*/ - {"notify", 3, (void*)NotifyOp, 3, 3, "keyword",}, - {"offset", 1, (void*)OffsetOp, 2, 3, "?offset?",}, - {"populate", 1, (void*)PopulateOp, 4, 4, "vecName density",}, - {"random", 4, (void*)RandomOp, 2, 2, "",}, /*Deprecated*/ - {"range", 4, (void*)RangeOp, 2, 4, "first last",}, - {"search", 3, (void*)SearchOp, 3, 5, "?-value? value ?value?",}, - {"seq", 3, (void*)SeqOp, 4, 5, "begin end ?num?",}, - {"set", 3, (void*)SetOp, 3, 3, "list",}, - {"simplify", 2, (void*)SimplifyOp, 2, 2, }, - {"sort", 2, (void*)SortOp, 2, 0, "?switches? ?vecName...?",}, - {"split", 2, (void*)SplitOp, 2, 0, "?vecName...?",}, - {"values", 3, (void*)ValuesOp, 2, 0, "?switches?",}, - {"variable", 3, (void*)MapOp, 2, 3, "?varName?",}, - }; - -static int nInstOps = sizeof(vectorInstOps) / sizeof(Blt_OpSpec); - -int Blt::Vec_InstCmd(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - Vector* vPtr = (Vector*)clientData; - vPtr->first = 0; - vPtr->last = vPtr->length - 1; - VectorCmdProc *proc = - (VectorCmdProc*)GetOpFromObj(interp, nInstOps, vectorInstOps, - BLT_OP_ARG1, objc, objv, 0); - if (proc == NULL) - return TCL_ERROR; - - return (*proc) (vPtr, interp, objc, objv); -} - -#define MAX_ERR_MSG 1023 -static char message[MAX_ERR_MSG + 1]; -char* Blt::Vec_VarTrace(ClientData clientData, Tcl_Interp* interp, - const char *part1, const char *part2, int flags) -{ - Blt_VectorIndexProc *indexProc; - Vector* vPtr = (Vector*)clientData; - - if (part2 == NULL) { - if (flags & TCL_TRACE_UNSETS) { - free((void*)(vPtr->arrayName)); - vPtr->arrayName = NULL; - if (vPtr->freeOnUnset) - Vec_Free(vPtr); - } - - return NULL; - } - - int first; - int last; - int varFlags; - - if (Vec_GetIndexRange(interp, vPtr, part2, INDEX_ALL_FLAGS, &indexProc) - != TCL_OK) - goto error; - - first = vPtr->first; - last = vPtr->last; - varFlags = TCL_LEAVE_ERR_MSG | (TCL_GLOBAL_ONLY & flags); - if (flags & TCL_TRACE_WRITES) { - // Tried to set "min" or "max" - if (first == SPECIAL_INDEX) - return (char *)"read-only index"; - - Tcl_Obj* objPtr = Tcl_GetVar2Ex(interp, part1, part2, varFlags); - if (objPtr == NULL) - goto error; - - double value; - if (Blt_ExprDoubleFromObj(interp, objPtr, &value) != TCL_OK) { - // Single numeric index. Reset the array element to - // its old value on errors - if ((last == first) && (first >= 0)) - Tcl_SetVar2Ex(interp, part1, part2, objPtr, varFlags); - goto error; - } - - if (first == vPtr->length) { - if (Vec_ChangeLength((Tcl_Interp *)NULL, vPtr, vPtr->length + 1) - != TCL_OK) - return (char *)"error resizing vector"; - } - - // Set possibly an entire range of values - ReplicateValue(vPtr, first, last, value); - } - else if (flags & TCL_TRACE_READS) { - Tcl_Obj *objPtr; - - if (vPtr->length == 0) { - if (Tcl_SetVar2(interp, part1, part2, "", varFlags) == NULL) - goto error; - - return NULL; - } - - if (first == vPtr->length) - return (char *)"write-only index"; - - if (first == last) { - double value; - if (first >= 0) - value = vPtr->valueArr[first]; - else { - vPtr->first = 0, vPtr->last = vPtr->length - 1; - value = (*indexProc) ((Blt_Vector *) vPtr); - } - - objPtr = Tcl_NewDoubleObj(value); - if (Tcl_SetVar2Ex(interp, part1, part2, objPtr, varFlags) == NULL) { - Tcl_DecrRefCount(objPtr); - goto error; - } - } - else { - objPtr = GetValues(vPtr, first, last); - if (Tcl_SetVar2Ex(interp, part1, part2, objPtr, varFlags) == NULL) - Tcl_DecrRefCount(objPtr); - goto error; - } - } - else if (flags & TCL_TRACE_UNSETS) { - if ((first == vPtr->length) || (first == SPECIAL_INDEX)) - return (char *)"special vector index"; - - // Collapse the vector from the point of the first unset element. - // Also flush any array variable entries so that the shift is - // reflected when the array variable is read. - for (int i = first, j = last + 1; j < vPtr->length; i++, j++) - vPtr->valueArr[i] = vPtr->valueArr[j]; - - vPtr->length -= ((last - first) + 1); - if (vPtr->flush) - Vec_FlushCache(vPtr); - - } - else - return (char *)"unknown variable trace flag"; - - if (flags & (TCL_TRACE_UNSETS | TCL_TRACE_WRITES)) - Vec_UpdateClients(vPtr); - - Tcl_ResetResult(interp); - return NULL; - - error: - strncpy(message, Tcl_GetStringResult(interp), MAX_ERR_MSG); - message[MAX_ERR_MSG] = '\0'; - return message; -} diff --git a/src/tkbltVecInt.h b/src/tkbltVecInt.h deleted file mode 100644 index cc516a1..0000000 --- a/src/tkbltVecInt.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * 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 1995-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 "tkbltChain.h" -#include "tkbltVector.h" - -#define VECTOR_THREAD_KEY "BLT Vector Data" -#define VECTOR_MAGIC ((unsigned int) 0x46170277) - -/* These defines allow parsing of different types of indices */ - -#define INDEX_SPECIAL (1<<0) /* Recognize "min", "max", and "++end" as - * valid indices */ -#define INDEX_COLON (1<<1) /* Also recognize a range of indices separated - * by a colon */ -#define INDEX_CHECK (1<<2) /* Verify that the specified index or range of - * indices are within limits */ -#define INDEX_ALL_FLAGS (INDEX_SPECIAL | INDEX_COLON | INDEX_CHECK) - -#define SPECIAL_INDEX -2 - -#define FFT_NO_CONSTANT (1<<0) -#define FFT_BARTLETT (1<<1) -#define FFT_SPECTRUM (1<<2) - -#define NOTIFY_UPDATED ((int)BLT_VECTOR_NOTIFY_UPDATE) -#define NOTIFY_DESTROYED ((int)BLT_VECTOR_NOTIFY_DESTROY) - -#define NOTIFY_NEVER (1<<3) /* Never notify clients of updates to - * the vector */ -#define NOTIFY_ALWAYS (1<<4) /* Notify clients after each update - * of the vector is made */ -#define NOTIFY_WHENIDLE (1<<5) /* Notify clients at the next idle point - * that the vector has been updated. */ - -#define NOTIFY_PENDING (1<<6) /* A do-when-idle notification of the - * vector's clients is pending. */ -#define NOTIFY_NOW (1<<7) /* Notify clients of changes once - * immediately */ - -#define NOTIFY_WHEN_MASK (NOTIFY_NEVER|NOTIFY_ALWAYS|NOTIFY_WHENIDLE) - -#define UPDATE_RANGE (1<<9) /* The data of the vector has changed. - * Update the min and max limits when - * they are needed */ - -#define FindRange(array, first, last, min, max) \ - { \ - min = max = 0.0; \ - if (first <= last) { \ - register int i; \ - min = max = array[first]; \ - for (i = first + 1; i <= last; i++) { \ - if (min > array[i]) { \ - min = array[i]; \ - } else if (max < array[i]) { \ - max = array[i]; \ - } \ - } \ - } \ - } - -namespace Blt { - - typedef struct { - double x; - double y; - } Point2d; - - typedef struct { - Tcl_HashTable vectorTable; /* Table of vectors */ - Tcl_HashTable mathProcTable; /* Table of vector math functions */ - Tcl_HashTable indexProcTable; - Tcl_Interp* interp; - unsigned int nextId; - } VectorInterpData; - - typedef struct { - // If you change these fields, make sure you change the definition of - // Blt_Vector in blt.h too. - double *valueArr; /* Array of values (malloc-ed) */ - int length; /* Current number of values in the array. */ - int size; /* Maximum number of values that can be stored - * in the value array. */ - double min, max; /* Minimum and maximum values in the vector */ - int dirty; /* Indicates if the vector has been updated */ - int reserved; - - /* The following fields are local to this module */ - - const char *name; /* The namespace-qualified name of the vector. - * It points to the hash key allocated for the - * entry in the vector hash table. */ - VectorInterpData *dataPtr; - Tcl_Interp* interp; /* Interpreter associated with the vector */ - Tcl_HashEntry *hashPtr; /* If non-NULL, pointer in a hash table to - * track the vectors in use. */ - Tcl_FreeProc *freeProc; /* Address of procedure to call to release - * storage for the value array, Optionally can - * be one of the following: TCL_STATIC, - * TCL_DYNAMIC, or TCL_VOLATILE. */ - const char *arrayName; /* The name of the TCL array variable mapped - * to the vector (malloc'ed). If NULL, - * indicates that the vector isn't mapped to - * any variable */ - Tcl_Namespace *nsPtr; /* Namespace context of the vector itself. */ - int offset; /* Offset from zero of the vector's starting - * index */ - Tcl_Command cmdToken; /* Token for vector's TCL command. */ - Chain* chain; /* List of clients using this vector */ - int notifyFlags; /* Notification flags. See definitions - * below */ - int varFlags; /* Indicate if the variable is global, - * namespace, or local */ - int freeOnUnset; /* For backward compatibility only: If - * non-zero, free the vector when its variable - * is unset. */ - int flush; - int first, last; /* Selected region of vector. This is used - * mostly for the math routines */ - } Vector; - - extern const char* Itoa(int value); - extern int Vec_GetIndex(Tcl_Interp* interp, Vector *vPtr, - const char *string, int *indexPtr, int flags, - Blt_VectorIndexProc **procPtrPtr); - extern int Vec_GetIndexRange(Tcl_Interp* interp, Vector *vPtr, - const char *string, int flags, - Blt_VectorIndexProc **procPtrPtr); - extern Vector* Vec_ParseElement(Tcl_Interp* interp, VectorInterpData *dataPtr, - const char *start, const char **endPtr, - int flags); - extern int Vec_SetLength(Tcl_Interp* interp, Vector *vPtr, int length); - extern int Vec_SetSize(Tcl_Interp* interp, Vector *vPtr, int size); - extern void Vec_FlushCache(Vector *vPtr); - extern void Vec_UpdateRange(Vector *vPtr); - extern void Vec_UpdateClients(Vector *vPtr); - extern void Vec_Free(Vector *vPtr); - extern Vector* Vec_New(VectorInterpData *dataPtr); - extern int Vec_MapVariable(Tcl_Interp* interp, Vector *vPtr, - const char *name); - extern int Vec_ChangeLength(Tcl_Interp* interp, Vector *vPtr, int length); - extern Vector* Vec_Create(VectorInterpData *dataPtr, const char *name, - const char *cmdName, const char *varName, - int *newPtr); - extern int Vec_LookupName(VectorInterpData *dataPtr, const char *vecName, - Vector **vPtrPtr); - extern VectorInterpData* Vec_GetInterpData (Tcl_Interp* interp); - extern int Vec_Reset(Vector *vPtr, double *dataArr, int nValues, - int arraySize, Tcl_FreeProc *freeProc); - extern int Vec_FFT(Tcl_Interp* interp, Vector *realPtr, - Vector *phasesPtr, Vector *freqPtr, double delta, - int flags, Vector *srcPtr); - extern int Vec_InverseFFT(Tcl_Interp* interp, Vector *iSrcPtr, - Vector *rDestPtr, Vector *iDestPtr, - Vector *srcPtr); - extern int Vec_Duplicate(Vector *destPtr, Vector *srcPtr); - extern size_t *Vec_SortMap(Vector **vectors, int nVectors); - extern double Vec_Max(Vector *vecObjPtr); - extern double Vec_Min(Vector *vecObjPtr); - extern int ExprVector(Tcl_Interp* interp, char *string, Blt_Vector *vector); - - extern Tcl_ObjCmdProc Vec_InstCmd; - extern Tcl_VarTraceProc Vec_VarTrace; - extern void Vec_InstallMathFunctions(Tcl_HashTable *tablePtr); - extern void Vec_UninstallMathFunctions(Tcl_HashTable *tablePtr); - extern void Vec_InstallSpecialIndices(Tcl_HashTable *tablePtr); -}; - -extern Tcl_IdleProc Blt_Vec_NotifyClients; - -#ifdef _WIN32 -double drand48(void); -void srand48(long int seed); -#endif diff --git a/src/tkbltVecMath.C b/src/tkbltVecMath.C deleted file mode 100644 index 099f5f4..0000000 --- a/src/tkbltVecMath.C +++ /dev/null @@ -1,1609 +0,0 @@ -/* - * 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 1995-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 - -#include -#include -#include -#include - -#include "tkbltVecInt.h" -#include "tkbltNsUtil.h" -#include "tkbltParse.h" - -using namespace Blt; - -/* - * Three types of math functions: - * - * ComponentProc Function is applied in multiple calls to - * each component of the vector. - * VectorProc Entire vector is passed, each component is - * modified. - * ScalarProc Entire vector is passed, single scalar value - * is returned. - */ - -typedef double (ComponentProc)(double value); -typedef int (VectorProc)(Vector *vPtr); -typedef double (ScalarProc)(Vector *vPtr); - -/* - * Built-in math functions: - */ -typedef int (GenericMathProc) (void*, Tcl_Interp*, Vector*); - -/* - * MathFunction -- - * - * Contains information about math functions that can be called - * for vectors. The table of math functions is global within the - * application. So you can't define two different "sqrt" - * functions. - */ -typedef struct { - const char *name; /* Name of built-in math function. If - * NULL, indicates that the function - * was user-defined and dynamically - * allocated. Function names are - * global across all interpreters. */ - - void *proc; /* Procedure that implements this math - * function. */ - - ClientData clientData; /* Argument to pass when invoking the - * function. */ - -} MathFunction; - -/* The data structure below is used to describe an expression value, - * which can be either a double-precision floating-point value, or a - * string. A given number has only one value at a time. */ - -#define STATIC_STRING_SPACE 150 - -/* - * Tokens -- - * - * The token types are defined below. In addition, there is a - * table associating a precedence with each operator. The order - * of types is important. Consult the code before changing it. - */ -enum Tokens { - VALUE, OPEN_PAREN, CLOSE_PAREN, COMMA, END, UNKNOWN, - MULT = 8, DIVIDE, MOD, PLUS, MINUS, - LEFT_SHIFT, RIGHT_SHIFT, - LESS, GREATER, LEQ, GEQ, EQUAL, NEQ, - OLD_BIT_AND, EXPONENT, OLD_BIT_OR, OLD_QUESTY, OLD_COLON, - AND, OR, UNARY_MINUS, OLD_UNARY_PLUS, NOT, OLD_BIT_NOT -}; - -typedef struct { - Vector *vPtr; - char staticSpace[STATIC_STRING_SPACE]; - ParseValue pv; /* Used to hold a string value, if any. */ -} Value; - -/* - * ParseInfo -- - * - * The data structure below describes the state of parsing an - * expression. It's passed among the routines in this module. - */ -typedef struct { - const char *expr; /* The entire right-hand side of the - * expression, as originally passed to - * Blt_ExprVector. */ - - const char *nextPtr; /* Position of the next character to - * be scanned from the expression - * string. */ - - enum Tokens token; /* Type of the last token to be parsed - * from nextPtr. See below for - * definitions. Corresponds to the - * characters just before nextPtr. */ - -} ParseInfo; - -/* - * Precedence table. The values for non-operator token types are ignored. - */ -static int precTable[] = - { - 0, 0, 0, 0, 0, 0, 0, 0, - 12, 12, 12, /* MULT, DIVIDE, MOD */ - 11, 11, /* PLUS, MINUS */ - 10, 10, /* LEFT_SHIFT, RIGHT_SHIFT */ - 9, 9, 9, 9, /* LESS, GREATER, LEQ, GEQ */ - 8, 8, /* EQUAL, NEQ */ - 7, /* OLD_BIT_AND */ - 13, /* EXPONENTIATION */ - 5, /* OLD_BIT_OR */ - 4, /* AND */ - 3, /* OR */ - 2, /* OLD_QUESTY */ - 1, /* OLD_COLON */ - 14, 14, 14, 14 /* UNARY_MINUS, OLD_UNARY_PLUS, NOT, - * OLD_BIT_NOT */ - }; - - -/* - * Forward declarations. - */ - -static int NextValue(Tcl_Interp* interp, ParseInfo *piPtr, int prec, - Value *valuePtr); - -static int Sort(Vector *vPtr) -{ - size_t* map = Vec_SortMap(&vPtr, 1); - double* values = (double*)malloc(sizeof(double) * vPtr->length); - for(int ii = vPtr->first; ii <= vPtr->last; ii++) - values[ii] = vPtr->valueArr[map[ii]]; - - free(map); - for (int ii = vPtr->first; ii <= vPtr->last; ii++) - vPtr->valueArr[ii] = values[ii]; - - free(values); - return TCL_OK; -} - -static double Length(Blt_Vector *vectorPtr) -{ - Vector *vPtr = (Vector *)vectorPtr; - return (double)(vPtr->last - vPtr->first + 1); -} - -double Blt_VecMax(Blt_Vector *vectorPtr) -{ - Vector *vPtr = (Vector *)vectorPtr; - return Vec_Max(vPtr); -} - -double Blt_VecMin(Blt_Vector *vectorPtr) -{ - Vector *vPtr = (Vector *)vectorPtr; - return Vec_Min(vPtr); -} - -int Blt_ExprVector(Tcl_Interp* interp, char *string, Blt_Vector *vector) -{ - return ExprVector(interp,string,vector); -} - -static double Product(Blt_Vector *vectorPtr) -{ - Vector *vPtr = (Vector *)vectorPtr; - double prod; - double *vp, *vend; - - prod = 1.0; - for(vp = vPtr->valueArr + vPtr->first, - vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) { - prod *= *vp; - } - return prod; -} - -static double Sum(Blt_Vector *vectorPtr) -{ - // Kahan summation algorithm - - Vector *vPtr = (Vector *)vectorPtr; - double* vp = vPtr->valueArr + vPtr->first; - double sum = *vp++; - double c = 0.0; /* A running compensation for lost - * low-order bits.*/ - for (double* vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) { - double y = *vp - c; /* So far, so good: c is zero.*/ - double t = sum + y; /* Alas, sum is big, y small, so - * low-order digits of y are lost.*/ - c = (t - sum) - y; /* (t - sum) recovers the high-order - * part of y; subtracting y recovers - * -(low part of y) */ - sum = t; - } - - return sum; -} - -static double Mean(Blt_Vector *vectorPtr) -{ - Vector *vPtr = (Vector *)vectorPtr; - double sum = Sum(vectorPtr); - int n = vPtr->last - vPtr->first + 1; - - return sum / (double)n; -} - -// var = 1/N Sum( (x[i] - mean)^2 ) -static double Variance(Blt_Vector *vectorPtr) -{ - Vector *vPtr = (Vector *)vectorPtr; - double mean = Mean(vectorPtr); - double var = 0.0; - int count = 0; - for(double *vp=vPtr->valueArr+vPtr->first, *vend=vPtr->valueArr+vPtr->last; - vp <= vend; vp++) { - double dx = *vp - mean; - var += dx * dx; - count++; - } - - if (count < 2) - return 0.0; - - var /= (double)(count - 1); - return var; -} - -// skew = Sum( (x[i] - mean)^3 ) / (var^3/2) -static double Skew(Blt_Vector *vectorPtr) -{ - Vector *vPtr = (Vector *)vectorPtr; - double mean = Mean(vectorPtr); - double var = 0; - double skew = 0; - int count = 0; - for(double *vp=vPtr->valueArr+vPtr->first, *vend=vPtr->valueArr+vPtr->last; - vp <= vend; vp++) { - double diff = *vp - mean; - diff = fabs(diff); - double diffsq = diff * diff; - var += diffsq; - skew += diffsq * diff; - count++; - } - - if (count < 2) - return 0.0; - - var /= (double)(count - 1); - skew /= count * var * sqrt(var); - return skew; -} - -static double StdDeviation(Blt_Vector *vectorPtr) -{ - double var; - - var = Variance(vectorPtr); - if (var > 0.0) { - return sqrt(var); - } - return 0.0; -} - -static double AvgDeviation(Blt_Vector *vectorPtr) -{ - Vector *vPtr = (Vector *)vectorPtr; - double mean = Mean(vectorPtr); - double avg = 0.0; - int count = 0; - for(double *vp=vPtr->valueArr+vPtr->first, *vend=vPtr->valueArr+vPtr->last; - vp <= vend; vp++) { - double diff = *vp - mean; - avg += fabs(diff); - count++; - } - - if (count < 2) - return 0.0; - - avg /= (double)count; - return avg; -} - -static double Kurtosis(Blt_Vector *vectorPtr) -{ - Vector *vPtr = (Vector *)vectorPtr; - double mean = Mean(vectorPtr); - double var = 0; - double kurt = 0; - int count = 0; - for(double *vp=vPtr->valueArr+vPtr->first, *vend=vPtr->valueArr+vPtr->last; - vp <= vend; vp++) { - double diff = *vp - mean; - double diffsq = diff * diff; - var += diffsq; - kurt += diffsq * diffsq; - count++; - } - - if (count < 2) - return 0.0; - - var /= (double)(count - 1); - - if (var == 0.0) - return 0.0; - - kurt /= (count * var * var); - return kurt - 3.0; /* Fisher Kurtosis */ -} - -static double Median(Blt_Vector *vectorPtr) -{ - Vector *vPtr = (Vector *)vectorPtr; - size_t *map; - double q2; - int mid; - - if (vPtr->length == 0) { - return -DBL_MAX; - } - map = Vec_SortMap(&vPtr, 1); - mid = (vPtr->length - 1) / 2; - - /* - * Determine Q2 by checking if the number of elements [0..n-1] is - * odd or even. If even, we must take the average of the two - * middle values. - */ - if (vPtr->length & 1) { /* Odd */ - q2 = vPtr->valueArr[map[mid]]; - } else { /* Even */ - q2 = (vPtr->valueArr[map[mid]] + - vPtr->valueArr[map[mid + 1]]) * 0.5; - } - free(map); - return q2; -} - -static double Q1(Blt_Vector *vectorPtr) -{ - Vector *vPtr = (Vector *)vectorPtr; - double q1; - size_t *map; - - if (vPtr->length == 0) { - return -DBL_MAX; - } - map = Vec_SortMap(&vPtr, 1); - - if (vPtr->length < 4) { - q1 = vPtr->valueArr[map[0]]; - } else { - int mid, q; - - mid = (vPtr->length - 1) / 2; - q = mid / 2; - - /* - * Determine Q1 by checking if the number of elements in the - * bottom half [0..mid) is odd or even. If even, we must - * take the average of the two middle values. - */ - if (mid & 1) { /* Odd */ - q1 = vPtr->valueArr[map[q]]; - } else { /* Even */ - q1 = (vPtr->valueArr[map[q]] + - vPtr->valueArr[map[q + 1]]) * 0.5; - } - } - free(map); - return q1; -} - -static double Q3(Blt_Vector *vectorPtr) -{ - Vector *vPtr = (Vector *)vectorPtr; - double q3; - size_t *map; - - if (vPtr->length == 0) { - return -DBL_MAX; - } - - map = Vec_SortMap(&vPtr, 1); - - if (vPtr->length < 4) { - q3 = vPtr->valueArr[map[vPtr->length - 1]]; - } else { - int mid, q; - - mid = (vPtr->length - 1) / 2; - q = (vPtr->length + mid) / 2; - - /* - * Determine Q3 by checking if the number of elements in the - * upper half (mid..n-1] is odd or even. If even, we must - * take the average of the two middle values. - */ - if (mid & 1) { /* Odd */ - q3 = vPtr->valueArr[map[q]]; - } else { /* Even */ - q3 = (vPtr->valueArr[map[q]] + - vPtr->valueArr[map[q + 1]]) * 0.5; - } - } - free(map); - return q3; -} - -static int Norm(Blt_Vector *vector) -{ - Vector *vPtr = (Vector *)vector; - double norm, range, min, max; - int i; - - min = Vec_Min(vPtr); - max = Vec_Max(vPtr); - range = max - min; - for (i = 0; i < vPtr->length; i++) { - norm = (vPtr->valueArr[i] - min) / range; - vPtr->valueArr[i] = norm; - } - return TCL_OK; -} - -static double Nonzeros(Blt_Vector *vector) -{ - Vector *vPtr = (Vector *)vector; - int count; - double *vp, *vend; - - count = 0; - for(vp = vPtr->valueArr + vPtr->first, vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) { - if (*vp == 0.0) - count++; - } - return (double) count; -} - -static double Fabs(double value) -{ - if (value < 0.0) - return -value; - return value; -} - -static double Round(double value) -{ - if (value < 0.0) - return ceil(value - 0.5); - else - return floor(value + 0.5); -} - -static double Fmod(double x, double y) -{ - if (y == 0.0) - return 0.0; - return x - (floor(x / y) * y); -} - -/* - *--------------------------------------------------------------------------- - * - * MathError -- - * - * This procedure is called when an error occurs during a - * floating-point operation. It reads errno and sets - * interp->result accordingly. - * - * Results: - * Interp->result is set to hold an error message. - * - * Side effects: - * None. - * - *--------------------------------------------------------------------------- - */ -static void MathError(Tcl_Interp* interp, double value) -{ - if ((errno == EDOM) || (value != value)) { - Tcl_AppendResult(interp, "domain error: argument not in valid range", - (char *)NULL); - Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", - Tcl_GetStringResult(interp), (char *)NULL); - } - else if ((errno == ERANGE) || std::isinf(value)) { - if (value == 0.0) { - Tcl_AppendResult(interp, - "floating-point value too small to represent", - (char *)NULL); - Tcl_SetErrorCode(interp, "ARITH", "UNDERFLOW", - Tcl_GetStringResult(interp), (char *)NULL); - } - else { - Tcl_AppendResult(interp, - "floating-point value too large to represent", - (char *)NULL); - Tcl_SetErrorCode(interp, "ARITH", "OVERFLOW", - Tcl_GetStringResult(interp), (char *)NULL); - } - } - else { - Tcl_AppendResult(interp, "unknown floating-point error, ", - "errno = ", Itoa(errno), (char *)NULL); - Tcl_SetErrorCode(interp, "ARITH", "UNKNOWN", - Tcl_GetStringResult(interp), (char *)NULL); - } -} - -static int ParseString(Tcl_Interp* interp, const char *string, Value *valuePtr) -{ - const char *endPtr; - double value; - - errno = 0; - - /* - * The string can be either a number or a vector. First try to - * convert the string to a number. If that fails then see if - * we can find a vector by that name. - */ - - value = strtod(string, (char **)&endPtr); - if ((endPtr != string) && (*endPtr == '\0')) { - if (errno != 0) { - Tcl_ResetResult(interp); - MathError(interp, value); - return TCL_ERROR; - } - /* Numbers are stored as single element vectors. */ - if (Vec_ChangeLength(interp, valuePtr->vPtr, 1) != TCL_OK) { - return TCL_ERROR; - } - valuePtr->vPtr->valueArr[0] = value; - return TCL_OK; - } else { - Vector *vPtr; - - while (isspace((unsigned char)(*string))) { - string++; /* Skip spaces leading the vector name. */ - } - vPtr = Vec_ParseElement(interp, valuePtr->vPtr->dataPtr, - string, &endPtr, NS_SEARCH_BOTH); - if (vPtr == NULL) { - return TCL_ERROR; - } - if (*endPtr != '\0') { - Tcl_AppendResult(interp, "extra characters after vector", - (char *)NULL); - return TCL_ERROR; - } - /* Copy the designated vector to our temporary. */ - Vec_Duplicate(valuePtr->vPtr, vPtr); - } - return TCL_OK; -} - -static int ParseMathFunction(Tcl_Interp* interp, const char *start, - ParseInfo *piPtr, Value *valuePtr) -{ - Tcl_HashEntry *hPtr; - MathFunction *mathPtr; /* Info about math function. */ - char *p; - VectorInterpData *dataPtr; /* Interpreter-specific data. */ - GenericMathProc *proc; - - /* - * Find the end of the math function's name and lookup the - * record for the function. - */ - p = (char *)start; - while (isspace((unsigned char)(*p))) { - p++; - } - piPtr->nextPtr = p; - while (isalnum((unsigned char)(*p)) || (*p == '_')) { - p++; - } - if (*p != '(') { - return TCL_RETURN; /* Must start with open parenthesis */ - } - dataPtr = valuePtr->vPtr->dataPtr; - *p = '\0'; - hPtr = Tcl_FindHashEntry(&dataPtr->mathProcTable, piPtr->nextPtr); - *p = '('; - if (hPtr == NULL) { - return TCL_RETURN; /* Name doesn't match any known function */ - } - /* Pick up the single value as the argument to the function */ - piPtr->token = OPEN_PAREN; - piPtr->nextPtr = p + 1; - valuePtr->pv.next = valuePtr->pv.buffer; - if (NextValue(interp, piPtr, -1, valuePtr) != TCL_OK) { - return TCL_ERROR; /* Parse error */ - } - if (piPtr->token != CLOSE_PAREN) { - Tcl_AppendResult(interp, "unmatched parentheses in expression \"", - piPtr->expr, "\"", (char *)NULL); - return TCL_ERROR; /* Missing right parenthesis */ - } - mathPtr = (MathFunction*)Tcl_GetHashValue(hPtr); - proc = (GenericMathProc*)mathPtr->proc; - if ((*proc) (mathPtr->clientData, interp, valuePtr->vPtr) != TCL_OK) { - return TCL_ERROR; /* Function invocation error */ - } - piPtr->token = VALUE; - return TCL_OK; -} - -static int NextToken(Tcl_Interp* interp, ParseInfo *piPtr, Value *valuePtr) -{ - const char *p; - const char *endPtr; - const char *var; - int result; - - p = piPtr->nextPtr; - while (isspace((unsigned char)(*p))) { - p++; - } - if (*p == '\0') { - piPtr->token = END; - piPtr->nextPtr = p; - return TCL_OK; - } - /* - * Try to parse the token as a floating-point number. But check - * that the first character isn't a "-" or "+", which "strtod" - * will happily accept as an unary operator. Otherwise, we might - * accidently treat a binary operator as unary by mistake, which - * will eventually cause a syntax error. - */ - if ((*p != '-') && (*p != '+')) { - double value; - - errno = 0; - value = strtod(p, (char **)&endPtr); - if (endPtr != p) { - if (errno != 0) { - MathError(interp, value); - return TCL_ERROR; - } - piPtr->token = VALUE; - piPtr->nextPtr = endPtr; - - /* - * Save the single floating-point value as an 1-component vector. - */ - if (Vec_ChangeLength(interp, valuePtr->vPtr, 1) != TCL_OK) { - return TCL_ERROR; - } - valuePtr->vPtr->valueArr[0] = value; - return TCL_OK; - } - } - piPtr->nextPtr = p + 1; - switch (*p) { - case '$': - piPtr->token = VALUE; - var = Tcl_ParseVar(interp, p, &endPtr); - if (var == NULL) { - return TCL_ERROR; - } - piPtr->nextPtr = endPtr; - Tcl_ResetResult(interp); - result = ParseString(interp, var, valuePtr); - return result; - - case '[': - piPtr->token = VALUE; - result = ParseNestedCmd(interp, p + 1, 0, &endPtr, &valuePtr->pv); - if (result != TCL_OK) { - return result; - } - piPtr->nextPtr = endPtr; - Tcl_ResetResult(interp); - result = ParseString(interp, valuePtr->pv.buffer, valuePtr); - return result; - - case '"': - piPtr->token = VALUE; - result = ParseQuotes(interp, p + 1, '"', 0, &endPtr, &valuePtr->pv); - if (result != TCL_OK) { - return result; - } - piPtr->nextPtr = endPtr; - Tcl_ResetResult(interp); - result = ParseString(interp, valuePtr->pv.buffer, valuePtr); - return result; - - case '{': - piPtr->token = VALUE; - result = ParseBraces(interp, p + 1, &endPtr, &valuePtr->pv); - if (result != TCL_OK) { - return result; - } - piPtr->nextPtr = endPtr; - Tcl_ResetResult(interp); - result = ParseString(interp, valuePtr->pv.buffer, valuePtr); - return result; - - case '(': - piPtr->token = OPEN_PAREN; - break; - - case ')': - piPtr->token = CLOSE_PAREN; - break; - - case ',': - piPtr->token = COMMA; - break; - - case '*': - piPtr->token = MULT; - break; - - case '/': - piPtr->token = DIVIDE; - break; - - case '%': - piPtr->token = MOD; - break; - - case '+': - piPtr->token = PLUS; - break; - - case '-': - piPtr->token = MINUS; - break; - - case '^': - piPtr->token = EXPONENT; - break; - - case '<': - switch (*(p + 1)) { - case '<': - piPtr->nextPtr = p + 2; - piPtr->token = LEFT_SHIFT; - break; - case '=': - piPtr->nextPtr = p + 2; - piPtr->token = LEQ; - break; - default: - piPtr->token = LESS; - break; - } - break; - - case '>': - switch (*(p + 1)) { - case '>': - piPtr->nextPtr = p + 2; - piPtr->token = RIGHT_SHIFT; - break; - case '=': - piPtr->nextPtr = p + 2; - piPtr->token = GEQ; - break; - default: - piPtr->token = GREATER; - break; - } - break; - - case '=': - if (*(p + 1) == '=') { - piPtr->nextPtr = p + 2; - piPtr->token = EQUAL; - } else { - piPtr->token = UNKNOWN; - } - break; - - case '&': - if (*(p + 1) == '&') { - piPtr->nextPtr = p + 2; - piPtr->token = AND; - } else { - piPtr->token = UNKNOWN; - } - break; - - case '|': - if (*(p + 1) == '|') { - piPtr->nextPtr = p + 2; - piPtr->token = OR; - } else { - piPtr->token = UNKNOWN; - } - break; - - case '!': - if (*(p + 1) == '=') { - piPtr->nextPtr = p + 2; - piPtr->token = NEQ; - } else { - piPtr->token = NOT; - } - break; - - default: - piPtr->token = VALUE; - result = ParseMathFunction(interp, p, piPtr, valuePtr); - if ((result == TCL_OK) || (result == TCL_ERROR)) { - return result; - } else { - Vector *vPtr; - - while (isspace((unsigned char)(*p))) { - p++; /* Skip spaces leading the vector name. */ - } - vPtr = Vec_ParseElement(interp, valuePtr->vPtr->dataPtr, - p, &endPtr, NS_SEARCH_BOTH); - if (vPtr == NULL) { - return TCL_ERROR; - } - Vec_Duplicate(valuePtr->vPtr, vPtr); - piPtr->nextPtr = endPtr; - } - } - return TCL_OK; -} - -static int NextValue(Tcl_Interp* interp, ParseInfo *piPtr, - int prec, Value *valuePtr) -{ - Value value2; /* Second operand for current operator. */ - int oper; /* Current operator (either unary or binary). */ - int gotOp; /* Non-zero means already lexed the operator - * (while picking up value for unary operator). - * Don't lex again. */ - int result; - Vector *vPtr, *v2Ptr; - int i; - - /* - * There are two phases to this procedure. First, pick off an initial - * value. Then, parse (binary operator, value) pairs until done. - */ - - vPtr = valuePtr->vPtr; - v2Ptr = Vec_New(vPtr->dataPtr); - gotOp = 0; - value2.vPtr = v2Ptr; - value2.pv.buffer = value2.pv.next = value2.staticSpace; - value2.pv.end = value2.pv.buffer + STATIC_STRING_SPACE - 1; - value2.pv.expandProc = ExpandParseValue; - value2.pv.clientData = NULL; - - result = NextToken(interp, piPtr, valuePtr); - if (result != TCL_OK) { - goto done; - } - if (piPtr->token == OPEN_PAREN) { - - /* Parenthesized sub-expression. */ - - result = NextValue(interp, piPtr, -1, valuePtr); - if (result != TCL_OK) { - goto done; - } - if (piPtr->token != CLOSE_PAREN) { - Tcl_AppendResult(interp, "unmatched parentheses in expression \"", - piPtr->expr, "\"", (char *)NULL); - result = TCL_ERROR; - goto done; - } - } else { - if (piPtr->token == MINUS) { - piPtr->token = UNARY_MINUS; - } - if (piPtr->token >= UNARY_MINUS) { - oper = piPtr->token; - result = NextValue(interp, piPtr, precTable[oper], valuePtr); - if (result != TCL_OK) { - goto done; - } - gotOp = 1; - /* Process unary operators. */ - switch (oper) { - case UNARY_MINUS: - for(i = 0; i < vPtr->length; i++) { - vPtr->valueArr[i] = -(vPtr->valueArr[i]); - } - break; - - case NOT: - for(i = 0; i < vPtr->length; i++) { - vPtr->valueArr[i] = (double)(!vPtr->valueArr[i]); - } - break; - default: - Tcl_AppendResult(interp, "unknown operator", (char *)NULL); - goto error; - } - } else if (piPtr->token != VALUE) { - Tcl_AppendResult(interp, "missing operand", (char *)NULL); - goto error; - } - } - if (!gotOp) { - result = NextToken(interp, piPtr, &value2); - if (result != TCL_OK) { - goto done; - } - } - /* - * Got the first operand. Now fetch (operator, operand) pairs. - */ - for (;;) { - oper = piPtr->token; - - value2.pv.next = value2.pv.buffer; - if ((oper < MULT) || (oper >= UNARY_MINUS)) { - if ((oper == END) || (oper == CLOSE_PAREN) || - (oper == COMMA)) { - result = TCL_OK; - goto done; - } else { - Tcl_AppendResult(interp, "bad operator", (char *)NULL); - goto error; - } - } - if (precTable[oper] <= prec) { - result = TCL_OK; - goto done; - } - result = NextValue(interp, piPtr, precTable[oper], &value2); - if (result != TCL_OK) { - goto done; - } - if ((piPtr->token < MULT) && (piPtr->token != VALUE) && - (piPtr->token != END) && (piPtr->token != CLOSE_PAREN) && - (piPtr->token != COMMA)) { - Tcl_AppendResult(interp, "unexpected token in expression", - (char *)NULL); - goto error; - } - /* - * At this point we have two vectors and an operator. - */ - - if (v2Ptr->length == 1) { - double *opnd; - double scalar; - - /* - * 2nd operand is a scalar. - */ - scalar = v2Ptr->valueArr[0]; - opnd = vPtr->valueArr; - switch (oper) { - case MULT: - for(i = 0; i < vPtr->length; i++) { - opnd[i] *= scalar; - } - break; - - case DIVIDE: - if (scalar == 0.0) { - Tcl_AppendResult(interp, "divide by zero", (char *)NULL); - goto error; - } - for(i = 0; i < vPtr->length; i++) { - opnd[i] /= scalar; - } - break; - - case PLUS: - for(i = 0; i < vPtr->length; i++) { - opnd[i] += scalar; - } - break; - - case MINUS: - for(i = 0; i < vPtr->length; i++) { - opnd[i] -= scalar; - } - break; - - case EXPONENT: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = pow(opnd[i], scalar); - } - break; - - case MOD: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = Fmod(opnd[i], scalar); - } - break; - - case LESS: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(opnd[i] < scalar); - } - break; - - case GREATER: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(opnd[i] > scalar); - } - break; - - case LEQ: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(opnd[i] <= scalar); - } - break; - - case GEQ: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(opnd[i] >= scalar); - } - break; - - case EQUAL: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(opnd[i] == scalar); - } - break; - - case NEQ: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(opnd[i] != scalar); - } - break; - - case AND: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(opnd[i] && scalar); - } - break; - - case OR: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(opnd[i] || scalar); - } - break; - - case LEFT_SHIFT: - { - int offset; - - offset = (int)scalar % vPtr->length; - if (offset > 0) { - double *hold; - int j; - - hold = (double*)malloc(sizeof(double) * offset); - for (i = 0; i < offset; i++) { - hold[i] = opnd[i]; - } - for (i = offset, j = 0; i < vPtr->length; i++, j++) { - opnd[j] = opnd[i]; - } - for (i = 0, j = vPtr->length - offset; - j < vPtr->length; i++, j++) { - opnd[j] = hold[i]; - } - free(hold); - } - } - break; - - case RIGHT_SHIFT: - { - int offset; - - offset = (int)scalar % vPtr->length; - if (offset > 0) { - double *hold; - int j; - - hold = (double*)malloc(sizeof(double) * offset); - for (i = vPtr->length - offset, j = 0; - i < vPtr->length; i++, j++) { - hold[j] = opnd[i]; - } - for (i = vPtr->length - offset - 1, - j = vPtr->length - 1; i >= 0; i--, j--) { - opnd[j] = opnd[i]; - } - for (i = 0; i < offset; i++) { - opnd[i] = hold[i]; - } - free(hold); - } - } - break; - - default: - Tcl_AppendResult(interp, "unknown operator in expression", - (char *)NULL); - goto error; - } - - } else if (vPtr->length == 1) { - double *opnd; - double scalar; - - /* - * 1st operand is a scalar. - */ - scalar = vPtr->valueArr[0]; - Vec_Duplicate(vPtr, v2Ptr); - opnd = vPtr->valueArr; - switch (oper) { - case MULT: - for(i = 0; i < vPtr->length; i++) { - opnd[i] *= scalar; - } - break; - - case PLUS: - for(i = 0; i < vPtr->length; i++) { - opnd[i] += scalar; - } - break; - - case DIVIDE: - for(i = 0; i < vPtr->length; i++) { - if (opnd[i] == 0.0) { - Tcl_AppendResult(interp, "divide by zero", - (char *)NULL); - goto error; - } - opnd[i] = (scalar / opnd[i]); - } - break; - - case MINUS: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = scalar - opnd[i]; - } - break; - - case EXPONENT: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = pow(scalar, opnd[i]); - } - break; - - case MOD: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = Fmod(scalar, opnd[i]); - } - break; - - case LESS: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(scalar < opnd[i]); - } - break; - - case GREATER: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(scalar > opnd[i]); - } - break; - - case LEQ: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(scalar >= opnd[i]); - } - break; - - case GEQ: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(scalar <= opnd[i]); - } - break; - - case EQUAL: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(opnd[i] == scalar); - } - break; - - case NEQ: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(opnd[i] != scalar); - } - break; - - case AND: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(opnd[i] && scalar); - } - break; - - case OR: - for(i = 0; i < vPtr->length; i++) { - opnd[i] = (double)(opnd[i] || scalar); - } - break; - - case LEFT_SHIFT: - case RIGHT_SHIFT: - Tcl_AppendResult(interp, "second shift operand must be scalar", - (char *)NULL); - goto error; - - default: - Tcl_AppendResult(interp, "unknown operator in expression", - (char *)NULL); - goto error; - } - } else { - double *opnd1, *opnd2; - /* - * Carry out the function of the specified operator. - */ - if (vPtr->length != v2Ptr->length) { - Tcl_AppendResult(interp, "vectors are different lengths", - (char *)NULL); - goto error; - } - opnd1 = vPtr->valueArr, opnd2 = v2Ptr->valueArr; - switch (oper) { - case MULT: - for (i = 0; i < vPtr->length; i++) { - opnd1[i] *= opnd2[i]; - } - break; - - case DIVIDE: - for (i = 0; i < vPtr->length; i++) { - if (opnd2[i] == 0.0) { - Tcl_AppendResult(interp, - "can't divide by 0.0 vector component", - (char *)NULL); - goto error; - } - opnd1[i] /= opnd2[i]; - } - break; - - case PLUS: - for (i = 0; i < vPtr->length; i++) { - opnd1[i] += opnd2[i]; - } - break; - - case MINUS: - for (i = 0; i < vPtr->length; i++) { - opnd1[i] -= opnd2[i]; - } - break; - - case MOD: - for (i = 0; i < vPtr->length; i++) { - opnd1[i] = Fmod(opnd1[i], opnd2[i]); - } - break; - - case EXPONENT: - for (i = 0; i < vPtr->length; i++) { - opnd1[i] = pow(opnd1[i], opnd2[i]); - } - break; - - case LESS: - for (i = 0; i < vPtr->length; i++) { - opnd1[i] = (double)(opnd1[i] < opnd2[i]); - } - break; - - case GREATER: - for (i = 0; i < vPtr->length; i++) { - opnd1[i] = (double)(opnd1[i] > opnd2[i]); - } - break; - - case LEQ: - for (i = 0; i < vPtr->length; i++) { - opnd1[i] = (double)(opnd1[i] <= opnd2[i]); - } - break; - - case GEQ: - for (i = 0; i < vPtr->length; i++) { - opnd1[i] = (double)(opnd1[i] >= opnd2[i]); - } - break; - - case EQUAL: - for (i = 0; i < vPtr->length; i++) { - opnd1[i] = (double)(opnd1[i] == opnd2[i]); - } - break; - - case NEQ: - for (i = 0; i < vPtr->length; i++) { - opnd1[i] = (double)(opnd1[i] != opnd2[i]); - } - break; - - case AND: - for (i = 0; i < vPtr->length; i++) { - opnd1[i] = (double)(opnd1[i] && opnd2[i]); - } - break; - - case OR: - for (i = 0; i < vPtr->length; i++) { - opnd1[i] = (double)(opnd1[i] || opnd2[i]); - } - break; - - case LEFT_SHIFT: - case RIGHT_SHIFT: - Tcl_AppendResult(interp, "second shift operand must be scalar", - (char *)NULL); - goto error; - - default: - Tcl_AppendResult(interp, "unknown operator in expression", - (char *)NULL); - goto error; - } - } - } - done: - if (value2.pv.buffer != value2.staticSpace) { - free(value2.pv.buffer); - } - Vec_Free(v2Ptr); - return result; - - error: - if (value2.pv.buffer != value2.staticSpace) { - free(value2.pv.buffer); - } - Vec_Free(v2Ptr); - return TCL_ERROR; -} - -static int EvaluateExpression(Tcl_Interp* interp, char *string, - Value *valuePtr) -{ - ParseInfo info; - int result; - Vector *vPtr; - double *vp, *vend; - - info.expr = info.nextPtr = string; - valuePtr->pv.buffer = valuePtr->pv.next = valuePtr->staticSpace; - valuePtr->pv.end = valuePtr->pv.buffer + STATIC_STRING_SPACE - 1; - valuePtr->pv.expandProc = ExpandParseValue; - valuePtr->pv.clientData = NULL; - - result = NextValue(interp, &info, -1, valuePtr); - if (result != TCL_OK) { - return result; - } - if (info.token != END) { - Tcl_AppendResult(interp, ": syntax error in expression \"", - string, "\"", (char *)NULL); - return TCL_ERROR; - } - vPtr = valuePtr->vPtr; - - /* Check for NaN's and overflows. */ - for (vp = vPtr->valueArr, vend = vp + vPtr->length; vp < vend; vp++) { - if (!std::isfinite(*vp)) { - /* - * IEEE floating-point error. - */ - MathError(interp, *vp); - return TCL_ERROR; - } - } - return TCL_OK; -} - -static int ComponentFunc(ClientData clientData, Tcl_Interp* interp, - Vector *vPtr) -{ - ComponentProc *procPtr = (ComponentProc *) clientData; - double *vp, *vend; - - errno = 0; - for(vp = vPtr->valueArr + vPtr->first, - vend = vPtr->valueArr + vPtr->last; vp <= vend; vp++) { - *vp = (*procPtr) (*vp); - if (errno != 0) { - MathError(interp, *vp); - return TCL_ERROR; - } - if (!std::isfinite(*vp)) { - /* - * IEEE floating-point error. - */ - MathError(interp, *vp); - return TCL_ERROR; - } - } - return TCL_OK; -} - -static int ScalarFunc(ClientData clientData, Tcl_Interp* interp, Vector *vPtr) -{ - double value; - ScalarProc *procPtr = (ScalarProc *) clientData; - - errno = 0; - value = (*procPtr) (vPtr); - if (errno != 0) { - MathError(interp, value); - return TCL_ERROR; - } - if (Vec_ChangeLength(interp, vPtr, 1) != TCL_OK) { - return TCL_ERROR; - } - vPtr->valueArr[0] = value; - return TCL_OK; -} - -static int VectorFunc(ClientData clientData, Tcl_Interp* interp, Vector *vPtr) -{ - VectorProc *procPtr = (VectorProc *) clientData; - - return (*procPtr) (vPtr); -} - - -static MathFunction mathFunctions[] = - { - {"abs", (void*)ComponentFunc, (ClientData)Fabs}, - {"acos", (void*)ComponentFunc, (ClientData)acos}, - {"asin", (void*)ComponentFunc, (ClientData)asin}, - {"atan", (void*)ComponentFunc, (ClientData)atan}, - {"adev", (void*)ScalarFunc, (ClientData)AvgDeviation}, - {"ceil", (void*)ComponentFunc, (ClientData)ceil}, - {"cos", (void*)ComponentFunc, (ClientData)cos}, - {"cosh", (void*)ComponentFunc, (ClientData)cosh}, - {"exp", (void*)ComponentFunc, (ClientData)exp}, - {"floor", (void*)ComponentFunc, (ClientData)floor}, - {"kurtosis",(void*)ScalarFunc, (ClientData)Kurtosis}, - {"length", (void*)ScalarFunc, (ClientData)Length}, - {"log", (void*)ComponentFunc, (ClientData)log}, - {"log10", (void*)ComponentFunc, (ClientData)log10}, - {"max", (void*)ScalarFunc, (ClientData)Blt_VecMax}, - {"mean", (void*)ScalarFunc, (ClientData)Mean}, - {"median", (void*)ScalarFunc, (ClientData)Median}, - {"min", (void*)ScalarFunc, (ClientData)Blt_VecMin}, - {"norm", (void*)VectorFunc, (ClientData)Norm}, - {"nz", (void*)ScalarFunc, (ClientData)Nonzeros}, - {"q1", (void*)ScalarFunc, (ClientData)Q1}, - {"q3", (void*)ScalarFunc, (ClientData)Q3}, - {"prod", (void*)ScalarFunc, (ClientData)Product}, - {"random", (void*)ComponentFunc, (ClientData)drand48}, - {"round", (void*)ComponentFunc, (ClientData)Round}, - {"sdev", (void*)ScalarFunc, (ClientData)StdDeviation}, - {"sin", (void*)ComponentFunc, (ClientData)sin}, - {"sinh", (void*)ComponentFunc, (ClientData)sinh}, - {"skew", (void*)ScalarFunc, (ClientData)Skew}, - {"sort", (void*)VectorFunc, (ClientData)Sort}, - {"sqrt", (void*)ComponentFunc, (ClientData)sqrt}, - {"sum", (void*)ScalarFunc, (ClientData)Sum}, - {"tan", (void*)ComponentFunc, (ClientData)tan}, - {"tanh", (void*)ComponentFunc, (ClientData)tanh}, - {"var", (void*)ScalarFunc, (ClientData)Variance}, - {(char *)NULL,}, - }; - -void Blt::Vec_InstallMathFunctions(Tcl_HashTable *tablePtr) -{ - MathFunction *mathPtr; - - for (mathPtr = mathFunctions; mathPtr->name != NULL; mathPtr++) { - Tcl_HashEntry *hPtr; - int isNew; - - hPtr = Tcl_CreateHashEntry(tablePtr, mathPtr->name, &isNew); - Tcl_SetHashValue(hPtr, (ClientData)mathPtr); - } -} - -void Blt::Vec_UninstallMathFunctions(Tcl_HashTable *tablePtr) -{ - Tcl_HashEntry *hPtr; - Tcl_HashSearch cursor; - - for (hPtr = Tcl_FirstHashEntry(tablePtr, &cursor); hPtr != NULL; - hPtr = Tcl_NextHashEntry(&cursor)) { - MathFunction *mathPtr = (MathFunction*)Tcl_GetHashValue(hPtr); - if (mathPtr->name == NULL) - free(mathPtr); - } -} - -static void InstallIndexProc(Tcl_HashTable *tablePtr, const char *string, - Blt_VectorIndexProc *procPtr) -{ - Tcl_HashEntry *hPtr; - int dummy; - - hPtr = Tcl_CreateHashEntry(tablePtr, string, &dummy); - if (procPtr == NULL) - Tcl_DeleteHashEntry(hPtr); - else - Tcl_SetHashValue(hPtr, (ClientData)procPtr); -} - -void Blt::Vec_InstallSpecialIndices(Tcl_HashTable *tablePtr) -{ - InstallIndexProc(tablePtr, "min", Blt_VecMin); - InstallIndexProc(tablePtr, "max", Blt_VecMax); - InstallIndexProc(tablePtr, "mean", Mean); - InstallIndexProc(tablePtr, "sum", Sum); - InstallIndexProc(tablePtr, "prod", Product); -} - -int Blt::ExprVector(Tcl_Interp* interp, char *string, Blt_Vector *vector) -{ - VectorInterpData *dataPtr; /* Interpreter-specific data. */ - Vector *vPtr = (Vector *)vector; - Value value; - - dataPtr = (vector != NULL) ? vPtr->dataPtr : Vec_GetInterpData(interp); - value.vPtr = Vec_New(dataPtr); - if (EvaluateExpression(interp, string, &value) != TCL_OK) { - Vec_Free(value.vPtr); - return TCL_ERROR; - } - if (vPtr != NULL) { - Vec_Duplicate(vPtr, value.vPtr); - } else { - Tcl_Obj *listObjPtr; - double *vp, *vend; - - /* No result vector. Put values in interp->result. */ - listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); - for (vp = value.vPtr->valueArr, vend = vp + value.vPtr->length; - vp < vend; vp++) { - Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(*vp)); - } - Tcl_SetObjResult(interp, listObjPtr); - } - Vec_Free(value.vPtr); - return TCL_OK; -} - -#ifdef _WIN32 -double drand48(void) -{ - return (double)rand() / (double)RAND_MAX; -} - -void srand48(long int seed) -{ - srand(seed); -} -#endif diff --git a/src/tkbltVecOp.C b/src/tkbltVecOp.C deleted file mode 100644 index 6c84723..0000000 --- a/src/tkbltVecOp.C +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 - -#include "tkbltVecInt.h" - -using namespace Blt; - -extern Tcl_ObjCmdProc VectorObjCmd; - -int Blt_VectorCmdInitProc(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::vector"; - Tcl_Command cmdToken = Tcl_FindCommand(interp, cmdPath, NULL, 0); - if (cmdToken) - return TCL_OK; - cmdToken = Tcl_CreateObjCommand(interp, cmdPath, VectorObjCmd, - Vec_GetInterpData(interp), NULL); - if (Tcl_Export(interp, nsPtr, "vector", 0) != TCL_OK) - return TCL_ERROR; - - return TCL_OK; -} diff --git a/src/tkbltVector.C b/src/tkbltVector.C deleted file mode 100644 index e6262ec..0000000 --- a/src/tkbltVector.C +++ /dev/null @@ -1,1874 +0,0 @@ -/* - * 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 1995-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. - */ - -/* - * TODO: - * o Add H. Kirsch's vector binary read operation - * x binread file0 - * x binread -file file0 - * - * o Add ASCII/binary file reader - * x read fileName - * - * o Allow Tcl-based client notifications. - * vector x - * x notify call Display - * x notify delete Display - * x notify reorder #1 #2 - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include "tkbltVecInt.h" -#include "tkbltNsUtil.h" -#include "tkbltSwitch.h" -#include "tkbltOp.h" - -using namespace Blt; - -#define DEF_ARRAY_SIZE 64 -#define TRACE_ALL (TCL_TRACE_WRITES | TCL_TRACE_READS | TCL_TRACE_UNSETS) - - -#define VECTOR_CHAR(c) ((isalnum((unsigned char)(c))) || \ - (c == '_') || (c == ':') || (c == '@') || (c == '.')) - -/* - * VectorClient -- - * - * A vector can be shared by several clients. Each client allocates this - * structure that acts as its key for using the vector. Clients can also - * designate a callback routine that is executed whenever the vector is - * updated or destroyed. - * - */ -typedef struct { - unsigned int magic; /* Magic value designating whether this really - * is a vector token or not */ - Vector* serverPtr; /* Pointer to the master record of the vector. - * If NULL, indicates that the vector has been - * destroyed but as of yet, this client hasn't - * recognized it. */ - Blt_VectorChangedProc *proc;/* Routine to call when the contents of the - * vector change or the vector is deleted. */ - ClientData clientData; /* Data passed whenever the vector change - * procedure is called. */ - ChainLink* link; /* Used to quickly remove this entry from its - * server's client chain. */ -} VectorClient; - -static Tcl_CmdDeleteProc VectorInstDeleteProc; -extern Tcl_ObjCmdProc VectorCmd; -static Tcl_InterpDeleteProc VectorInterpDeleteProc; - -typedef struct { - char *varName; /* Requested variable name. */ - char *cmdName; /* Requested command name. */ - int flush; /* Flush */ - int watchUnset; /* Watch when variable is unset. */ -} CreateSwitches; - -static Blt_SwitchSpec createSwitches[] = - { - {BLT_SWITCH_STRING, "-variable", "varName", - Tk_Offset(CreateSwitches, varName), BLT_SWITCH_NULL_OK}, - {BLT_SWITCH_STRING, "-command", "command", - Tk_Offset(CreateSwitches, cmdName), BLT_SWITCH_NULL_OK}, - {BLT_SWITCH_BOOLEAN, "-watchunset", "bool", - Tk_Offset(CreateSwitches, watchUnset), 0}, - {BLT_SWITCH_BOOLEAN, "-flush", "bool", - Tk_Offset(CreateSwitches, flush), 0}, - {BLT_SWITCH_END} - }; - -typedef int (VectorCmdProc)(Vector* vecObjPtr, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]); - -static char stringRep[200]; - -const char *Blt::Itoa(int value) -{ - snprintf(stringRep, 200, "%d", value); - return stringRep; -} - -static char* Blt_Strdup(const char *string) -{ - size_t size = strlen(string) + 1; - char* ptr = (char*)malloc(size * sizeof(char)); - if (ptr != NULL) - strcpy(ptr, string); - - return ptr; -} - -static Vector* FindVectorInNamespace(VectorInterpData *dataPtr, - Blt_ObjectName *objNamePtr) -{ - Tcl_DString dString; - const char* name = MakeQualifiedName(objNamePtr, &dString); - Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&dataPtr->vectorTable, name); - Tcl_DStringFree(&dString); - if (hPtr != NULL) - return (Vector*)Tcl_GetHashValue(hPtr); - - return NULL; -} - -static Vector* GetVectorObject(VectorInterpData *dataPtr, const char *name, - int flags) -{ - Tcl_Interp* interp = dataPtr->interp; - Blt_ObjectName objName; - if (!ParseObjectName(interp, name, &objName, BLT_NO_ERROR_MSG | BLT_NO_DEFAULT_NS)) - return NULL; - - Vector* vPtr = NULL; - if (objName.nsPtr != NULL) - vPtr = FindVectorInNamespace(dataPtr, &objName); - else { - if (flags & NS_SEARCH_CURRENT) { - objName.nsPtr = Tcl_GetCurrentNamespace(interp); - vPtr = FindVectorInNamespace(dataPtr, &objName); - } - if ((vPtr == NULL) && (flags & NS_SEARCH_GLOBAL)) { - objName.nsPtr = Tcl_GetGlobalNamespace(interp); - vPtr = FindVectorInNamespace(dataPtr, &objName); - } - } - - return vPtr; -} - -void Blt::Vec_UpdateRange(Vector* vPtr) -{ - double* vp = vPtr->valueArr + vPtr->first; - double* vend = vPtr->valueArr + vPtr->last; - double min = *vp; - double max = *vp++; - for (/* empty */; vp <= vend; vp++) { - if (min > *vp) - min = *vp; - else if (max < *vp) - max = *vp; - } - vPtr->min = min; - vPtr->max = max; - vPtr->notifyFlags &= ~UPDATE_RANGE; -} - -int Blt::Vec_GetIndex(Tcl_Interp* interp, Vector* vPtr, const char *string, - int *indexPtr, int flags, Blt_VectorIndexProc **procPtrPtr) -{ - int value; - char c = string[0]; - - // Treat the index "end" like a numeric index - if ((c == 'e') && (strcmp(string, "end") == 0)) { - if (vPtr->length < 1) { - if (interp != NULL) { - Tcl_AppendResult(interp, "bad index \"end\": vector is empty", - (char *)NULL); - } - return TCL_ERROR; - } - *indexPtr = vPtr->length - 1; - return TCL_OK; - } else if ((c == '+') && (strcmp(string, "++end") == 0)) { - *indexPtr = vPtr->length; - return TCL_OK; - } - if (procPtrPtr != NULL) { - Tcl_HashEntry *hPtr; - - hPtr = Tcl_FindHashEntry(&vPtr->dataPtr->indexProcTable, string); - if (hPtr != NULL) { - *indexPtr = SPECIAL_INDEX; - *procPtrPtr = (Blt_VectorIndexProc*)Tcl_GetHashValue(hPtr); - return TCL_OK; - } - } - if (Tcl_GetInt(interp, (char *)string, &value) != TCL_OK) { - long int lvalue; - /* - * Unlike Tcl_GetInt, Tcl_ExprLong needs a valid interpreter, but the - * interp passed in may be NULL. So we have to use vPtr->interp and - * then reset the result. - */ - if (Tcl_ExprLong(vPtr->interp, (char *)string, &lvalue) != TCL_OK) { - Tcl_ResetResult(vPtr->interp); - if (interp != NULL) { - Tcl_AppendResult(interp, "bad index \"", string, "\"", - (char *)NULL); - } - return TCL_ERROR; - } - value = (int)lvalue; - } - /* - * Correct the index by the current value of the offset. This makes all - * the numeric indices non-negative, which is how we distinguish the - * special non-numeric indices. - */ - value -= vPtr->offset; - - if ((value < 0) || ((flags & INDEX_CHECK) && (value >= vPtr->length))) { - if (interp != NULL) { - Tcl_AppendResult(interp, "index \"", string, "\" is out of range", - (char *)NULL); - } - return TCL_ERROR; - } - *indexPtr = (int)value; - return TCL_OK; -} - -int Blt::Vec_GetIndexRange(Tcl_Interp* interp, Vector* vPtr, const char *string, - int flags, Blt_VectorIndexProc** procPtrPtr) -{ - int ielem; - char* colon = NULL; - if (flags & INDEX_COLON) - colon = (char*)strchr(string, ':'); - - if (colon != NULL) { - if (string == colon) { - vPtr->first = 0; /* Default to the first index */ - } - else { - int result; - - *colon = '\0'; - result = Vec_GetIndex(interp, vPtr, string, &ielem, flags, - (Blt_VectorIndexProc **) NULL); - *colon = ':'; - if (result != TCL_OK) { - return TCL_ERROR; - } - vPtr->first = ielem; - } - if (*(colon + 1) == '\0') { - /* Default to the last index */ - vPtr->last = (vPtr->length > 0) ? vPtr->length - 1 : 0; - } else { - if (Vec_GetIndex(interp, vPtr, colon + 1, &ielem, flags, - (Blt_VectorIndexProc **) NULL) != TCL_OK) { - return TCL_ERROR; - } - vPtr->last = ielem; - } - if (vPtr->first > vPtr->last) { - if (interp != NULL) { - Tcl_AppendResult(interp, "bad range \"", string, - "\" (first > last)", (char *)NULL); - } - return TCL_ERROR; - } - } else { - if (Vec_GetIndex(interp, vPtr, string, &ielem, flags, - procPtrPtr) != TCL_OK) { - return TCL_ERROR; - } - vPtr->last = vPtr->first = ielem; - } - return TCL_OK; -} - -Vector* Blt::Vec_ParseElement(Tcl_Interp* interp, VectorInterpData *dataPtr, - const char* start, const char** endPtr, int flags) -{ - char* p = (char*)start; - // Find the end of the vector name - while (VECTOR_CHAR(*p)) { - p++; - } - char saved = *p; - *p = '\0'; - - Vector* vPtr = GetVectorObject(dataPtr, start, flags); - if (vPtr == NULL) { - if (interp != NULL) { - Tcl_AppendResult(interp, "can't find vector \"", start, "\"", - (char *)NULL); - } - *p = saved; - return NULL; - } - *p = saved; - vPtr->first = 0; - vPtr->last = vPtr->length - 1; - if (*p == '(') { - int count, result; - - start = p + 1; - p++; - - /* Find the matching right parenthesis */ - count = 1; - while (*p != '\0') { - if (*p == ')') { - count--; - if (count == 0) { - break; - } - } else if (*p == '(') { - count++; - } - p++; - } - if (count > 0) { - if (interp != NULL) { - Tcl_AppendResult(interp, "unbalanced parentheses \"", start, - "\"", (char *)NULL); - } - return NULL; - } - *p = '\0'; - result = Vec_GetIndexRange(interp, vPtr, start, (INDEX_COLON | INDEX_CHECK), (Blt_VectorIndexProc **) NULL); - *p = ')'; - if (result != TCL_OK) { - return NULL; - } - p++; - } - if (endPtr != NULL) { - *endPtr = p; - } - return vPtr; -} - -void Blt_Vec_NotifyClients(ClientData clientData) -{ - Vector* vPtr = (Vector*)clientData; - ChainLink *link, *next; - Blt_VectorNotify notify; - - notify = (vPtr->notifyFlags & NOTIFY_DESTROYED) - ? BLT_VECTOR_NOTIFY_DESTROY : BLT_VECTOR_NOTIFY_UPDATE; - vPtr->notifyFlags &= ~(NOTIFY_UPDATED | NOTIFY_DESTROYED | NOTIFY_PENDING); - for (link = Chain_FirstLink(vPtr->chain); link; link = next) { - next = Chain_NextLink(link); - VectorClient *clientPtr = (VectorClient*)Chain_GetValue(link); - if ((clientPtr->proc != NULL) && (clientPtr->serverPtr != NULL)) { - (*clientPtr->proc) (vPtr->interp, clientPtr->clientData, notify); - } - } - - // Some clients may not handle the "destroy" callback properly (they - // should call Blt_FreeVectorId to release the client identifier), so mark - // any remaining clients to indicate that vector's server has gone away. - if (notify == BLT_VECTOR_NOTIFY_DESTROY) { - for (link = Chain_FirstLink(vPtr->chain); link; - link = Chain_NextLink(link)) { - VectorClient *clientPtr = (VectorClient*)Chain_GetValue(link); - clientPtr->serverPtr = NULL; - } - } -} - -void Blt::Vec_UpdateClients(Vector* vPtr) -{ - vPtr->dirty++; - vPtr->max = vPtr->min = NAN; - if (vPtr->notifyFlags & NOTIFY_NEVER) { - return; - } - vPtr->notifyFlags |= NOTIFY_UPDATED; - if (vPtr->notifyFlags & NOTIFY_ALWAYS) { - Blt_Vec_NotifyClients(vPtr); - return; - } - if (!(vPtr->notifyFlags & NOTIFY_PENDING)) { - vPtr->notifyFlags |= NOTIFY_PENDING; - Tcl_DoWhenIdle(Blt_Vec_NotifyClients, vPtr); - } -} - -void Blt::Vec_FlushCache(Vector* vPtr) -{ - Tcl_Interp* interp = vPtr->interp; - - if (vPtr->arrayName == NULL) - return; - - /* Turn off the trace temporarily so that we can unset all the - * elements in the array. */ - - Tcl_UntraceVar2(interp, vPtr->arrayName, (char *)NULL, - TRACE_ALL | vPtr->varFlags, Vec_VarTrace, vPtr); - - /* Clear all the element entries from the entire array */ - Tcl_UnsetVar2(interp, vPtr->arrayName, (char *)NULL, vPtr->varFlags); - - /* Restore the "end" index by default and the trace on the entire array */ - Tcl_SetVar2(interp, vPtr->arrayName, "end", "", vPtr->varFlags); - Tcl_TraceVar2(interp, vPtr->arrayName, (char *)NULL, - TRACE_ALL | vPtr->varFlags, Vec_VarTrace, vPtr); -} - -int Blt::Vec_LookupName(VectorInterpData *dataPtr, const char *vecName, - Vector** vPtrPtr) -{ - - const char *endPtr; - Vector* vPtr = Vec_ParseElement(dataPtr->interp, dataPtr, vecName, &endPtr, NS_SEARCH_BOTH); - if (vPtr == NULL) - return TCL_ERROR; - - if (*endPtr != '\0') { - Tcl_AppendResult(dataPtr->interp, - "extra characters after vector name", (char *)NULL); - return TCL_ERROR; - } - - *vPtrPtr = vPtr; - return TCL_OK; -} - -double Blt::Vec_Min(Vector* vecObjPtr) -{ - double* vp = vecObjPtr->valueArr + vecObjPtr->first; - double* vend = vecObjPtr->valueArr + vecObjPtr->last; - double min = *vp++; - for (/* empty */; vp <= vend; vp++) { - if (min > *vp) - min = *vp; - } - vecObjPtr->min = min; - return vecObjPtr->min; -} - -double Blt::Vec_Max(Vector* vecObjPtr) -{ - double max = NAN; - double* vp = vecObjPtr->valueArr + vecObjPtr->first; - double* vend = vecObjPtr->valueArr + vecObjPtr->last; - max = *vp++; - for (/* empty */; vp <= vend; vp++) { - if (max < *vp) - max = *vp; - } - vecObjPtr->max = max; - return vecObjPtr->max; -} - -static void DeleteCommand(Vector* vPtr) -{ - Tcl_Interp* interp = vPtr->interp; - char *qualName; - Tcl_CmdInfo cmdInfo; - Tcl_DString dString; - Blt_ObjectName objName; - - Tcl_DStringInit(&dString); - objName.name = Tcl_GetCommandName(interp, vPtr->cmdToken); - objName.nsPtr = GetCommandNamespace(vPtr->cmdToken); - qualName = MakeQualifiedName(&objName, &dString); - if (Tcl_GetCommandInfo(interp, qualName, &cmdInfo)) { - // Disable the callback before deleting the TCL command - cmdInfo.deleteProc = NULL; - Tcl_SetCommandInfo(interp, qualName, &cmdInfo); - Tcl_DeleteCommandFromToken(interp, vPtr->cmdToken); - } - Tcl_DStringFree(&dString); - vPtr->cmdToken = 0; -} - -static void UnmapVariable(Vector* vPtr) -{ - Tcl_Interp* interp = vPtr->interp; - - // Unset the entire array - Tcl_UntraceVar2(interp, vPtr->arrayName, (char *)NULL, - (TRACE_ALL | vPtr->varFlags), Vec_VarTrace, vPtr); - Tcl_UnsetVar2(interp, vPtr->arrayName, (char *)NULL, vPtr->varFlags); - - if (vPtr->arrayName != NULL) { - free((void*)(vPtr->arrayName)); - vPtr->arrayName = NULL; - } -} - -int Blt::Vec_MapVariable(Tcl_Interp* interp, Vector* vPtr, const char *path) -{ - Blt_ObjectName objName; - char *newPath; - const char *result; - Tcl_DString dString; - - if (vPtr->arrayName != NULL) { - UnmapVariable(vPtr); - } - if ((path == NULL) || (path[0] == '\0')) { - return TCL_OK; /* If the variable pathname is the empty - * string, simply return after removing any - * existing variable. */ - } - /* Get the variable name (without the namespace qualifier). */ - if (!ParseObjectName(interp, path, &objName, BLT_NO_DEFAULT_NS)) { - return TCL_ERROR; - } - if (objName.nsPtr == NULL) { - /* - * If there was no namespace qualifier, try harder to see if the - * variable is non-local. - */ - objName.nsPtr = GetVariableNamespace(interp, objName.name); - } - Tcl_DStringInit(&dString); - vPtr->varFlags = 0; - if (objName.nsPtr != NULL) { /* Global or namespace variable. */ - newPath = MakeQualifiedName(&objName, &dString); - vPtr->varFlags |= (TCL_GLOBAL_ONLY); - } else { /* Local variable. */ - newPath = (char *)objName.name; - } - - /* - * To play it safe, delete the variable first. This has the benefical - * side-effect of unmapping the variable from another vector that may be - * currently associated with it. - */ - Tcl_UnsetVar2(interp, newPath, (char *)NULL, 0); - - /* - * Set the index "end" in the array. This will create the variable - * immediately so that we can check its namespace context. - */ - result = Tcl_SetVar2(interp, newPath, "end", "", TCL_LEAVE_ERR_MSG); - if (result == NULL) { - Tcl_DStringFree(&dString); - return TCL_ERROR; - } - /* Create a full-array trace on reads, writes, and unsets. */ - Tcl_TraceVar2(interp, newPath, (char *)NULL, TRACE_ALL, Vec_VarTrace, - vPtr); - vPtr->arrayName = Blt_Strdup(newPath); - Tcl_DStringFree(&dString); - return TCL_OK; -} - -int Blt::Vec_SetSize(Tcl_Interp* interp, Vector* vPtr, int newSize) -{ - if (newSize <= 0) { - newSize = DEF_ARRAY_SIZE; - } - if (newSize == vPtr->size) { - /* Same size, use the current array. */ - return TCL_OK; - } - if (vPtr->freeProc == TCL_DYNAMIC) { - /* Old memory was dynamically allocated, so use realloc. */ - double* newArr = (double*)realloc(vPtr->valueArr, newSize * sizeof(double)); - if (newArr == NULL) { - if (interp != NULL) { - Tcl_AppendResult(interp, "can't reallocate ", - Itoa(newSize), " elements for vector \"", - vPtr->name, "\"", (char *)NULL); - } - return TCL_ERROR; - } - vPtr->size = newSize; - vPtr->valueArr = newArr; - return TCL_OK; - } - - { - /* Old memory was created specially (static or special allocator). - * Replace with dynamically allocated memory (malloc-ed). */ - - double* newArr = (double*)calloc(newSize, sizeof(double)); - if (newArr == NULL) { - if (interp != NULL) { - Tcl_AppendResult(interp, "can't allocate ", - Itoa(newSize), " elements for vector \"", - vPtr->name, "\"", (char *)NULL); - } - return TCL_ERROR; - } - { - int used, wanted; - - /* Copy the contents of the old memory into the new. */ - used = vPtr->length; - wanted = newSize; - - if (used > wanted) { - used = wanted; - } - /* Copy any previous data */ - if (used > 0) { - memcpy(newArr, vPtr->valueArr, used * sizeof(double)); - } - } - - /* - * We're not using the old storage anymore, so free it if it's not - * TCL_STATIC. It's static because the user previously reset the - * vector with a statically allocated array (setting freeProc to - * TCL_STATIC). - */ - if (vPtr->freeProc != TCL_STATIC) { - if (vPtr->freeProc == TCL_DYNAMIC) { - free(vPtr->valueArr); - } else { - (*vPtr->freeProc) ((char *)vPtr->valueArr); - } - } - vPtr->freeProc = TCL_DYNAMIC; /* Set the type of the new storage */ - vPtr->valueArr = newArr; - vPtr->size = newSize; - } - return TCL_OK; -} - -int Blt::Vec_SetLength(Tcl_Interp* interp, Vector* vPtr, int newLength) -{ - if (vPtr->size < newLength) { - if (Vec_SetSize(interp, vPtr, newLength) != TCL_OK) { - return TCL_ERROR; - } - } - vPtr->length = newLength; - vPtr->first = 0; - vPtr->last = newLength - 1; - return TCL_OK; -} - -int Blt::Vec_ChangeLength(Tcl_Interp* interp, Vector* vPtr, int newLength) -{ - if (newLength < 0) { - newLength = 0; - } - if (newLength > vPtr->size) { - int newSize; /* Size of array in elements */ - - /* Compute the new size of the array. It's a multiple of - * DEF_ARRAY_SIZE. */ - newSize = DEF_ARRAY_SIZE; - while (newSize < newLength) { - newSize += newSize; - } - if (newSize != vPtr->size) { - if (Vec_SetSize(interp, vPtr, newSize) != TCL_OK) { - return TCL_ERROR; - } - } - } - vPtr->length = newLength; - vPtr->first = 0; - vPtr->last = newLength - 1; - return TCL_OK; - -} - -int Blt::Vec_Reset(Vector* vPtr, double *valueArr, int length, - int size, Tcl_FreeProc *freeProc) -{ - if (vPtr->valueArr != valueArr) { /* New array of values resides - * in different memory than - * the current vector. */ - if ((valueArr == NULL) || (size == 0)) { - /* Empty array. Set up default values */ - valueArr = (double*)malloc(sizeof(double) * DEF_ARRAY_SIZE); - size = DEF_ARRAY_SIZE; - if (valueArr == NULL) { - Tcl_AppendResult(vPtr->interp, "can't allocate ", - Itoa(size), " elements for vector \"", - vPtr->name, "\"", (char *)NULL); - return TCL_ERROR; - } - freeProc = TCL_DYNAMIC; - length = 0; - } - else if (freeProc == TCL_VOLATILE) { - /* Data is volatile. Make a copy of the value array. */ - double* newArr = (double*)malloc(size * sizeof(double)); - if (newArr == NULL) { - Tcl_AppendResult(vPtr->interp, "can't allocate ", - Itoa(size), " elements for vector \"", - vPtr->name, "\"", (char *)NULL); - return TCL_ERROR; - } - memcpy((char *)newArr, (char *)valueArr, - sizeof(double) * length); - valueArr = newArr; - freeProc = TCL_DYNAMIC; - } - - if (vPtr->freeProc != TCL_STATIC) { - /* Old data was dynamically allocated. Free it before attaching - * new data. */ - if (vPtr->freeProc == TCL_DYNAMIC) { - free(vPtr->valueArr); - } else { - (*freeProc) ((char *)vPtr->valueArr); - } - } - vPtr->freeProc = freeProc; - vPtr->valueArr = valueArr; - vPtr->size = size; - } - - vPtr->length = length; - if (vPtr->flush) { - Vec_FlushCache(vPtr); - } - Vec_UpdateClients(vPtr); - return TCL_OK; -} - -Vector* Blt::Vec_New(VectorInterpData *dataPtr) -{ - Vector* vPtr = (Vector*)calloc(1, sizeof(Vector)); - vPtr->valueArr = (double*)malloc(sizeof(double) * DEF_ARRAY_SIZE); - if (vPtr->valueArr == NULL) { - free(vPtr); - return NULL; - } - vPtr->size = DEF_ARRAY_SIZE; - vPtr->freeProc = TCL_DYNAMIC; - vPtr->length = 0; - vPtr->interp = dataPtr->interp; - vPtr->hashPtr = NULL; - vPtr->chain = new Chain(); - vPtr->flush = 0; - vPtr->min = vPtr->max = NAN; - vPtr->notifyFlags = NOTIFY_WHENIDLE; - vPtr->dataPtr = dataPtr; - return vPtr; -} - -void Blt::Vec_Free(Vector* vPtr) -{ - ChainLink* link; - - if (vPtr->cmdToken != 0) { - DeleteCommand(vPtr); - } - if (vPtr->arrayName != NULL) { - UnmapVariable(vPtr); - } - vPtr->length = 0; - - /* Immediately notify clients that vector is going away */ - if (vPtr->notifyFlags & NOTIFY_PENDING) { - vPtr->notifyFlags &= ~NOTIFY_PENDING; - Tcl_CancelIdleCall(Blt_Vec_NotifyClients, vPtr); - } - vPtr->notifyFlags |= NOTIFY_DESTROYED; - Blt_Vec_NotifyClients(vPtr); - - for (link = Chain_FirstLink(vPtr->chain); link; link = Chain_NextLink(link)) { - VectorClient *clientPtr = (VectorClient*)Chain_GetValue(link); - free(clientPtr); - } - delete vPtr->chain; - if ((vPtr->valueArr != NULL) && (vPtr->freeProc != TCL_STATIC)) { - if (vPtr->freeProc == TCL_DYNAMIC) { - free(vPtr->valueArr); - } else { - (*vPtr->freeProc) ((char *)vPtr->valueArr); - } - } - if (vPtr->hashPtr != NULL) { - Tcl_DeleteHashEntry(vPtr->hashPtr); - } -#ifdef NAMESPACE_DELETE_NOTIFY - if (vPtr->nsPtr != NULL) { - Blt_DestroyNsDeleteNotify(vPtr->interp, vPtr->nsPtr, vPtr); - } -#endif /* NAMESPACE_DELETE_NOTIFY */ - free(vPtr); -} - -static void VectorInstDeleteProc(ClientData clientData) -{ - Vector* vPtr = (Vector*)clientData; - vPtr->cmdToken = 0; - Vec_Free(vPtr); -} - -Vector* Blt::Vec_Create(VectorInterpData *dataPtr, const char *vecName, - const char *cmdName, const char *varName, int *isNewPtr) -{ - Tcl_DString dString; - Blt_ObjectName objName; - char *qualName; - Tcl_HashEntry *hPtr; - Tcl_Interp* interp = dataPtr->interp; - - int isNew = 0; - Vector* vPtr = NULL; - - if (!ParseObjectName(interp, vecName, &objName, 0)) - return NULL; - - Tcl_DStringInit(&dString); - if ((objName.name[0] == '#') && (strcmp(objName.name, "#auto") == 0)) { - - do { /* Generate a unique vector name. */ - char string[200]; - - snprintf(string, 200, "vector%d", dataPtr->nextId++); - objName.name = string; - qualName = MakeQualifiedName(&objName, &dString); - hPtr = Tcl_FindHashEntry(&dataPtr->vectorTable, qualName); - } while (hPtr != NULL); - } else { - const char *p; - - for (p = objName.name; *p != '\0'; p++) { - if (!VECTOR_CHAR(*p)) { - Tcl_AppendResult(interp, "bad vector name \"", objName.name, - "\": must contain digits, letters, underscore, or period", - (char *)NULL); - goto error; - } - } - qualName = MakeQualifiedName(&objName, &dString); - vPtr = Vec_ParseElement((Tcl_Interp *)NULL, dataPtr, qualName, - NULL, NS_SEARCH_CURRENT); - } - if (vPtr == NULL) { - hPtr = Tcl_CreateHashEntry(&dataPtr->vectorTable, qualName, &isNew); - vPtr = Vec_New(dataPtr); - vPtr->hashPtr = hPtr; - vPtr->nsPtr = objName.nsPtr; - - vPtr->name = (const char*)Tcl_GetHashKey(&dataPtr->vectorTable, hPtr); -#ifdef NAMESPACE_DELETE_NOTIFY - Blt_CreateNsDeleteNotify(interp, objName.nsPtr, vPtr, - VectorInstDeleteProc); -#endif /* NAMESPACE_DELETE_NOTIFY */ - Tcl_SetHashValue(hPtr, vPtr); - } - if (cmdName != NULL) { - Tcl_CmdInfo cmdInfo; - - if ((cmdName == vecName) || - ((cmdName[0] == '#') && (strcmp(cmdName, "#auto")==0))) { - cmdName = qualName; - } - if (Tcl_GetCommandInfo(interp, (char *)cmdName, &cmdInfo)) { - if (vPtr != cmdInfo.objClientData) { - Tcl_AppendResult(interp, "command \"", cmdName, - "\" already exists", (char *)NULL); - goto error; - } - /* We get here only if the old name is the same as the new. */ - goto checkVariable; - } - } - if (vPtr->cmdToken != 0) { - DeleteCommand(vPtr); /* Command already exists, delete old first */ - } - if (cmdName != NULL) { - Tcl_DString dString2; - - Tcl_DStringInit(&dString2); - if (cmdName != qualName) { - if (!ParseObjectName(interp, cmdName, &objName, 0)) { - goto error; - } - cmdName = MakeQualifiedName(&objName, &dString2); - } - vPtr->cmdToken = Tcl_CreateObjCommand(interp, (char *)cmdName, Vec_InstCmd, - vPtr, VectorInstDeleteProc); - Tcl_DStringFree(&dString2); - } - checkVariable: - if (varName != NULL) { - if ((varName[0] == '#') && (strcmp(varName, "#auto") == 0)) { - varName = qualName; - } - if (Vec_MapVariable(interp, vPtr, varName) != TCL_OK) { - goto error; - } - } - - Tcl_DStringFree(&dString); - *isNewPtr = isNew; - return vPtr; - - error: - Tcl_DStringFree(&dString); - if (vPtr != NULL) { - Vec_Free(vPtr); - } - return NULL; -} - -int Blt::Vec_Duplicate(Vector* destPtr, Vector* srcPtr) -{ - size_t nBytes; - size_t length; - - if (destPtr == srcPtr) { - /* Copying the same vector. */ - } - length = srcPtr->last - srcPtr->first + 1; - if (Vec_ChangeLength(destPtr->interp, destPtr, length) != TCL_OK) { - return TCL_ERROR; - } - nBytes = length * sizeof(double); - memcpy(destPtr->valueArr, srcPtr->valueArr + srcPtr->first, nBytes); - destPtr->offset = srcPtr->offset; - return TCL_OK; -} - - -static int VectorNamesOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - VectorInterpData* dataPtr = (VectorInterpData*)clientData; - Tcl_Obj *listObjPtr; - - listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); - if (objc == 2) { - Tcl_HashEntry *hPtr; - Tcl_HashSearch cursor; - - for (hPtr = Tcl_FirstHashEntry(&dataPtr->vectorTable, &cursor); - hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { - char *name = (char*)Tcl_GetHashKey(&dataPtr->vectorTable, hPtr); - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewStringObj(name, -1)); - } - } else { - Tcl_HashEntry *hPtr; - Tcl_HashSearch cursor; - - for (hPtr = Tcl_FirstHashEntry(&dataPtr->vectorTable, &cursor); - hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { - char *name = (char*)Tcl_GetHashKey(&dataPtr->vectorTable, hPtr); - int i; - for (i = 2; i < objc; i++) { - char *pattern; - - pattern = Tcl_GetString(objv[i]); - if (Tcl_StringMatch(name, pattern)) { - Tcl_ListObjAppendElement(interp, listObjPtr, - Tcl_NewStringObj(name, -1)); - break; - } - } - } - } - Tcl_SetObjResult(interp, listObjPtr); - return TCL_OK; -} - -static int VectorCreate2(ClientData clientData, Tcl_Interp* interp, - int argStart, int objc, Tcl_Obj* const objv[]) -{ - VectorInterpData *dataPtr = (VectorInterpData*)clientData; - Vector* vPtr; - int count, i; - CreateSwitches switches; - - // Handle switches to the vector command and collect the vector name - // arguments into an array. - count = 0; - vPtr = NULL; - for (i = argStart; i < objc; i++) { - char *string; - - string = Tcl_GetString(objv[i]); - if (string[0] == '-') { - break; - } - } - count = i - argStart; - if (count == 0) { - Tcl_AppendResult(interp, "no vector names supplied", (char *)NULL); - return TCL_ERROR; - } - memset(&switches, 0, sizeof(switches)); - if (ParseSwitches(interp, createSwitches, objc - i, objv + i, - &switches, BLT_SWITCH_DEFAULTS) < 0) { - return TCL_ERROR; - } - if (count > 1) { - if (switches.cmdName != NULL) { - Tcl_AppendResult(interp, - "can't specify more than one vector with \"-command\" switch", - (char *)NULL); - goto error; - } - if (switches.varName != NULL) { - Tcl_AppendResult(interp, - "can't specify more than one vector with \"-variable\" switch", - (char *)NULL); - goto error; - } - } - for (i = 0; i < count; i++) { - char *leftParen, *rightParen; - char *string; - int isNew; - int size, first, last; - - size = first = last = 0; - string = Tcl_GetString(objv[i + argStart]); - leftParen = strchr(string, '('); - rightParen = strchr(string, ')'); - if (((leftParen != NULL) && (rightParen == NULL)) || - ((leftParen == NULL) && (rightParen != NULL)) || - (leftParen > rightParen)) { - Tcl_AppendResult(interp, "bad vector specification \"", string, - "\"", (char *)NULL); - goto error; - } - if (leftParen != NULL) { - int result; - char *colon; - - *rightParen = '\0'; - colon = strchr(leftParen + 1, ':'); - if (colon != NULL) { - - /* Specification is in the form vecName(first:last) */ - *colon = '\0'; - result = Tcl_GetInt(interp, leftParen + 1, &first); - if ((*(colon + 1) != '\0') && (result == TCL_OK)) { - result = Tcl_GetInt(interp, colon + 1, &last); - if (first > last) { - Tcl_AppendResult(interp, "bad vector range \"", - string, "\"", (char *)NULL); - result = TCL_ERROR; - } - size = (last - first) + 1; - } - *colon = ':'; - } else { - /* Specification is in the form vecName(size) */ - result = Tcl_GetInt(interp, leftParen + 1, &size); - } - *rightParen = ')'; - if (result != TCL_OK) { - goto error; - } - if (size < 0) { - Tcl_AppendResult(interp, "bad vector size \"", string, "\"", - (char *)NULL); - goto error; - } - } - if (leftParen != NULL) { - *leftParen = '\0'; - } - /* - * By default, we create a TCL command by the name of the vector. - */ - vPtr = Vec_Create(dataPtr, string, - (switches.cmdName == NULL) ? string : switches.cmdName, - (switches.varName == NULL) ? string : switches.varName, &isNew); - if (leftParen != NULL) { - *leftParen = '('; - } - if (vPtr == NULL) { - goto error; - } - vPtr->freeOnUnset = switches.watchUnset; - vPtr->flush = switches.flush; - vPtr->offset = first; - if (size > 0) { - if (Vec_ChangeLength(interp, vPtr, size) != TCL_OK) { - goto error; - } - } - if (!isNew) { - if (vPtr->flush) { - Vec_FlushCache(vPtr); - } - Vec_UpdateClients(vPtr); - } - } - FreeSwitches(createSwitches, (char *)&switches, 0); - if (vPtr != NULL) { - /* Return the name of the last vector created */ - Tcl_SetStringObj(Tcl_GetObjResult(interp), vPtr->name, -1); - } - return TCL_OK; - error: - FreeSwitches(createSwitches, (char *)&switches, 0); - return TCL_ERROR; -} - -static int VectorCreateOp(ClientData clientData, Tcl_Interp* interp, - int objc, Tcl_Obj* const objv[]) -{ - return VectorCreate2(clientData, interp, 2, objc, objv); -} - -static int VectorDestroyOp(ClientData clientData, Tcl_Interp* interp, - int objc,Tcl_Obj* const objv[]) -{ - VectorInterpData *dataPtr = (VectorInterpData*)clientData; - - for (int ii=2; ii 1) { - char *string; - char c; - int i; - Blt_OpSpec *specPtr; - - string = Tcl_GetString(objv[1]); - c = string[0]; - for (specPtr = vectorCmdOps, i = 0; i < nCmdOps; i++, specPtr++) { - if ((c == specPtr->name[0]) && - (strcmp(string, specPtr->name) == 0)) { - goto doOp; - } - } - // The first argument is not an operation, so assume that its - // actually the name of a vector to be created - return VectorCreate2(clientData, interp, 1, objc, objv); - } - doOp: - /* Do the usual vector operation lookup now. */ - proc = (VectorCmdProc*)GetOpFromObj(interp, nCmdOps, vectorCmdOps, - BLT_OP_ARG1, objc, objv,0); - if (proc == NULL) { - return TCL_ERROR; - } - return (*proc) ((Vector*)clientData, interp, objc, objv); -} - -static void VectorInterpDeleteProc(ClientData clientData, Tcl_Interp* interp) -{ - VectorInterpData *dataPtr = (VectorInterpData*)clientData; - Tcl_HashEntry *hPtr; - Tcl_HashSearch cursor; - - for (hPtr = Tcl_FirstHashEntry(&dataPtr->vectorTable, &cursor); - hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { - Vector* vPtr = (Vector*)Tcl_GetHashValue(hPtr); - vPtr->hashPtr = NULL; - Vec_Free(vPtr); - } - Tcl_DeleteHashTable(&dataPtr->vectorTable); - - /* If any user-defined math functions were installed, remove them. */ - Vec_UninstallMathFunctions(&dataPtr->mathProcTable); - Tcl_DeleteHashTable(&dataPtr->mathProcTable); - - Tcl_DeleteHashTable(&dataPtr->indexProcTable); - Tcl_DeleteAssocData(interp, VECTOR_THREAD_KEY); - free(dataPtr); -} - -VectorInterpData* Blt::Vec_GetInterpData(Tcl_Interp* interp) -{ - VectorInterpData *dataPtr; - Tcl_InterpDeleteProc *proc; - - dataPtr = (VectorInterpData *) - Tcl_GetAssocData(interp, VECTOR_THREAD_KEY, &proc); - if (dataPtr == NULL) { - dataPtr = (VectorInterpData*)malloc(sizeof(VectorInterpData)); - dataPtr->interp = interp; - dataPtr->nextId = 0; - Tcl_SetAssocData(interp, VECTOR_THREAD_KEY, VectorInterpDeleteProc, - dataPtr); - Tcl_InitHashTable(&dataPtr->vectorTable, TCL_STRING_KEYS); - Tcl_InitHashTable(&dataPtr->mathProcTable, TCL_STRING_KEYS); - Tcl_InitHashTable(&dataPtr->indexProcTable, TCL_STRING_KEYS); - Vec_InstallMathFunctions(&dataPtr->mathProcTable); - Vec_InstallSpecialIndices(&dataPtr->indexProcTable); - srand48(time((time_t *) NULL)); - } - return dataPtr; -} - -/* C Application interface to vectors */ - -int Blt_CreateVector2(Tcl_Interp* interp, const char *vecName, - const char *cmdName, const char *varName, - int initialSize, Blt_Vector* *vecPtrPtr) -{ - VectorInterpData *dataPtr; /* Interpreter-specific data. */ - Vector* vPtr; - int isNew; - char *nameCopy; - - if (initialSize < 0) { - Tcl_AppendResult(interp, "bad vector size \"", Itoa(initialSize), - "\"", (char *)NULL); - return TCL_ERROR; - } - dataPtr = Vec_GetInterpData(interp); - - nameCopy = Blt_Strdup(vecName); - vPtr = Vec_Create(dataPtr, nameCopy, cmdName, varName, &isNew); - free(nameCopy); - - if (vPtr == NULL) { - return TCL_ERROR; - } - if (initialSize > 0) { - if (Vec_ChangeLength(interp, vPtr, initialSize) != TCL_OK) { - return TCL_ERROR; - } - } - if (vecPtrPtr != NULL) { - *vecPtrPtr = (Blt_Vector* ) vPtr; - } - return TCL_OK; -} - -int Blt_CreateVector(Tcl_Interp* interp, const char *name, int size, - Blt_Vector* *vecPtrPtr) -{ - return Blt_CreateVector2(interp, name, name, name, size, vecPtrPtr); -} - -int Blt_DeleteVector(Blt_Vector* vecPtr) -{ - Vector* vPtr = (Vector* )vecPtr; - - Vec_Free(vPtr); - return TCL_OK; -} - -int Blt_DeleteVectorByName(Tcl_Interp* interp, const char *name) -{ - // If the vector name was passed via a read-only string (e.g. "x"), the - // Vec_ParseElement routine will segfault when it tries to write into - // the string. Therefore make a writable copy and free it when we're done. - char* nameCopy = Blt_Strdup(name); - VectorInterpData *dataPtr = Vec_GetInterpData(interp); - Vector* vPtr; - int result = Vec_LookupName(dataPtr, nameCopy, &vPtr); - free(nameCopy); - - if (result != TCL_OK) - return TCL_ERROR; - - Vec_Free(vPtr); - return TCL_OK; -} - -int Blt_VectorExists2(Tcl_Interp* interp, const char *vecName) -{ - VectorInterpData *dataPtr; - - dataPtr = Vec_GetInterpData(interp); - if (GetVectorObject(dataPtr, vecName, NS_SEARCH_BOTH) != NULL) { - return 1; - } - return 0; -} - -int Blt_VectorExists(Tcl_Interp* interp, const char *vecName) -{ - char *nameCopy; - int result; - - /* - * If the vector name was passed via a read-only string (e.g. "x"), the - * Blt_VectorParseName routine will segfault when it tries to write into - * the string. Therefore make a writable copy and free it when we're - * done. - */ - nameCopy = Blt_Strdup(vecName); - result = Blt_VectorExists2(interp, nameCopy); - free(nameCopy); - return result; -} - -int Blt_GetVector(Tcl_Interp* interp, const char *name, Blt_Vector* *vecPtrPtr) -{ - VectorInterpData *dataPtr; /* Interpreter-specific data. */ - Vector* vPtr; - char *nameCopy; - int result; - - dataPtr = Vec_GetInterpData(interp); - /* - * If the vector name was passed via a read-only string (e.g. "x"), the - * Blt_VectorParseName routine will segfault when it tries to write into - * the string. Therefore make a writable copy and free it when we're - * done. - */ - nameCopy = Blt_Strdup(name); - result = Vec_LookupName(dataPtr, nameCopy, &vPtr); - free(nameCopy); - if (result != TCL_OK) { - return TCL_ERROR; - } - Vec_UpdateRange(vPtr); - *vecPtrPtr = (Blt_Vector* ) vPtr; - return TCL_OK; -} - -int Blt_GetVectorFromObj(Tcl_Interp* interp, Tcl_Obj *objPtr, - Blt_Vector* *vecPtrPtr) -{ - VectorInterpData *dataPtr; /* Interpreter-specific data. */ - Vector* vPtr; - - dataPtr = Vec_GetInterpData(interp); - if (Vec_LookupName(dataPtr, Tcl_GetString(objPtr), &vPtr) != TCL_OK) { - return TCL_ERROR; - } - Vec_UpdateRange(vPtr); - *vecPtrPtr = (Blt_Vector* ) vPtr; - return TCL_OK; -} - -int Blt_ResetVector(Blt_Vector* vecPtr, double *valueArr, int length, - int size, Tcl_FreeProc *freeProc) -{ - Vector* vPtr = (Vector* )vecPtr; - - if (size < 0) { - Tcl_AppendResult(vPtr->interp, "bad array size", (char *)NULL); - return TCL_ERROR; - } - return Vec_Reset(vPtr, valueArr, length, size, freeProc); -} - -int Blt_ResizeVector(Blt_Vector* vecPtr, int length) -{ - Vector* vPtr = (Vector* )vecPtr; - - if (Vec_ChangeLength((Tcl_Interp *)NULL, vPtr, length) != TCL_OK) { - Tcl_AppendResult(vPtr->interp, "can't resize vector \"", vPtr->name, - "\"", (char *)NULL); - return TCL_ERROR; - } - if (vPtr->flush) { - Vec_FlushCache(vPtr); - } - Vec_UpdateClients(vPtr); - return TCL_OK; -} - -Blt_VectorId Blt_AllocVectorId(Tcl_Interp* interp, const char *name) -{ - VectorInterpData *dataPtr; /* Interpreter-specific data. */ - Vector* vPtr; - VectorClient *clientPtr; - Blt_VectorId clientId; - int result; - char *nameCopy; - - dataPtr = Vec_GetInterpData(interp); - /* - * If the vector name was passed via a read-only string (e.g. "x"), the - * Blt_VectorParseName routine will segfault when it tries to write into - * the string. Therefore make a writable copy and free it when we're - * done. - */ - nameCopy = Blt_Strdup(name); - result = Vec_LookupName(dataPtr, nameCopy, &vPtr); - free(nameCopy); - - if (result != TCL_OK) { - return (Blt_VectorId) 0; - } - /* Allocate a new client structure */ - clientPtr = (VectorClient*)calloc(1, sizeof(VectorClient)); - clientPtr->magic = VECTOR_MAGIC; - - /* Add the new client to the server's list of clients */ - clientPtr->link = vPtr->chain->append(clientPtr); - clientPtr->serverPtr = vPtr; - clientId = (Blt_VectorId) clientPtr; - return clientId; -} - -void Blt_SetVectorChangedProc(Blt_VectorId clientId, - Blt_VectorChangedProc *proc, - ClientData clientData) -{ - VectorClient *clientPtr = (VectorClient *)clientId; - - if (clientPtr->magic != VECTOR_MAGIC) { - return; /* Not a valid token */ - } - clientPtr->clientData = clientData; - clientPtr->proc = proc; -} - -void Blt_FreeVectorId(Blt_VectorId clientId) -{ - VectorClient *clientPtr = (VectorClient *)clientId; - - if (clientPtr->magic != VECTOR_MAGIC) - return; - - if (clientPtr->serverPtr != NULL) { - // Remove the client from the server's list - clientPtr->serverPtr->chain->deleteLink(clientPtr->link); - } - free(clientPtr); -} - -const char* Blt_NameOfVectorId(Blt_VectorId clientId) -{ - VectorClient *clientPtr = (VectorClient *)clientId; - - if ((clientPtr->magic != VECTOR_MAGIC) || (clientPtr->serverPtr == NULL)) { - return NULL; - } - return clientPtr->serverPtr->name; -} - -const char* Blt_NameOfVector(Blt_Vector* vecPtr) /* Vector to query. */ -{ - Vector* vPtr = (Vector* )vecPtr; - return vPtr->name; -} - -int Blt_GetVectorById(Tcl_Interp* interp, Blt_VectorId clientId, - Blt_Vector* *vecPtrPtr) -{ - VectorClient *clientPtr = (VectorClient *)clientId; - - if (clientPtr->magic != VECTOR_MAGIC) { - Tcl_AppendResult(interp, "bad vector token", (char *)NULL); - return TCL_ERROR; - } - if (clientPtr->serverPtr == NULL) { - Tcl_AppendResult(interp, "vector no longer exists", (char *)NULL); - return TCL_ERROR; - } - Vec_UpdateRange(clientPtr->serverPtr); - *vecPtrPtr = (Blt_Vector* ) clientPtr->serverPtr; - return TCL_OK; -} - -void Blt_InstallIndexProc(Tcl_Interp* interp, const char *string, - Blt_VectorIndexProc *procPtr) -{ - VectorInterpData *dataPtr; /* Interpreter-specific data. */ - Tcl_HashEntry *hPtr; - int isNew; - - dataPtr = Vec_GetInterpData(interp); - hPtr = Tcl_CreateHashEntry(&dataPtr->indexProcTable, string, &isNew); - if (procPtr == NULL) { - Tcl_DeleteHashEntry(hPtr); - } else { - Tcl_SetHashValue(hPtr, procPtr); - } -} - -#define SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr - -/* routine by Brenner - * data is the array of complex data points, perversely - * starting at 1 - * nn is the number of complex points, i.e. half the length of data - * isign is 1 for forward, -1 for inverse - */ -static void four1(double *data, unsigned long nn, int isign) -{ - unsigned long n,mmax,m,j,istep,i; - double wtemp,wr,wpr,wpi,wi,theta; - double tempr,tempi; - - n=nn << 1; - j=1; - for (i = 1;i i) { - SWAP(data[j],data[i]); - SWAP(data[j+1],data[i+1]); - } - m=n >> 1; - while (m >= 2 && j > m) { - j -= m; - m >>= 1; - } - j += m; - } - mmax=2; - while (n > mmax) { - istep=mmax << 1; - theta=isign*(6.28318530717959/mmax); - wtemp=sin(0.5*theta); - wpr = -2.0*wtemp*wtemp; - wpi=sin(theta); - wr=1.0; - wi=0.0; - for (m=1;mlast - srcPtr->first + 1; - /* new length */ - pow2len = smallest_power_of_2_not_less_than( length ); - - /* We do not do in-place FFTs */ - if (realPtr == srcPtr) { - Tcl_AppendResult(interp, "real vector \"", realPtr->name, - "\" can't be the same as the source", (char *)NULL); - return TCL_ERROR; - } - if (phasesPtr != NULL) { - if (phasesPtr == srcPtr) { - Tcl_AppendResult(interp, "imaginary vector \"", phasesPtr->name, - "\" can't be the same as the source", (char *)NULL); - return TCL_ERROR; - } - if (Vec_ChangeLength(interp, phasesPtr, - pow2len/2-noconstant+middle) != TCL_OK) { - return TCL_ERROR; - } - } - if (freqPtr != NULL) { - if (freqPtr == srcPtr) { - Tcl_AppendResult(interp, "frequency vector \"", freqPtr->name, - "\" can't be the same as the source", (char *)NULL); - return TCL_ERROR; - } - if (Vec_ChangeLength(interp, freqPtr, - pow2len/2-noconstant+middle) != TCL_OK) { - return TCL_ERROR; - } - } - - /* Allocate memory zero-filled array. */ - paddedData = (double*)calloc(pow2len * 2, sizeof(double)); - if (paddedData == NULL) { - Tcl_AppendResult(interp, "can't allocate memory for padded data", - (char *)NULL); - return TCL_ERROR; - } - - /* - * Since we just do real transforms, only even locations will be - * filled with data. - */ - if (flags & FFT_BARTLETT) { /* Bartlett window 1 - ( (x - N/2) / (N/2) ) */ - double Nhalf = pow2len*0.5; - double Nhalf_1 = 1.0 / Nhalf; - double w; - - for (i = 0; i < length; i++) { - w = 1.0 - fabs( (i-Nhalf) * Nhalf_1 ); - Wss += w; - paddedData[2*i] = w * srcPtr->valueArr[i]; - } - for(/*empty*/; i < pow2len; i++) { - w = 1.0 - fabs((i-Nhalf) * Nhalf_1); - Wss += w; - } - } else { /* Squared window, i.e. no data windowing. */ - for (i = 0; i < length; i++) { - paddedData[2*i] = srcPtr->valueArr[i]; - } - Wss = pow2len; - } - - /* Fourier */ - four1(paddedData-1, pow2len, 1); - - /* - for(i=0;ivalueArr; - - for (i = 0 + noconstant; i < pow2len / 2; i++) { - re = paddedData[2*i]; - im = paddedData[2*i+1]; - reS = paddedData[2*pow2len-2*i-2]; - imS = paddedData[2*pow2len-2*i-1]; - v[i - noconstant] = factor * ( -# if 0 - hypot( paddedData[2*i], paddedData[2*i+1] ) - + hypot( - paddedData[pow2len*2-2*i-2], - paddedData[pow2len*2-2*i-1] - ) -# else - sqrt( re*re + im* im ) + sqrt( reS*reS + imS*imS ) -# endif - ); - } - } else { - for(i = 0 + noconstant; i < pow2len / 2 + middle; i++) { - realPtr->valueArr[i - noconstant] = paddedData[2*i]; - } - } - if( phasesPtr != NULL ){ - for (i = 0 + noconstant; i < pow2len / 2 + middle; i++) { - phasesPtr->valueArr[i-noconstant] = paddedData[2*i+1]; - } - } - - /* Compute frequencies */ - if (freqPtr != NULL) { - double N = pow2len; - double denom = 1.0 / N / delta; - for( i=0+noconstant; ivalueArr[i-noconstant] = ((double) i) * denom; - } - } - - /* Memory is necessarily dynamic, because nobody touched it ! */ - free(paddedData); - - realPtr->offset = 0; - return TCL_OK; -} - - -int Blt::Vec_InverseFFT(Tcl_Interp* interp, Vector* srcImagPtr, - Vector* destRealPtr, Vector* destImagPtr, Vector* srcPtr) -{ - int length; - int pow2len; - double *paddedData; - int i; - double oneOverN; - - if ((destRealPtr == srcPtr) || (destImagPtr == srcPtr )){ - /* we do not do in-place FFTs */ - return TCL_ERROR; - } - length = srcPtr->last - srcPtr->first + 1; - - /* minus one because of the magical middle element! */ - pow2len = smallest_power_of_2_not_less_than( (length-1)*2 ); - oneOverN = 1.0 / pow2len; - - if (Vec_ChangeLength(interp, destRealPtr, pow2len) != TCL_OK) { - return TCL_ERROR; - } - if (Vec_ChangeLength(interp, destImagPtr, pow2len) != TCL_OK) { - return TCL_ERROR; - } - - if( length != (srcImagPtr->last - srcImagPtr->first + 1) ){ - Tcl_AppendResult(srcPtr->interp, - "the length of the imagPart vector must ", - "be the same as the real one", (char *)NULL); - return TCL_ERROR; - } - - paddedData = (double*)malloc( pow2len*2*sizeof(double) ); - if( paddedData == NULL ){ - if (interp != NULL) { - Tcl_AppendResult(interp, "memory allocation failed", (char *)NULL); - } - return TCL_ERROR; - } - for(i=0;ivalueArr[i]; - paddedData[2*i+1] = srcImagPtr->valueArr[i]; - paddedData[pow2len*2 - 2*i - 2 ] = srcPtr->valueArr[i+1]; - paddedData[pow2len*2 - 2*i - 1 ] = - srcImagPtr->valueArr[i+1]; - } - /* mythical middle element */ - paddedData[(length-1)*2] = srcPtr->valueArr[length-1]; - paddedData[(length-1)*2+1] = srcImagPtr->valueArr[length-1]; - - /* - for(i=0;ivalueArr[i] = paddedData[2*i] * oneOverN; - destImagPtr->valueArr[i] = paddedData[2*i+1] * oneOverN; - } - - /* memory is necessarily dynamic, because nobody touched it ! */ - free( paddedData ); - - return TCL_OK; -} - -static double FindSplit(Point2d *points, int i, int j, int *split) -{ - double maxDist2; - - maxDist2 = -1.0; - if ((i + 1) < j) { - int k; - double a, b, c; - - /* - * - * dist2 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++) { - double dist2; - - dist2 = (points[k].x * a) + (points[k].y * b) + c; - if (dist2 < 0.0) { - dist2 = -dist2; - } - if (dist2 > maxDist2) { - maxDist2 = dist2; /* Track the maximum. */ - *split = k; - } - } - /* Correction for segment length---should be redone if can == 0 */ - maxDist2 *= maxDist2 / (a * a + b * b); - } - return maxDist2; -} - -// Douglas-Peucker line simplification algorithm */ -int Blt_SimplifyLine(Point2d *inputPts, int low, int 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 dist2, tolerance2; - int s = -1; /* Points to top stack item. */ - int count; - - stack = (int*)malloc(sizeof(int) * (high - low + 1)); - StackPush(high); - count = 0; - indices[count++] = 0; - tolerance2 = tolerance * tolerance; - while (!StackEmpty()) { - dist2 = FindSplit(inputPts, low, StackTop(), &split); - if (dist2 > tolerance2) { - StackPush(split); - } else { - indices[count++] = StackTop(); - StackPop(low); - } - } - free(stack); - return count; -} - diff --git a/src/tkbltVector.h b/src/tkbltVector.h deleted file mode 100644 index e6ee3b3..0000000 --- a/src/tkbltVector.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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 1993-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. - */ - -#ifndef _BLT_VECTOR_H -#define _BLT_VECTOR_H - -#include - -typedef enum { - BLT_VECTOR_NOTIFY_UPDATE = 1, /* The vector's values has been updated */ - BLT_VECTOR_NOTIFY_DESTROY /* The vector has been destroyed and the client - * should no longer use its data (calling - * Blt_FreeVectorId) */ -} Blt_VectorNotify; - -typedef struct _Blt_VectorId *Blt_VectorId; - -typedef void (Blt_VectorChangedProc)(Tcl_Interp* interp, ClientData clientData, - Blt_VectorNotify notify); - -typedef struct { - double *valueArr; /* Array of values (possibly malloc-ed) */ - int numValues; /* Number of values in the array */ - int arraySize; /* Size of the allocated space */ - double min, max; /* Minimum and maximum values in the vector */ - int dirty; /* Indicates if the vector has been updated */ - int reserved; /* Reserved for future use */ - -} Blt_Vector; - -typedef double (Blt_VectorIndexProc)(Blt_Vector * vecPtr); - -typedef enum { - BLT_MATH_FUNC_SCALAR = 1, /* The function returns a single double - * precision value. */ - BLT_MATH_FUNC_VECTOR /* The function processes the entire vector. */ -} Blt_MathFuncType; - -/* - * To be safe, use the macros below, rather than the fields of the - * structure directly. - * - * The Blt_Vector is basically an opaque type. But it's also the - * actual memory address of the vector itself. I wanted to make the - * API as unobtrusive as possible. So instead of giving you a copy of - * the vector, providing various functions to access and update the - * vector, you get your hands on the actual memory (array of doubles) - * shared by all the vector's clients. - * - * The trade-off for speed and convenience is safety. You can easily - * break things by writing into the vector when other clients are - * using it. Use Blt_ResetVector to get around this. At least the - * macros are a reminder it isn't really safe to reset the data - * fields, except by the API routines. - */ -#define Blt_VecData(v) ((v)->valueArr) -#define Blt_VecLength(v) ((v)->numValues) -#define Blt_VecSize(v) ((v)->arraySize) -#define Blt_VecDirty(v) ((v)->dirty) - -#ifdef __cplusplus -extern "C" { -#endif - int Blt_CreateVector(Tcl_Interp* interp, const char *vecName, - int size, Blt_Vector** vecPtrPtr); - int Blt_CreateVector2(Tcl_Interp* interp, const char *vecName, - const char *cmdName, const char *varName, - int initialSize, Blt_Vector **vecPtrPtr); - int Blt_DeleteVectorByName(Tcl_Interp* interp, const char *vecName); - int Blt_DeleteVector(Blt_Vector *vecPtr); - int Blt_GetVector(Tcl_Interp* interp, const char *vecName, - Blt_Vector **vecPtrPtr); - int Blt_GetVectorFromObj(Tcl_Interp* interp, Tcl_Obj *objPtr, - Blt_Vector **vecPtrPtr); - int Blt_ResetVector(Blt_Vector *vecPtr, double *dataArr, int n, - int arraySize, Tcl_FreeProc *freeProc); - int Blt_ResizeVector(Blt_Vector *vecPtr, int n); - int Blt_VectorExists(Tcl_Interp* interp, const char *vecName); - int Blt_VectorExists2(Tcl_Interp* interp, const char *vecName); - Blt_VectorId Blt_AllocVectorId(Tcl_Interp* interp, const char *vecName); - int Blt_GetVectorById(Tcl_Interp* interp, Blt_VectorId clientId, - Blt_Vector **vecPtrPtr); - void Blt_SetVectorChangedProc(Blt_VectorId clientId, - Blt_VectorChangedProc *proc, - ClientData clientData); - void Blt_FreeVectorId(Blt_VectorId clientId); - const char *Blt_NameOfVectorId(Blt_VectorId clientId); - const char *Blt_NameOfVector(Blt_Vector *vecPtr); - int Blt_ExprVector(Tcl_Interp* interp, char *expr, Blt_Vector *vecPtr); - void Blt_InstallIndexProc(Tcl_Interp* interp, const char *indexName, - Blt_VectorIndexProc * procPtr); - double Blt_VecMin(Blt_Vector *vPtr); - double Blt_VecMax(Blt_Vector *vPtr); -#ifdef __cplusplus -} -#endif - -#include "tkbltDecls.h" - -#endif /* _BLT_VECTOR_H */ -- cgit v0.12