123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- /**
- * 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<l; i++) {
- currentNT = low + i;
- temp = r/(currentNT-1);
- sd = gsf(temp);
- temp = Math.abs(nttarget - currentNT) + sd.digitsRight;
- if (temp < badness) {
- badness = temp;
- bestNT = currentNT;
- bestPrec = sd.digitsRight;
- }
- else if (temp === badness) {
- // let nicer ticks trump number ot ticks
- if (sd.digitsRight < bestPrec) {
- bestNT = currentNT;
- bestPrec = sd.digitsRight;
- }
- }
- }
- fsd = Math.max(bestPrec, Math.max(gsf(min).digitsRight, gsf(max).digitsRight));
- if (fsd === 0) {
- fs = '%d';
- }
- else {
- fs = '%.' + fsd + 'f';
- }
- temp = r / (bestNT - 1);
- // min, max, number ticks, format string, tick interval
- return [min, max, bestNT, fs, temp];
- }
- // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n
- // it is based soley on the range and number of ticks. So if user specifies
- // number of ticks, use this.
- function bestInterval(range, numberTicks) {
- numberTicks = numberTicks || 7;
- var minimum = range / (numberTicks - 1);
- var magnitude = Math.pow(10, Math.floor(Math.log(minimum) / Math.LN10));
- var residual = minimum / magnitude;
- var interval;
- // "nicest" ranges are 1, 2, 5 or powers of these.
- // for magnitudes below 1, only allow these.
- if (magnitude < 1) {
- if (residual > 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);
|