Skip to content

Commit 1d35b13

Browse files
authored
Merge pull request #1874 from colinl/alternative_yaxis_scaling
Proposed yaxis scaling algorithm for ui-chart
2 parents 6bfe379 + 8f1b260 commit 1d35b13

File tree

2 files changed

+44
-43
lines changed

2 files changed

+44
-43
lines changed

test/helpers/axis.helper.spec.js

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,22 @@ const axisHelper = require('../../ui/src/widgets/ui-chart/helpers/axis.helper.js
55
describe('axis.helper', function () {
66
describe('getAxisMinMax', function () {
77
const testCases = [
8-
{ min: 3.2, max: 7.8, expectedMin: 0, expectedMax: 10, description: 'values < 10' },
9-
{ min: 15.7, max: 23.4, expectedMin: 10, expectedMax: 30, description: 'values 10-99' },
8+
{ min: 3.2, max: 3.2, expectedMin: 3.2, expectedMax: 4.2, description: 'min === max' },
9+
{ min: 3.2, max: 7.8, expectedMin: 3, expectedMax: 8, description: 'values < 10' },
10+
{ min: 15.7, max: 23.4, expectedMin: 14, expectedMax: 24, description: 'values 10-99' },
1011
{ min: 47.3, max: 89.1, expectedMin: 40, expectedMax: 90, description: 'values 10-99' },
1112
{ min: 123.5, max: 456.7, expectedMin: 100, expectedMax: 500, description: 'values 100-999' },
1213
{ min: 234.8, max: 567.2, expectedMin: 200, expectedMax: 600, description: 'values 100-999' },
1314
{ min: 1234.5, max: 4567.8, expectedMin: 1000, expectedMax: 5000, description: 'values 1000-9999' },
1415
{ min: 2345.6, max: 5678.9, expectedMin: 2000, expectedMax: 6000, description: 'values 1000-9999' },
15-
{ min: 1234.5, max: 2345.6, expectedMin: 1000, expectedMax: 3000, description: 'values 1000-9999' },
16+
{ min: 1234.5, max: 2345.6, expectedMin: 1200, expectedMax: 2400, description: 'values 1000-9999' },
1617
{ min: 567.8, max: 1234.5, expectedMin: 500, expectedMax: 1300, description: 'mixed ranges' },
17-
{ min: 89.1, max: 1234.5, expectedMin: 0, expectedMax: 2000, description: 'mixed ranges' },
18-
{ min: 512.1, max: 526.5, expectedMin: 510, expectedMax: 530, description: 'close values in 100-999 range' },
19-
{ min: 412.1, max: 526.5, expectedMin: 400, expectedMax: 600, description: 'close values in 100-999 range' },
20-
{ min: -183, max: -165, expectedMin: -190, expectedMax: -160, description: 'negative values' },
21-
{ min: -0.3, max: 0, expectedMin: -1, expectedMax: 0, description: 'negative small values' },
22-
{ min: 0.000154, max: 0.000195, expectedMin: 0, expectedMax: 0.1, description: 'very small decimal values' }
18+
{ min: 89.1, max: 1234.5, expectedMin: 0, expectedMax: 1400, description: 'mixed ranges' },
19+
{ min: 512.1, max: 526.5, expectedMin: 512, expectedMax: 528, description: 'close values in 100-999 range' },
20+
{ min: 412.1, max: 526.5, expectedMin: 400, expectedMax: 540, description: 'close values in 100-999 range' },
21+
{ min: -183, max: -165, expectedMin: -185, expectedMax: -165, description: 'negative values' },
22+
{ min: -0.3, max: 0, expectedMin: -0.3, expectedMax: 0, description: 'negative small values' },
23+
{ min: 0.000154, max: 0.000195, expectedMin: 0.00015, expectedMax: 0.00020, description: 'very small decimal values' }
2324
]
2425

2526
testCases.forEach((testCase, index) => {
@@ -34,14 +35,14 @@ describe('axis.helper', function () {
3435
describe('getAxisMin', function () {
3536
it('should return only the min value', function () {
3637
const result = axisHelper.getAxisMin({ min: 15.7, max: 23.4 })
37-
result.should.equal(10)
38+
result.should.equal(14)
3839
})
3940
})
4041

4142
describe('getAxisMax', function () {
4243
it('should return only the max value', function () {
4344
const result = axisHelper.getAxisMax({ min: 15.7, max: 23.4 })
44-
result.should.equal(30)
45+
result.should.equal(24)
4546
})
4647
})
4748
})

ui/src/widgets/ui-chart/helpers/axis.helper.js

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,42 @@
1+
// Round x to a nice number, using factors of 1, 2, 5 or 10
2+
// and determine how many decimal places are needed to show it
3+
const niceNumber = (x) => {
4+
// find x as a number between 1 and 9.9999 with power of 10 multiplier
5+
// eg, 68.7 is 6.87 * 10^1 so exp is 1 and f is 6.8
6+
const exp = Math.floor(Math.log10(x))
7+
const f = x / Math.pow(10, exp)
8+
9+
// round f to 1, 2 5 or 10
10+
let niceFraction
11+
if (f < 1.5) niceFraction = 1
12+
else if (f < 3) niceFraction = 2
13+
else if (f < 7) niceFraction = 5
14+
else niceFraction = 10
15+
// and scale it up to the range of the input value so if x were 68.7 this returns 50
16+
const niceX = niceFraction * Math.pow(10, exp)
17+
// determine the number of decimal places necessary to represent this
18+
const decimals = Math.max(0, -exp)
19+
return [niceX, decimals]
20+
}
21+
122
const getAxisMinMax = (value) => {
223
const min = typeof value?.min === 'number' ? value?.min : 0
3-
const max = typeof value?.max === 'number' ? value?.max : 1
24+
let max = typeof value?.max === 'number' ? value?.max : 1
25+
// protect against min == max which is the case when only one point has been added
26+
max = (max === min) ? min + 1 : max
427

528
const range = max - min
629

7-
// Determine rounding unit based on magnitude
8-
const getRoundingUnit = (val) => {
9-
const absVal = Math.abs(val)
10-
if (absVal < 0.1) {
11-
return 0.1
12-
} else if (absVal < 1) {
13-
return 1
14-
} else if (absVal < 100) {
15-
return 10
16-
} else if (absVal < 1000) {
17-
return 100
18-
} else if (absVal < 10000) {
19-
return 1000
20-
} else if (absVal < 100000) {
21-
return 10000
22-
} else {
23-
// For values >= 100000, use powers of 10
24-
return Math.pow(10, Math.floor(Math.log10(absVal)))
25-
}
26-
}
30+
const targetTicks = 6 // assume 6 ticks on y axis (5 divisions)
31+
const roughStep = range / (targetTicks - 1)
2732

28-
// Round min down to nearest appropriate unit
29-
const roundingUnit = getRoundingUnit(range)
30-
let axisMin = 0
31-
if (roundingUnit > 0) {
32-
axisMin = Math.floor(min / roundingUnit) * roundingUnit
33-
}
33+
// round the step size to a nice number and determine how many decimal places
34+
// are needed to show it
35+
const [step, decimals] = niceNumber(roughStep)
3436

35-
// Round max up to nearest appropriate unit
36-
let axisMax = 10
37-
if (roundingUnit > 0) {
38-
axisMax = Math.ceil(max / roundingUnit) * roundingUnit
39-
}
37+
// round min down and max up using multiples of step
38+
const axisMin = Number((Math.floor(min / step) * step).toFixed(decimals))
39+
const axisMax = Number((Math.ceil(max / step) * step).toFixed(decimals))
4040

4141
return {
4242
min: axisMin,

0 commit comments

Comments
 (0)