summaryrefslogtreecommitdiffstats
path: root/src/bltGraphSup.C
diff options
context:
space:
mode:
authorjoye <joye>2014-04-21 14:33:30 (GMT)
committerjoye <joye>2014-04-21 14:33:30 (GMT)
commit9edd29aa1cc0ba26de84c2306194a84ceee289a4 (patch)
treee12962bc973b0bea7161fc416aebfca212dc3ef1 /src/bltGraphSup.C
parentc70e0bdd2857fb0e4f41cd62e38f1fa24d68b142 (diff)
downloadblt-9edd29aa1cc0ba26de84c2306194a84ceee289a4.zip
blt-9edd29aa1cc0ba26de84c2306194a84ceee289a4.tar.gz
blt-9edd29aa1cc0ba26de84c2306194a84ceee289a4.tar.bz2
*** empty log message ***
Diffstat (limited to 'src/bltGraphSup.C')
-rw-r--r--src/bltGraphSup.C581
1 files changed, 581 insertions, 0 deletions
diff --git a/src/bltGraphSup.C b/src/bltGraphSup.C
new file mode 100644
index 0000000..ac207f3
--- /dev/null
+++ b/src/bltGraphSup.C
@@ -0,0 +1,581 @@
+/*
+ * 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 "bltMath.h"
+
+extern "C" {
+#include "bltGraph.h"
+}
+
+#include "bltGrAxis.h"
+#include "bltGrElem.h"
+#include "bltGrLegd.h"
+#include "bltGrMisc.h"
+
+#define AXIS_PAD_TITLE 2
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * 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 titleY;
+ int plotWidth, plotHeight;
+ int inset, inset2;
+
+ 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;
+
+ inset = (inset_ + ops->plotBW);
+ 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;
+
+ plotWidth = (ops->reqPlotWidth > 0) ? ops->reqPlotWidth :
+ width - (inset2 + left + right); /* Plot width. */
+ plotHeight = (ops->reqPlotHeight > 0) ? ops->reqPlotHeight :
+ height - (inset2 + top + bottom); /* Plot height. */
+ legend_->map(plotWidth, plotHeight);
+
+ /*
+ * Step 2: 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:
+ /* Do nothing. */
+ 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
+ * th left and/or right margins. If there's too little, grow the
+ * graph width to accomodate it.
+ */
+ int w = plotWidth + inset2 + left + right;
+ if (width > w) { /* Extra space in window. */
+ 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;
+ }
+ }
+ if (ops->reqPlotHeight > 0) { /* Constrain the plotarea height. */
+ /*
+ * 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;
+ if (height > h) { /* Extra space in window. */
+ int extra;
+
+ 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 = titleHeight_;
+ 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 (Blt_ChainLink link = Blt_Chain_FirstLink(marginPtr->axes);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Axis* axisPtr = (Axis*)Blt_Chain_GetValue(link);
+ AxisOptions* ops = (AxisOptions*)axisPtr->ops();
+ if (!ops->hide && axisPtr->use_) {
+ nVisible++;
+ if (flags & GET_AXIS_GEOMETRY)
+ getAxisGeometry(axisPtr);
+
+ 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 (Blt_ChainLink link = Blt_Chain_FirstLink(marginPtr->axes);
+ link != NULL; link = Blt_Chain_NextLink(link)) {
+ Axis* axisPtr = (Axis*)Blt_Chain_GetValue(link);
+ AxisOptions* ops = (AxisOptions*)axisPtr->ops();
+ if (!ops->hide && axisPtr->use_) {
+ nVisible++;
+ if (flags & GET_AXIS_GEOMETRY)
+ getAxisGeometry(axisPtr);
+
+ 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::getAxisGeometry(Axis *axisPtr)
+{
+ GraphOptions* ops = (GraphOptions*)ops_;
+ AxisOptions* aops = (AxisOptions*)axisPtr->ops();
+ axisPtr->freeTickLabels();
+
+ // Leave room for axis baseline and padding
+ unsigned int y =0;
+ if (aops->exterior && (ops->plotRelief != TK_RELIEF_SOLID))
+ y += aops->lineWidth + 2;
+
+ axisPtr->maxTickHeight_ = axisPtr->maxTickWidth_ = 0;
+
+ if (axisPtr->t1Ptr_)
+ free(axisPtr->t1Ptr_);
+ axisPtr->t1Ptr_ = axisPtr->generateTicks(&axisPtr->majorSweep_);
+ if (axisPtr->t2Ptr_)
+ free(axisPtr->t2Ptr_);
+ axisPtr->t2Ptr_ = axisPtr->generateTicks(&axisPtr->minorSweep_);
+
+ if (aops->showTicks) {
+ Ticks* t1Ptr = aops->t1UPtr ? aops->t1UPtr : axisPtr->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 (aops->labelOffset)
+ x2 += axisPtr->majorSweep_.step * 0.5;
+
+ if (!axisPtr->inRange(x2, &axisPtr->axisRange_))
+ continue;
+
+ TickLabel* labelPtr = axisPtr->makeLabel(x);
+ Blt_Chain_Append(axisPtr->tickLabels_, labelPtr);
+ nLabels++;
+ /*
+ * Get the dimensions of each tick label. Remember tick labels
+ * can be multi-lined and/or rotated.
+ */
+ unsigned int lw, lh; /* Label width and height. */
+ Blt_GetTextExtents(aops->tickFont, 0, labelPtr->string, -1, &lw, &lh);
+ labelPtr->width = lw;
+ labelPtr->height = lh;
+
+ if (aops->tickAngle != 0.0f) {
+ double rlw, rlh; /* Rotated label width and height. */
+ Blt_GetBoundingBox(lw, lh, aops->tickAngle, &rlw, &rlh, NULL);
+ lw = ROUND(rlw), lh = ROUND(rlh);
+ }
+ if (axisPtr->maxTickWidth_ < int(lw))
+ axisPtr->maxTickWidth_ = lw;
+
+ if (axisPtr->maxTickHeight_ < int(lh))
+ axisPtr->maxTickHeight_ = lh;
+ }
+
+ unsigned int pad =0;
+ if (aops->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 = ((aops->lineWidth * 12) / 8);
+ }
+ if (axisPtr->isHorizontal())
+ y += axisPtr->maxTickHeight_ + pad;
+ else {
+ y += axisPtr->maxTickWidth_ + pad;
+ if (axisPtr->maxTickWidth_ > 0)
+ // Pad either size of label.
+ y += 5;
+ }
+ y += 2 * AXIS_PAD_TITLE;
+ if ((aops->lineWidth > 0) && aops->exterior)
+ // Distance from axis line to tick label.
+ y += aops->tickLength;
+
+ } // showTicks
+
+ if (aops->title) {
+ if (aops->titleAlternate) {
+ if (y < axisPtr->titleHeight_)
+ y = axisPtr->titleHeight_;
+ }
+ else
+ y += axisPtr->titleHeight_ + AXIS_PAD_TITLE;
+ }
+
+ // Correct for orientation of the axis
+ if (axisPtr->isHorizontal())
+ axisPtr->height_ = y;
+ else
+ axisPtr->width_ = y;
+}
+
+