plotly.js
Version:
The open source javascript graphing library that powers plotly
163 lines (122 loc) • 4.96 kB
JavaScript
var d3 = require('@plotly/d3');
var isNumeric = require('fast-isnumeric');
var Drawing = require('../drawing');
var subTypes = require('../../traces/scatter/subtypes');
module.exports = function plot(gd, traces, plotinfo, transitionOpts) {
var isNew;
var xa = plotinfo.xaxis;
var ya = plotinfo.yaxis;
var hasAnimation = transitionOpts && transitionOpts.duration > 0;
var isStatic = gd._context.staticPlot;
traces.each(function(d) {
var trace = d[0].trace;
// || {} is in case the trace (specifically scatterternary)
// doesn't support error bars at all, but does go through
// the scatter.plot mechanics, which calls ErrorBars.plot
// internally
var xObj = trace.error_x || {};
var yObj = trace.error_y || {};
var keyFunc;
if(trace.ids) {
keyFunc = function(d) {return d.id;};
}
var sparse = (
subTypes.hasMarkers(trace) &&
trace.marker.maxdisplayed > 0
);
if(!yObj.visible && !xObj.visible) d = [];
var errorbars = d3.select(this).selectAll('g.errorbar')
.data(d, keyFunc);
errorbars.exit().remove();
if(!d.length) return;
if(!xObj.visible) errorbars.selectAll('path.xerror').remove();
if(!yObj.visible) errorbars.selectAll('path.yerror').remove();
errorbars.style('opacity', 1);
var enter = errorbars.enter().append('g')
.classed('errorbar', true);
if(hasAnimation) {
enter.style('opacity', 0).transition()
.duration(transitionOpts.duration)
.style('opacity', 1);
}
Drawing.setClipUrl(errorbars, plotinfo.layerClipId, gd);
errorbars.each(function(d) {
var errorbar = d3.select(this);
var coords = errorCoords(d, xa, ya);
if(sparse && !d.vis) return;
var path;
var yerror = errorbar.select('path.yerror');
if(yObj.visible && isNumeric(coords.x) &&
isNumeric(coords.yh) &&
isNumeric(coords.ys)) {
var yw = yObj.width;
path = 'M' + (coords.x - yw) + ',' +
coords.yh + 'h' + (2 * yw) + // hat
'm-' + yw + ',0V' + coords.ys; // bar
if(!coords.noYS) path += 'm-' + yw + ',0h' + (2 * yw); // shoe
isNew = !yerror.size();
if(isNew) {
yerror = errorbar.append('path')
.style('vector-effect', isStatic ? 'none' : 'non-scaling-stroke')
.classed('yerror', true);
} else if(hasAnimation) {
yerror = yerror
.transition()
.duration(transitionOpts.duration)
.ease(transitionOpts.easing);
}
yerror.attr('d', path);
} else yerror.remove();
var xerror = errorbar.select('path.xerror');
if(xObj.visible && isNumeric(coords.y) &&
isNumeric(coords.xh) &&
isNumeric(coords.xs)) {
var xw = (xObj.copy_ystyle ? yObj : xObj).width;
path = 'M' + coords.xh + ',' +
(coords.y - xw) + 'v' + (2 * xw) + // hat
'm0,-' + xw + 'H' + coords.xs; // bar
if(!coords.noXS) path += 'm0,-' + xw + 'v' + (2 * xw); // shoe
isNew = !xerror.size();
if(isNew) {
xerror = errorbar.append('path')
.style('vector-effect', isStatic ? 'none' : 'non-scaling-stroke')
.classed('xerror', true);
} else if(hasAnimation) {
xerror = xerror
.transition()
.duration(transitionOpts.duration)
.ease(transitionOpts.easing);
}
xerror.attr('d', path);
} else xerror.remove();
});
});
};
// compute the coordinates of the error-bar objects
function errorCoords(d, xa, ya) {
var out = {
x: xa.c2p(d.x),
y: ya.c2p(d.y)
};
// calculate the error bar size and hat and shoe locations
if(d.yh !== undefined) {
out.yh = ya.c2p(d.yh);
out.ys = ya.c2p(d.ys);
// if the shoes go off-scale (ie log scale, error bars past zero)
// clip the bar and hide the shoes
if(!isNumeric(out.ys)) {
out.noYS = true;
out.ys = ya.c2p(d.ys, true);
}
}
if(d.xh !== undefined) {
out.xh = xa.c2p(d.xh);
out.xs = xa.c2p(d.xs);
if(!isNumeric(out.xs)) {
out.noXS = true;
out.xs = xa.c2p(d.xs, true);
}
}
return out;
}
;