Skip to content

Some error bar clean ups #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Dec 14, 2015
15 changes: 8 additions & 7 deletions devtools/image_viewer/viewer.js
Original file line number Diff line number Diff line change
@@ -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(
Expand Down
61 changes: 61 additions & 0 deletions src/components/errorbars/calc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* 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 makeComputeError = 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 xa = Axes.getFromId(gd, trace.xaxis),
ya = Axes.getFromId(gd, trace.yaxis);

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});
}
94 changes: 94 additions & 0 deletions src/components/errorbars/compute_error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* 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';


/**
* Error bar computing function generator
*
* 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} 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
* - error[1] : " " " " positive "
*/
module.exports = function makeComputeError(opts) {
var type = opts.type,
symmetric = opts.symmetric;

if(type === 'data') {
var array = opts.array,
arrayminus = opts.arrayminus;

if(symmetric || arrayminus === undefined) {
return function computeError(dataPt, index) {
var val = +(array[index]);
return [val, val];
};
}
else {
return function computeError(dataPt, index) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return [+arrayminus[index], +array[index]];
};
}
}
else {
var computeErrorValue = makeComputeErrorValue(type, opts.value),
computeErrorValueMinus = makeComputeErrorValue(type, opts.valueminus);

if(symmetric || opts.valueminus === undefined) {
return function computeError(dataPt) {
var val = computeErrorValue(dataPt);
return [val, val];
};
}
else {
return function computeError(dataPt) {
return [
computeErrorValueMinus(dataPt),
computeErrorValue(dataPt)
];
};
}
}
};

/**
* Compute error bar magnitude (for all types except data)
*
* @param {string} type error bar type
* @param {numeric} value error bar value
*
* @return {function} :
* @param {numeric} 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));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcjohnson is this what you had in mind?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like an ideal place to use a switch statement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've been reluctant to use swtich statements in plotly.js so far. Maybe this is the time to use them more.

@bpostlethwaite switch statements are kinda messy in js, are they considered a good pattern?

};
}
}
75 changes: 75 additions & 0 deletions src/components/errorbars/defaults.js
Original file line number Diff line number Diff line change
@@ -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'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

);

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);
}
};
Loading