/** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: @VERSION * Revision: @REVISION * * Copyright (c) 2009-2013 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * The following code was generaously given to me a while back by Scott Prahl. * He did a good job at computing axes min, max and number of ticks for the * case where the user has not set any scale related parameters (tickInterval, * numberTicks, min or max). I had ignored this use case for a long time, * focusing on the more difficult case where user has set some option controlling * tick generation. Anyway, about time I got this into jqPlot. * Thanks Scott!! */ /** * Copyright (c) 2010 Scott Prahl * The next three routines are currently available for use in all personal * or commercial projects under both the MIT and GPL version 2.0 licenses. * This means that you can choose the license that best suits your project * and use it accordingly. */ // A good format string depends on the interval. If the interval is greater // than 1 then there is no need to show any decimal digits. If it is < 1.0, then // use the magnitude of the interval to determine the number of digits to show. function bestFormatString (interval) { var fstr; interval = Math.abs(interval); if (interval >= 10) { fstr = '%d'; } else if (interval > 1) { if (interval === parseInt(interval, 10)) { fstr = '%d'; } else { fstr = '%.1f'; } } else { var expv = -Math.floor(Math.log(interval)/Math.LN10); fstr = '%.' + expv + 'f'; } return fstr; } var _factors = [0.1, 0.2, 0.3, 0.4, 0.5, 0.8, 1, 2, 3, 4, 5]; var _getLowerFactor = function(f) { var i = _factors.indexOf(f); if (i > 0) { return _factors[i-1]; } else { return _factors[_factors.length - 1] / 100; } }; var _getHigherFactor = function(f) { var i = _factors.indexOf(f); if (i < _factors.length-1) { return _factors[i+1]; } else { return _factors[0] * 100; } }; // Given a fixed minimum and maximum and a target number ot ticks // figure out the best interval and // return min, max, number ticks, format string and tick interval function bestConstrainedInterval(min, max, nttarget) { // run through possible number to ticks and see which interval is best var low = Math.floor(nttarget/2); var hi = Math.ceil(nttarget*1.5); var badness = Number.MAX_VALUE; var r = (max - min); var temp; var sd; var bestNT; var gsf = $.jqplot.getSignificantFigures; var fsd; var fs; var currentNT; var bestPrec; for (var i=0, l=hi-low+1; i 5) { interval = 10 * magnitude; } else if (residual > 2) { interval = 5 * magnitude; } else if (residual > 1) { interval = 2 * magnitude; } else { interval = magnitude; } } // for large ranges (whole integers), allow intervals like 3, 4 or powers of these. // this helps a lot with poor choices for number of ticks. else { if (residual > 5) { interval = 10 * magnitude; } else if (residual > 4) { interval = 5 * magnitude; } else if (residual > 3) { interval = 4 * magnitude; } else if (residual > 2) { interval = 3 * magnitude; } else if (residual > 1) { interval = 2 * magnitude; } else { interval = magnitude; } } return interval; } // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n // it is based soley on the range of data, number of ticks must be computed later. function bestLinearInterval(range, scalefact) { scalefact = scalefact || 1; var expv = Math.floor(Math.log(range)/Math.LN10); var magnitude = Math.pow(10, expv); // 0 < f < 10 var f = range / magnitude; var fact; // for large plots, scalefact will decrease f and increase number of ticks. // for small plots, scalefact will increase f and decrease number of ticks. f = f/scalefact; // for large plots, smaller interval, more ticks. if (f<=0.38) { fact = 0.1; } else if (f<=1.6) { fact = 0.2; } else if (f<=4.0) { fact = 0.5; } else if (f<=8.0) { fact = 1.0; } // for very small plots, larger interval, less ticks in number ticks else if (f<=16.0) { fact = 2; } else { fact = 5; } return fact*magnitude; } function bestLinearComponents(range, scalefact) { var expv = Math.floor(Math.log(range)/Math.LN10); var magnitude = Math.pow(10, expv); // 0 < f < 10 var f = range / magnitude; var interval; var fact; // for large plots, scalefact will decrease f and increase number of ticks. // for small plots, scalefact will increase f and decrease number of ticks. f = f/scalefact; // for large plots, smaller interval, more ticks. if (f<=0.38) { fact = 0.1; } else if (f<=1.6) { fact = 0.2; } else if (f<=4.0) { fact = 0.5; } else if (f<=8.0) { fact = 1.0; } // for very small plots, larger interval, less ticks in number ticks else if (f<=16.0) { fact = 2; } // else if (f<=20.0) { // fact = 3; // } // else if (f<=24.0) { // fact = 4; // } else { fact = 5; } interval = fact * magnitude; return [interval, fact, magnitude]; } // Given the min and max for a dataset, return suitable endpoints // for the graphing, a good number for the number of ticks, and a // format string so that extraneous digits are not displayed. // returned is an array containing [min, max, nTicks, format] $.jqplot.LinearTickGenerator = function(axis_min, axis_max, scalefact, numberTicks, keepMin, keepMax) { // Set to preserve EITHER min OR max. // If min is preserved, max must be free. keepMin = (keepMin === null) ? false : keepMin; keepMax = (keepMax === null || keepMin) ? false : keepMax; // if endpoints are equal try to include zero otherwise include one if (axis_min === axis_max) { axis_max = (axis_max) ? 0 : 1; } scalefact = scalefact || 1.0; // make sure range is positive if (axis_max < axis_min) { var a = axis_max; axis_max = axis_min; axis_min = a; } var r = []; var ss = bestLinearInterval(axis_max - axis_min, scalefact); var gsf = $.jqplot.getSignificantFigures; if (numberTicks == null) { // Figure out the axis min, max and number of ticks // the min and max will be some multiple of the tick interval, // 1*10^n, 2*10^n or 5*10^n. This gaurantees that, if the // axis min is negative, 0 will be a tick. if (!keepMin && !keepMax) { r[0] = Math.floor(axis_min / ss) * ss; // min r[1] = Math.ceil(axis_max / ss) * ss; // max r[2] = Math.round((r[1]-r[0])/ss+1.0); // number of ticks r[3] = bestFormatString(ss); // format string r[4] = ss; // tick Interval } else if (keepMin) { r[0] = axis_min; // min r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0); // number of ticks r[1] = axis_min + (r[2] - 1) * ss; // max var digitsMin = gsf(axis_min).digitsRight; var digitsSS = gsf(ss).digitsRight; if (digitsMin < digitsSS) { r[3] = bestFormatString(ss); // format string } else { r[3] = '%.' + digitsMin + 'f'; } r[4] = ss; // tick Interval } else if (keepMax) { r[1] = axis_max; // max r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0); // number of ticks r[0] = axis_max - (r[2] - 1) * ss; // min var digitsMax = gsf(axis_max).digitsRight; var digitsSS = gsf(ss).digitsRight; if (digitsMax < digitsSS) { r[3] = bestFormatString(ss); // format string } else { r[3] = '%.' + digitsMax + 'f'; } r[4] = ss; // tick Interval } } else { var tempr = []; // Figure out the axis min, max and number of ticks // the min and max will be some multiple of the tick interval, // 1*10^n, 2*10^n or 5*10^n. This gaurantees that, if the // axis min is negative, 0 will be a tick. tempr[0] = Math.floor(axis_min / ss) * ss; // min tempr[1] = Math.ceil(axis_max / ss) * ss; // max tempr[2] = Math.round((tempr[1]-tempr[0])/ss+1.0); // number of ticks tempr[3] = bestFormatString(ss); // format string tempr[4] = ss; // tick Interval // first, see if we happen to get the right number of ticks if (tempr[2] === numberTicks) { r = tempr; } else { var newti = bestInterval(tempr[1] - tempr[0], numberTicks); r[0] = tempr[0]; // min r[2] = numberTicks; // number of ticks r[4] = newti; // tick interval r[3] = bestFormatString(newti); // format string r[1] = r[0] + (r[2] - 1) * r[4]; // max } } return r; }; $.jqplot.LinearTickGenerator.bestLinearInterval = bestLinearInterval; $.jqplot.LinearTickGenerator.bestInterval = bestInterval; $.jqplot.LinearTickGenerator.bestLinearComponents = bestLinearComponents; $.jqplot.LinearTickGenerator.bestConstrainedInterval = bestConstrainedInterval; })(jQuery);