Skip to content

Commit

Permalink
Add back Chart.Ticks.formatters (#5088)
Browse files Browse the repository at this point in the history
  • Loading branch information
benmccann authored and simonbrunel committed Jan 6, 2018
1 parent fcd4633 commit 33c7d94
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 144 deletions.
1 change: 1 addition & 0 deletions src/chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Chart.Element = require('./core/core.element');
Chart.elements = require('./elements/index');
Chart.Interaction = require('./core/core.interaction');
Chart.platform = require('./platforms/platform');
Chart.Ticks = require('./core/core.ticks');

require('./core/core.plugin')(Chart);
require('./core/core.animation')(Chart);
Expand Down
141 changes: 0 additions & 141 deletions src/core/core.ticks.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,147 +7,6 @@ var helpers = require('../helpers/index');
* @namespace Chart.Ticks
*/
module.exports = {
/**
* Namespace to hold generators for different types of ticks
* @namespace Chart.Ticks.generators
*/
generators: {
/**
* Interface for the options provided to the numeric tick generator
* @interface INumericTickGenerationOptions
*/
/**
* The maximum number of ticks to display
* @name INumericTickGenerationOptions#maxTicks
* @type Number
*/
/**
* The distance between each tick.
* @name INumericTickGenerationOptions#stepSize
* @type Number
* @optional
*/
/**
* Forced minimum for the ticks. If not specified, the minimum of the data range is used to calculate the tick minimum
* @name INumericTickGenerationOptions#min
* @type Number
* @optional
*/
/**
* The maximum value of the ticks. If not specified, the maximum of the data range is used to calculate the tick maximum
* @name INumericTickGenerationOptions#max
* @type Number
* @optional
*/

/**
* Generate a set of linear ticks
* @method Chart.Ticks.generators.linear
* @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
* @param dataRange {IRange} the range of the data
* @returns {Array<Number>} array of tick values
*/
linear: function(generationOptions, dataRange) {
var ticks = [];
// To get a "nice" value for the tick spacing, we will use the appropriately named
// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
// for details.

var spacing;
if (generationOptions.stepSize && generationOptions.stepSize > 0) {
spacing = generationOptions.stepSize;
} else {
var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
}
var niceMin = Math.floor(dataRange.min / spacing) * spacing;
var niceMax = Math.ceil(dataRange.max / spacing) * spacing;

// If min, max and stepSize is set and they make an evenly spaced scale use it.
if (generationOptions.min && generationOptions.max && generationOptions.stepSize) {
// If very close to our whole number, use it.
if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) {
niceMin = generationOptions.min;
niceMax = generationOptions.max;
}
}

var numSpaces = (niceMax - niceMin) / spacing;
// If very close to our rounded value, use it.
if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
numSpaces = Math.round(numSpaces);
} else {
numSpaces = Math.ceil(numSpaces);
}

var precision = 1;
if (spacing < 1) {
precision = Math.pow(10, spacing.toString().length - 2);
niceMin = Math.round(niceMin * precision) / precision;
niceMax = Math.round(niceMax * precision) / precision;
}
ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);
for (var j = 1; j < numSpaces; ++j) {
ticks.push(Math.round((niceMin + j * spacing) * precision) / precision);
}
ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);

return ticks;
},

/**
* Generate a set of logarithmic ticks
* @method Chart.Ticks.generators.logarithmic
* @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
* @param dataRange {IRange} the range of the data
* @returns {Array<Number>} array of tick values
*/
logarithmic: function(generationOptions, dataRange) {
var ticks = [];
var valueOrDefault = helpers.valueOrDefault;

// Figure out what the max number of ticks we can support it is based on the size of
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
// the graph
var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));

var endExp = Math.floor(helpers.log10(dataRange.max));
var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
var exp, significand;

if (tickVal === 0) {
exp = Math.floor(helpers.log10(dataRange.minNotZero));
significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));

ticks.push(tickVal);
tickVal = significand * Math.pow(10, exp);
} else {
exp = Math.floor(helpers.log10(tickVal));
significand = Math.floor(tickVal / Math.pow(10, exp));
}
var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;

do {
ticks.push(tickVal);

++significand;
if (significand === 10) {
significand = 1;
++exp;
precision = exp >= 0 ? 1 : precision;
}

tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision;
} while (exp < endExp || (exp === endExp && significand < endSignificand));

var lastTick = valueOrDefault(generationOptions.max, tickVal);
ticks.push(lastTick);

return ticks;
}
},

