plotly.js
Version:
The open source javascript graphing library that powers plotly
186 lines (160 loc) • 6.02 kB
JavaScript
'use strict';
var cluster = require('@plotly/point-cluster');
var Lib = require('../../lib');
var AxisIDs = require('../../plots/cartesian/axis_ids');
var findExtremes = require('../../plots/cartesian/autorange').findExtremes;
var alignPeriod = require('../../plots/cartesian/align_period');
var scatterCalc = require('../scatter/calc');
var calcMarkerSize = scatterCalc.calcMarkerSize;
var calcAxisExpansion = scatterCalc.calcAxisExpansion;
var setFirstScatter = scatterCalc.setFirstScatter;
var calcColorscale = require('../scatter/colorscale_calc');
var convert = require('./convert');
var sceneUpdate = require('./scene_update');
var BADNUM = require('../../constants/numerical').BADNUM;
var TOO_MANY_POINTS = require('./constants').TOO_MANY_POINTS;
module.exports = function calc(gd, trace) {
var fullLayout = gd._fullLayout;
var xa = trace._xA = AxisIDs.getFromId(gd, trace.xaxis, 'x');
var ya = trace._yA = AxisIDs.getFromId(gd, trace.yaxis, 'y');
var subplot = fullLayout._plots[trace.xaxis + trace.yaxis];
var len = trace._length;
var hasTooManyPoints = len >= TOO_MANY_POINTS;
var len2 = len * 2;
var stash = {};
var i;
var origX = xa.makeCalcdata(trace, 'x');
var origY = ya.makeCalcdata(trace, 'y');
var xObj = alignPeriod(trace, xa, 'x', origX);
var yObj = alignPeriod(trace, ya, 'y', origY);
var x = xObj.vals;
var y = yObj.vals;
trace._x = x;
trace._y = y;
if(trace.xperiodalignment) {
trace._origX = origX;
trace._xStarts = xObj.starts;
trace._xEnds = xObj.ends;
}
if(trace.yperiodalignment) {
trace._origY = origY;
trace._yStarts = yObj.starts;
trace._yEnds = yObj.ends;
}
// we need hi-precision for scatter2d,
// regl-scatter2d uses NaNs for bad/missing values
var positions = new Array(len2);
var _ids = new Array(len);
for(i = 0; i < len; i++) {
positions[i * 2] = x[i] === BADNUM ? NaN : x[i];
positions[i * 2 + 1] = y[i] === BADNUM ? NaN : y[i];
// Pre-compute ids.
_ids[i] = i;
}
if(xa.type === 'log') {
for(i = 0; i < len2; i += 2) {
positions[i] = xa.c2l(positions[i]);
}
}
if(ya.type === 'log') {
for(i = 1; i < len2; i += 2) {
positions[i] = ya.c2l(positions[i]);
}
}
// we don't build a tree for log axes since it takes long to convert log2px
// and it is also
if(hasTooManyPoints && (xa.type !== 'log' && ya.type !== 'log')) {
// FIXME: delegate this to webworker
stash.tree = cluster(positions);
} else {
stash.ids = _ids;
}
// create scene options and scene
calcColorscale(gd, trace);
var opts = sceneOptions(gd, subplot, trace, positions, x, y);
var scene = sceneUpdate(gd, subplot);
// Reuse SVG scatter axis expansion routine.
// For graphs with very large number of points and array marker.size,
// use average marker size instead to speed things up.
setFirstScatter(fullLayout, trace);
var ppad;
if(!hasTooManyPoints) {
ppad = calcMarkerSize(trace, len);
} else if(opts.marker) {
ppad = opts.marker.sizeAvg || Math.max(opts.marker.size, 3);
}
calcAxisExpansion(gd, trace, xa, ya, x, y, ppad);
if(opts.errorX) expandForErrorBars(trace, xa, opts.errorX);
if(opts.errorY) expandForErrorBars(trace, ya, opts.errorY);
// set flags to create scene renderers
if(opts.fill && !scene.fill2d) scene.fill2d = true;
if(opts.marker && !scene.scatter2d) scene.scatter2d = true;
if(opts.line && !scene.line2d) scene.line2d = true;
if((opts.errorX || opts.errorY) && !scene.error2d) scene.error2d = true;
if(opts.text && !scene.glText) scene.glText = true;
if(opts.marker) opts.marker.snap = len;
scene.lineOptions.push(opts.line);
scene.errorXOptions.push(opts.errorX);
scene.errorYOptions.push(opts.errorY);
scene.fillOptions.push(opts.fill);
scene.markerOptions.push(opts.marker);
scene.markerSelectedOptions.push(opts.markerSel);
scene.markerUnselectedOptions.push(opts.markerUnsel);
scene.textOptions.push(opts.text);
scene.textSelectedOptions.push(opts.textSel);
scene.textUnselectedOptions.push(opts.textUnsel);
scene.selectBatch.push([]);
scene.unselectBatch.push([]);
stash._scene = scene;
stash.index = scene.count;
stash.x = x;
stash.y = y;
stash.positions = positions;
scene.count++;
return [{x: false, y: false, t: stash, trace: trace}];
};
function expandForErrorBars(trace, ax, opts) {
var extremes = trace._extremes[ax._id];
var errExt = findExtremes(ax, opts._bnds, {padded: true});
extremes.min = extremes.min.concat(errExt.min);
extremes.max = extremes.max.concat(errExt.max);
}
function sceneOptions(gd, subplot, trace, positions, x, y) {
var opts = convert.style(gd, trace);
if(opts.marker) {
opts.marker.positions = positions;
}
if(opts.line && positions.length > 1) {
Lib.extendFlat(
opts.line,
convert.linePositions(gd, trace, positions)
);
}
if(opts.errorX || opts.errorY) {
var errors = convert.errorBarPositions(gd, trace, positions, x, y);
if(opts.errorX) {
Lib.extendFlat(opts.errorX, errors.x);
}
if(opts.errorY) {
Lib.extendFlat(opts.errorY, errors.y);
}
}
if(opts.text) {
Lib.extendFlat(
opts.text,
{positions: positions},
convert.textPosition(gd, trace, opts.text, opts.marker)
);
Lib.extendFlat(
opts.textSel,
{positions: positions},
convert.textPosition(gd, trace, opts.text, opts.markerSel)
);
Lib.extendFlat(
opts.textUnsel,
{positions: positions},
convert.textPosition(gd, trace, opts.text, opts.markerUnsel)
);
}
return opts;
}