callback: function(value, index, arr) {
var remain = value / (Math.pow(10, Math.floor(helpers.log10(value))));
- if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === arr.length - 1) {
+ if (value === 0){
+ return '0';
+ } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === arr.length - 1) {
return value.toExponential();
} else {
return '';
// Calculate Range
me.min = null;
me.max = null;
+ me.minNotZero = null;
if (opts.stacked) {
var valuesPerType = {};
} else if (value > me.max) {
me.max = value;
}
+
+ if(value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
+ me.minNotZero = value;
+ }
});
}
});
while (tickVal < me.max) {
ticks.push(tickVal);
- var exp = Math.floor(helpers.log10(tickVal));
- var significand = Math.floor(tickVal / Math.pow(10, exp)) + 1;
+ var exp;
+ var significand;
+
+ if(tickVal === 0){
+ exp = Math.floor(helpers.log10(me.minNotZero));
+ significand = Math.round(me.minNotZero / Math.pow(10, exp));
+ } else {
+ exp = Math.floor(helpers.log10(tickVal));
+ significand = Math.floor(tickVal / Math.pow(10, exp)) + 1;
+ }
if (significand === 10) {
significand = 1;
var start = me.start;
var newVal = +me.getRightValue(value);
- var range = helpers.log10(me.end) - helpers.log10(start);
+ var range;
var paddingTop = me.paddingTop;
var paddingBottom = me.paddingBottom;
var paddingLeft = me.paddingLeft;
+ var opts = me.options;
+ var tickOpts = opts.ticks;
if (me.isHorizontal()) {
-
+ range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0
if (newVal === 0) {
pixel = me.left + paddingLeft;
} else {
}
} else {
// Bottom - top since pixels increase downard on a screen
- if (newVal === 0) {
- pixel = me.top + paddingTop;
+ innerDimension = me.height - (paddingTop + paddingBottom);
+ if(start === 0 && !tickOpts.reverse){
+ range = helpers.log10(me.end) - helpers.log10(me.minNotZero);
+ if (newVal === start) {
+ pixel = me.bottom - paddingBottom;
+ } else if(newVal === me.minNotZero){
+ pixel = me.bottom - paddingBottom - innerDimension * 0.02;
+ } else {
+ pixel = me.bottom - paddingBottom - innerDimension * 0.02 - (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
+ }
+ } else if (me.end === 0 && tickOpts.reverse){
+ range = helpers.log10(me.start) - helpers.log10(me.minNotZero);
+ if (newVal === me.end) {
+ pixel = me.top + paddingTop;
+ } else if(newVal === me.minNotZero){
+ pixel = me.top + paddingTop + innerDimension * 0.02;
+ } else {
+ pixel = me.top + paddingTop + innerDimension * 0.02 + (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
+ }
} else {
+ range = helpers.log10(me.end) - helpers.log10(start);
innerDimension = me.height - (paddingTop + paddingBottom);
pixel = (me.bottom - paddingBottom) - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
- }
+ }
}
-
return pixel;
},
getValueForPixel: function(pixel) {
if (me.isHorizontal()) {
innerDimension = me.width - (me.paddingLeft + me.paddingRight);
value = me.start * Math.pow(10, (pixel - me.left - me.paddingLeft) * range / innerDimension);
- } else {
+ } else { // todo: if start === 0
innerDimension = me.height - (me.paddingTop + me.paddingBottom);
value = Math.pow(10, (me.bottom - me.paddingBottom - pixel) * range / innerDimension) / me.start;
}
-
return value;
}
});
}, {
yAxisID: 'yScale1',
data: [150]
+ }, {
+ yAxisID: 'yScale2',
+ data: [20, 0, 150, 1800, 3040]
+ }, {
+ yAxisID: 'yScale3',
+ data: [67, 0.0004, 0, 820, 0.001]
}],
labels: ['a', 'b', 'c', 'd', 'e']
},
}, {
id: 'yScale1',
type: 'logarithmic'
+ },
+ {
+ id: 'yScale2',
+ type: 'logarithmic'
+ },
+ {
+ id: 'yScale3',
+ type: 'logarithmic'
}]
}
}
expect(chart.scales.yScale1).not.toEqual(undefined); // must construct
expect(chart.scales.yScale1.min).toBe(1);
expect(chart.scales.yScale1.max).toBe(5000);
+
+ expect(chart.scales.yScale2).not.toEqual(undefined); // must construct
+ expect(chart.scales.yScale2.min).toBe(0);
+ expect(chart.scales.yScale2.max).toBe(4000);
+
+ expect(chart.scales.yScale3).not.toEqual(undefined); // must construct
+ expect(chart.scales.yScale3.min).toBe(0);
+ expect(chart.scales.yScale3.max).toBe(900);
});
it('should correctly determine the max & min of string data values', function() {
}, {
yAxisID: 'yScale1',
data: ['150']
+ }, {
+ yAxisID: 'yScale2',
+ data: ['20', '0', '150', '1800', '3040']
+ }, {
+ yAxisID: 'yScale3',
+ data: ['67', '0.0004', '0', '820', '0.001']
}],
labels: ['a', 'b', 'c', 'd', 'e']
},
}, {
id: 'yScale1',
type: 'logarithmic'
+ }, {
+ id: 'yScale2',
+ type: 'logarithmic'
+ },
+ {
+ id: 'yScale3',
+ type: 'logarithmic'
}]
}
}
expect(chart.scales.yScale1).not.toEqual(undefined); // must construct
expect(chart.scales.yScale1.min).toBe(1);
expect(chart.scales.yScale1.max).toBe(5000);
+
+ expect(chart.scales.yScale2).not.toEqual(undefined); // must construct
+ expect(chart.scales.yScale2.min).toBe(0);
+ expect(chart.scales.yScale2.max).toBe(4000);
+
+ expect(chart.scales.yScale3).not.toEqual(undefined); // must construct
+ expect(chart.scales.yScale3.min).toBe(0);
+ expect(chart.scales.yScale3.max).toBe(900);
});
it('should correctly determine the max & min data values when there are hidden datasets', function() {
yAxisID: 'yScale1',
data: [50000],
hidden: true
+ }, {
+ yAxisID: 'yScale2',
+ data: [20, 0, 7400, 14, 291]
+ }, {
+ yAxisID: 'yScale2',
+ data: [6, 0.0007, 9, 890, 60000],
+ hidden: true
}],
labels: ['a', 'b', 'c', 'd', 'e']
},
}, {
id: 'yScale1',
type: 'logarithmic'
+ }, {
+ id: 'yScale2',
+ type: 'logarithmic'
}]
}
}
expect(chart.scales.yScale1).not.toEqual(undefined); // must construct
expect(chart.scales.yScale1.min).toBe(1);
expect(chart.scales.yScale1.max).toBe(5000);
+
+ expect(chart.scales.yScale2).not.toEqual(undefined); // must construct
+ expect(chart.scales.yScale2.min).toBe(0);
+ expect(chart.scales.yScale2.max).toBe(8000);
});
it('should correctly determine the max & min data values when there is NaN data', function() {
type: 'bar',
data: {
datasets: [{
+ yAxisID: 'yScale0',
data: [undefined, 10, null, 5, 5000, NaN, 78, 450]
}, {
+ yAxisID: 'yScale0',
data: [undefined, 28, null, 1000, 500, NaN, 50, 42]
+ }, {
+ yAxisID: 'yScale1',
+ data: [undefined, 30, null, 9400, 0, NaN, 54, 836]
+ }, {
+ yAxisID: 'yScale1',
+ data: [undefined, 0, null, 800, 9, NaN, 894, 21]
}],
labels: ['a', 'b', 'c', 'd', 'e', 'f' ,'g']
},
options: {
scales: {
yAxes: [{
- id: 'yScale',
+ id: 'yScale0',
+ type: 'logarithmic'
+ }, {
+ id: 'yScale1',
type: 'logarithmic'
}]
}
}
});
- expect(chart.scales.yScale).not.toEqual(undefined); // must construct
- expect(chart.scales.yScale.min).toBe(1);
- expect(chart.scales.yScale.max).toBe(5000);
+ expect(chart.scales.yScale0).not.toEqual(undefined); // must construct
+ expect(chart.scales.yScale0.min).toBe(1);
+ expect(chart.scales.yScale0.max).toBe(5000);
// Turn on stacked mode since it uses it's own
chart.options.scales.yAxes[0].stacked = true;
chart.update();
- expect(chart.scales.yScale.min).toBe(10);
- expect(chart.scales.yScale.max).toBe(6000);
+ expect(chart.scales.yScale0.min).toBe(10);
+ expect(chart.scales.yScale0.max).toBe(6000);
+
+ expect(chart.scales.yScale1).not.toEqual(undefined); // must construct
+ expect(chart.scales.yScale1.min).toBe(0);
+ expect(chart.scales.yScale1.max).toBe(10000);
});
it('should correctly determine the max & min for scatter data', function() {
expect(chart.scales.yScale.max).toBe(200);
});
+ it('should correctly determine the max & min for scatter data when 0 values are present', function() {
+ var chart = window.acquireChart({
+ type: 'line',
+ data: {
+ datasets: [{
+ data: [
+ { x: 7, y: 950 },
+ { x: 289, y: 0 },
+ { x: 0, y: 8 },
+ { x: 23, y: 0.04 }
+ ]
+ }]
+ },
+ options: {
+ scales: {
+ xAxes: [{
+ id: 'xScale',
+ type: 'logarithmic',
+ position: 'bottom'
+ }],
+ yAxes: [{
+ id: 'yScale',
+ type: 'logarithmic'
+ }]
+ }
+ }
+ });
+
+ expect(chart.scales.xScale.min).toBe(0);
+ expect(chart.scales.xScale.max).toBe(300);
+
+ expect(chart.scales.yScale.min).toBe(0);
+ expect(chart.scales.yScale.max).toBe(1000);
+ });
+
it('should correctly determine the min and max data values when stacked mode is turned on', function() {
var chart = window.acquireChart({
type: 'bar',
}));
});
+ it('should generate tick marks when 0 values are present', function() {
+ var chart = window.acquireChart({
+ type: 'bar',
+ data: {
+ datasets: [{
+ data: [11, 0.8, 0, 28, 7]
+ }],
+ labels: []
+ },
+ options: {
+ scales: {
+ yAxes: [{
+ id: 'yScale',
+ type: 'logarithmic',
+ ticks: {
+ callback: function(value) {
+ return value;
+ }
+ }
+ }]
+ }
+ }
+ });
+
+ // Counts down because the lines are drawn top to bottom
+ expect(chart.scales.yScale).toEqual(jasmine.objectContaining({
+ ticks: [30, 20, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0.9, 0.8, 0],
+ start: 0,
+ end: 30
+ }));
+ });
+
+
it('should generate tick marks in the correct order in reversed mode', function() {
var chart = window.acquireChart({
type: 'line',
}));
});
+ it('should generate tick marks in the correct order in reversed mode when 0 values are present', function() {
+ var chart = window.acquireChart({
+ type: 'line',
+ data: {
+ datasets: [{
+ data: [21, 9, 0, 10, 25]
+ }],
+ labels: []
+ },
+ options: {
+ scales: {
+ yAxes: [{
+ id: 'yScale',
+ type: 'logarithmic',
+ ticks: {
+ reverse: true,
+ callback: function(value) {
+ return value;
+ }
+ }
+ }]
+ }
+ }
+ });
+
+ // Counts down because the lines are drawn top to bottom
+ expect(chart.scales.yScale).toEqual(jasmine.objectContaining({
+ ticks: [0, 9, 10, 20, 30],
+ start: 30,
+ end: 0
+ }));
+ });
+
it('should build labels using the default template', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
- data: [10, 5, 1, 25, 78]
+ data: [10, 5, 1, 25, 0, 78]
}],
labels: []
},
}
});
- expect(chart.scales.yScale.ticks).toEqual(['8e+1', '', '', '5e+1', '', '', '2e+1', '1e+1', '', '', '', '', '5e+0', '', '', '2e+0', '1e+0']);
+ expect(chart.scales.yScale.ticks).toEqual(['8e+1', '', '', '5e+1', '', '', '2e+1', '1e+1', '', '', '', '', '5e+0', '', '', '2e+0', '1e+0', '0']);
});
it('should build labels using the user supplied callback', function() {
expect(yScale.getValueForPixel(456)).toBeCloseTo(1, 1e-4);
expect(yScale.getValueForPixel(234)).toBeCloseTo(10, 1e-4);
});
+
+ it('should get the correct pixel value for a point when 0 values are present', function() {
+ var chart = window.acquireChart({
+ type: 'bar',
+ data: {
+ datasets: [{
+ yAxisID: 'yScale',
+ data: [0.063, 4, 0, 63, 10, 0.5]
+ }],
+ labels: []
+ },
+ options: {
+ scales: {
+ yAxes: [{
+ id: 'yScale',
+ type: 'logarithmic',
+ ticks: {
+ reverse: false
+ }
+ }]
+ }
+ }
+ });
+
+ var yScale = chart.scales.yScale;
+ expect(yScale.getPixelForValue(70, 0, 0)).toBeCloseToPixel(32); // top + paddingTop
+ expect(yScale.getPixelForValue(0, 0, 0)).toBeCloseToPixel(484); // bottom - paddingBottom
+ expect(yScale.getPixelForValue(0.063, 0, 0)).toBeCloseToPixel(475); // minNotZero 2% from range
+ expect(yScale.getPixelForValue(0.5, 0, 0)).toBeCloseToPixel(344);
+ expect(yScale.getPixelForValue(4, 0, 0)).toBeCloseToPixel(213);
+ expect(yScale.getPixelForValue(10, 0, 0)).toBeCloseToPixel(155);
+ expect(yScale.getPixelForValue(63, 0, 0)).toBeCloseToPixel(38.5);
+
+ chart.options.scales.yAxes[0].ticks.reverse = true; // Reverse mode
+ chart.update();
+
+ expect(yScale.getPixelForValue(70, 0, 0)).toBeCloseToPixel(484); // bottom - paddingBottom
+ expect(yScale.getPixelForValue(0, 0, 0)).toBeCloseToPixel(32); // top + paddingTop
+ expect(yScale.getPixelForValue(0.063, 0, 0)).toBeCloseToPixel(41); // minNotZero 2% from range
+ expect(yScale.getPixelForValue(0.5, 0, 0)).toBeCloseToPixel(172);
+ expect(yScale.getPixelForValue(4, 0, 0)).toBeCloseToPixel(303);
+ expect(yScale.getPixelForValue(10, 0, 0)).toBeCloseToPixel(361);
+ expect(yScale.getPixelForValue(63, 0, 0)).toBeCloseToPixel(477);
+ });
});