summaryrefslogtreecommitdiffstats
path: root/tkblt/generic/tkbltGrAxis.C
diff options
context:
space:
mode:
Diffstat (limited to 'tkblt/generic/tkbltGrAxis.C')
-rw-r--r--tkblt/generic/tkbltGrAxis.C1984
1 files changed, 1984 insertions, 0 deletions
diff --git a/tkblt/generic/tkbltGrAxis.C b/tkblt/generic/tkbltGrAxis.C
new file mode 100644
index 0000000..be3e995
--- /dev/null
+++ b/tkblt/generic/tkbltGrAxis.C
@@ -0,0 +1,1984 @@
+/*
+ * 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 <float.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cmath>
+
+#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; ii<t1Ptr->nTicks; ii++) {
+ double value = t1Ptr->values[ii];
+ if (ops->showGridMinor) {
+ for (int jj=0; jj<t2Ptr->nTicks; 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.drawTextBBox(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.drawTextBBox(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.drawTextBBox(drawable, minPtr, graphPtr_->left_, hMin, &ww, &hh);
+ hMin -= (hh + spacing);
+ }
+ else {
+ ops->limitsTextStyle.angle = 0.0;
+
+ int ww, hh;
+ ts.drawTextBBox(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 >= (2 * DBL_EPSILON)) {
+ step *= 0.5;
+ }
+ }
+ else {
+ range = niceNum(range, 0);
+ step = niceNum(range / ops->reqNumMajorTicks, 1);
+ }
+ if (step >= DBL_EPSILON) {
+ axisMin = tickMin = floor(min / step) * step + 0.0;
+ axisMax = tickMax = ceil(max / step) * step + 0.0;
+ nTicks = (int)((tickMax-tickMin) / step) + 1;
+ } else {
+ /*
+ * A zero step can result from having a too small range, such that
+ * the floating point can no longer represent fractions of it (think
+ * subnormals). In such a case, let's just have two steps: the
+ * minimum and the maximum.
+ */
+ axisMin = tickMin = min;
+ axisMax = tickMax = min + DBL_EPSILON;
+ step = DBL_EPSILON;
+ nTicks = 2;
+ }
+ }
+ 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 = DBL_EPSILON;
+ }
+ 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->reqMin < -DBL_MAX) {
+ ops->reqMin = -DBL_MAX;
+ }
+ if (ops->reqMax > DBL_MAX) {
+ ops->reqMax = DBL_MAX;
+ }
+ 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];
+
+ // zero out any extremely small numbers
+ if (value<DBL_EPSILON && value>-DBL_EPSILON)
+ value =0;
+
+ 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 = 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 = 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; ii<nMajorTicks; ii++) {
+ double t1 = t1Ptr->values[ii];
+ /* Minor ticks */
+ for (int jj=0; jj<nMinorTicks; jj++) {
+ double t2 = t1 + (majorSweep_.step*t2Ptr->values[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; ii<sweepPtr->nSteps; ii++)
+ ticksPtr->values[ii] = logTable[ii];
+ }
+ else {
+ double value = sweepPtr->initial;
+ for (int ii=0; ii<sweepPtr->nSteps; 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; ii<nTicks; ii++) {
+ double x = t1Ptr->values[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;
+}
+