# Graph Limits Solution

After a little discussion in the comments of my previous entry that ended up with me having a long explanatory conversation with myself, I hit upon a solution that seems to be a good one.  In a nutshell, I needed to stop caring about the specific number of horizontal gridlines dividing the range, and just use what fits "best". Here's the code:

<cffunction name="getBounds" access="private" output="false" returntype="struct">
<cfargument name="low" type="numeric" required="true" />
<cfargument name="high" type="numeric" required="true" />
<cfargument name="gridSections" type="numeric" required="true" />
<cfscript>
var result = structNew();

var range = arguments.high – arguments.low;
var factor = gridSections ^ (len(int(range)) – 1);
var oneSigFig = ceiling(range / factor) * factor;

result.interval = cleanNumber(oneSigFig / arguments.gridSections);
result.low = cleanNumber(int(arguments.low / result.interval) * result.interval);
result.high = cleanNumber(ceiling(arguments.high / result.interval) * result.interval);
result.gridSections = ((result.high – result.low) / result.interval);

return result;
</cfscript>
</cffunction>

<cffunction name="cleanNumber" access="private" output="false" returntype="numeric">
<cfargument name="num" type="numeric" required="true" />

<cfset var INCLUDE_FACTOR = 100000000000 />

<cfset num = num * INCLUDE_FACTOR />
<cfreturn fix(num) / INCLUDE_FACTOR />
</cffunction>

The three parameters are the low point of the data series, the high point of the data series, and the number of grid sections you'd like.  The return values contains the bounds' low end, high end, number of grid sections, and the interval between gridlines.  Note that the returned grid sections value is not necessarily what you passed in.

The `cleanNumber` function is not strictly necessary, but the way I was using the values caused errors if there were "floating point arithmetic artifacts" in the results, so I wanted to clean them.  All it does is remove any decimal portion of the number that's smaller than 1/100,000,000,000.  I found that this was enough to catch any such artifacts resulting from floating point arithmetic, while being sufficiently small enough to no affect any legitimate digits.  YMMV if you've got really small numbers.

One thing that it doesn't do, however, is ensure there's "padding" around the series on the graph.  In other words, it's possible that for the high or low point of the series to end up at the maximum or minimum value of the graph.