Skip to content

Commit a132b85

Browse files
authoredMay 10, 2018
Merge pull request #2623 from plotly/stash-svg-pts
Faster svg pan
2 parents c5aa951 + 813d47f commit a132b85

File tree

9 files changed

+74
-63
lines changed

9 files changed

+74
-63
lines changed
 

‎src/components/drawing/index.js

+14-21
Original file line numberDiff line numberDiff line change
@@ -100,18 +100,17 @@ drawing.hideOutsideRangePoint = function(d, sel, xa, ya, xcalendar, ycalendar) {
100100
);
101101
};
102102

103-
drawing.hideOutsideRangePoints = function(traceGroups, subplot, selector) {
103+
drawing.hideOutsideRangePoints = function(traceGroups, subplot) {
104104
if(!subplot._hasClipOnAxisFalse) return;
105105

106-
selector = selector || '.point,.textpoint';
107-
108106
var xa = subplot.xaxis;
109107
var ya = subplot.yaxis;
110108

111109
traceGroups.each(function(d) {
112110
var trace = d[0].trace;
113111
var xcalendar = trace.xcalendar;
114112
var ycalendar = trace.ycalendar;
113+
var selector = trace.type === 'bar' ? '.bartext' : '.point,.textpoint';
115114

116115
traceGroups.selectAll(selector).each(function(d) {
117116
drawing.hideOutsideRangePoint(d, d3.select(this), xa, ya, xcalendar, ycalendar);
@@ -1057,38 +1056,32 @@ drawing.setScale = function(element, x, y) {
10571056
return transform;
10581057
};
10591058

1060-
drawing.setPointGroupScale = function(selection, x, y) {
1061-
var t, scale, re;
1059+
var SCALE_RE = /\s*sc.*/;
10621060

1063-
x = x || 1;
1064-
y = y || 1;
1061+
drawing.setPointGroupScale = function(selection, xScale, yScale) {
1062+
xScale = xScale || 1;
1063+
yScale = yScale || 1;
10651064

1066-
if(x === 1 && y === 1) {
1067-
scale = '';
1068-
} else {
1069-
// The same scale transform for every point:
1070-
scale = ' scale(' + x + ',' + y + ')';
1071-
}
1065+
if(!selection) return;
10721066

1073-
// A regex to strip any existing scale:
1074-
re = /\s*sc.*/;
1067+
// The same scale transform for every point:
1068+
var scale = (xScale === 1 && yScale === 1) ?
1069+
'' :
1070+
' scale(' + xScale + ',' + yScale + ')';
10751071

10761072
selection.each(function() {
1077-
// Get the transform:
1078-
t = (this.getAttribute('transform') || '').replace(re, '');
1073+
var t = (this.getAttribute('transform') || '').replace(SCALE_RE, '');
10791074
t += scale;
10801075
t = t.trim();
1081-
1082-
// Append the scale transform
10831076
this.setAttribute('transform', t);
10841077
});
1085-
1086-
return scale;
10871078
};
10881079

10891080
var TEXT_POINT_LAST_TRANSLATION_RE = /translate\([^)]*\)\s*$/;
10901081

10911082
drawing.setTextPointsScale = function(selection, xScale, yScale) {
1083+
if(!selection) return;
1084+
10921085
selection.each(function() {
10931086
var transforms;
10941087
var el = d3.select(this);

‎src/plots/cartesian/dragbox.js

+25-27
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
8282
var isSubplotConstrained;
8383
// do we need to edit x/y ranges?
8484
var editX, editY;
85+
// graph-wide optimization flags
86+
var hasScatterGl, hasOnlyLargeSploms, hasSplom, hasSVG;
8587

8688
function recomputeAxisLists() {
8789
xa0 = plotinfo.xaxis;
@@ -117,6 +119,12 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
117119
isSubplotConstrained = links.isSubplotConstrained;
118120
editX = ew || isSubplotConstrained;
119121
editY = ns || isSubplotConstrained;
122+
123+
var fullLayout = gd._fullLayout;
124+
hasScatterGl = fullLayout._has('scattergl');
125+
hasOnlyLargeSploms = fullLayout._hasOnlyLargeSploms;
126+
hasSplom = hasOnlyLargeSploms || fullLayout._has('splom');
127+
hasSVG = fullLayout._has('svg');
120128
}
121129

122130
recomputeAxisLists();
@@ -688,6 +696,10 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
688696
], gd);
689697
}
690698

699+
// x/y scaleFactor stash,
700+
// minimizes number of per-point DOM updates in updateSubplots below
701+
var xScaleFactorOld, yScaleFactorOld;
702+
691703
// updateSubplots - find all plot viewboxes that should be
692704
// affected by this drag, and update them. look for all plots
693705
// sharing an affected axis (including the one being dragged),
@@ -696,14 +708,6 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
696708
var fullLayout = gd._fullLayout;
697709
var plotinfos = fullLayout._plots;
698710
var subplots = fullLayout._subplots.cartesian;
699-
700-
// TODO can we move these to outer scope?
701-
var hasScatterGl = fullLayout._has('scattergl');
702-
var hasOnlyLargeSploms = fullLayout._hasOnlyLargeSploms;
703-
var hasSplom = hasOnlyLargeSploms || fullLayout._has('splom');
704-
var hasSVG = fullLayout._has('svg');
705-
var hasDraggedPts = fullLayout._has('draggedPts');
706-
707711
var i, sp, xa, ya;
708712

709713
if(hasSplom || hasScatterGl) {
@@ -788,26 +792,20 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
788792
.call(Drawing.setTranslate, plotDx, plotDy)
789793
.call(Drawing.setScale, 1 / xScaleFactor2, 1 / yScaleFactor2);
790794

791-
// TODO move these selectAll calls out of here
792-
// and stash them somewhere nice, see:
793-
// https://github.com/plotly/plotly.js/issues/2548
794-
if(hasDraggedPts) {
795-
var traceGroups = sp.plot
796-
.selectAll('.scatterlayer .trace, .boxlayer .trace, .violinlayer .trace');
797-
798-
// This is specifically directed at marker points in scatter, box and violin traces,
799-
// applying an inverse scale to individual points to counteract
800-
// the scale of the trace as a whole:
801-
traceGroups.selectAll('.point')
802-
.call(Drawing.setPointGroupScale, xScaleFactor2, yScaleFactor2);
803-
traceGroups.selectAll('.textpoint')
804-
.call(Drawing.setTextPointsScale, xScaleFactor2, yScaleFactor2);
805-
traceGroups
806-
.call(Drawing.hideOutsideRangePoints, sp);
807-
808-
sp.plot.selectAll('.barlayer .trace')
809-
.call(Drawing.hideOutsideRangePoints, sp, '.bartext');
795+
// apply an inverse scale to individual points to counteract
796+
// the scale of the trace group.
797+
// apply only when scale changes, as adjusting the scale of
798+
// all the points can be expansive.
799+
if(xScaleFactor2 !== xScaleFactorOld || yScaleFactor2 !== yScaleFactorOld) {
800+
Drawing.setPointGroupScale(sp.zoomScalePts, xScaleFactor2, yScaleFactor2);
801+
Drawing.setTextPointsScale(sp.zoomScaleTxt, xScaleFactor2, yScaleFactor2);
810802
}
803+
804+
Drawing.hideOutsideRangePoints(sp.clipOnAxisFalseTraces, sp);
805+
806+
// update x/y scaleFactor stash
807+
xScaleFactorOld = xScaleFactor2;
808+
yScaleFactorOld = yScaleFactor2;
811809
}
812810
}
813811
}

‎src/plots/cartesian/index.js

+25-1
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,14 @@ function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback
188188
var _module, cdModuleAndOthers, cdModule;
189189

190190
var layerData = [];
191+
var zoomScaleQueryParts = [];
191192

192193
for(var i = 0; i < modules.length; i++) {
193194
_module = modules[i];
194195
var name = _module.name;
196+
var categories = Registry.modules[name].categories;
195197

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

@@ -212,6 +214,10 @@ function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback
212214
cdModule: cdModule
213215
});
214216
}
217+
218+
if(categories.zoomScale) {
219+
zoomScaleQueryParts.push('.' + className);
220+
}
215221
}
216222
}
217223

@@ -249,6 +255,24 @@ function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback
249255
cdModule = getModuleCalcData(cdSubplot, _module)[0];
250256
_module.plot(gd, plotinfo, cdModule);
251257
}
258+
259+
// stash "hot" selections for faster interaction on drag and scroll
260+
if(!gd._context.staticPlot) {
261+
if(plotinfo._hasClipOnAxisFalse) {
262+
plotinfo.clipOnAxisFalseTraces = plotinfo.plot
263+
.selectAll('.scatterlayer, .barlayer')
264+
.selectAll('.trace');
265+
}
266+
267+
if(zoomScaleQueryParts.length) {
268+
var traces = plotinfo.plot
269+
.selectAll(zoomScaleQueryParts.join(','))
270+
.selectAll('.trace');
271+
272+
plotinfo.zoomScalePts = traces.selectAll('path.point');
273+
plotinfo.zoomScaleTxt = traces.selectAll('.textpoint');
274+
}
275+
}
252276
}
253277

