Skip to content

Commit c5cda6b

Browse files
committed
add _hasOnlyLargeSploms flag
which: - adds only a limited number svg layer in each <g .subplot> - use regl-line2d to draw grids
1 parent 20f01cc commit c5cda6b

File tree

3 files changed

+185
-34
lines changed

3 files changed

+185
-34
lines changed

Diff for: src/plots/cartesian/index.js

+58-34
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,15 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout)
229229
var hadGl, hasGl;
230230
var i, k, subplotInfo, moduleName;
231231

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

233242
for(i = 0; i < oldModules.length; i++) {
234243
moduleName = oldModules[i].name;
@@ -330,7 +339,7 @@ exports.drawFramework = function(gd) {
330339
// initialize list of overlay subplots
331340
plotinfo.overlays = [];
332341

333-
makeSubplotLayer(plotinfo);
342+
makeSubplotLayer(gd, plotinfo);
334343

335344
// fill in list of overlay subplots
336345
if(plotinfo.mainplot) {
@@ -346,7 +355,7 @@ exports.drawFramework = function(gd) {
346355
};
347356

348357
exports.rangePlot = function(gd, plotinfo, cdSubplot) {
349-
makeSubplotLayer(plotinfo);
358+
makeSubplotLayer(gd, plotinfo);
350359
plotOne(gd, plotinfo, cdSubplot);
351360
Plots.style(gd);
352361
};
@@ -377,45 +386,58 @@ function makeSubplotData(gd) {
377386
return subplotData;
378387
}
379388

380-
function makeSubplotLayer(plotinfo) {
389+
function makeSubplotLayer(gd, plotinfo) {
381390
var plotgroup = plotinfo.plotgroup;
382391
var id = plotinfo.id;
383392
var xLayer = constants.layerValue2layerClass[plotinfo.xaxis.layer];
384393
var yLayer = constants.layerValue2layerClass[plotinfo.yaxis.layer];
394+
var hasOnlyLargeSploms = gd._fullLayout._hasOnlyLargeSploms;
385395

386396
if(!plotinfo.mainplot) {
387-
var backLayer = ensureSingle(plotgroup, 'g', 'layer-subplot');
388-
plotinfo.shapelayer = ensureSingle(backLayer, 'g', 'shapelayer');
389-
plotinfo.imagelayer = ensureSingle(backLayer, 'g', 'imagelayer');
390-
391-
plotinfo.gridlayer = ensureSingle(plotgroup, 'g', 'gridlayer');
397+
if(hasOnlyLargeSploms) {
398+
// TODO could do even better
399+
// - we don't need plot (but we would have to mock it in lsInner
400+
// and other places
401+
// - we don't (x|y)lines and (x|y)axislayer for most subplots
402+
// usually just the bottom x and left y axes.
403+
plotinfo.plot = ensureSingle(plotgroup, 'g', 'plot');
404+
plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above');
405+
plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above');
406+
plotinfo.xaxislayer = ensureSingle(plotgroup, 'g', 'xaxislayer-above');
407+
plotinfo.yaxislayer = ensureSingle(plotgroup, 'g', 'yaxislayer-above');
408+
}
409+
else {
410+
var backLayer = ensureSingle(plotgroup, 'g', 'layer-subplot');
411+
plotinfo.shapelayer = ensureSingle(backLayer, 'g', 'shapelayer');
412+
plotinfo.imagelayer = ensureSingle(backLayer, 'g', 'imagelayer');
392413

393-
plotinfo.zerolinelayer = ensureSingle(plotgroup, 'g', 'zerolinelayer');
414+
plotinfo.gridlayer = ensureSingle(plotgroup, 'g', 'gridlayer');
415+
plotinfo.zerolinelayer = ensureSingle(plotgroup, 'g', 'zerolinelayer');
394416

395-
ensureSingle(plotgroup, 'path', 'xlines-below');
396-
ensureSingle(plotgroup, 'path', 'ylines-below');
397-
plotinfo.overlinesBelow = ensureSingle(plotgroup, 'g', 'overlines-below');
417+
ensureSingle(plotgroup, 'path', 'xlines-below');
418+
ensureSingle(plotgroup, 'path', 'ylines-below');
419+
plotinfo.overlinesBelow = ensureSingle(plotgroup, 'g', 'overlines-below');
398420

399-
ensureSingle(plotgroup, 'g', 'xaxislayer-below');
400-
ensureSingle(plotgroup, 'g', 'yaxislayer-below');
401-
plotinfo.overaxesBelow = ensureSingle(plotgroup, 'g', 'overaxes-below');
421+
ensureSingle(plotgroup, 'g', 'xaxislayer-below');
422+
ensureSingle(plotgroup, 'g', 'yaxislayer-below');
423+
plotinfo.overaxesBelow = ensureSingle(plotgroup, 'g', 'overaxes-below');
402424

403-
plotinfo.plot = ensureSingle(plotgroup, 'g', 'plot');
404-
plotinfo.overplot = ensureSingle(plotgroup, 'g', 'overplot');
425+
plotinfo.plot = ensureSingle(plotgroup, 'g', 'plot');
426+
plotinfo.overplot = ensureSingle(plotgroup, 'g', 'overplot');
405427

406-
ensureSingle(plotgroup, 'path', 'xlines-above');
407-
ensureSingle(plotgroup, 'path', 'ylines-above');
408-
plotinfo.overlinesAbove = ensureSingle(plotgroup, 'g', 'overlines-above');
428+
plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above');
429+
plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above');
409430

410-
ensureSingle(plotgroup, 'g', 'xaxislayer-above');
411-
ensureSingle(plotgroup, 'g', 'yaxislayer-above');
412-
plotinfo.overaxesAbove = ensureSingle(plotgroup, 'g', 'overaxes-above');
431+
ensureSingle(plotgroup, 'g', 'xaxislayer-above');
432+
ensureSingle(plotgroup, 'g', 'yaxislayer-above');
433+
plotinfo.overaxesAbove = ensureSingle(plotgroup, 'g', 'overaxes-above');
413434

414-
// set refs to correct layers as determined by 'axis.layer'
415-
plotinfo.xlines = plotgroup.select('.xlines-' + xLayer);
416-
plotinfo.ylines = plotgroup.select('.ylines-' + yLayer);
417-
plotinfo.xaxislayer = plotgroup.select('.xaxislayer-' + xLayer);
418-
plotinfo.yaxislayer = plotgroup.select('.yaxislayer-' + yLayer);
435+
// set refs to correct layers as determined by 'axis.layer'
436+
plotinfo.xlines = plotgroup.select('.xlines-' + xLayer);
437+
plotinfo.ylines = plotgroup.select('.ylines-' + yLayer);
438+
plotinfo.xaxislayer = plotgroup.select('.xaxislayer-' + xLayer);
439+
plotinfo.yaxislayer = plotgroup.select('.yaxislayer-' + yLayer);
440+
}
419441
}
420442
else {
421443
var mainplotinfo = plotinfo.mainplotinfo;
@@ -450,14 +472,16 @@ function makeSubplotLayer(plotinfo) {
450472
plotinfo.yaxislayer = mainplotgroup.select('.overaxes-' + yLayer).select('.' + yId);
451473
}
452474

453-
ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.xaxis._id);
454-
ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.yaxis._id);
455-
plotinfo.gridlayer.selectAll('g').sort(axisIds.idSort);
456-
457475
// common attributes for all subplots, overlays or not
458476

459-
for(var i = 0; i < constants.traceLayerClasses.length; i++) {
460-
ensureSingle(plotinfo.plot, 'g', constants.traceLayerClasses[i]);
477+
if(!hasOnlyLargeSploms) {
478+
ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.xaxis._id);
479+
ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.yaxis._id);
480+
plotinfo.gridlayer.selectAll('g').sort(axisIds.idSort);
481+
482+
for(var i = 0; i < constants.traceLayerClasses.length; i++) {
483+
ensureSingle(plotinfo.plot, 'g', constants.traceLayerClasses[i]);
484+
}
461485
}
462486

463487
plotinfo.xlines

Diff for: src/plots/plots.js

+13
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,19 @@ plots.supplyDefaults = function(gd) {
405405
// finally, fill in the pieces of layout that may need to look at data
406406
plots.supplyLayoutModuleDefaults(newLayout, newFullLayout, newFullData, gd._transitionData);
407407

408+
// turn on flag to optimize large splom-only graphs
409+
// mostly by omitting SVG layers during Cartesian.drawFramework
410+
if(
411+
newFullLayout._basePlotModules.length === 1 &&
412+
newFullLayout._basePlotModules[0].name === 'splom' &&
413+
splomXa.length > 15 &&
414+
splomYa.length > 15 &&
415+
newFullLayout.shapes.length === 0 &&
416+
newFullLayout.images.length === 0
417+
) {
418+
newFullLayout._hasOnlyLargeSploms = 1;
419+
}
420+
408421
// TODO remove in v2.0.0
409422
// add has-plot-type refs to fullLayout for backward compatibility
410423
newFullLayout._hasCartesian = newFullLayout._has('cartesian');

Diff for: src/traces/splom/base_plot.js

+114
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
'use strict';
1010

1111
var createRegl = require('regl');
12+
var createLine = require('regl-line2d');
1213

1314
var Registry = require('../../registry');
15+
var Lib = require('../../lib');
1416
var getModuleCalcData = require('../../plots/get_data').getModuleCalcData;
1517
var Cartesian = require('../../plots/cartesian');
1618
var AxisIDs = require('../../plots/cartesian/axis_ids');
@@ -43,11 +45,16 @@ function plot(gd) {
4345
});
4446
});
4547

48+
if(fullLayout._hasOnlyLargeSploms) {
49+
drawGrid(gd);
50+
}
51+
4652
_module.plot(gd, {}, splomCalcData);
4753
}
4854

