diff --git a/src/plots/gl3d/scene.js b/src/plots/gl3d/scene.js index 2a3c9a9d489..6d4f2d802c7 100644 --- a/src/plots/gl3d/scene.js +++ b/src/plots/gl3d/scene.js @@ -523,7 +523,11 @@ proto.plot = function(sceneData, fullLayout, layout) { var objBounds = obj.bounds; var pad = obj._trace.data._pad || 0; - sceneBounds[0][i] = Math.min(sceneBounds[0][i], objBounds[0][i] / dataScale[i] - pad); + if(obj.constructor.name === 'ErrorBars' && axis._lowerLogErrorBound) { + sceneBounds[0][i] = Math.min(sceneBounds[0][i], axis._lowerLogErrorBound); + } else { + sceneBounds[0][i] = Math.min(sceneBounds[0][i], objBounds[0][i] / dataScale[i] - pad); + } sceneBounds[1][i] = Math.max(sceneBounds[1][i], objBounds[1][i] / dataScale[i] + pad); } diff --git a/src/traces/scatter3d/calc_errors.js b/src/traces/scatter3d/calc_errors.js index 221d26b55e6..d9bc59e2667 100644 --- a/src/traces/scatter3d/calc_errors.js +++ b/src/traces/scatter3d/calc_errors.js @@ -10,7 +10,7 @@ var Registry = require('../../registry'); -function calculateAxisErrors(data, params, scaleFactor) { +function calculateAxisErrors(data, params, scaleFactor, axis) { if(!params || !params.visible) return null; var computeError = Registry.getComponentMethod('errorbars', 'makeComputeError')(params); @@ -19,10 +19,28 @@ function calculateAxisErrors(data, params, scaleFactor) { for(var i = 0; i < data.length; i++) { var errors = computeError(+data[i], i); - result[i] = [ - -errors[0] * scaleFactor, - errors[1] * scaleFactor - ]; + if(axis.type === 'log') { + var point = axis.c2l(data[i]); + var min = data[i] - errors[0], + max = data[i] + errors[1]; + + result[i] = [ + (axis.c2l(min, true) - point) * scaleFactor, + (axis.c2l(max, true) - point) * scaleFactor + ]; + + // Keep track of the lower error bound which isn't negative! + if(min > 0) { + var lower = axis.c2l(min); + if(!axis._lowerLogErrorBound) axis._lowerLogErrorBound = lower; + axis._lowerErrorBound = Math.min(axis._lowerLogErrorBound, lower); + } + } else { + result[i] = [ + -errors[0] * scaleFactor, + errors[1] * scaleFactor + ]; + } } return result; @@ -35,11 +53,11 @@ function dataLength(array) { return 0; } -function calculateErrors(data, scaleFactor) { +function calculateErrors(data, scaleFactor, sceneLayout) { var errors = [ - calculateAxisErrors(data.x, data.error_x, scaleFactor[0]), - calculateAxisErrors(data.y, data.error_y, scaleFactor[1]), - calculateAxisErrors(data.z, data.error_z, scaleFactor[2]) + calculateAxisErrors(data.x, data.error_x, scaleFactor[0], sceneLayout.xaxis), + calculateAxisErrors(data.y, data.error_y, scaleFactor[1], sceneLayout.yaxis), + calculateAxisErrors(data.z, data.error_z, scaleFactor[2], sceneLayout.zaxis) ]; var n = dataLength(errors); diff --git a/src/traces/scatter3d/convert.js b/src/traces/scatter3d/convert.js index 1a7fdc7968a..1e14157125c 100644 --- a/src/traces/scatter3d/convert.js +++ b/src/traces/scatter3d/convert.js @@ -252,7 +252,7 @@ function convertPlotlyOptions(scene, data) { } } - params.errorBounds = calculateError(data, scaleFactor); + params.errorBounds = calculateError(data, scaleFactor, sceneLayout); var errorParams = calculateErrorParams([data.error_x, data.error_y, data.error_z]); params.errorColor = errorParams.color; diff --git a/test/image/baselines/gl3d_error_bars_log.png b/test/image/baselines/gl3d_error_bars_log.png new file mode 100644 index 00000000000..c53ff0f0f15 Binary files /dev/null and b/test/image/baselines/gl3d_error_bars_log.png differ diff --git a/test/image/baselines/gl3d_error_bars_log_2.png b/test/image/baselines/gl3d_error_bars_log_2.png new file mode 100644 index 00000000000..4f19f372e78 Binary files /dev/null and b/test/image/baselines/gl3d_error_bars_log_2.png differ diff --git a/test/image/mocks/gl3d_error_bars_log.json b/test/image/mocks/gl3d_error_bars_log.json new file mode 100644 index 00000000000..9c77a6f3451 --- /dev/null +++ b/test/image/mocks/gl3d_error_bars_log.json @@ -0,0 +1,26 @@ +{ + "data": [{ + "x": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + "y": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "z": [1e00, 1e+01, 1e+02, 1e+03, 1e+04, + 1e+05, 1e+06, 1e+07, 1e+08, 1e+09 + ], + "type": "scatter3d", + "error_z": { + "array": [1e-01, 1e+00, 1e+02, 9e+03, 5e+03, + 0.9e+05, 1e+05, 1e+07, 9e+08, 2e+09 + ], + "type": "data", + "visible": true + } + }], + "layout": { + "scene": { + "zaxis": { + "type": "log" + } + }, + "width": 800, + "height": 800 + } +} diff --git a/test/image/mocks/gl3d_error_bars_log_2.json b/test/image/mocks/gl3d_error_bars_log_2.json new file mode 100644 index 00000000000..41b522b078d --- /dev/null +++ b/test/image/mocks/gl3d_error_bars_log_2.json @@ -0,0 +1,22 @@ +{ + "data": [{ + "x": [0], + "y": [0], + "z": [1e-08], + "type": "scatter3d", + "error_z": { + "array": [0.9e-08], + "type": "data", + "visible": true + } + }], + "layout": { + "scene": { + "zaxis": { + "type": "log" + } + }, + "width": 800, + "height": 800 + } +}