Skip to content

Better cartesian trace updates and removal #2574

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 19 commits into from
Apr 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6d88255
make bar, box & violin plot method use d3 enter/exit/update pattern
etpinard Apr 19, 2018
31e5b12
lint and minor perf improvements in Cartesian.clean
etpinard Apr 19, 2018
156f127
move up linkSubplots before cleanPlot
etpinard Apr 19, 2018
13a15d1
improve cartesian trace delete pattern
etpinard Apr 19, 2018
b4ea2ce
use plotMethods stash in range slider range plots
etpinard Apr 19, 2018
072f028
fixup trace deletion for carpet traces
etpinard Apr 19, 2018
e5c860a
simplify arg1 -> plotMethod logic
etpinard Apr 20, 2018
400bfe6
ping imagetest after having restarting nw.js
etpinard Apr 24, 2018
284c206
introduce new strategy for cartesian trace module layer updates
etpinard Apr 24, 2018
da63ed4
make heatmap/contour(carpet)/carpet plot method remove its own trace
etpinard Apr 24, 2018
a1c76bc
sub imagelayer->heatmaplayer, maplayer->contourlayer in tests
etpinard Apr 24, 2018
fa38ca5
update `cliponaxis: false`
etpinard Apr 24, 2018
48ee0d8
:lock: contour with heatmap coloring layer order
etpinard Apr 24, 2018
362da1f
:lock: scattercarpet + scatter coexistence
etpinard Apr 24, 2018
3bef9a9
add getUidsFromCalcData helper
etpinard Apr 30, 2018
d47276f
make visible trace module -> layer data a 1D loop w/ + indexOf + sort
etpinard Apr 30, 2018
83422f7
skip gl tags in flaky-tagged jasmine test cmd
etpinard Apr 30, 2018
8c9a7bf
Revert "skip gl tags in flaky-tagged jasmine test cmd"
etpinard Apr 30, 2018
f771310
Merge pull request #2579 from plotly/uid-trace-removal
etpinard Apr 30, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/components/rangeslider/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -448,8 +448,8 @@ function drawRangePlot(rangeSlider, gd, axisOpts, opts) {

Plots.supplyDefaults(mockFigure);

var xa = mockFigure._fullLayout.xaxis,
ya = mockFigure._fullLayout[oppAxisName];
var xa = mockFigure._fullLayout.xaxis;
var ya = mockFigure._fullLayout[oppAxisName];

var plotinfo = {
id: id,
Expand Down
38 changes: 5 additions & 33 deletions src/plot_api/subroutines.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ var Titles = require('../components/titles');
var ModeBar = require('../components/modebar');

var Axes = require('../plots/cartesian/axes');
var cartesianConstants = require('../plots/cartesian/constants');
var alignmentConstants = require('../constants/alignment');
var axisConstraints = require('../plots/cartesian/constraints');
var enforceAxisConstraints = axisConstraints.enforce;
Expand Down Expand Up @@ -199,15 +198,9 @@ exports.lsInner = function(gd) {

Drawing.setClipUrl(plotinfo.plot, plotClipId);

for(i = 0; i < cartesianConstants.traceLayerClasses.length; i++) {
var layer = cartesianConstants.traceLayerClasses[i];
if(layer !== 'scatterlayer' && layer !== 'barlayer') {
plotinfo.plot.selectAll('g.' + layer).call(Drawing.setClipUrl, layerClipId);
}
}

// stash layer clipId value (null or same as clipId)
// to DRY up Drawing.setClipUrl calls downstream
// to DRY up Drawing.setClipUrl calls on trace-module and trace layers
// downstream
plotinfo.layerClipId = layerClipId;

// figure out extra axis line and tick positions as needed
Expand Down Expand Up @@ -495,34 +488,13 @@ exports.doCamera = function(gd) {
exports.drawData = function(gd) {
var fullLayout = gd._fullLayout;
var calcdata = gd.calcdata;
var rangesliderContainers = fullLayout._infolayer.selectAll('g.rangeslider-container');
var i;

// in case of traces that were heatmaps or contour maps
// previously, remove them and their colorbars explicitly
// remove old colorbars explicitly
for(i = 0; i < calcdata.length; i++) {
var trace = calcdata[i][0].trace;
var isVisible = (trace.visible === true);
var uid = trace.uid;

if(!isVisible || !Registry.traceIs(trace, '2dMap')) {
var query = (
'.hm' + uid +
',.contour' + uid +
',#clip' + uid
);

fullLayout._paper
.selectAll(query)
.remove();

rangesliderContainers
.selectAll(query)
.remove();
}

if(!isVisible || !trace._module.colorbar) {
fullLayout._infolayer.selectAll('.cb' + uid).remove();
if(trace.visible !== true || !trace._module.colorbar) {
fullLayout._infolayer.select('.cb' + trace.uid).remove();
}
}

Expand Down
7 changes: 4 additions & 3 deletions src/plots/cartesian/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,16 @@ module.exports = {
DFLTRANGEY: [-1, 4],

// Layers to keep trace types in the right order
// N.B. each 'unique' plot method must have its own layer
traceLayerClasses: [
'imagelayer',
'maplayer',
'heatmaplayer',
'contourcarpetlayer', 'contourlayer',
'barlayer',
'carpetlayer',
'violinlayer',
'boxlayer',
'ohlclayer',
'scatterlayer'
'scattercarpetlayer', 'scatterlayer'
],

layerValue2layerClass: {
Expand Down
186 changes: 87 additions & 99 deletions src/plots/cartesian/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@
'use strict';

var d3 = require('d3');

var Registry = require('../../registry');
var Lib = require('../../lib');
var Plots = require('../plots');
var getModuleCalcData = require('../get_data').getModuleCalcData;
var Drawing = require('../../components/drawing');

var getModuleCalcData = require('../get_data').getModuleCalcData;
var axisIds = require('./axis_ids');
var constants = require('./constants');
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');
Expand Down Expand Up @@ -129,23 +132,20 @@ exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
// traces are removed
if(!Array.isArray(traces)) {
traces = [];

for(i = 0; i < calcdata.length; i++) {
traces.push(i);
}
for(i = 0; i < calcdata.length; i++) traces.push(i);
}

for(i = 0; i < subplots.length; i++) {
var subplot = subplots[i],
subplotInfo = fullLayout._plots[subplot];
var subplot = subplots[i];
var subplotInfo = fullLayout._plots[subplot];

// Get all calcdata for this subplot:
var cdSubplot = [];
var pcd;

for(var j = 0; j < calcdata.length; j++) {
var cd = calcdata[j],
trace = cd[0].trace;
var cd = calcdata[j];
var trace = cd[0].trace;

// Skip trace if whitelist provided and it's not whitelisted:
// if (Array.isArray(traces) && traces.indexOf(i) === -1) continue;
Expand Down Expand Up @@ -182,115 +182,110 @@ exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
};

function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback) {
var fullLayout = gd._fullLayout,
modules = fullLayout._modules;

// remove old traces, then redraw everything
//
// TODO: scatterlayer is manually excluded from this since it knows how
// to update instead of fully removing and redrawing every time. The
// remaining plot traces should also be able to do this. Once implemented,
// we won't need this - which should sometimes be a big speedup.
if(plotinfo.plot) {
plotinfo.plot.selectAll('g:not(.scatterlayer):not(.ohlclayer)').selectAll('g.trace').remove();
var traceLayerClasses = constants.traceLayerClasses;
var fullLayout = gd._fullLayout;
var modules = fullLayout._modules;
var _module, cdModuleAndOthers, cdModule;

var layerData = [];

for(var i = 0; i < modules.length; i++) {
_module = modules[i];
var name = _module.name;

if(Registry.modules[name].categories.svg) {
var className = (_module.layerName || name + 'layer');
var plotMethod = _module.plot;

// plot all traces of this type on this subplot at once
cdModuleAndOthers = getModuleCalcData(cdSubplot, plotMethod);
cdModule = cdModuleAndOthers[0];
// don't need to search the found traces again - in fact we need to NOT
// so that if two modules share the same plotter we don't double-plot
cdSubplot = cdModuleAndOthers[1];

if(cdModule.length) {
layerData.push({
i: traceLayerClasses.indexOf(className),
className: className,
plotMethod: plotMethod,
cdModule: cdModule
});
}
}
}

// plot all traces for each module at once
for(var j = 0; j < modules.length; j++) {
var _module = modules[j];
layerData.sort(function(a, b) { return a.i - b.i; });

var layers = plotinfo.plot.selectAll('g.mlayer')
.data(layerData, function(d) { return d.className; });

// skip over non-cartesian trace modules
if(!_module.plot || _module.basePlotModule.name !== 'cartesian') continue;
layers.enter().append('g')
.attr('class', function(d) { return d.className; })
.classed('mlayer', true);

// plot all traces of this type on this subplot at once
var cdModuleAndOthers = getModuleCalcData(cdSubplot, _module);
var cdModule = cdModuleAndOthers[0];
// don't need to search the found traces again - in fact we need to NOT
// so that if two modules share the same plotter we don't double-plot
cdSubplot = cdModuleAndOthers[1];
layers.exit().remove();

_module.plot(gd, plotinfo, cdModule, transitionOpts, makeOnCompleteCallback);
layers.order();

layers.each(function(d) {
var sel = d3.select(this);
var className = d.className;

d.plotMethod(
gd, plotinfo, d.cdModule, sel,
transitionOpts, makeOnCompleteCallback
);

// layers that allow `cliponaxis: false`
if(className !== 'scatterlayer' && className !== 'barlayer') {
Drawing.setClipUrl(sel, plotinfo.layerClipId);
}
});

// call Scattergl.plot separately
if(fullLayout._has('scattergl')) {
_module = Registry.getModule('scattergl');
cdModule = getModuleCalcData(cdSubplot, _module)[0];
_module.plot(gd, plotinfo, cdModule);
}
}

exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
var oldModules = oldFullLayout._modules || [];
var newModules = newFullLayout._modules || [];
var oldPlots = oldFullLayout._plots || {};

var hadScatter, hasScatter;
var hadOHLC, hasOHLC;
var hadGl, hasGl;
var i, k, subplotInfo, moduleName;
var newPlots = newFullLayout._plots || {};
var oldSubplotList = oldFullLayout._subplots || {};
var plotinfo;
var i, k;

// when going from a large splom graph to something else,
// we need to clear <g subplot> so that the new cartesian subplot
// can have the correct layer ordering
if(oldFullLayout._hasOnlyLargeSploms && !newFullLayout._hasOnlyLargeSploms) {
for(k in oldPlots) {
subplotInfo = oldPlots[k];
if(subplotInfo.plotgroup) subplotInfo.plotgroup.remove();
plotinfo = oldPlots[k];
if(plotinfo.plotgroup) plotinfo.plotgroup.remove();
}
}

for(i = 0; i < oldModules.length; i++) {
moduleName = oldModules[i].name;
if(moduleName === 'scatter') hadScatter = true;
else if(moduleName === 'scattergl') hadGl = true;
else if(moduleName === 'ohlc') hadOHLC = true;
}

for(i = 0; i < newModules.length; i++) {
moduleName = newModules[i].name;
if(moduleName === 'scatter') hasScatter = true;
else if(moduleName === 'scattergl') hasGl = true;
else if(moduleName === 'ohlc') hasOHLC = true;
}

var layersToEmpty = [];
if(hadScatter && !hasScatter) layersToEmpty.push('g.scatterlayer');
if(hadOHLC && !hasOHLC) layersToEmpty.push('g.ohlclayer');

if(layersToEmpty.length) {
for(var layeri = 0; layeri < layersToEmpty.length; layeri++) {
for(k in oldPlots) {
subplotInfo = oldPlots[k];
if(subplotInfo.plot) {
subplotInfo.plot.select(layersToEmpty[layeri])
.selectAll('g.trace')
.remove();
}
}

oldFullLayout._infolayer.selectAll('g.rangeslider-container')
.select(layersToEmpty[layeri])
.selectAll('g.trace')
.remove();
}
}
var hadGl = (oldFullLayout._has && oldFullLayout._has('gl'));
var hasGl = (newFullLayout._has && newFullLayout._has('gl'));

if(hadGl && !hasGl) {
for(k in oldPlots) {
subplotInfo = oldPlots[k];

if(subplotInfo._scene) {
subplotInfo._scene.destroy();
}
plotinfo = oldPlots[k];
if(plotinfo._scene) plotinfo._scene.destroy();
}
}

var oldSubplotList = oldFullLayout._subplots || {};
var newSubplotList = newFullLayout._subplots || {xaxis: [], yaxis: []};

// delete any titles we don't need anymore
// check if axis list has changed, and if so clear old titles
if(oldSubplotList.xaxis && oldSubplotList.yaxis) {
var oldAxIDs = oldSubplotList.xaxis.concat(oldSubplotList.yaxis);
var newAxIDs = newSubplotList.xaxis.concat(newSubplotList.yaxis);

var oldAxIDs = axisIds.listIds({_fullLayout: oldFullLayout});
for(i = 0; i < oldAxIDs.length; i++) {
if(newAxIDs.indexOf(oldAxIDs[i]) === -1) {
oldFullLayout._infolayer.selectAll('.g-' + oldAxIDs[i] + 'title').remove();
var oldAxId = oldAxIDs[i];
if(!newFullLayout[axisIds.id2name(oldAxId)]) {
oldFullLayout._infolayer.selectAll('.g-' + oldAxId + 'title').remove();
}
}
}
Expand All @@ -308,7 +303,7 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout)
else if(oldSubplotList.cartesian) {
for(i = 0; i < oldSubplotList.cartesian.length; i++) {
var oldSubplotId = oldSubplotList.cartesian[i];
if(newSubplotList.cartesian.indexOf(oldSubplotId) === -1) {
if(!newPlots[oldSubplotId]) {
var selector = '.' + oldSubplotId + ',.' + oldSubplotId + '-x,.' + oldSubplotId + '-y';
oldFullLayout._cartesianlayer.selectAll(selector).remove();
removeSubplotExtras(oldSubplotId, oldFullLayout);
Expand Down Expand Up @@ -482,10 +477,6 @@ function makeSubplotLayer(gd, plotinfo) {
ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.xaxis._id);
ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.yaxis._id);
plotinfo.gridlayer.selectAll('g').sort(axisIds.idSort);

for(var i = 0; i < constants.traceLayerClasses.length; i++) {
ensureSingle(plotinfo.plot, 'g', constants.traceLayerClasses[i]);
}
}

plotinfo.xlines
Expand Down Expand Up @@ -516,11 +507,8 @@ function purgeSubplotLayers(layers, fullLayout) {

// must remove overlaid subplot trace layers 'manually'

var subplots = fullLayout._plots;
var subplotIds = Object.keys(subplots);

for(var i = 0; i < subplotIds.length; i++) {
var subplotInfo = subplots[subplotIds[i]];
for(var k in fullLayout._plots) {
var subplotInfo = fullLayout._plots[k];
var overlays = subplotInfo.overlays || [];

for(var j = 0; j < overlays.length; j++) {
Expand Down
Loading