From 4ed9b7e4b14164cc0fcd6390808fdfb8c160e3fc Mon Sep 17 00:00:00 2001 From: "tommy.dugger" Date: Tue, 13 Dec 2016 16:54:52 -0700 Subject: [PATCH 1/5] Reworks Chart utils file to match mobile app calcs --- src/utils/Chart.js | 80 ++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/src/utils/Chart.js b/src/utils/Chart.js index 03d2930f8..cee9a59ee 100644 --- a/src/utils/Chart.js +++ b/src/utils/Chart.js @@ -1,44 +1,54 @@ -const d3 = require('d3'); - const Chart = { - getDataMinMaxValues (data, axis) { - let max = d3.max(data, d => { - return d[axis]; - }); - - let min = d3.min(data, d => { - return d[axis]; - }); - - const maxDigits = Math.ceil(Math.abs(max)).toString().length - 1; - const minDigits = Math.floor(Math.abs(min)).toString().length - 1; - - const maxMultiplier = Math.pow(10, maxDigits) < 100 ? 100 : Math.pow(10, maxDigits); - const minMultiplier = Math.pow(10, minDigits) < 100 ? 100 : Math.pow(10, maxDigits); - - max = Math.ceil(max / maxMultiplier) * maxMultiplier; - min = Math.floor(min / minMultiplier) * minMultiplier; - - return { min, max }; - }, - - getAxisTickValues (data, axis) { - const estimatedNumberOfTicks = 6; - const minMaxValues = this.getDataMinMaxValues(data, axis); - const range = minMaxValues.max - minMaxValues.min; - const tempStep = range / estimatedNumberOfTicks; - const magnitude = Math.floor(Math.log(tempStep) / Math.LN10); - const magnitudePower = Math.pow(10, magnitude); - const magnitudeMultiplier = parseInt(tempStep / magnitudePower + 0.5, 10); - const stepSize = magnitudeMultiplier * magnitudePower; + getAxisTickSpecification (min, max) { + // Needs to maintain parity with mobile app. + // https://git.moneydesktop.com/dev/moneymobilex/blob/master/Classes/utils/maths.cpp + // generate_tick_specification method + + let step = 0; + let numberOfTicks = 0; + let start = 0; + let end = 0; + + if (min < max) { + // Math.Log10 not supported in IE 10/11 so we have to use Math.log / Math.LN10 + step = Math.pow(10, Math.floor(Math.log(max - min) / Math.LN10)); + + if (step >= 100) { + const range = Math.ceil(max / step) * step - Math.floor(min / step) * step; + + numberOfTicks = range / step; + + if (numberOfTicks <= 3) { + step /= 2; + } else if (numberOfTicks >= 7) { + step *= 2; + } + } else { + step = 100; + } + + start = Math.floor(min / step) * step; + end = Math.ceil(max / step) * step; + numberOfTicks = Math.max((end - start) / step, 2); + } else { + if (min === max) { + start = Math.floor(min / 100) * 100; + } else { + start = 0; + } + + step = 50; + numberOfTicks = 2; + end = start + 100; + } const values = []; - for (let min = minMaxValues.min; min <= minMaxValues.max; min += stepSize) { - values.push(min); + for (let value = start; value <= end; value += step) { + values.push(value); } - return values; + return { min: start, max: end, tickValues: values }; } }; From 2e9346a2c5a5b59737d80015fee1c2868fb4d52e Mon Sep 17 00:00:00 2001 From: "tommy.dugger" Date: Tue, 13 Dec 2016 16:55:11 -0700 Subject: [PATCH 2/5] Updates TimeBasedLineChart to use updated ChartUtils --- src/components/TimeBasedLineChart.js | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/components/TimeBasedLineChart.js b/src/components/TimeBasedLineChart.js index 4db8c67d4..b7974bff7 100644 --- a/src/components/TimeBasedLineChart.js +++ b/src/components/TimeBasedLineChart.js @@ -272,9 +272,17 @@ const TimeBasedLineChart = React.createClass({ }, _yRangeContainsZero () { - const minMaxValues = ChartUtils.getDataMinMaxValues(this.props.data, 'y'); + const max = d3.max(this.props.data, d => { + return d.y; + }); + + const min = d3.min(this.props.data, d => { + return d.y; + }); + + const tickSpec = ChartUtils.getAxisTickSpecification(min, max); - return minMaxValues.min <= 0 && minMaxValues.max >= 0; + return tickSpec.min <= 0 && tickSpec.max >= 0; }, // Handle functions @@ -365,11 +373,19 @@ const TimeBasedLineChart = React.createClass({ }, _getYScaleFunction () { - const minMaxValues = ChartUtils.getDataMinMaxValues(this.props.data, 'y'); + const max = d3.max(this.props.data, d => { + return d.y; + }); + + const min = d3.min(this.props.data, d => { + return d.y; + }); + + const tickSpec = ChartUtils.getAxisTickSpecification(min, max); return d3.scale.linear() .range([this.state.adjustedHeight, 0]) - .domain([minMaxValues.min, minMaxValues.max]); + .domain([tickSpec.min, tickSpec.max]); }, _getYScaleValue (value) { From 6c919a63b61ceb3760477e644cda57c8cc0c8b92 Mon Sep 17 00:00:00 2001 From: "tommy.dugger" Date: Tue, 13 Dec 2016 16:55:33 -0700 Subject: [PATCH 3/5] Updates GridLinesGroup component to use updated ChartUtils --- src/components/d3/GridLinesGroup.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/d3/GridLinesGroup.js b/src/components/d3/GridLinesGroup.js index ef08f66ff..4cb6763bc 100644 --- a/src/components/d3/GridLinesGroup.js +++ b/src/components/d3/GridLinesGroup.js @@ -30,7 +30,15 @@ const GridLinesGroup = React.createClass({ }, _renderGridLines () { - const tickValues = ChartUtils.getAxisTickValues(this.props.data, this.props.axis); + const max = d3.max(this.props.data, d => { + return d[this.props.axis]; + }); + + const min = d3.min(this.props.data, d => { + return d[this.props.axis]; + }); + + const { tickValues } = ChartUtils.getAxisTickSpecification(min, max); const gridLinesFunction = d3.svg.axis() .scale(this.props.scaleFunction()) From d0e08d72393881da68fae48ee3436eff06542503 Mon Sep 17 00:00:00 2001 From: "tommy.dugger" Date: Tue, 13 Dec 2016 16:55:54 -0700 Subject: [PATCH 4/5] Updates AxisGroup to use updated ChartUtils --- src/components/d3/AxisGroup.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/d3/AxisGroup.js b/src/components/d3/AxisGroup.js index 2e746b4c7..651c9f0e2 100644 --- a/src/components/d3/AxisGroup.js +++ b/src/components/d3/AxisGroup.js @@ -28,7 +28,16 @@ const AxisGroup = React.createClass({ }, _renderAxis () { - const tickValues = ChartUtils.getAxisTickValues(this.props.data, this .props.axis); + const max = d3.max(this.props.data, d => { + return d[this.props.axis]; + }); + + const min = d3.min(this.props.data, d => { + return d[this.props.axis]; + }); + + const { tickValues } = ChartUtils.getAxisTickSpecification(min, max); + const axisFunction = d3.svg.axis() .scale(this.props.scaleFunction()) .orient(this.props.orientation) From 6bc36b71f2d90b5a3e731bb153780f223cf21092 Mon Sep 17 00:00:00 2001 From: "tommy.dugger" Date: Wed, 14 Dec 2016 10:36:40 -0700 Subject: [PATCH 5/5] Uses shorthand for getting min max values --- src/components/TimeBasedLineChart.js | 20 ++++---------------- src/components/d3/AxisGroup.js | 10 ++-------- src/components/d3/GridLinesGroup.js | 10 ++-------- 3 files changed, 8 insertions(+), 32 deletions(-) diff --git a/src/components/TimeBasedLineChart.js b/src/components/TimeBasedLineChart.js index b7974bff7..161d51c64 100644 --- a/src/components/TimeBasedLineChart.js +++ b/src/components/TimeBasedLineChart.js @@ -272,14 +272,8 @@ const TimeBasedLineChart = React.createClass({ }, _yRangeContainsZero () { - const max = d3.max(this.props.data, d => { - return d.y; - }); - - const min = d3.min(this.props.data, d => { - return d.y; - }); - + const max = d3.max(this.props.data, d => d.y); + const min = d3.min(this.props.data, d => d.y); const tickSpec = ChartUtils.getAxisTickSpecification(min, max); return tickSpec.min <= 0 && tickSpec.max >= 0; @@ -373,14 +367,8 @@ const TimeBasedLineChart = React.createClass({ }, _getYScaleFunction () { - const max = d3.max(this.props.data, d => { - return d.y; - }); - - const min = d3.min(this.props.data, d => { - return d.y; - }); - + const max = d3.max(this.props.data, d => d.y); + const min = d3.min(this.props.data, d => d.y); const tickSpec = ChartUtils.getAxisTickSpecification(min, max); return d3.scale.linear() diff --git a/src/components/d3/AxisGroup.js b/src/components/d3/AxisGroup.js index 651c9f0e2..7e37aab2d 100644 --- a/src/components/d3/AxisGroup.js +++ b/src/components/d3/AxisGroup.js @@ -28,14 +28,8 @@ const AxisGroup = React.createClass({ }, _renderAxis () { - const max = d3.max(this.props.data, d => { - return d[this.props.axis]; - }); - - const min = d3.min(this.props.data, d => { - return d[this.props.axis]; - }); - + const max = d3.max(this.props.data, d => d[this.props.axis]); + const min = d3.min(this.props.data, d => d[this.props.axis]); const { tickValues } = ChartUtils.getAxisTickSpecification(min, max); const axisFunction = d3.svg.axis() diff --git a/src/components/d3/GridLinesGroup.js b/src/components/d3/GridLinesGroup.js index 4cb6763bc..9e1fb2527 100644 --- a/src/components/d3/GridLinesGroup.js +++ b/src/components/d3/GridLinesGroup.js @@ -30,14 +30,8 @@ const GridLinesGroup = React.createClass({ }, _renderGridLines () { - const max = d3.max(this.props.data, d => { - return d[this.props.axis]; - }); - - const min = d3.min(this.props.data, d => { - return d[this.props.axis]; - }); - + const max = d3.max(this.props.data, d => d[this.props.axis]); + const min = d3.min(this.props.data, d => d[this.props.axis]); const { tickValues } = ChartUtils.getAxisTickSpecification(min, max); const gridLinesFunction = d3.svg.axis()