4955
function drag(gd) {
5056
var cd = gd.calcdata;
57+
var fullLayout = gd._fullLayout;
5158

5259
for(var i = 0; i < cd.length; i++) {
5360
var cd0 = cd[i][0];
@@ -68,6 +75,112 @@ function drag(gd) {
6875
scene.matrix.draw();
6976
}
7077
}
78+
79+
if(fullLayout._hasOnlyLargeSploms) {
80+
fullLayout._modules[0].basePlotModule.drawGrid(gd);
81+
}
82+
}
83+
84+
function drawGrid(gd) {
85+
var fullLayout = gd._fullLayout;
86+
var regl = fullLayout._glcanvas.data()[0].regl;
87+
var splomGrid = fullLayout._splomGrid;
88+
89+
if(!splomGrid) {
90+
splomGrid = fullLayout._splomGrid = createLine(regl);
91+
}
92+
splomGrid.update(makeGridData(gd));
93+
splomGrid.draw();
94+
}
95+
96+
function makeGridData(gd) {
97+
var fullLayout = gd._fullLayout;
98+
var gs = fullLayout._size;
99+
var fullView = [0, 0, fullLayout.width, fullLayout.height];
100+
var splomXa = Object.keys(fullLayout._splomAxes.x);
101+
var splomYa = Object.keys(fullLayout._splomAxes.y);
102+
var lookup = {};
103+
var k;
104+
105+
function push(prefix, ax, x0, x1, y0, y1) {
106+
var lcolor = ax[prefix + 'color'];
107+
var lwidth = ax[prefix + 'width'];
108+
var key = String(lcolor + lwidth);
109+
110+
if(key in lookup) {
111+
lookup[key].data.push(NaN, NaN, x0, x1, y0, y1);
112+
} else {
113+
lookup[key] = {
114+
data: [x0, x1, y0, y1],
115+
join: 'rect',
116+
thickness: lwidth,
117+
color: lcolor,
118+
viewport: fullView,
119+
range: fullView
120+
};
121+
}
122+
}
123+
124+
for(var i = 0; i < splomXa.length; i++) {
125+
var xa = AxisIDs.getFromId(gd, splomXa[i]);
126+
var xVals = xa._vals;
127+
var xShowZl = showZeroLine(xa);
128+
129+
for(var j = 0; j < splomYa.length; j++) {
130+
var ya = AxisIDs.getFromId(gd, splomYa[j]);
131+
var yVals = ya._vals;
132+
var yShowZl = showZeroLine(ya);
133+
134+
// ya.l2p assumes top-to-bottom coordinate system (a la SVG),
135+
// we need to compute bottom-to-top offsets and slopes:
136+
var yOffset = gs.b + ya.domain[0] * gs.h;
137+
var ym = -ya._m;
138+
var yb = -ym * ya.r2l(ya.range[0], ya.calendar);
139+
140+
var x, y;
141+
142+
if(xa.showgrid) {
143+
for(k = 0; k < xVals.length; k++) {
144+
x = xa._offset + xa.l2p(xVals[k].x);
145+
push('grid', xa, x, yOffset, x, yOffset + ya._length);
146+
}
147+
}
148+
if(xShowZl) {
149+
x = xa._offset + xa.l2p(0);
150+
push('zeroline', xa, x, yOffset, x, yOffset + ya._length);
151+
}
152+
if(ya.showgrid) {
153+
for(k = 0; k < yVals.length; k++) {
154+
y = yOffset + yb + ym * yVals[k].x;
155+
push('grid', ya, xa._offset, y, xa._offset + xa._length, y);
156+
}
157+
}
158+
if(yShowZl) {
159+
y = yOffset + yb + 0;
160+
push('zeroline', ya, xa._offset, y, xa._offset + xa._length, y);
161+
}
162+
}
163+
}
164+
165+
var gridBatches = [];
166+
for(k in lookup) {
167+
gridBatches.push(lookup[k]);
168+
}
169+
170+
return gridBatches;
171+
}
172+
173+
function showZeroLine(ax) {
174+
var rng = Lib.simpleMap(ax.range, ax.r2l);
175+
var p0 = ax.l2p(0);
176+
177+
return (
178+
ax.zeroline &&
179+
ax._vals && ax._vals.length &&
180+
(rng[0] * rng[1] <= 0) &&
181+
(ax.type === 'linear' || ax.type === '-') &&
182+
((p0 > 1 && p0 < ax._length - 1) || !ax.showline)
183+
);
71184
}
72185

73186
function clean(newFullData, newFullLayout, oldFullData, oldFullLayout) {
@@ -85,6 +198,7 @@ module.exports = {
85198
drawFramework: Cartesian.drawFramework,
86199
plot: plot,
87200
drag: drag,
201+
drawGrid: drawGrid,
88202
clean: clean,
89203
toSVG: Cartesian.toSVG
90204
};

0 commit comments

Comments
 (0)