Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/core/core.ticks.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ const formatters = {
if (tickValue === 0) {
return '0';
}
const remain = tickValue / (Math.pow(10, Math.floor(log10(tickValue))));
if (remain === 1 || remain === 2 || remain === 5) {
const remain = ticks[index].significand || (tickValue / (Math.pow(10, Math.floor(log10(tickValue)))));
if ([1, 2, 3, 5, 10, 15].includes(remain) || index > 0.8 * ticks.length) {
return formatters.numeric.call(this, tickValue, index, ticks);
}
return '';
Expand Down
91 changes: 60 additions & 31 deletions src/scales/scale.logarithmic.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,68 @@ import Scale from '../core/core.scale';
import LinearScaleBase from './scale.linearbase';
import Ticks from '../core/core.ticks';

const log10Floor = v => Math.floor(log10(v));
const changeExponent = (v, m) => Math.pow(10, log10Floor(v) + m);

function isMajor(tickVal) {
const remain = tickVal / (Math.pow(10, Math.floor(log10(tickVal))));
const remain = tickVal / (Math.pow(10, log10Floor(tickVal)));
return remain === 1;
}

function steps(min, max, rangeExp) {
const rangeStep = Math.pow(10, rangeExp);
const start = Math.floor(min / rangeStep);
const end = Math.ceil(max / rangeStep);
return end - start;
}

function startExp(min, max) {
const range = max - min;
let rangeExp = log10Floor(range);
while (steps(min, max, rangeExp) > 10) {
rangeExp++;
}
while (steps(min, max, rangeExp) < 10) {
rangeExp--;
}
return Math.min(rangeExp, log10Floor(min));
}


/**
* Generate a set of logarithmic ticks
* @param generationOptions the options used to generate the ticks
* @param dataRange the range of the data
* @returns {object[]} array of tick objects
*/
function generateTicks(generationOptions, dataRange) {
const endExp = Math.floor(log10(dataRange.max));
const endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
function generateTicks(generationOptions, {min, max}) {
min = finiteOrDefault(generationOptions.min, min);
const ticks = [];
let tickVal = finiteOrDefault(generationOptions.min, Math.pow(10, Math.floor(log10(dataRange.min))));
let exp = Math.floor(log10(tickVal));
let significand = Math.floor(tickVal / Math.pow(10, exp));
const minExp = log10Floor(min);
let exp = startExp(min, max);
let precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;

do {
ticks.push({value: tickVal, major: isMajor(tickVal)});

++significand;
if (significand === 10) {
significand = 1;
++exp;
const stepSize = Math.pow(10, exp);
const base = minExp > exp ? Math.pow(10, minExp) : 0;
const start = Math.round((min - base) * precision) / precision;
const offset = Math.floor((min - base) / stepSize / 10) * stepSize * 10;
let significand = Math.floor((start - offset) / Math.pow(10, exp));
let value = finiteOrDefault(generationOptions.min, Math.round((base + offset + significand * Math.pow(10, exp)) * precision) / precision);
while (value < max) {
ticks.push({value, major: isMajor(value), significand});
if (significand >= 10) {
significand = significand < 15 ? 15 : 20;
} else {
significand++;
}
if (significand >= 20) {
exp++;
significand = 2;
precision = exp >= 0 ? 1 : precision;
}

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

const lastTick = finiteOrDefault(generationOptions.max, tickVal);
ticks.push({value: lastTick, major: isMajor(tickVal)});
value = Math.round((base + offset + significand * Math.pow(10, exp)) * precision) / precision;
}
const lastTick = finiteOrDefault(generationOptions.max, value);
ticks.push({value: lastTick, major: isMajor(lastTick), significand});

return ticks;
}
Expand Down Expand Up @@ -77,6 +104,12 @@ export default class LogarithmicScale extends Scale {
this._zero = true;
}

// if data has `0` in it or `beginAtZero` is true, min (non zero) value is at bottom
// of scale, and it does not equal suggestedMin, lower the min bound by one exp.
if (this._zero && this.min !== this._suggestedMin && !isFinite(this._userMin)) {
this.min = min === changeExponent(this.min, 0) ? changeExponent(this.min, -1) : changeExponent(this.min, 0);
}

this.handleTickRangeOptions();
}

Expand All @@ -87,28 +120,24 @@ export default class LogarithmicScale extends Scale {

const setMin = v => (min = minDefined ? min : v);
const setMax = v => (max = maxDefined ? max : v);
const exp = (v, m) => Math.pow(10, Math.floor(log10(v)) + m);

if (min === max) {
if (min <= 0) { // includes null
setMin(1);
setMax(10);
} else {
setMin(exp(min, -1));
setMax(exp(max, +1));
setMin(changeExponent(min, -1));
setMax(changeExponent(max, +1));
}
}
if (min <= 0) {
setMin(exp(max, -1));
setMin(changeExponent(max, -1));
}
if (max <= 0) {
setMax(exp(min, +1));
}
// if data has `0` in it or `beginAtZero` is true, min (non zero) value is at bottom
// of scale, and it does not equal suggestedMin, lower the min bound by one exp.
if (this._zero && this.min !== this._suggestedMin && min === exp(this.min, 0)) {
setMin(exp(min, -1));

setMax(changeExponent(min, +1));
}

this.min = min;
this.max = max;
}
Expand Down
31 changes: 31 additions & 0 deletions test/fixtures/scale.logarithmic/large-range.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module.exports = {
config: {
type: 'line',
data: {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
backgroundColor: 'red',
borderColor: 'red',
fill: false,
data: [23, 21, 34, 52, 115, 3333, 5116]
}]
},
options: {
responsive: true,
scales: {
x: {
display: false,
},
y: {
type: 'logarithmic',
ticks: {
autoSkip: false
}
}
}
}
},
options: {
spriteText: true
}
};
Binary file added test/fixtures/scale.logarithmic/large-range.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions test/fixtures/scale.logarithmic/large-values-small-range.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module.exports = {
config: {
type: 'line',
data: {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
backgroundColor: 'red',
borderColor: 'red',
fill: false,
data: [5000.002, 5000.012, 5000.01, 5000.03, 5000.04, 5000.004, 5000.032]
}]
},
options: {
responsive: true,
scales: {
x: {
display: false,
},
y: {
type: 'logarithmic',
ticks: {
autoSkip: false
}
}
}
}
},
options: {
spriteText: true
}
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions test/fixtures/scale.logarithmic/med-range.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module.exports = {
config: {
type: 'line',
data: {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
backgroundColor: 'red',
borderColor: 'red',
fill: false,
data: [25, 24, 27, 32, 45, 30, 28]
}]
},
options: {
responsive: true,
scales: {
x: {
display: false,
},
y: {
type: 'logarithmic',
ticks: {
autoSkip: false
}
}
}
}
},
options: {
spriteText: true
}
};
Binary file added test/fixtures/scale.logarithmic/med-range.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions test/fixtures/scale.logarithmic/min-max.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module.exports = {
config: {
type: 'line',
data: {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
backgroundColor: 'red',
borderColor: 'red',
fill: false,
data: [250, 240, 270, 320, 450, 300, 280]
}]
},
options: {
responsive: true,
scales: {
x: {
display: false,
},
y: {
type: 'logarithmic',
min: 233,
max: 471,
ticks: {
autoSkip: false
}
}
}
}
},
options: {
spriteText: true
}
};
Binary file added test/fixtures/scale.logarithmic/min-max.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions test/fixtures/scale.logarithmic/small-range.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module.exports = {
config: {
type: 'line',
data: {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
backgroundColor: 'red',
borderColor: 'red',
fill: false,
data: [3, 1, 4, 2, 5, 3, 16]
}]
},
options: {
responsive: true,
scales: {
x: {
display: false,
},
y: {
type: 'logarithmic',
ticks: {
autoSkip: false
}
}
}
}
},
options: {
spriteText: true
}
};
Binary file added test/fixtures/scale.logarithmic/small-range.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions test/specs/core.ticks.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ describe('Test tick generators', function() {
min: 0.1,
max: 1,
ticks: {
autoSkip: false,
callback: function(value) {
return value.toString();
}
Expand All @@ -81,6 +82,7 @@ describe('Test tick generators', function() {
min: 0.1,
max: 1,
ticks: {
autoSkip: false,
callback: function(value) {
return value.toString();
}
Expand All @@ -93,8 +95,8 @@ describe('Test tick generators', function() {
var xLabels = getLabels(chart.scales.x);
var yLabels = getLabels(chart.scales.y);

expect(xLabels).toEqual(['0.1', '0.2', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9', '1']);
expect(yLabels).toEqual(['0.1', '0.2', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9', '1']);
expect(xLabels).toEqual(['0.1', '0.11', '0.12', '0.13', '0.14', '0.15', '0.16', '0.17', '0.18', '0.19', '0.2', '0.25', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9', '1']);
expect(yLabels).toEqual(['0.1', '0.11', '0.12', '0.13', '0.14', '0.15', '0.16', '0.17', '0.18', '0.19', '0.2', '0.25', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9', '1']);
});

describe('formatters.numeric', function() {
Expand Down
Loading