From 9531c6594ec1d50565152660914f362999baabfb Mon Sep 17 00:00:00 2001 From: Thomas Lamprecht Date: Fri, 16 Apr 2021 21:53:45 +0200 Subject: [PATCH] rrd chart: fix y-axis segmentation when using powerOfTwo The chart axis get initialized really, so changing the segmenter in initComponent is not possible anymore, we can only alter the chart base config in the constructor. Luckily, the actual segmentation happens later, so we can pass a flag to the y-axis and hook into the segmenter directly by creating a new one derived from 'Ext.chart.axis.segmenter.Numeric'. There we override the preferStep and exactStep methods to decide if we want to calculate with base 10 or base 2. So add a constructor to RRDChart and set the axis with the respective segmenter, depending on the powerOfTwo config, up there initially. Note: that makes overwriting the axes from a caller impossible, but we do not use that anywhere, and we can control the more important parts of the axes, like label or units already otherwise, so seems not really required, and if, its not to hard to solve (either by always using our new segmenter by default and handle the different bases there directly, or by adding an explicit do not touch axes config flag, or the like). Signed-off-by: Thomas Lamprecht --- src/panel/RRDChart.js | 93 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 15 deletions(-) diff --git a/src/panel/RRDChart.js b/src/panel/RRDChart.js index 3cea635..41c839e 100644 --- a/src/panel/RRDChart.js +++ b/src/panel/RRDChart.js @@ -1,3 +1,58 @@ +Ext.define('Proxmox.chart.axis.segmenter.NumericBase2', { + extend: 'Ext.chart.axis.segmenter.Numeric', + alias: 'segmenter.numericBase2', + + // derived from the original numeric segmenter but using 2 instead of 10 as base + preferredStep: function(min, estStepSize) { + // Getting an order of magnitude of the estStepSize with a common logarithm. + let order = Math.floor(Math.log2(estStepSize)); + let scale = Math.pow(2, order); + + estStepSize /= scale; + + // FIXME: below is not useful when using base 2 instead of base 10, we could + // just directly set estStepSize to 2 + if (estStepSize <= 1) { + estStepSize = 1; + } else if (estStepSize < 2) { + estStepSize = 2; + } + return { + unit: { + // When passed estStepSize is less than 1, its order of magnitude + // is equal to -number_of_leading_zeros in the estStepSize. + fixes: -order, // Number of fractional digits. + scale: scale, + }, + step: estStepSize, + }; + }, + + /** + * Wraps the provided estimated step size of a range without altering it into a step size object. + * + * @param {*} min The start point of range. + * @param {*} estStepSize The estimated step size. + * @return {Object} Return the step size by an object of step x unit. + * @return {Number} return.step The step count of units. + * @return {Object} return.unit The unit. + */ + // derived from the original numeric segmenter but using 2 instead of 10 as base + exactStep: function(min, estStepSize) { + let order = Math.floor(Math.log2(estStepSize)); + let scale = Math.pow(2, order); + + return { + unit: { + // add one decimal point if estStepSize is not a multiple of scale + fixes: -order + (estStepSize % scale === 0 ? 0 : 1), + scale: 1, + }, + step: estStepSize, + }; + }, +}); + Ext.define('Proxmox.widget.RRDChart', { extend: 'Ext.chart.CartesianChart', alias: 'widget.proxmoxRRDChart', @@ -81,25 +136,33 @@ Ext.define('Proxmox.widget.RRDChart', { legend: { padding: 0, }, - axes: [ - { - type: 'numeric', - position: 'left', - grid: true, - renderer: 'leftAxisRenderer', - minimum: 0, - }, - { - type: 'time', - position: 'bottom', - grid: true, - fields: ['time'], - }, - ], listeners: { animationend: 'onAfterAnimation', }, + constructor: function(config) { + let me = this; + + let segmenter = config.powerOfTwo ? 'numericBase2' : 'numeric'; + config.axes = [ + { + type: 'numeric', + position: 'left', + grid: true, + renderer: 'leftAxisRenderer', + minimum: 0, + segmenter, + }, + { + type: 'time', + position: 'bottom', + grid: true, + fields: ['time'], + }, + ]; + me.callParent([config]); + }, + initComponent: function() { let me = this;