Skip to content

Commit accae1a

Browse files
authored
Merge pull request #2159 from plotly/global-context
Introduce global contexts
2 parents 629f7a0 + de9b1aa commit accae1a

File tree

20 files changed

+247
-117
lines changed

20 files changed

+247
-117
lines changed

src/plot_api/helpers.js

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ exports.cleanLayout = function(layout) {
154154

155155
// clean old Camera coords
156156
var cameraposition = scene.cameraposition;
157+
157158
if(Array.isArray(cameraposition) && cameraposition[0].length === 4) {
158159
var rotation = cameraposition[0],
159160
center = cameraposition[1],

src/plot_api/plot_api.js

+43-2
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,43 @@ Plotly.plot = function(gd, data, layout, config) {
195195
}
196196
}
197197

198+
if(!fullLayout._glcanvas && fullLayout._has('gl')) {
199+
fullLayout._glcanvas = fullLayout._glcontainer.selectAll('.gl-canvas').data([{
200+
key: 'contextLayer',
201+
context: true,
202+
pick: false
203+
}, {
204+
key: 'focusLayer',
205+
context: false,
206+
pick: false
207+
}, {
208+
key: 'pickLayer',
209+
context: false,
210+
pick: true
211+
}], function(d) { return d.key; });
212+
213+
fullLayout._glcanvas.enter().append('canvas')
214+
.attr('class', function(d) {
215+
return 'gl-canvas gl-canvas-' + d.key.replace('Layer', '');
216+
})
217+
.style({
218+
'position': 'absolute',
219+
'top': 0,
220+
'left': 0,
221+
'width': '100%',
222+
'height': '100%',
223+
'overflow': 'visible'
224+
})
225+
.attr('width', fullLayout.width)
226+
.attr('height', fullLayout.height);
227+
228+
fullLayout._glcanvas.filter(function(d) {
229+
return !d.pick;
230+
}).style({
231+
'pointer-events': 'none'
232+
});
233+
}
234+
198235
return Lib.syncOrAsync([
199236
subroutines.layoutStyles
200237
], gd);
@@ -1993,7 +2030,7 @@ function _relayout(gd, aobj) {
19932030
else flags.plot = true;
19942031
}
19952032
else {
1996-
if(fullLayout._has('gl2d') &&
2033+
if((fullLayout._has('gl2d') || fullLayout._has('regl')) &&
19972034
(ai === 'dragmode' &&
19982035
(vi === 'lasso' || vi === 'select') &&
19992036
!(vOld === 'lasso' || vOld === 'select'))
@@ -2764,11 +2801,15 @@ function makePlotFramework(gd) {
27642801
// right, rather than enter/exit which can muck up the order
27652802
// TODO: sort out all the ordering so we don't have to
27662803
// explicitly delete anything
2804+
// FIXME: parcoords reuses this object, not the best pattern
27672805
fullLayout._glcontainer = fullLayout._paperdiv.selectAll('.gl-container')
2768-
.data([0]);
2806+
.data([{}]);
27692807
fullLayout._glcontainer.enter().append('div')
27702808
.classed('gl-container', true);
27712809

2810+
// That is initialized in drawFramework if there are `gl` traces
2811+
fullLayout._glcanvas = null;
2812+
27722813
fullLayout._paperdiv.selectAll('.main-svg').remove();
27732814

27742815
fullLayout._paper = fullLayout._paperdiv.insert('svg', ':first-child')

src/plots/cartesian/dragbox.js

+22-1
Original file line numberDiff line numberDiff line change
@@ -711,14 +711,35 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
711711
return ax._length * (1 - scaleFactor) * FROM_TL[ax.constraintoward || 'middle'];
712712
}
713713

714-
for(i = 0; i < subplots.length; i++) {
714+
// clear gl frame, if any, since we preserve drawing buffer
715+
// FIXME: code duplication with cartesian.plot
716+
if(fullLayout._glcanvas && fullLayout._glcanvas.size()) {
717+
fullLayout._glcanvas.each(function(d) {
718+
if(d.regl) {
719+
d.regl.clear({
720+
color: true
721+
});
722+
}
723+
});
724+
}
715725

726+
for(i = 0; i < subplots.length; i++) {
716727
var subplot = plotinfos[subplots[i]],
717728
xa2 = subplot.xaxis,
718729
ya2 = subplot.yaxis,
719730
editX2 = editX && !xa2.fixedrange && (xa.indexOf(xa2) !== -1),
720731
editY2 = editY && !ya2.fixedrange && (ya.indexOf(ya2) !== -1);
721732

733+
// scattergl translate
734+
if(subplot._scene && subplot._scene.update) {
735+
// FIXME: possibly we could update axis internal _r and _rl here
736+
var xaRange = Lib.simpleMap(xa2.range, xa2.r2l),
737+
yaRange = Lib.simpleMap(ya2.range, ya2.r2l);
738+
subplot._scene.update(
739+
{range: [xaRange[0], yaRange[0], xaRange[1], yaRange[1]]}
740+
);
741+
}
742+
722743
if(editX2) {
723744
xScaleFactor2 = xScaleFactor;
724745
clipDx = ew ? viewBox[0] : getShift(xa2, xScaleFactor2);

src/plots/cartesian/index.js

+11
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,17 @@ exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
4848
}
4949
}
5050

51+
// clear gl frame, if any, since we preserve drawing buffer
52+
if(fullLayout._glcanvas && fullLayout._glcanvas.size()) {
53+
fullLayout._glcanvas.each(function(d) {
54+
if(d.regl) {
55+
d.regl.clear({
56+
color: true
57+
});
58+
}
59+
});
60+
}
61+
5162
for(i = 0; i < subplots.length; i++) {
5263
var subplot = subplots[i],
5364
subplotInfo = fullLayout._plots[subplot];

src/plots/cartesian/select.js

-1
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,6 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
262262

263263
throttle.done(throttleID).then(function() {
264264
throttle.clear(throttleID);
265-
266265
if(!dragged && numclicks === 2) {
267266
// clear selection on doubleclick
268267
outlines.remove();

src/plots/gl2d/scene2d.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ proto.makeFramework = function() {
112112
this.gl = STATIC_CONTEXT;
113113
}
114114
else {
115-
var liveCanvas = document.createElement('canvas');
115+
var liveCanvas = this.container.querySelector('.gl-canvas-focus');
116116

117117
var gl = getContext({
118118
canvas: liveCanvas,
@@ -140,7 +140,7 @@ proto.makeFramework = function() {
140140
// disabling user select on the canvas
141141
// sanitizes double-clicks interactions
142142
// ref: https://github.com/plotly/plotly.js/issues/744
143-
canvas.className += 'user-select-none';
143+
canvas.className += ' user-select-none';
144144

145145
// create SVG container for hover text
146146
var svgContainer = this.svgContainer = document.createElementNS(
@@ -157,9 +157,11 @@ proto.makeFramework = function() {
157157
mouseContainer.style.position = 'absolute';
158158
mouseContainer.style['pointer-events'] = 'auto';
159159

160+
this.pickCanvas = this.container.querySelector('.gl-canvas-pick');
161+
162+
160163
// append canvas, hover svg and mouse div to container
161164
var container = this.container;
162-
container.appendChild(canvas);
163165
container.appendChild(svgContainer);
164166
container.appendChild(mouseContainer);
165167

@@ -370,7 +372,6 @@ proto.destroy = function() {
370372

371373
this.glplot.dispose();
372374

373-
if(!this.staticPlot) this.container.removeChild(this.canvas);
374375
this.container.removeChild(this.svgContainer);
375376
this.container.removeChild(this.mouseContainer);
376377

@@ -533,8 +534,10 @@ proto.updateTraces = function(fullData, calcData) {
533534
proto.updateFx = function(dragmode) {
534535
// switch to svg interactions in lasso/select mode
535536
if(dragmode === 'lasso' || dragmode === 'select') {
537+
this.pickCanvas.style['pointer-events'] = 'none';
536538
this.mouseContainer.style['pointer-events'] = 'none';
537539
} else {
540+
this.pickCanvas.style['pointer-events'] = 'auto';
538541
this.mouseContainer.style['pointer-events'] = 'auto';
539542
}
540543

src/plots/plots.js

+32-2
Original file line numberDiff line numberDiff line change
@@ -569,15 +569,29 @@ plots.createTransitionData = function(gd) {
569569

570570
// helper function to be bound to fullLayout to check
571571
// whether a certain plot type is present on plot
572+
// or trace has a category
572573
plots._hasPlotType = function(category) {
574+
// check plot
575+
573576
var basePlotModules = this._basePlotModules || [];
577+
var i;
574578

575-
for(var i = 0; i < basePlotModules.length; i++) {
579+
for(i = 0; i < basePlotModules.length; i++) {
576580
var _module = basePlotModules[i];
577581

578582
if(_module.name === category) return true;
579583
}
580584

585+
// check trace
586+
var modules = this._modules || [];
587+
588+
for(i = 0; i < modules.length; i++) {
589+
var _ = modules[i];
590+
if(_.categories && _.categories.indexOf(category) >= 0) {
591+
return true;
592+
}
593+
}
594+
581595
return false;
582596
};
583597

@@ -595,6 +609,15 @@ plots.cleanPlot = function(newFullData, newFullLayout, oldFullData, oldFullLayou
595609

596610
var hasPaper = !!oldFullLayout._paper;
597611
var hasInfoLayer = !!oldFullLayout._infolayer;
612+
var hadGl = oldFullLayout._has && oldFullLayout._has('gl');
613+
var hasGl = newFullLayout._has && newFullLayout._has('gl');
614+
615+
if(hadGl && !hasGl) {
616+
if(oldFullLayout._glcontainer !== undefined) {
617+
oldFullLayout._glcontainer.selectAll('.gl-canvas').remove();
618+
oldFullLayout._glcanvas = null;
619+
}
620+
}
598621

599622
oldLoop:
600623
for(i = 0; i < oldFullData.length; i++) {
@@ -1308,7 +1331,11 @@ plots.purge = function(gd) {
13081331
// a new plot, and may have been set outside of our scope.
13091332

13101333
var fullLayout = gd._fullLayout || {};
1311-
if(fullLayout._glcontainer !== undefined) fullLayout._glcontainer.remove();
1334+
if(fullLayout._glcontainer !== undefined) {
1335+
fullLayout._glcontainer.selectAll('.gl-canvas').remove();
1336+
fullLayout._glcontainer.remove();
1337+
fullLayout._glcanvas = null;
1338+
}
13121339
if(fullLayout._geocontainer !== undefined) fullLayout._geocontainer.remove();
13131340

13141341
// remove modebar
@@ -1326,6 +1353,9 @@ plots.purge = function(gd) {
13261353
}
13271354
}
13281355

1356+
// remove any planned throttles
1357+
Lib.clearThrottle();
1358+
13291359
// data and layout
13301360
delete gd.data;
13311361
delete gd.layout;

src/traces/contourgl/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ ContourGl.plot = require('./convert');
2323
ContourGl.moduleType = 'trace';
2424
ContourGl.name = 'contourgl';
2525
ContourGl.basePlotModule = require('../../plots/gl2d');
26-
ContourGl.categories = ['gl2d', '2dMap'];
26+
ContourGl.categories = ['gl', 'gl2d', '2dMap'];
2727
ContourGl.meta = {
2828
description: [
2929
'WebGL contour (beta)'

src/traces/heatmapgl/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ HeatmapGl.plot = require('./convert');
2121
HeatmapGl.moduleType = 'trace';
2222
HeatmapGl.name = 'heatmapgl';
2323
HeatmapGl.basePlotModule = require('../../plots/gl2d');
24-
HeatmapGl.categories = ['gl2d', '2dMap'];
24+
HeatmapGl.categories = ['gl', 'gl2d', '2dMap'];
2525
HeatmapGl.meta = {
2626
description: [
2727
'WebGL version of the heatmap trace type.'

src/traces/parcoords/base_plot.js

+6-12
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ var d3 = require('d3');
1212
var Plots = require('../../plots/plots');
1313
var parcoordsPlot = require('./plot');
1414
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');
15-
var c = require('./constants');
1615

1716
exports.name = 'parcoords';
1817

@@ -28,9 +27,6 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout)
2827
var hasParcoords = (newFullLayout._has && newFullLayout._has('parcoords'));
2928

3029
if(hadParcoords && !hasParcoords) {
31-
oldFullLayout._paperdiv.selectAll('.parcoords-line-layers').remove();
32-
oldFullLayout._paperdiv.selectAll('.parcoords-line-layers').remove();
33-
oldFullLayout._paperdiv.selectAll('.parcoords').remove();
3430
oldFullLayout._paperdiv.selectAll('.parcoords').remove();
3531
oldFullLayout._glimages.selectAll('*').remove();
3632
}
@@ -41,22 +37,20 @@ exports.toSVG = function(gd) {
4137
var imageRoot = gd._fullLayout._glimages;
4238
var root = d3.select(gd).selectAll('.svg-container');
4339
var canvases = root.filter(function(d, i) {return i === root.size() - 1;})
44-
.selectAll('.parcoords-lines.context, .parcoords-lines.focus');
40+
.selectAll('.gl-canvas-context, .gl-canvas-focus');
4541

46-
function canvasToImage(d) {
42+
function canvasToImage() {
4743
var canvas = this;
4844
var imageData = canvas.toDataURL('image/png');
4945
var image = imageRoot.append('svg:image');
50-
var size = gd._fullLayout._size;
51-
var domain = gd._fullData[d.model.key].domain;
5246

5347
image.attr({
5448
xmlns: xmlnsNamespaces.svg,
5549
'xlink:href': imageData,
56-
x: size.l + size.w * domain.x[0] - c.overdrag,
57-
y: size.t + size.h * (1 - domain.y[1]),
58-
width: (domain.x[1] - domain.x[0]) * size.w + 2 * c.overdrag,
59-
height: (domain.y[1] - domain.y[0]) * size.h,
50+
x: 0,
51+
y: 0,
52+
width: canvas.width,
53+
height: canvas.height,
6054
preserveAspectRatio: 'none'
6155
});
6256
}

src/traces/parcoords/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Parcoords.colorbar = require('./colorbar');
1919
Parcoords.moduleType = 'trace';
2020
Parcoords.name = 'parcoords';
2121
Parcoords.basePlotModule = require('./base_plot');
22-
Parcoords.categories = ['gl', 'noOpacity'];
22+
Parcoords.categories = ['gl', 'regl', 'noOpacity'];
2323
Parcoords.meta = {
2424
description: [
2525
'Parallel coordinates for multidimensional exploratory data analysis.',

0 commit comments

Comments
 (0)