From ae66e10893469a5f08d3c2abefd31851a56ac6e6 Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 4 Dec 2015 13:56:04 -0500 Subject: [PATCH 01/11] remove unused ErrorBars.pushRef2GDC --- src/components/errorbars/index.js | 46 ------------------------------- 1 file changed, 46 deletions(-) diff --git a/src/components/errorbars/index.js b/src/components/errorbars/index.js index 6575e8ef84f..340fe0a6502 100644 --- a/src/components/errorbars/index.js +++ b/src/components/errorbars/index.js @@ -67,52 +67,6 @@ errorBars.supplyDefaults = function(traceIn, traceOut, defaultColor, opts) { } }; -errorBars.pushRef2GDC = function(gd, selCurve, astr, val){ - // Copy the error bar data into gdc - // This is called from the style-box, where - // either the reference trace was selected. - // This function copies the data from the referenced trace - // into the gdc object - // selCurve: the selected curve (i.e. gdc = gd[selCurve]) - // astr: the string that was modified - var iRef, - various = false, - parts = astr.split('.'), - container = parts[0], - attr = parts[1], - letter = container.charAt(container.length-1); - if(attr==='type'){ - if(selCurve==='various'){ - various = true; - selCurve = 0; - } - // if the 'trace' type was just selected - iRef = Number(gd.calcdata[Number(selCurve)][0].trace['error_' + letter].traceref)||0; - } - else if(attr==='traceref' || attr==='tracerefminus'){ - if(selCurve==='various') various = true; - // if the trace reference was just modified - iRef = Number(val)||0; - } - - // now copy the appropriate referenced error bar data into gdc - // TODO: do this through restyle so we can undo it - // the error bar data that we're referencing - var newdata = gd.data[iRef][letter].map(Number); - - function setarrays(i) { - var eb = gd.data[i][container]; - eb[attr==='tracerefminus' ? 'arrayminus' : 'array'] = newdata; - } - - if(!various) setarrays(Number(selCurve)); - else{ - // copy all of the data - // TODO: this won't work right if we just select some traces, right? - for(var i=0; i Date: Fri, 4 Dec 2015 13:58:16 -0500 Subject: [PATCH 02/11] break up default step for errorbar in separate file --- src/components/errorbars/defaults.js | 75 ++++++++++++++++++++++++++++ src/components/errorbars/index.js | 50 +------------------ 2 files changed, 76 insertions(+), 49 deletions(-) create mode 100644 src/components/errorbars/defaults.js diff --git a/src/components/errorbars/defaults.js b/src/components/errorbars/defaults.js new file mode 100644 index 00000000000..cf5a48a322d --- /dev/null +++ b/src/components/errorbars/defaults.js @@ -0,0 +1,75 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var isNumeric = require('fast-isnumeric'); + +var Plots = require('../../plots/plots'); +var Lib = require('../../lib'); + +var attributes = require('./attributes'); + + +module.exports = function(traceIn, traceOut, defaultColor, opts) { + var objName = 'error_' + opts.axis, + containerOut = traceOut[objName] = {}, + containerIn = traceIn[objName] || {}; + + function coerce (attr, dflt) { + return Lib.coerce(containerIn, containerOut, attributes, attr, dflt); + } + + var hasErrorBars = ( + containerIn.array !== undefined || + containerIn.value !== undefined || + containerIn.type === 'sqrt' + ); + + var visible = coerce('visible', hasErrorBars); + + if(visible === false) return; + + var type = coerce('type', 'array' in containerIn ? 'data' : 'percent'), + symmetric = true; + + if(type !== 'sqrt') { + symmetric = coerce('symmetric', + !((type === 'data' ? 'arrayminus' : 'valueminus') in containerIn)); + } + + if(type === 'data') { + var array = coerce('array'); + if(!array) containerOut.array = []; + coerce('traceref'); + if(!symmetric) { + var arrayminus = coerce('arrayminus'); + if(!arrayminus) containerOut.arrayminus = []; + coerce('tracerefminus'); + } + } + else if(type==='percent' || type==='constant') { + coerce('value'); + if(!symmetric) coerce('valueminus'); + } + + var copyAttr = 'copy_'+opts.inherit+'style'; + if(opts.inherit) { + var inheritObj = traceOut['error_' + opts.inherit]; + if((inheritObj||{}).visible) { + coerce(copyAttr, !(containerIn.color || + isNumeric(containerIn.thickness) || + isNumeric(containerIn.width))); + } + } + if(!opts.inherit || !containerOut[copyAttr]) { + coerce('color', defaultColor); + coerce('thickness'); + coerce('width', Plots.traceIs(traceOut, 'gl3d') ? 0 : 4); + } +}; diff --git a/src/components/errorbars/index.js b/src/components/errorbars/index.js index 340fe0a6502..b1fc31484cd 100644 --- a/src/components/errorbars/index.js +++ b/src/components/errorbars/index.js @@ -17,55 +17,7 @@ var errorBars = module.exports = {}; errorBars.attributes = require('./attributes'); -errorBars.supplyDefaults = function(traceIn, traceOut, defaultColor, opts) { - var objName = 'error_' + opts.axis, - containerOut = traceOut[objName] = {}, - containerIn = traceIn[objName] || {}; - - function coerce (attr, dflt) { - return Plotly.Lib.coerce(containerIn, containerOut, errorBars.attributes, attr, dflt); - } - - var visible = coerce('visible', 'array' in containerIn || 'value' in containerIn); - if(visible) { - var type = coerce('type', 'array' in containerIn ? 'data' : 'percent'), - symmetric = true; - if(type!=='sqrt') { - symmetric = coerce('symmetric', - !((type==='data' ? 'arrayminus' : 'valueminus') in containerIn)); - } - - if(type==='data') { - var array = coerce('array'); - if(!array) containerOut.array = []; - coerce('traceref'); - if(!symmetric) { - var arrayminus = coerce('arrayminus'); - if(!arrayminus) containerOut.arrayminus = []; - coerce('tracerefminus'); - } - } - else if(type==='percent' || type==='constant') { - coerce('value'); - if(!symmetric) coerce('valueminus'); - } - - var copyAttr = 'copy_'+opts.inherit+'style'; - if(opts.inherit) { - var inheritObj = traceOut['error_' + opts.inherit]; - if((inheritObj||{}).visible) { - coerce(copyAttr, !(containerIn.color || - isNumeric(containerIn.thickness) || - isNumeric(containerIn.width))); - } - } - if(!opts.inherit || !containerOut[copyAttr]) { - coerce('color', defaultColor); - coerce('thickness'); - coerce('width', Plotly.Plots.traceIs(traceOut, 'gl3d') ? 0 : 4); - } - } -}; +errorBars.supplyDefaults = require('./defaults'); // size the error bar itself (for all types except data) function errorval(type, dataval, errval) { From 77ab00382782f8ec664a11fc74e9a578da218296 Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 4 Dec 2015 14:14:37 -0500 Subject: [PATCH 03/11] add image tests for sqrt errorbars --- test/image/baselines/error_bar_sqrt.png | Bin 0 -> 21521 bytes test/image/baselines/gl3d_errorbars_sqrt.png | Bin 0 -> 27432 bytes test/image/mocks/error_bar_sqrt.json | 22 ++++++++ test/image/mocks/gl3d_errorbars_sqrt.json | 56 +++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 test/image/baselines/error_bar_sqrt.png create mode 100644 test/image/baselines/gl3d_errorbars_sqrt.png create mode 100644 test/image/mocks/error_bar_sqrt.json create mode 100644 test/image/mocks/gl3d_errorbars_sqrt.json diff --git a/test/image/baselines/error_bar_sqrt.png b/test/image/baselines/error_bar_sqrt.png new file mode 100644 index 0000000000000000000000000000000000000000..1be589a31cc1ade1d4af13062775afaaee71ee6d GIT binary patch literal 21521 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 N({j)D^leAO{tts~#RLEV literal 0 HcmV?d00001 diff --git a/test/image/baselines/gl3d_errorbars_sqrt.png b/test/image/baselines/gl3d_errorbars_sqrt.png new file mode 100644 index 0000000000000000000000000000000000000000..95cbf64eab44e62089881a4be884c5beb60d7743 GIT binary patch literal 27432 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 Date: Fri, 4 Dec 2015 18:10:44 -0500 Subject: [PATCH 04/11] split error bar calc step into calc.js and compute_error.js : - compute_error is the reusable piece of logic that gl3d and gl2d error bars will use. --- src/components/errorbars/calc.js | 66 +++++++++++++++++++++++ src/components/errorbars/compute_error.js | 53 ++++++++++++++++++ src/components/errorbars/index.js | 64 +--------------------- 3 files changed, 120 insertions(+), 63 deletions(-) create mode 100644 src/components/errorbars/calc.js create mode 100644 src/components/errorbars/compute_error.js diff --git a/src/components/errorbars/calc.js b/src/components/errorbars/calc.js new file mode 100644 index 00000000000..627b63d95fd --- /dev/null +++ b/src/components/errorbars/calc.js @@ -0,0 +1,66 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var isNumeric = require('fast-isnumeric'); + +var Plots = require('../../plots/plots'); +var Axes = require('../../plots/cartesian/axes'); + +var compureError = require('./compute_error'); + + +module.exports = function calc(gd) { + var calcdata = gd.calcdata; + + for(var i = 0; i < calcdata.length; i++) { + var calcTrace = calcdata[i], + trace = calcTrace[0].trace; + + if(!Plots.traceIs(trace, 'errorBarsOK')) continue; + + var xObj = trace.error_x || {}, + yObj = trace.error_y || {}, + xa = Axes.getFromId(gd, trace.xaxis), + ya = Axes.getFromId(gd, trace.yaxis), + xVis = xObj.visible && ['linear', 'log'].indexOf(xa.type)!==-1, + yVis = yObj.visible && ['linear', 'log'].indexOf(ya.type)!==-1; + + if(!xVis && !yVis) continue; + + var xVals = [], + yVals = []; + + for(var j = 0; j < calcTrace.length; j++) { + var calcPt = calcTrace[j], + calcX = calcPt.x, + calcY = calcPt.y; + + if(!isNumeric(ya.c2l(calcY)) || !isNumeric(xa.c2l(calcX))) continue; + + var errorY = compureError(calcY, j, yObj); + if(isNumeric(errorY[0]) && isNumeric(errorY[1])) { + calcPt.ys = calcY - errorY[0]; + calcPt.yh = calcY + errorY[1]; + yVals.push(calcPt.ys, calcPt.yh); + } + + var errorX = compureError(calcX, j, xObj); + if(isNumeric(errorX[0]) && isNumeric(errorX[1])) { + calcPt.xs = calcX - errorX[0]; + calcPt.xh = calcX + errorX[1]; + xVals.push(calcPt.xs, calcPt.xh); + } + } + + Axes.expand(ya, yVals, {padded: true}); + Axes.expand(xa, xVals, {padded: true}); + } +}; diff --git a/src/components/errorbars/compute_error.js b/src/components/errorbars/compute_error.js new file mode 100644 index 00000000000..c2ae372b4cb --- /dev/null +++ b/src/components/errorbars/compute_error.js @@ -0,0 +1,53 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + + +/** + * Compute the positive and negative error bar magnitudes. + * + * N.B. This function does not clean the dataPt entries, non-numeric + * entries result in undefined *error* + * + * @param {numeric} dataPt data point from where to compute the error magnitue + * @param {number} index index of dataPt in its corresponding data array + * @param {object} opts error bar attributes + * + * @return {array of two numbers} + * - error[0] : error magnitude in the negative direction + * - error[1] : " " " " positive " + */ +module.exports = function computeError(dataPt, index, opts) { + var type = opts.type, + error = new Array(2); + + if(type === 'data') { + error[1] = +(opts.array[index]); + error[0] = (opts.symmetric || opts.arrayminus === undefined) ? + error[1] : + +(opts.arrayminus[index]); + } + else { + error[1] = getErrorVal(type, dataPt, opts.value); + error[0] = (opts.symmetric || opts.valueminus === undefined) ? + error[1] : + getErrorVal(type, dataPt, opts.valueminus); + } + + + return error; +}; + +// size the error bar itself (for all types except data) +function getErrorVal(type, dataPt, value) { + if(type === 'percent') return Math.abs(dataPt * value / 100); + if(type === 'constant') return Math.abs(value); + if(type === 'sqrt') return Math.sqrt(Math.abs(dataPt)); +} diff --git a/src/components/errorbars/index.js b/src/components/errorbars/index.js index b1fc31484cd..b04bfaad4a9 100644 --- a/src/components/errorbars/index.js +++ b/src/components/errorbars/index.js @@ -19,69 +19,7 @@ errorBars.attributes = require('./attributes'); errorBars.supplyDefaults = require('./defaults'); -// size the error bar itself (for all types except data) -function errorval(type, dataval, errval) { - if(type === 'percent') return Math.abs(dataval * errval / 100); - if(type === 'constant') return Math.abs(errval); - if(type === 'sqrt') return Math.sqrt(Math.abs(dataval)); - - return 0; -} - -errorBars.calc = function(gd) { - (gd.calcdata||[]).forEach(function(cdi){ - var trace = cdi[0].trace; - - if(!Plotly.Plots.traceIs(trace, 'errorBarsOK')) return; - - var xObj = trace.error_x || {}, - yObj = trace.error_y || {}, - xa = Plotly.Axes.getFromId(gd, trace.xaxis), - ya = Plotly.Axes.getFromId(gd, trace.yaxis), - xvis = xObj.visible && ['linear', 'log'].indexOf(xa.type)!==-1, - yvis = yObj.visible && ['linear', 'log'].indexOf(ya.type)!==-1; - - if(!xvis && !yvis) return; - - var xvals = [], - yvals = []; - - cdi.forEach(function(d,j) { - try { - if(isNumeric(ya.c2l(d.y)) && isNumeric(xa.c2l(d.x))){ - [ - {letter:'x', obj: xObj, visible: xvis, vals: xvals}, - {letter:'y', obj: yObj, visible: yvis, vals: yvals} - ].forEach(function(o){ - if(o.visible) { - var dataVal = d[o.letter], - obj = o.obj, - ep, en; - if(o.obj.type==='data') { - ep = Number(obj.array[j]); - en = (obj.symmetric || !('arrayminus' in obj)) ? - ep : Number(obj.arrayminus[j]); - } - else { - ep = errorval(obj.type, dataVal, obj.value); - en = (obj.symmetric || !('valueminus' in obj)) ? - ep : errorval(obj.type, dataVal, obj.valueminus); - } - if(isNumeric(ep) && isNumeric(en)) { - var shoe = d[o.letter + 'h'] = dataVal + ep; - var hat = d[o.letter + 's'] = dataVal - en; - o.vals.push(shoe, hat); - } - } - }); - } - } - catch(e) { console.log(e); } - }); - Plotly.Axes.expand(ya, yvals, {padded: true}); - Plotly.Axes.expand(xa, xvals, {padded: true}); - }); -}; +errorBars.calc = require('./calc'); errorBars.calcFromTrace = function(trace, layout) { var x = trace.x || [], From 460ee0c34e45cbdfc1a5fa0fc557b4a30998510a Mon Sep 17 00:00:00 2001 From: etpinard Date: Tue, 8 Dec 2015 14:41:30 -0500 Subject: [PATCH 05/11] use computeError in gl3d error bar convert step --- src/traces/scatter3d/calc_errors.js | 58 +++++------------------------ src/traces/scatter3d/convert.js | 1 - 2 files changed, 10 insertions(+), 49 deletions(-) diff --git a/src/traces/scatter3d/calc_errors.js b/src/traces/scatter3d/calc_errors.js index 6f193fb40a9..26bf127b94a 100644 --- a/src/traces/scatter3d/calc_errors.js +++ b/src/traces/scatter3d/calc_errors.js @@ -9,59 +9,21 @@ 'use strict'; -function calculateAxisErrors(data, params, scaleFactor) { - if(!params || !params.visible) return null; +var computeError = require('../../components/errorbars/compute_error'); - function option(name, value) { - if(name in params) return params[name]; - return value; - } - var result = new Array(data.length), - type = option('type', 'percent'), - symmetric = option('symmetric', true), - value = +option('value', 10), - minusValue = +option('valueminus', 10), - error = option('array', null), - minusError = option('arrayminus', null); - - if(symmetric) { - minusValue = value; - minusError = error; - } +function calculateAxisErrors(data, params, scaleFactor) { + if(!params || !params.visible) return null; - if(type === 'data' && (!error || !minusError)) return null; + var result = new Array(data.length); for(var i = 0; i < data.length; i++) { - var x = +data[i]; - - switch(type) { - case 'percent': - result[i] = [ - -Math.abs(x) * (minusValue / 100.0) * scaleFactor, - Math.abs(x) * (value / 100.0) * scaleFactor - ]; - break; - - case 'constant': - result[i] = [ - -minusValue * scaleFactor, - value * scaleFactor - ]; - break; - - case 'sqrt': - var r = Math.sqrt(Math.abs(x)) * scaleFactor; - result[i] = [-r, r]; - break; - - case 'data': - result[i] = [ - -(+minusError[i]) * scaleFactor, - (+error[i]) * scaleFactor - ]; - break; - } + var errors = computeError(+data[i], i, params); + + result[i] = [ + -errors[0] * scaleFactor, + errors[1] * scaleFactor + ]; } return result; diff --git a/src/traces/scatter3d/convert.js b/src/traces/scatter3d/convert.js index e6dea9a74b9..6bd61f5d8ad 100644 --- a/src/traces/scatter3d/convert.js +++ b/src/traces/scatter3d/convert.js @@ -23,7 +23,6 @@ var formatColor = require('../../lib/gl_format_color'); var DASH_PATTERNS = require('../../constants/gl3d_dashes.json'); var MARKER_SYMBOLS = require('../../constants/gl_markers.json'); -// TODO use ErrorBars.calcFromTrace instead var calculateError = require('./calc_errors'); function LineWithMarkers(scene, uid) { From b66b6200696adbc8a8e59a6b108940c00d3cd32c Mon Sep 17 00:00:00 2001 From: etpinard Date: Tue, 8 Dec 2015 16:08:10 -0500 Subject: [PATCH 06/11] lint --- src/components/errorbars/calc.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/errorbars/calc.js b/src/components/errorbars/calc.js index 627b63d95fd..a8dfec63842 100644 --- a/src/components/errorbars/calc.js +++ b/src/components/errorbars/calc.js @@ -14,7 +14,7 @@ var isNumeric = require('fast-isnumeric'); var Plots = require('../../plots/plots'); var Axes = require('../../plots/cartesian/axes'); -var compureError = require('./compute_error'); +var computeError = require('./compute_error'); module.exports = function calc(gd) { @@ -30,8 +30,8 @@ module.exports = function calc(gd) { yObj = trace.error_y || {}, xa = Axes.getFromId(gd, trace.xaxis), ya = Axes.getFromId(gd, trace.yaxis), - xVis = xObj.visible && ['linear', 'log'].indexOf(xa.type)!==-1, - yVis = yObj.visible && ['linear', 'log'].indexOf(ya.type)!==-1; + xVis = (xObj.visible && ['linear', 'log'].indexOf(xa.type) !== -1), + yVis = (yObj.visible && ['linear', 'log'].indexOf(ya.type) !== -1); if(!xVis && !yVis) continue; @@ -45,14 +45,14 @@ module.exports = function calc(gd) { if(!isNumeric(ya.c2l(calcY)) || !isNumeric(xa.c2l(calcX))) continue; - var errorY = compureError(calcY, j, yObj); + var errorY = computeError(calcY, j, yObj); if(isNumeric(errorY[0]) && isNumeric(errorY[1])) { calcPt.ys = calcY - errorY[0]; calcPt.yh = calcY + errorY[1]; yVals.push(calcPt.ys, calcPt.yh); } - var errorX = compureError(calcX, j, xObj); + var errorX = computeError(calcX, j, xObj); if(isNumeric(errorX[0]) && isNumeric(errorX[1])) { calcPt.xs = calcX - errorX[0]; calcPt.xh = calcX + errorX[1]; From 3c219183633a6414f47e958bc6b66e879fb76708 Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 9 Dec 2015 12:45:30 -0500 Subject: [PATCH 07/11] rm require.resolve from image viewer, - browserify doesn't like them --- devtools/image_viewer/viewer.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/devtools/image_viewer/viewer.js b/devtools/image_viewer/viewer.js index 3a38a5cf36f..5b1d8e4fbca 100644 --- a/devtools/image_viewer/viewer.js +++ b/devtools/image_viewer/viewer.js @@ -1,18 +1,19 @@ var fs = require('fs'); +var path = require('path'); var d3 = require('d3'); -var constants = require('../../tasks/util/constants'); - - var $plotlist = document.getElementById('plot-list'), $images = document.getElementById('plot-images'), $mock = document.getElementById('plot-mock'); -var dirBaseline = constants.pathToTestImageBaselines, - dirTest = constants.pathToTestImages, - dirDiff = constants.pathToTestImagesDiff, - dirMocks = constants.pathToTestImageMocks; +var pathToRoot = path.join(__dirname, '../../'), + pathToImageTest = path.join(pathToRoot, 'test/image'), + pathToBuild = path.join(pathToRoot, 'build/'), + dirMocks = path.join(pathToImageTest, 'mocks/'), + dirBaseline = path.join(pathToImageTest, 'baselines/'), + dirTest = path.join(pathToBuild, 'test_images/'), + dirDiff = path.join(pathToBuild, 'test_images_diff/'); // N.B. brfs only understand hard-coded paths var imageNames = fs.readFileSync( From 007b8d142f0e4f743eb07c2a61ac28c307afe7fe Mon Sep 17 00:00:00 2001 From: etpinard Date: Wed, 9 Dec 2015 12:47:30 -0500 Subject: [PATCH 08/11] make compute_error export a compute-error generating function, - so that the opts object is destructure only once once per trace instead of on every data pt. --- src/components/errorbars/calc.js | 21 ++++---- src/components/errorbars/compute_error.js | 63 ++++++++++++++++------- src/traces/scatter3d/calc_errors.js | 5 +- 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/src/components/errorbars/calc.js b/src/components/errorbars/calc.js index a8dfec63842..6851df8ac4e 100644 --- a/src/components/errorbars/calc.js +++ b/src/components/errorbars/calc.js @@ -14,7 +14,7 @@ var isNumeric = require('fast-isnumeric'); var Plots = require('../../plots/plots'); var Axes = require('../../plots/cartesian/axes'); -var computeError = require('./compute_error'); +var makeComputeError = require('./compute_error'); module.exports = function calc(gd) { @@ -26,33 +26,36 @@ module.exports = function calc(gd) { if(!Plots.traceIs(trace, 'errorBarsOK')) continue; - var xObj = trace.error_x || {}, - yObj = trace.error_y || {}, + var xOpts = trace.error_x || {}, + yOpts = trace.error_y || {}, xa = Axes.getFromId(gd, trace.xaxis), ya = Axes.getFromId(gd, trace.yaxis), - xVis = (xObj.visible && ['linear', 'log'].indexOf(xa.type) !== -1), - yVis = (yObj.visible && ['linear', 'log'].indexOf(ya.type) !== -1); + xVis = (xOpts.visible && ['linear', 'log'].indexOf(xa.type) !== -1), + yVis = (yOpts.visible && ['linear', 'log'].indexOf(ya.type) !== -1); if(!xVis && !yVis) continue; var xVals = [], yVals = []; + var computeErrorY = makeComputeError(yOpts), + computeErrorX = makeComputeError(xOpts); + for(var j = 0; j < calcTrace.length; j++) { var calcPt = calcTrace[j], - calcX = calcPt.x, - calcY = calcPt.y; + calcY = calcPt.y, + calcX = calcPt.x; if(!isNumeric(ya.c2l(calcY)) || !isNumeric(xa.c2l(calcX))) continue; - var errorY = computeError(calcY, j, yObj); + var errorY = computeErrorY(calcY, j); if(isNumeric(errorY[0]) && isNumeric(errorY[1])) { calcPt.ys = calcY - errorY[0]; calcPt.yh = calcY + errorY[1]; yVals.push(calcPt.ys, calcPt.yh); } - var errorX = computeError(calcX, j, xObj); + var errorX = computeErrorX(calcX, j); if(isNumeric(errorX[0]) && isNumeric(errorX[1])) { calcPt.xs = calcX - errorX[0]; calcPt.xh = calcX + errorX[1]; diff --git a/src/components/errorbars/compute_error.js b/src/components/errorbars/compute_error.js index c2ae372b4cb..ed4fd1edde4 100644 --- a/src/components/errorbars/compute_error.js +++ b/src/components/errorbars/compute_error.js @@ -11,41 +11,64 @@ /** - * Compute the positive and negative error bar magnitudes. + * Error bar computing function generator * * N.B. This function does not clean the dataPt entries, non-numeric * entries result in undefined *error* * - * @param {numeric} dataPt data point from where to compute the error magnitue - * @param {number} index index of dataPt in its corresponding data array * @param {object} opts error bar attributes * - * @return {array of two numbers} - * - error[0] : error magnitude in the negative direction - * - error[1] : " " " " positive " + * @return {function} : + * @param {numeric} dataVal error magnitude in the negative direction + * @param {number} index index of dataPt in its corresponding data array + * @return {array} + * - error[0] : error magnitude in the negative direction + * - error[1] : " " " " positive " */ -module.exports = function computeError(dataPt, index, opts) { +module.exports = function makeComputeError(opts) { var type = opts.type, - error = new Array(2); + symmetric = opts.symmetric; if(type === 'data') { - error[1] = +(opts.array[index]); - error[0] = (opts.symmetric || opts.arrayminus === undefined) ? - error[1] : - +(opts.arrayminus[index]); + var array = opts.array, + arrayminus = opts.arrayminus; + + return (symmetric || arrayminus === undefined) ? + function computeError(dataPt, index) { + var val = +(array[index]); + return [val, val]; + } : + function computeError(dataPt, index) { + return [+arrayminus[index], +array[index]]; + }; } else { - error[1] = getErrorVal(type, dataPt, opts.value); - error[0] = (opts.symmetric || opts.valueminus === undefined) ? - error[1] : - getErrorVal(type, dataPt, opts.valueminus); - } + var value = opts.value, + valueminus = opts.valueminus; - - return error; + return (symmetric || valueminus === undefined) ? + function computeError(dataPt) { + var val = getErrorVal(type, dataPt, value); + return [val, val]; + } : + function computeError(dataPt) { + return [ + getErrorVal(type, dataPt, valueminus), + getErrorVal(type, dataPt, value) + ]; + }; + } }; -// size the error bar itself (for all types except data) +/** + * Compute error bar magnitude (for all types except data) + * + * @param {string} type error bar type + * @param {numeric} dataPt + * data point from where to compute the error magnitude + * @param {numeric} [value] error bar value + * + */ function getErrorVal(type, dataPt, value) { if(type === 'percent') return Math.abs(dataPt * value / 100); if(type === 'constant') return Math.abs(value); diff --git a/src/traces/scatter3d/calc_errors.js b/src/traces/scatter3d/calc_errors.js index 26bf127b94a..f16221fc2de 100644 --- a/src/traces/scatter3d/calc_errors.js +++ b/src/traces/scatter3d/calc_errors.js @@ -9,16 +9,17 @@ 'use strict'; -var computeError = require('../../components/errorbars/compute_error'); +var makeComputeError = require('../../components/errorbars/compute_error'); function calculateAxisErrors(data, params, scaleFactor) { if(!params || !params.visible) return null; + var computeError = makeComputeError(params); var result = new Array(data.length); for(var i = 0; i < data.length; i++) { - var errors = computeError(+data[i], i, params); + var errors = computeError(+data[i], i); result[i] = [ -errors[0] * scaleFactor, From 2d8ed7f700e50c6cc1a10908bc8d554d6f4b641f Mon Sep 17 00:00:00 2001 From: etpinard Date: Thu, 10 Dec 2015 10:10:07 -0500 Subject: [PATCH 09/11] make makeComputeError more readable --- src/components/errorbars/compute_error.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/components/errorbars/compute_error.js b/src/components/errorbars/compute_error.js index ed4fd1edde4..42ceed2f623 100644 --- a/src/components/errorbars/compute_error.js +++ b/src/components/errorbars/compute_error.js @@ -33,30 +33,36 @@ module.exports = function makeComputeError(opts) { var array = opts.array, arrayminus = opts.arrayminus; - return (symmetric || arrayminus === undefined) ? - function computeError(dataPt, index) { + if(symmetric || arrayminus === undefined) { + return function computeError(dataPt, index) { var val = +(array[index]); return [val, val]; - } : - function computeError(dataPt, index) { + }; + } + else { + return function computeError(dataPt, index) { return [+arrayminus[index], +array[index]]; }; + } } else { var value = opts.value, valueminus = opts.valueminus; - return (symmetric || valueminus === undefined) ? - function computeError(dataPt) { + if(symmetric || valueminus === undefined) { + return function computeError(dataPt) { var val = getErrorVal(type, dataPt, value); return [val, val]; - } : - function computeError(dataPt) { + }; + } + else { + return function computeError(dataPt) { return [ getErrorVal(type, dataPt, valueminus), getErrorVal(type, dataPt, value) ]; }; + } } }; From 9d5f143fbbcf16d6338505ead047ff2b3b039713 Mon Sep 17 00:00:00 2001 From: etpinard Date: Thu, 10 Dec 2015 13:48:07 -0500 Subject: [PATCH 10/11] generate compute-error-value function once per trace. --- src/components/errorbars/compute_error.js | 44 ++++++++++++++--------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/components/errorbars/compute_error.js b/src/components/errorbars/compute_error.js index 42ceed2f623..601bb76f781 100644 --- a/src/components/errorbars/compute_error.js +++ b/src/components/errorbars/compute_error.js @@ -13,13 +13,13 @@ /** * Error bar computing function generator * - * N.B. This function does not clean the dataPt entries, non-numeric - * entries result in undefined *error* + * N.B. The generated function does not clean the dataPt entries. Non-numeric + * entries result in undefined error magnitudes. * * @param {object} opts error bar attributes * * @return {function} : - * @param {numeric} dataVal error magnitude in the negative direction + * @param {numeric} dataPt data point from where to compute the error magnitude * @param {number} index index of dataPt in its corresponding data array * @return {array} * - error[0] : error magnitude in the negative direction @@ -46,20 +46,20 @@ module.exports = function makeComputeError(opts) { } } else { - var value = opts.value, - valueminus = opts.valueminus; + var computeErrorValue = makeComputeErrorValue(type, opts.value), + computeErrorValueMinus = makeComputeErrorValue(type, opts.valueminus); - if(symmetric || valueminus === undefined) { + if(symmetric || opts.valueminus === undefined) { return function computeError(dataPt) { - var val = getErrorVal(type, dataPt, value); + var val = computeErrorValue(dataPt); return [val, val]; }; } else { return function computeError(dataPt) { return [ - getErrorVal(type, dataPt, valueminus), - getErrorVal(type, dataPt, value) + computeErrorValueMinus(dataPt), + computeErrorValue(dataPt) ]; }; } @@ -70,13 +70,25 @@ module.exports = function makeComputeError(opts) { * Compute error bar magnitude (for all types except data) * * @param {string} type error bar type - * @param {numeric} dataPt - * data point from where to compute the error magnitude - * @param {numeric} [value] error bar value + * @param {numeric} value error bar value * + * @return {function} : + * @param {numeric} dataPt */ -function getErrorVal(type, dataPt, value) { - if(type === 'percent') return Math.abs(dataPt * value / 100); - if(type === 'constant') return Math.abs(value); - if(type === 'sqrt') return Math.sqrt(Math.abs(dataPt)); +function makeComputeErrorValue(type, value) { + if(type === 'percent') { + return function(dataPt) { + return Math.abs(dataPt * value / 100); + }; + } + if(type === 'constant') { + return function() { + return Math.abs(value); + }; + } + if(type === 'sqrt') { + return function(dataPt) { + return Math.sqrt(Math.abs(dataPt)); + }; + } } From 66ce3fa2d42584585f257a31863d3623209af1c7 Mon Sep 17 00:00:00 2001 From: etpinard Date: Thu, 10 Dec 2015 13:48:31 -0500 Subject: [PATCH 11/11] split error bar calc step into a per-axis function --- src/components/errorbars/calc.js | 68 ++++++++++++++------------------ 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/src/components/errorbars/calc.js b/src/components/errorbars/calc.js index 6851df8ac4e..5f732c20e8f 100644 --- a/src/components/errorbars/calc.js +++ b/src/components/errorbars/calc.js @@ -26,44 +26,36 @@ module.exports = function calc(gd) { if(!Plots.traceIs(trace, 'errorBarsOK')) continue; - var xOpts = trace.error_x || {}, - yOpts = trace.error_y || {}, - xa = Axes.getFromId(gd, trace.xaxis), - ya = Axes.getFromId(gd, trace.yaxis), - xVis = (xOpts.visible && ['linear', 'log'].indexOf(xa.type) !== -1), - yVis = (yOpts.visible && ['linear', 'log'].indexOf(ya.type) !== -1); - - if(!xVis && !yVis) continue; - - var xVals = [], - yVals = []; - - var computeErrorY = makeComputeError(yOpts), - computeErrorX = makeComputeError(xOpts); - - for(var j = 0; j < calcTrace.length; j++) { - var calcPt = calcTrace[j], - calcY = calcPt.y, - calcX = calcPt.x; - - if(!isNumeric(ya.c2l(calcY)) || !isNumeric(xa.c2l(calcX))) continue; - - var errorY = computeErrorY(calcY, j); - if(isNumeric(errorY[0]) && isNumeric(errorY[1])) { - calcPt.ys = calcY - errorY[0]; - calcPt.yh = calcY + errorY[1]; - yVals.push(calcPt.ys, calcPt.yh); - } - - var errorX = computeErrorX(calcX, j); - if(isNumeric(errorX[0]) && isNumeric(errorX[1])) { - calcPt.xs = calcX - errorX[0]; - calcPt.xh = calcX + errorX[1]; - xVals.push(calcPt.xs, calcPt.xh); - } - } + var xa = Axes.getFromId(gd, trace.xaxis), + ya = Axes.getFromId(gd, trace.yaxis); - Axes.expand(ya, yVals, {padded: true}); - Axes.expand(xa, xVals, {padded: true}); + calcOneAxis(calcTrace, trace, xa, 'x'); + calcOneAxis(calcTrace, trace, ya, 'y'); } }; + +function calcOneAxis(calcTrace, trace, axis, coord) { + var opts = trace['error_' + coord] || {}, + isVisible = (opts.visible && ['linear', 'log'].indexOf(axis.type) !== -1), + vals = []; + + if(!isVisible) return; + + var computeError = makeComputeError(opts); + + for(var i = 0; i < calcTrace.length; i++) { + var calcPt = calcTrace[i], + calcCoord = calcPt[coord]; + + if(!isNumeric(axis.c2l(calcCoord))) continue; + + var errors = computeError(calcCoord, i); + if(isNumeric(errors[0]) && isNumeric(errors[1])) { + var shoe = calcPt[coord + 's'] = calcCoord - errors[0], + hat = calcPt[coord + 'h'] = calcCoord + errors[1]; + vals.push(shoe, hat); + } + } + + Axes.expand(axis, vals, {padded: true}); +}