Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add back Chart.Ticks.formatters #5088

Merged
merged 1 commit into from Jan 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/chart.js
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
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
@@ -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
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
@@ -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