254278
exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {

‎src/plots/cartesian/transition_axes.js

+5-9
Original file line numberDiff line numberDiff line change
@@ -239,16 +239,12 @@ module.exports = function transitionAxes(gd, newLayout, transitionOpts, makeOnCo
239239

240240
subplot.plot
241241
.call(Drawing.setTranslate, plotDx, plotDy)
242-
.call(Drawing.setScale, xScaleFactor, yScaleFactor)
242+
.call(Drawing.setScale, xScaleFactor, yScaleFactor);
243243

244-
// This is specifically directed at scatter traces, applying an inverse
245-
// scale to individual points to counteract the scale of the trace
246-
// as a whole:
247-
.selectAll('.points').selectAll('.point')
248-
.call(Drawing.setPointGroupScale, 1 / xScaleFactor, 1 / yScaleFactor);
249-
250-
subplot.plot.selectAll('.points').selectAll('.textpoint')
251-
.call(Drawing.setTextPointsScale, 1 / xScaleFactor, 1 / yScaleFactor);
244+
// apply an inverse scale to individual points to counteract
245+
// the scale of the trace group.
246+
Drawing.setPointGroupScale(subplot.zoomScalePts, 1 / xScaleFactor, 1 / yScaleFactor);
247+
Drawing.setTextPointsScale(subplot.zoomScaleTxt, 1 / xScaleFactor, 1 / yScaleFactor);
252248
}
253249

254250
var onComplete;

‎src/traces/bar/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Bar.selectPoints = require('./select');
2828
Bar.moduleType = 'trace';
2929
Bar.name = 'bar';
3030
Bar.basePlotModule = require('../../plots/cartesian');
31-
Bar.categories = ['cartesian', 'svg', 'bar', 'oriented', 'markerColorscale', 'errorBarsOK', 'showLegend', 'draggedPts'];
31+
Bar.categories = ['cartesian', 'svg', 'bar', 'oriented', 'markerColorscale', 'errorBarsOK', 'showLegend', 'zoomScale'];
3232
Bar.meta = {
3333
description: [
3434
'The data visualized by the span of the bars is set in `y`',

‎src/traces/box/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Box.selectPoints = require('./select');
2525
Box.moduleType = 'trace';
2626
Box.name = 'box';
2727
Box.basePlotModule = require('../../plots/cartesian');
28-
Box.categories = ['cartesian', 'svg', 'symbols', 'oriented', 'box-violin', 'showLegend', 'draggedPts', 'boxLayout'];
28+
Box.categories = ['cartesian', 'svg', 'symbols', 'oriented', 'box-violin', 'showLegend', 'boxLayout', 'zoomScale'];
2929
Box.meta = {
3030
description: [
3131
'In vertical (horizontal) box plots,',

‎src/traces/scatter/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Scatter.animatable = true;
3535
Scatter.moduleType = 'trace';
3636
Scatter.name = 'scatter';
3737
Scatter.basePlotModule = require('../../plots/cartesian');
38-
Scatter.categories = ['cartesian', 'svg', 'symbols', 'markerColorscale', 'errorBarsOK', 'showLegend', 'scatter-like', 'draggedPts'];
38+
Scatter.categories = ['cartesian', 'svg', 'symbols', 'markerColorscale', 'errorBarsOK', 'showLegend', 'scatter-like', 'zoomScale'];
3939
Scatter.meta = {
4040
description: [
4141
'The scatter trace type encompasses line charts, scatter charts, text charts, and bubble charts.',

‎src/traces/scattercarpet/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ ScatterCarpet.eventData = require('./event_data');
2424
ScatterCarpet.moduleType = 'trace';
2525
ScatterCarpet.name = 'scattercarpet';
2626
ScatterCarpet.basePlotModule = require('../../plots/cartesian');
27-
ScatterCarpet.categories = ['svg', 'carpet', 'symbols', 'markerColorscale', 'showLegend', 'carpetDependent', 'draggedPts'];
27+
ScatterCarpet.categories = ['svg', 'carpet', 'symbols', 'markerColorscale', 'showLegend', 'carpetDependent', 'zoomScale'];
2828
ScatterCarpet.meta = {
2929
hrName: 'scatter_carpet',
3030
description: [

‎src/traces/violin/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ module.exports = {
2424
moduleType: 'trace',
2525
name: 'violin',
2626
basePlotModule: require('../../plots/cartesian'),
27-
categories: ['cartesian', 'svg', 'symbols', 'oriented', 'box-violin', 'showLegend', 'draggedPts', 'violinLayout'],
27+
categories: ['cartesian', 'svg', 'symbols', 'oriented', 'box-violin', 'showLegend', 'violinLayout', 'zoomScale'],
2828
meta: {
2929
description: [
3030
'In vertical (horizontal) violin plots,',

0 commit comments

Comments
 (0)
Please sign in to comment.