diff --git a/src/components/errorbars/index.js b/src/components/errorbars/index.js index 59c223c443d..0a87b900d14 100644 --- a/src/components/errorbars/index.js +++ b/src/components/errorbars/index.js @@ -12,7 +12,6 @@ var Lib = require('../../lib'); var overrideAll = require('../../plot_api/edit_types').overrideAll; var attributes = require('./attributes'); -var calc = require('./calc'); var xyAttrs = { error_x: Lib.extendFlat({}, attributes), @@ -48,38 +47,14 @@ module.exports = { supplyDefaults: require('./defaults'), - calc: calc, - calcFromTrace: calcFromTrace, + calc: require('./calc'), + makeComputeError: require('./compute_error'), plot: require('./plot'), style: require('./style'), hoverInfo: hoverInfo }; -function calcFromTrace(trace, layout) { - var x = trace.x || [], - y = trace.y || [], - len = x.length || y.length; - - var calcdataMock = new Array(len); - - for(var i = 0; i < len; i++) { - calcdataMock[i] = { - x: x[i], - y: y[i] - }; - } - - calcdataMock[0].trace = trace; - - calc({ - calcdata: [calcdataMock], - _fullLayout: layout - }); - - return calcdataMock; -} - function hoverInfo(calcPoint, trace, hoverPoint) { if((trace.error_y || {}).visible) { hoverPoint.yerr = calcPoint.yh - calcPoint.y; diff --git a/src/traces/scatter3d/calc_errors.js b/src/traces/scatter3d/calc_errors.js index ecc1f8ee87d..221d26b55e6 100644 --- a/src/traces/scatter3d/calc_errors.js +++ b/src/traces/scatter3d/calc_errors.js @@ -6,15 +6,14 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; -var makeComputeError = require('../../components/errorbars/compute_error'); +var Registry = require('../../registry'); function calculateAxisErrors(data, params, scaleFactor) { if(!params || !params.visible) return null; - var computeError = makeComputeError(params); + var computeError = Registry.getComponentMethod('errorbars', 'makeComputeError')(params); var result = new Array(data.length); for(var i = 0; i < data.length; i++) { diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 304674f19f1..83788abe95a 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -8,12 +8,14 @@ 'use strict'; +var isNumeric = require('fast-isnumeric'); var svgSdf = require('svg-path-sdf'); var rgba = require('color-normalize'); var Registry = require('../../registry'); var Lib = require('../../lib'); var Drawing = require('../../components/drawing'); +var Axes = require('../../plots/cartesian/axes'); var AxisIDs = require('../../plots/cartesian/axis_ids'); var formatColor = require('../../lib/gl_format_color').formatColor; @@ -360,42 +362,60 @@ function convertLinePositions(gd, trace, positions) { }; } -function convertErrorBarPositions(gd, trace, positions) { - var calcFromTrace = Registry.getComponentMethod('errorbars', 'calcFromTrace'); - var vals = calcFromTrace(trace, gd._fullLayout); +function convertErrorBarPositions(gd, trace, positions, x, y) { + var makeComputeError = Registry.getComponentMethod('errorbars', 'makeComputeError'); + var xa = AxisIDs.getFromId(gd, trace.xaxis); + var ya = AxisIDs.getFromId(gd, trace.yaxis); var count = positions.length / 2; var out = {}; - function put(axLetter) { - var errors = new Float64Array(4 * count); - var ax = AxisIDs.getFromId(gd, trace[axLetter + 'axis']); - var pOffset = {x: 0, y: 1}[axLetter]; - var eOffset = {x: [0, 1, 2, 3], y: [2, 3, 0, 1]}[axLetter]; - - for(var i = 0, p = 0; i < count; i++, p += 4) { - errors[p + eOffset[0]] = positions[i * 2 + pOffset] - ax.d2l(vals[i][axLetter + 's']) || 0; - errors[p + eOffset[1]] = ax.d2l(vals[i][axLetter + 'h']) - positions[i * 2 + pOffset] || 0; - errors[p + eOffset[2]] = 0; - errors[p + eOffset[3]] = 0; - } - - return errors; - } + function convertOneAxis(coords, ax) { + var axLetter = ax._id.charAt(0); + var opts = trace['error_' + axLetter]; + + if(opts && opts.visible && (ax.type === 'linear' || ax.type === 'log')) { + var computeError = makeComputeError(opts); + var pOffset = {x: 0, y: 1}[axLetter]; + var eOffset = {x: [0, 1, 2, 3], y: [2, 3, 0, 1]}[axLetter]; + var errors = new Float64Array(4 * count); + var minShoe = Infinity; + var maxHat = -Infinity; + + for(var i = 0, j = 0; i < count; i++, j += 4) { + var dc = coords[i]; + + if(isNumeric(dc)) { + var dl = positions[i * 2 + pOffset]; + var vals = computeError(dc, i); + var lv = vals[0]; + var hv = vals[1]; + + if(isNumeric(lv) && isNumeric(hv)) { + var shoe = dc - lv; + var hat = dc + hv; + + errors[j + eOffset[0]] = dl - ax.c2l(shoe); + errors[j + eOffset[1]] = ax.c2l(hat) - dl; + errors[j + eOffset[2]] = 0; + errors[j + eOffset[3]] = 0; + + minShoe = Math.min(minShoe, dc - lv); + maxHat = Math.max(maxHat, dc + hv); + } + } + } + Axes.expand(ax, [minShoe, maxHat], {padded: true}); - if(trace.error_x && trace.error_x.visible) { - out.x = { - positions: positions, - errors: put('x') - }; - } - if(trace.error_y && trace.error_y.visible) { - out.y = { - positions: positions, - errors: put('y') - }; + out[axLetter] = { + positions: positions, + errors: errors + }; + } } + convertOneAxis(x, xa); + convertOneAxis(y, ya); return out; } diff --git a/src/traces/scattergl/index.js b/src/traces/scattergl/index.js index d75ae2ca79a..934b60c9ca8 100644 --- a/src/traces/scattergl/index.js +++ b/src/traces/scattergl/index.js @@ -82,7 +82,7 @@ function calc(gd, trace) { // create scene options and scene calcColorscales(trace); - var opts = sceneOptions(gd, subplot, trace, positions); + var opts = sceneOptions(gd, subplot, trace, positions, x, y); var scene = sceneUpdate(gd, subplot); // Re-use SVG scatter axis expansion routine except @@ -133,7 +133,7 @@ function calc(gd, trace) { } // create scene options -function sceneOptions(gd, subplot, trace, positions) { +function sceneOptions(gd, subplot, trace, positions, x, y) { var opts = convertStyle(gd, trace); if(opts.marker) { @@ -148,7 +148,7 @@ function sceneOptions(gd, subplot, trace, positions) { } if(opts.errorX || opts.errorY) { - var errors = convertErrorBarPositions(gd, trace, positions); + var errors = convertErrorBarPositions(gd, trace, positions, x, y); if(opts.errorX) { Lib.extendFlat(opts.errorX, errors.x); diff --git a/test/image/baselines/gl2d_error_bars.png b/test/image/baselines/gl2d_error_bars.png index b48fcb35bba..21605d8215f 100644 Binary files a/test/image/baselines/gl2d_error_bars.png and b/test/image/baselines/gl2d_error_bars.png differ diff --git a/test/image/baselines/gl2d_error_bars_log.png b/test/image/baselines/gl2d_error_bars_log.png index 26cc4c4c472..066c3468480 100644 Binary files a/test/image/baselines/gl2d_error_bars_log.png and b/test/image/baselines/gl2d_error_bars_log.png differ diff --git a/test/image/mocks/gl2d_error_bars.json b/test/image/mocks/gl2d_error_bars.json index 593a7605f66..5a244541a3d 100644 --- a/test/image/mocks/gl2d_error_bars.json +++ b/test/image/mocks/gl2d_error_bars.json @@ -1,5 +1,15 @@ { "data": [ + { + "x": [6], + "y": ["2"], + "error_y": { + "type": "data", + "array": [2], + "arrayminus": [4] + }, + "type": "scattergl" + }, { "x": [ 0, @@ -92,6 +102,19 @@ "value": 10 }, "type": "scattergl" + }, + { + "y": [ + 3, + 3, + 6, + 5 + ], + "error_x": { + "type": "constant", + "value": 0.1 + }, + "type": "scattergl" } ] } diff --git a/test/image/mocks/gl2d_error_bars_log.json b/test/image/mocks/gl2d_error_bars_log.json index 02471ffbba1..8a134971118 100644 --- a/test/image/mocks/gl2d_error_bars_log.json +++ b/test/image/mocks/gl2d_error_bars_log.json @@ -1,10 +1,18 @@ { "data": [{ + "name": "array error bars", "type": "scattergl", "y": [10, 2e4, 5e6], "error_y": {"array": [5, 1e4, 4e6]} + }, { + "name": "50% percent error bars", + "type": "scattergl", + "x0": 0.2, + "y": [10, 2e4, 5e6], + "error_y": {"type": "percent", "value": 50} }], "layout": { - "yaxis": {"type": "log"} + "yaxis": {"type": "log"}, + "showlegend": false } }