diff options
author | joye <joye> | 2014-04-21 14:33:30 (GMT) |
---|---|---|
committer | joye <joye> | 2014-04-21 14:33:30 (GMT) |
commit | 9edd29aa1cc0ba26de84c2306194a84ceee289a4 (patch) | |
tree | e12962bc973b0bea7161fc416aebfca212dc3ef1 /src | |
parent | c70e0bdd2857fb0e4f41cd62e38f1fa24d68b142 (diff) | |
download | blt-9edd29aa1cc0ba26de84c2306194a84ceee289a4.zip blt-9edd29aa1cc0ba26de84c2306194a84ceee289a4.tar.gz blt-9edd29aa1cc0ba26de84c2306194a84ceee289a4.tar.bz2 |
*** empty log message ***
Diffstat (limited to 'src')
-rw-r--r-- | src/bltGrAxisOp.C | 544 | ||||
-rw-r--r-- | src/bltGraph.C | 3 | ||||
-rw-r--r-- | src/bltGraph.h | 3 | ||||
-rw-r--r-- | src/bltGraphSup.C | 581 |
4 files changed, 585 insertions, 546 deletions
diff --git a/src/bltGrAxisOp.C b/src/bltGrAxisOp.C index 31cb091..5e83f56 100644 --- a/src/bltGrAxisOp.C +++ b/src/bltGrAxisOp.C @@ -37,14 +37,8 @@ extern "C" { #include "bltGrAxis.h" #include "bltGrAxisOp.h" -#include "bltGrElem.h" -#include "bltGrLegd.h" #include "bltGrMisc.h" -#define AXIS_PAD_TITLE 2 - -static void GetAxisGeometry(Graph* graphPtr, Axis *axisPtr); -static int GetMarginGeometry(Graph* graphPtr, Margin *marginPtr); static int GetAxisScrollInfo(Tcl_Interp* interp, int objc, Tcl_Obj* const objv[], double *offsetPtr, double windowSize, @@ -593,544 +587,6 @@ Axis *Blt_GetFirstAxis(Blt_Chain chain) return (Axis*)Blt_Chain_GetValue(link); } -/* - *--------------------------------------------------------------------------- - * - * Blt_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 Blt_LayoutGraph(Graph* graphPtr) -{ - GraphOptions* gops = (GraphOptions*)graphPtr->ops_; - int titleY; - int plotWidth, plotHeight; - int inset, inset2; - - int width = graphPtr->width_; - int height = graphPtr->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(graphPtr, &gops->leftMargin); - int right = GetMarginGeometry(graphPtr, &gops->rightMargin); - int top = GetMarginGeometry(graphPtr, &gops->topMargin); - int bottom = GetMarginGeometry(graphPtr, &gops->bottomMargin); - - int pad = gops->bottomMargin.maxTickWidth; - if (pad < gops->topMargin.maxTickWidth) - pad = gops->topMargin.maxTickWidth; - - pad = pad / 2 + 3; - if (right < pad) - right = pad; - - if (left < pad) - left = pad; - - pad = gops->leftMargin.maxTickHeight; - if (pad < gops->rightMargin.maxTickHeight) - pad = gops->rightMargin.maxTickHeight; - - pad = pad / 2; - if (top < pad) - top = pad; - - if (bottom < pad) - bottom = pad; - - if (gops->leftMargin.reqSize > 0) - left = gops->leftMargin.reqSize; - - if (gops->rightMargin.reqSize > 0) - right = gops->rightMargin.reqSize; - - if (gops->topMargin.reqSize > 0) - top = gops->topMargin.reqSize; - - if (gops->bottomMargin.reqSize > 0) - bottom = gops->bottomMargin.reqSize; - - /* - * Step 2: Add the graph title height to the top margin. - */ - if (gops->title) - top += graphPtr->titleHeight_ + 6; - - inset = (graphPtr->inset_ + gops->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 = (gops->reqPlotWidth > 0) ? gops->reqPlotWidth : - width - (inset2 + left + right); /* Plot width. */ - plotHeight = (gops->reqPlotHeight > 0) ? gops->reqPlotHeight : - height - (inset2 + top + bottom); /* Plot height. */ - graphPtr->legend_->map(plotWidth, plotHeight); - - /* - * Step 2: Add the legend to the appropiate margin. - */ - if (!graphPtr->legend_->isHidden()) { - switch (graphPtr->legend_->position()) { - case Legend::RIGHT: - right += graphPtr->legend_->width() + 2; - break; - case Legend::LEFT: - left += graphPtr->legend_->width() + 2; - break; - case Legend::TOP: - top += graphPtr->legend_->height() + 2; - break; - case Legend::BOTTOM: - bottom += graphPtr->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 (gops->reqPlotWidth == 0) { - plotWidth = width - (inset2 + left + right); - if (plotWidth < 1) - plotWidth = 1; - } - if (gops->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 ((gops->reqPlotWidth == 0) && (gops->reqPlotHeight == 0) && - (gops->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 > gops->aspect) { - // Shrink the width - int scaledWidth = (int)(plotHeight * gops->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 / gops->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 < gops->leftMargin.axesTitleLength) - top = gops->leftMargin.axesTitleLength; - - if (right < gops->bottomMargin.axesTitleLength) - right = gops->bottomMargin.axesTitleLength; - - if (top < gops->rightMargin.axesTitleLength) - top = gops->rightMargin.axesTitleLength; - - if (right < gops->topMargin.axesTitleLength) - right = gops->topMargin.axesTitleLength; - - /* - * Step 7: Override calculated values with requested margin sizes. - */ - if (gops->leftMargin.reqSize > 0) - left = gops->leftMargin.reqSize; - - if (gops->rightMargin.reqSize > 0) - right = gops->rightMargin.reqSize; - - if (gops->topMargin.reqSize > 0) - top = gops->topMargin.reqSize; - - if (gops->bottomMargin.reqSize > 0) - bottom = gops->bottomMargin.reqSize; - - if (gops->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 (gops->leftMargin.reqSize == 0) { - left += extra; - if (gops->rightMargin.reqSize == 0) { - right += extra; - } - else { - left += extra; - } - } - else if (gops->rightMargin.reqSize == 0) { - right += extra + extra; - } - } - else if (width < w) { - width = w; - } - } - if (gops->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 (gops->topMargin.reqSize == 0) { - top += extra; - if (gops->bottomMargin.reqSize == 0) { - bottom += extra; - } - else { - top += extra; - } - } - else if (gops->bottomMargin.reqSize == 0) { - bottom += extra + extra; - } - } - else if (height < h) { - height = h; - } - } - graphPtr->width_ = width; - graphPtr->height_ = height; - graphPtr->left_ = left + inset; - graphPtr->top_ = top + inset; - graphPtr->right_ = width - right - inset; - graphPtr->bottom_ = height - bottom - inset; - - gops->leftMargin.width = left + graphPtr->inset_; - gops->rightMargin.width = right + graphPtr->inset_; - gops->topMargin.height = top + graphPtr->inset_; - gops->bottomMargin.height = bottom + graphPtr->inset_; - - graphPtr->vOffset_ = graphPtr->top_ + gops->yPad; - graphPtr->vRange_ = plotHeight - 2*gops->yPad; - graphPtr->hOffset_ = graphPtr->left_ + gops->xPad; - graphPtr->hRange_ = plotWidth - 2*gops->xPad; - - if (graphPtr->vRange_ < 1) - graphPtr->vRange_ = 1; - - if (graphPtr->hRange_ < 1) - graphPtr->hRange_ = 1; - - graphPtr->hScale_ = 1.0f / (float)graphPtr->hRange_; - graphPtr->vScale_ = 1.0f / (float)graphPtr->vRange_; - - // Calculate the placement of the graph title so it is centered within the - // space provided for it in the top margin - titleY = graphPtr->titleHeight_; - graphPtr->titleY_ = 3 + graphPtr->inset_; - graphPtr->titleX_ = (graphPtr->right_ + graphPtr->left_) / 2; -} - -static int GetMarginGeometry(Graph* graphPtr, Margin *marginPtr) -{ - GraphOptions* gops = (GraphOptions*)graphPtr->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 (gops->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 (graphPtr->flags & GET_AXIS_GEOMETRY) - GetAxisGeometry(graphPtr, 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 (graphPtr->flags & GET_AXIS_GEOMETRY) - GetAxisGeometry(graphPtr, 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; -} - -static void GetAxisGeometry(Graph* graphPtr, Axis *axisPtr) -{ - AxisOptions* ops = (AxisOptions*)axisPtr->ops(); - GraphOptions* gops = (GraphOptions*)graphPtr->ops_; - axisPtr->freeTickLabels(); - - // Leave room for axis baseline and padding - unsigned int y =0; - if (ops->exterior && (gops->plotRelief != TK_RELIEF_SOLID)) - y += ops->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 (ops->showTicks) { - Ticks* t1Ptr = ops->t1UPtr ? ops->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 (ops->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(ops->tickFont, 0, labelPtr->string, -1, &lw, &lh); - labelPtr->width = lw; - labelPtr->height = lh; - - if (ops->tickAngle != 0.0f) { - double rlw, rlh; /* Rotated label width and height. */ - Blt_GetBoundingBox(lw, lh, ops->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 (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 (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 ((ops->lineWidth > 0) && ops->exterior) - // Distance from axis line to tick label. - y += ops->tickLength; - - } // showTicks - - if (ops->title) { - if (ops->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; -} - double AdjustViewport(double offset, double windowSize) { // Canvas-style scrolling allows the world to be scrolled within the window. diff --git a/src/bltGraph.C b/src/bltGraph.C index cb5ee98..7c65b39 100644 --- a/src/bltGraph.C +++ b/src/bltGraph.C @@ -60,7 +60,6 @@ using namespace Blt; extern int Blt_CreatePageSetup(Graph* graphPtr); extern void Blt_DestroyPageSetup(Graph* graphPtr); -extern void Blt_LayoutGraph(Graph* graphPtr); extern int PostScriptPreamble(Graph* graphPtr, const char *fileName, Blt_Ps ps); static Blt_BindPickProc PickEntry; @@ -422,7 +421,7 @@ void Graph::map() resetAxes(); if (flags & LAYOUT_NEEDED) { - Blt_LayoutGraph(this); + layoutGraph(); flags &= ~LAYOUT_NEEDED; } diff --git a/src/bltGraph.h b/src/bltGraph.h index 69be1e6..f2de09b 100644 --- a/src/bltGraph.h +++ b/src/bltGraph.h @@ -200,10 +200,12 @@ class Graph { protected: void drawPlot(Drawable); + void layoutGraph(); void drawMargins(Drawable); void printMargins(Blt_Ps); void updateMarginTraces(); + int getMarginGeometry(Margin*); void destroyPens(); @@ -228,6 +230,7 @@ class Graph { void drawAxes(Drawable); void drawAxesLimits(Drawable); void drawAxesGrids(Drawable); + void getAxisGeometry(Axis*); void adjustAxes(); public: 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; +} + + |