From 1e4eb0105942debe27079174bd284bad5b1824f4 Mon Sep 17 00:00:00 2001 From: bigbenhur Date: Fri, 3 Jun 2016 15:54:14 +0200 Subject: [PATCH 1/3] Support auto grid min/max when using log scale, Issue #3090 --- public/app/plugins/panel/graph/graph.js | 58 ++++++++++--------- .../plugins/panel/graph/specs/graph_specs.ts | 6 +- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/public/app/plugins/panel/graph/graph.js b/public/app/plugins/panel/graph/graph.js index 86ad3b5f025..8019a51354b 100755 --- a/public/app/plugins/panel/graph/graph.js +++ b/public/app/plugins/panel/graph/graph.js @@ -373,42 +373,46 @@ function (angular, $, moment, _, kbn, GraphTooltip) { if (axis.logBase === 1) { return; } + if (axis.min < Number.MIN_VALUE) { + axis.min = null; + } var series, i; - var max = axis.max; + var max = axis.max, min = axis.min; - if (max === null) { - for (i = 0; i < data.length; i++) { - series = data[i]; - if (series.yaxis === axis.index) { - if (max < series.stats.max) { - max = series.stats.max; - } + for (i = 0; i < data.length; i++) { + series = data[i]; + if (series.yaxis === axis.index) { + if (max === null || max < series.stats.max) { + max = series.stats.max; + } + if (min === null || min > series.stats.min) { + min = series.stats.min; } } - if (max === void 0) { - max = Number.MAX_VALUE; - } + } + if (max === null && min === null) { + max = Math.pow(axis.logBase,+2); + min = Math.pow(axis.logBase,-2); + } else if (max === null) { + max = min*Math.pow(axis.logBase,+4); + } else if (min === null) { + min = max*Math.pow(axis.logBase,-4); } - axis.min = axis.min !== null ? axis.min : 0; - axis.ticks = [0, 1]; - var nextTick = 1; + axis.transform = function(v) { return Math.log(v) / Math.log(axis.logBase); }; + axis.inverseTransform = function (v) { return Math.pow(axis.logBase,v); }; - while (true) { - nextTick = nextTick * axis.logBase; + min = axis.inverseTransform(Math.floor(axis.transform(min))); + max = axis.inverseTransform(Math.ceil(axis.transform(max))); + + axis.min = axis.min !== null ? axis.min : min; + axis.max = axis.max !== null ? axis.max : max; + + axis.ticks = []; + var nextTick; + for (nextTick = min; nextTick <= max; nextTick *= axis.logBase) { axis.ticks.push(nextTick); - if (nextTick > max) { - break; - } - } - - if (axis.logBase === 10) { - axis.transform = function(v) { return Math.log(v+0.1); }; - axis.inverseTransform = function (v) { return Math.pow(10,v); }; - } else { - axis.transform = function(v) { return Math.log(v+0.1) / Math.log(axis.logBase); }; - axis.inverseTransform = function (v) { return Math.pow(axis.logBase,v); }; } } diff --git a/public/app/plugins/panel/graph/specs/graph_specs.ts b/public/app/plugins/panel/graph/specs/graph_specs.ts index b9c9362e5de..b383a31a976 100644 --- a/public/app/plugins/panel/graph/specs/graph_specs.ts +++ b/public/app/plugins/panel/graph/specs/graph_specs.ts @@ -172,9 +172,9 @@ describe('grafanaGraph', function() { it('should apply axis transform and ticks', function() { var axis = ctx.plotOptions.yaxes[0]; - expect(axis.transform(100)).to.be(Math.log(100+0.1)); - expect(axis.ticks[0]).to.be(0); - expect(axis.ticks[1]).to.be(1); + expect(axis.transform(100)).to.be(Math.log(100)/Math.log(10)); + expect(axis.ticks[0]).to.be(0.01); + expect(axis.ticks[1]).to.be(0.1); }); }); From 29b9d17faa05bf804eb9667810a29ab94d089885 Mon Sep 17 00:00:00 2001 From: bigbenhur Date: Fri, 3 Jun 2016 22:30:38 +0200 Subject: [PATCH 2/3] fix crash due to zero or negative data values in graph with log scale --- public/app/core/time_series2.ts | 6 ++++++ public/app/plugins/panel/graph/graph.js | 7 ++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/public/app/core/time_series2.ts b/public/app/core/time_series2.ts index dfae26fb48b..d01fe1156b3 100644 --- a/public/app/core/time_series2.ts +++ b/public/app/core/time_series2.ts @@ -97,6 +97,7 @@ export default class TimeSeries { this.stats.total = 0; this.stats.max = -Number.MAX_VALUE; this.stats.min = Number.MAX_VALUE; + this.stats.logmin = Number.MAX_VALUE; this.stats.avg = null; this.stats.current = null; this.allIsNull = true; @@ -133,6 +134,11 @@ export default class TimeSeries { if (currentValue < this.stats.min) { this.stats.min = currentValue; } + + if (currentValue < this.stats.logmin && currentValue > 0) { + this.stats.logmin = currentValue; + } + } if (currentValue !== 0) { diff --git a/public/app/plugins/panel/graph/graph.js b/public/app/plugins/panel/graph/graph.js index 8019a51354b..00fec42cd6f 100755 --- a/public/app/plugins/panel/graph/graph.js +++ b/public/app/plugins/panel/graph/graph.js @@ -386,11 +386,12 @@ function (angular, $, moment, _, kbn, GraphTooltip) { if (max === null || max < series.stats.max) { max = series.stats.max; } - if (min === null || min > series.stats.min) { - min = series.stats.min; + if (min === null || min > series.stats.logmin) { + min = series.stats.logmin; } } } + if (max === null && min === null) { max = Math.pow(axis.logBase,+2); min = Math.pow(axis.logBase,-2); @@ -400,7 +401,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) { min = max*Math.pow(axis.logBase,-4); } - axis.transform = function(v) { return Math.log(v) / Math.log(axis.logBase); }; + axis.transform = function(v) { return (v < Number.MIN_VALUE) ? null : Math.log(v) / Math.log(axis.logBase); }; axis.inverseTransform = function (v) { return Math.pow(axis.logBase,v); }; min = axis.inverseTransform(Math.floor(axis.transform(min))); From ef623846c1d2d5e610d738bda49127616b5ed59d Mon Sep 17 00:00:00 2001 From: bigbenhur Date: Mon, 6 Jun 2016 10:33:35 +0200 Subject: [PATCH 3/3] further tests for log-autoscale and log-fixedscale; only generate visible ticks; minor changes to prevent crashes due to bad user input --- public/app/plugins/panel/graph/graph.js | 38 ++++++++++++------- .../plugins/panel/graph/specs/graph_specs.ts | 35 ++++++++++++++--- 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/public/app/plugins/panel/graph/graph.js b/public/app/plugins/panel/graph/graph.js index 00fec42cd6f..a74997d6651 100755 --- a/public/app/plugins/panel/graph/graph.js +++ b/public/app/plugins/panel/graph/graph.js @@ -376,6 +376,9 @@ function (angular, $, moment, _, kbn, GraphTooltip) { if (axis.min < Number.MIN_VALUE) { axis.min = null; } + if (axis.max < Number.MIN_VALUE) { + axis.max = null; + } var series, i; var max = axis.max, min = axis.min; @@ -392,23 +395,32 @@ function (angular, $, moment, _, kbn, GraphTooltip) { } } - if (max === null && min === null) { - max = Math.pow(axis.logBase,+2); - min = Math.pow(axis.logBase,-2); - } else if (max === null) { - max = min*Math.pow(axis.logBase,+4); - } else if (min === null) { - min = max*Math.pow(axis.logBase,-4); - } - axis.transform = function(v) { return (v < Number.MIN_VALUE) ? null : Math.log(v) / Math.log(axis.logBase); }; axis.inverseTransform = function (v) { return Math.pow(axis.logBase,v); }; - min = axis.inverseTransform(Math.floor(axis.transform(min))); - max = axis.inverseTransform(Math.ceil(axis.transform(max))); + if (max === null && min === null) { + max = axis.inverseTransform(+2); + min = axis.inverseTransform(-2); + } else if (max === null) { + max = min*axis.inverseTransform(+4); + } else if (min === null) { + min = max*axis.inverseTransform(-4); + } - axis.min = axis.min !== null ? axis.min : min; - axis.max = axis.max !== null ? axis.max : max; + if (axis.min !== null) { + min = axis.inverseTransform(Math.ceil(axis.transform(axis.min))); + } else { + min = axis.min = axis.inverseTransform(Math.floor(axis.transform(min))); + } + if (axis.max !== null) { + max = axis.inverseTransform(Math.floor(axis.transform(axis.max))); + } else { + max = axis.max = axis.inverseTransform(Math.ceil(axis.transform(max))); + } + + if (min < Number.MIN_VALUE || max < Number.MIN_VALUE) { + return; + } axis.ticks = []; var nextTick; diff --git a/public/app/plugins/panel/graph/specs/graph_specs.ts b/public/app/plugins/panel/graph/specs/graph_specs.ts index b383a31a976..339b14edb3d 100644 --- a/public/app/plugins/panel/graph/specs/graph_specs.ts +++ b/public/app/plugins/panel/graph/specs/graph_specs.ts @@ -166,15 +166,38 @@ describe('grafanaGraph', function() { }); graphScenario('when logBase is log 10', function(ctx) { - ctx.setup(function(ctrl) { + ctx.setup(function(ctrl, data) { ctrl.panel.yaxes[0].logBase = 10; + data[0] = new TimeSeries({ + datapoints: [[2000,1],[0.002,2],[0,3],[-1,4]], + alias: 'seriesAutoscale', + }); + data[0].yaxis = 1; + ctrl.panel.yaxes[1].logBase = 10; + ctrl.panel.yaxes[1].min = 0.05; + ctrl.panel.yaxes[1].max = 1500; + data[1] = new TimeSeries({ + datapoints: [[2000,1],[0.002,2],[0,3],[-1,4]], + alias: 'seriesFixedscale', + }); + data[1].yaxis = 2; }); - it('should apply axis transform and ticks', function() { - var axis = ctx.plotOptions.yaxes[0]; - expect(axis.transform(100)).to.be(Math.log(100)/Math.log(10)); - expect(axis.ticks[0]).to.be(0.01); - expect(axis.ticks[1]).to.be(0.1); + it('should apply axis transform, autoscaling (if necessary) and ticks', function() { + var axisAutoscale = ctx.plotOptions.yaxes[0]; + expect(axisAutoscale.transform(100)).to.be(2); + expect(axisAutoscale.inverseTransform(-3)).to.be(0.001); + expect(axisAutoscale.min).to.be(0.001); + expect(axisAutoscale.max).to.be(10000); + expect(axisAutoscale.ticks.length).to.be(8); + expect(axisAutoscale.ticks[0]).to.be(0.001); + expect(axisAutoscale.ticks[7]).to.be(10000); + var axisFixedscale = ctx.plotOptions.yaxes[1]; + expect(axisFixedscale.min).to.be(0.05); + expect(axisFixedscale.max).to.be(1500); + expect(axisFixedscale.ticks.length).to.be(5); + expect(axisFixedscale.ticks[0]).to.be(0.1); + expect(axisFixedscale.ticks[4]).to.be(1000); }); });