/**
* Namespace to hold formatters for different types of ticks
* @namespace Chart.Ticks.formatters
Expand Down
58 changes: 56 additions & 2 deletions src/scales/scale.linearbase.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,61 @@
'use strict';

var helpers = require('../helpers/index');
var Ticks = require('../core/core.ticks');

/**
* Generate a set of linear ticks
* @param generationOptions the options used to generate the ticks
* @param dataRange the range of the data
* @returns {Array<Number>} array of tick values
*/
function generateTicks(generationOptions, dataRange) {
var ticks = [];
// To get a "nice" value for the tick spacing, we will use the appropriately named
// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
// for details.

var spacing;
if (generationOptions.stepSize && generationOptions.stepSize > 0) {
spacing = generationOptions.stepSize;
} else {
var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
}
var niceMin = Math.floor(dataRange.min / spacing) * spacing;
var niceMax = Math.ceil(dataRange.max / spacing) * spacing;

// If min, max and stepSize is set and they make an evenly spaced scale use it.
if (generationOptions.min && generationOptions.max && generationOptions.stepSize) {
// If very close to our whole number, use it.
if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) {
niceMin = generationOptions.min;
niceMax = generationOptions.max;
}
}

var numSpaces = (niceMax - niceMin) / spacing;
// If very close to our rounded value, use it.
if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
numSpaces = Math.round(numSpaces);
} else {
numSpaces = Math.ceil(numSpaces);
}

var precision = 1;
if (spacing < 1) {
precision = Math.pow(10, spacing.toString().length - 2);
niceMin = Math.round(niceMin * precision) / precision;
niceMax = Math.round(niceMax * precision) / precision;
}
ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);
for (var j = 1; j < numSpaces; ++j) {
ticks.push(Math.round((niceMin + j * spacing) * precision) / precision);
}
ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);

return ticks;
}


module.exports = function(Chart) {

Expand Down Expand Up @@ -102,7 +156,7 @@ module.exports = function(Chart) {
max: tickOpts.max,
stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
};
var ticks = me.ticks = Ticks.generators.linear(numericGeneratorOptions, me);
var ticks = me.ticks = generateTicks(numericGeneratorOptions, me);

me.handleDirectionalChanges();

Expand Down
54 changes: 53 additions & 1 deletion src/scales/scale.logarithmic.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,58 @@
var helpers = require('../helpers/index');
var Ticks = require('../core/core.ticks');

/**
* Generate a set of logarithmic ticks
* @param generationOptions the options used to generate the ticks
* @param dataRange the range of the data
* @returns {Array<Number>} array of tick values
*/
function generateTicks(generationOptions, dataRange) {
var ticks = [];
var valueOrDefault = helpers.valueOrDefault;

// Figure out what the max number of ticks we can support it is based on the size of
// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
// the graph
var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));

var endExp = Math.floor(helpers.log10(dataRange.max));
var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
var exp, significand;

if (tickVal === 0) {
exp = Math.floor(helpers.log10(dataRange.minNotZero));
significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));

ticks.push(tickVal);
tickVal = significand * Math.pow(10, exp);
} else {
exp = Math.floor(helpers.log10(tickVal));
significand = Math.floor(tickVal / Math.pow(10, exp));
}
var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;

do {
ticks.push(tickVal);

++significand;
if (significand === 10) {
significand = 1;
++exp;
precision = exp >= 0 ? 1 : precision;
}

tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision;
} while (exp < endExp || (exp === endExp && significand < endSignificand));

var lastTick = valueOrDefault(generationOptions.max, tickVal);
ticks.push(lastTick);

return ticks;
}


module.exports = function(Chart) {

var defaultConfig = {
Expand Down Expand Up @@ -167,7 +219,7 @@ module.exports = function(Chart) {
min: tickOpts.min,
max: tickOpts.max
};
var ticks = me.ticks = Ticks.generators.logarithmic(generationOptions, me);
var ticks = me.ticks = generateTicks(generationOptions, me);

// At this point, we need to update our max and min given the tick values since we have expanded the
// range of the scale
Expand Down
9 changes: 9 additions & 0 deletions test/specs/core.ticks.tests.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
describe('Test tick generators', function() {
// formatters are used as default config values so users want to be able to reference them
it('Should expose formatters api', function() {
expect(typeof Chart.Ticks).toBeDefined();
expect(typeof Chart.Ticks.formatters).toBeDefined();
expect(typeof Chart.Ticks.formatters.values).toBe('function');
expect(typeof Chart.Ticks.formatters.linear).toBe('function');
expect(typeof Chart.Ticks.formatters.logarithmic).toBe('function');
});

it('Should generate linear spaced ticks with correct precision', function() {
var chart = window.acquireChart({
type: 'line',
Expand Down

0 comments on commit 33c7d94

Please sign in to comment.