Skip to content

Commit e6fcbf5

Browse files
committed
Introduce global contexts
1 parent 5388d32 commit e6fcbf5

File tree

15 files changed

+176
-99
lines changed

15 files changed

+176
-99
lines changed

src/plot_api/plot_api.js

+53-3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ var enforceAxisConstraints = axisConstraints.enforce;
4242
var cleanAxisConstraints = axisConstraints.clean;
4343
var axisIds = require('../plots/cartesian/axis_ids');
4444

45+
var createRegl = require('regl');
46+
4547

4648
/**
4749
* Main plot-creation function
@@ -195,6 +197,51 @@ Plotly.plot = function(gd, data, layout, config) {
195197
}
196198
}
197199

200+
if(!fullLayout._glcanvas && fullLayout._has('gl')) {
201+
fullLayout._glcanvas = fullLayout._glcontainer.selectAll('.gl-canvas').data([{
202+
key: 'contextLayer',
203+
context: true,
204+
pick: false
205+
}, {
206+
key: 'focusLayer',
207+
context: false,
208+
pick: false
209+
}, {
210+
key: 'pickLayer',
211+
context: false,
212+
pick: true
213+
}]);
214+
215+
fullLayout._glcanvas.enter().append('canvas')
216+
.each(function(d) {
217+
d.regl = createRegl({
218+
canvas: this,
219+
attributes: {
220+
antialias: !d.pick,
221+
preserveDrawingBuffer: true
222+
},
223+
extensions: ['ANGLE_instanced_arrays', 'OES_element_index_uint'],
224+
pixelRatio: gd._context.plotGlPixelRatio || global.devicePixelRatio
225+
});
226+
})
227+
.attr('class', function(d) {
228+
return 'gl-canvas gl-canvas-' + d.key.replace('Layer', '');
229+
})
230+
.style({
231+
'position': 'absolute',
232+
'top': 0,
233+
'left': 0,
234+
'width': '100%',
235+
'height': '100%',
236+
'pointer-events': 'none',
237+
'overflow': 'visible'
238+
})
239+
.attr('width', fullLayout.width)
240+
.attr('height', fullLayout.height);
241+
242+
fullLayout._glcanvas.exit().remove();
243+
}
244+
198245
return Lib.syncOrAsync([
199246
subroutines.layoutStyles
200247
], gd);
@@ -1993,7 +2040,7 @@ function _relayout(gd, aobj) {
19932040
else flags.plot = true;
19942041
}
19952042
else {
1996-
if(fullLayout._has('gl2d') &&
2043+
if((fullLayout._has('gl2d') || fullLayout._has('regl')) &&
19972044
(ai === 'dragmode' &&
19982045
(vi === 'lasso' || vi === 'select') &&
19992046
!(vOld === 'lasso' || vOld === 'select'))
@@ -2754,7 +2801,7 @@ function makePlotFramework(gd) {
27542801
.classed('plotly', true);
27552802

27562803
// Make the svg container
2757-
fullLayout._paperdiv = fullLayout._container.selectAll('.svg-container').data([0]);
2804+
fullLayout._paperdiv = fullLayout._container.selectAll('.svg-container').data([{}]);
27582805
fullLayout._paperdiv.enter().append('div')
27592806
.classed('svg-container', true)
27602807
.style('position', 'relative');
@@ -2765,10 +2812,13 @@ function makePlotFramework(gd) {
27652812
// TODO: sort out all the ordering so we don't have to
27662813
// explicitly delete anything
27672814
fullLayout._glcontainer = fullLayout._paperdiv.selectAll('.gl-container')
2768-
.data([0]);
2815+
.data([{}]);
27692816
fullLayout._glcontainer.enter().append('div')
27702817
.classed('gl-container', true);
27712818

2819+
// That is initialized in drawFramework if there are `gl` traces
2820+
fullLayout._glcanvas = null;
2821+
27722822
fullLayout._paperdiv.selectAll('.main-svg').remove();
27732823

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

src/plots/cartesian/dragbox.js

+20-1
Original file line numberDiff line numberDiff line change
@@ -711,14 +711,33 @@ 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+
d.regl.clear({
719+
color: true
720+
});
721+
});
722+
}
715723

724+
for(i = 0; i < subplots.length; i++) {
716725
var subplot = plotinfos[subplots[i]],
717726
xa2 = subplot.xaxis,
718727
ya2 = subplot.yaxis,
719728
editX2 = editX && !xa2.fixedrange && (xa.indexOf(xa2) !== -1),
720729
editY2 = editY && !ya2.fixedrange && (ya.indexOf(ya2) !== -1);
721730

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

src/plots/cartesian/index.js

+9
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ 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+
d.regl.clear({
55+
color: true
56+
});
57+
});
58+
}
59+
5160
for(i = 0; i < subplots.length; i++) {
5261
var subplot = subplots[i],
5362
subplotInfo = fullLayout._plots[subplot];

src/plots/gl2d/scene2d.js

+2-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(
@@ -159,7 +159,6 @@ proto.makeFramework = function() {
159159

160160
// append canvas, hover svg and mouse div to container
161161
var container = this.container;
162-
container.appendChild(canvas);
163162
container.appendChild(svgContainer);
164163
container.appendChild(mouseContainer);
165164

@@ -370,7 +369,6 @@ proto.destroy = function() {
370369

371370
this.glplot.dispose();
372371

373-
if(!this.staticPlot) this.container.removeChild(this.canvas);
374372
this.container.removeChild(this.svgContainer);
375373
this.container.removeChild(this.mouseContainer);
376374

src/plots/plots.js

+18-1
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

@@ -1319,6 +1333,9 @@ plots.purge = function(gd) {
13191333
}
13201334
}
13211335

1336+
// remove any planned throttles
1337+
Lib.clearThrottle();
1338+
13221339
// data and layout
13231340
delete gd.data;
13241341
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.',

src/traces/parcoords/lines.js

+36-15
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@
88

99
'use strict';
1010

11-
var createREGL = require('regl');
1211
var glslify = require('glslify');
13-
var verticalPadding = require('./constants').verticalPadding;
12+
var c = require('./constants');
1413
var vertexShaderSource = glslify('./shaders/vertex.glsl');
1514
var pickVertexShaderSource = glslify('./shaders/pick_vertex.glsl');
1615
var fragmentShaderSource = glslify('./shaders/fragment.glsl');
@@ -165,7 +164,19 @@ function valid(i, offset, panelCount) {
165164
return i + offset <= panelCount;
166165
}
167166

168-
module.exports = function(canvasGL, lines, canvasWidth, canvasHeight, initialDimensions, initialPanels, unitToColor, context, pick, scatter) {
167+
module.exports = module.exports = function(canvasGL, d, scatter) {
168+
var model = d.model,
169+
vm = d.viewModel,
170+
domain = model.domain;
171+
172+
var lines = model.lines,
173+
canvasWidth = model.canvasWidth,
174+
canvasHeight = model.canvasHeight,
175+
initialDimensions = vm.dimensions,
176+
initialPanels = vm.panels,
177+
unitToColor = model.unitToColor,
178+
context = d.context,
179+
pick = d.pick;
169180

170181
var renderState = {
171182
currentRafs: {},
@@ -189,13 +200,7 @@ module.exports = function(canvasGL, lines, canvasWidth, canvasHeight, initialDim
189200
var points = makePoints(sampleCount, dimensionCount, initialDims, color);
190201
var attributes = makeAttributes(sampleCount, points);
191202

192-
var regl = createREGL({
193-
canvas: canvasGL,
194-
attributes: {
195-
preserveDrawingBuffer: true,
196-
antialias: !pick
197-
}
198-
});
203+
var regl = d.regl;
199204

200205
var paletteTexture = regl.texture({
201206
shape: [256, 1],
@@ -248,6 +253,13 @@ module.exports = function(canvasGL, lines, canvasWidth, canvasHeight, initialDim
248253
}
249254
},
250255

256+
viewport: {
257+
x: regl.prop('viewportX'),
258+
y: regl.prop('viewportY'),
259+
width: regl.prop('viewportWidth'),
260+
height: regl.prop('viewportHeight')
261+
},
262+
251263
dither: false,
252264

253265
vert: pick ? pickVertexShaderSource : vertexShaderSource,
@@ -297,7 +309,7 @@ module.exports = function(canvasGL, lines, canvasWidth, canvasHeight, initialDim
297309
function makeItem(i, ii, x, y, panelSizeX, canvasPanelSizeY, crossfilterDimensionIndex, scatter, I, leftmost, rightmost) {
298310
var loHi, abcd, d, index;
299311
var leftRight = [i, ii];
300-
var filterEpsilon = verticalPadding / canvasPanelSizeY;
312+
var filterEpsilon = c.verticalPadding / canvasPanelSizeY;
301313

302314
var dims = [0, 1].map(function() {return [0, 1, 2, 3].map(function() {return new Float32Array(16);});});
303315
var lims = [0, 1].map(function() {return [0, 1, 2, 3].map(function() {return new Float32Array(16);});});
@@ -341,10 +353,15 @@ module.exports = function(canvasGL, lines, canvasWidth, canvasHeight, initialDim
341353

342354
colorClamp: colorClamp,
343355
scatter: scatter || 0,
344-
scissorX: I === leftmost ? 0 : x + overdrag,
356+
scissorX: (I === leftmost ? 0 : x + overdrag) + (model.pad.l - overdrag) + model.layoutWidth * domain.x[0],
345357
scissorWidth: (I === rightmost ? canvasWidth - x + overdrag : panelSizeX + 0.5) + (I === leftmost ? x + overdrag : 0),
346-
scissorY: y,
347-
scissorHeight: canvasPanelSizeY
358+
scissorY: y + model.pad.b + model.layoutHeight * domain.y[0],
359+
scissorHeight: canvasPanelSizeY,
360+
361+
viewportX: model.pad.l - overdrag + model.layoutWidth * domain.x[0],
362+
viewportY: model.pad.b + model.layoutHeight * domain.y[0],
363+
viewportWidth: canvasWidth,
364+
viewportHeight: canvasHeight
348365
};
349366
}
350367

@@ -413,11 +430,15 @@ module.exports = function(canvasGL, lines, canvasWidth, canvasHeight, initialDim
413430
return pixelArray;
414431
}
415432

433+
function destroy() {
434+
paletteTexture.destroy();
435+
}
436+
416437
return {
417438
setColorDomain: setColorDomain,
418439
render: renderGLParcoords,
419440
readPixel: readPixel,
420441
readPixels: readPixels,
421-
destroy: regl.destroy
442+
destroy: destroy
422443
};
423444
};

0 commit comments

Comments
 (0)