Skip to content

Fix relayout constrained axes + various multi-subplot perf improvements #2628

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 11 commits into from
May 14, 2018
Merged
Prev Previous commit
split doTicks into multi-axes and doTicksSingle parts
  • Loading branch information
etpinard committed May 11, 2018
commit 72b655858d43a480d84785754a89589e1b651330
2 changes: 1 addition & 1 deletion src/components/colorbar/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ module.exports = function draw(gd, id) {
// this title call only handles side=right
return Lib.syncOrAsync([
function() {
return Axes.doTicks(gd, cbAxisOut, true);
return Axes.doTicksSingle(gd, cbAxisOut, true);
},
function() {
if(['top', 'bottom'].indexOf(opts.titleside) === -1) {
Expand Down
103 changes: 58 additions & 45 deletions src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -1498,74 +1498,87 @@ axes.makeClipPaths = function(gd) {
});
};

/** Main axis drawing routine!
*
* This routine draws axis ticks and much more (... grids, labels, title etc.)
* Supports multiple argument signatures.
* N.B. this thing is async in general (because of MathJax rendering)
/** Main multi-axis drawing routine!
*
* @param {DOM element} gd : graph div
* @param {object or string or array of strings} arg : polymorphic argument
* @param {string or array of strings} arg : polymorphic argument
* @param {boolean} skipTitle : optional flag to skip axis title draw/update
* @return {promise}
*
* Signature 1: Axes.doTicks(gd, ax)
* where ax is an axis object as in fullLayout
*
* Signature 2: Axes.doTicks(gd, axId)
* where axId is a axis id string
*
* Signature 3: Axes.doTicks(gd, 'redraw')
* Signature 1: Axes.doTicks(gd, 'redraw')
* use this to clear and redraw all axes on graph
*
* Signature 4: Axes.doTicks(gd, '')
* Signature 2: Axes.doTicks(gd, '')
* use this to draw all axes on graph w/o the selectAll().remove()
* of the 'redraw' signature
*
* Signature 5: Axes.doTicks(gd, [axId, axId2, ...])
* Signature 3: Axes.doTicks(gd, [axId, axId2, ...])
* where the items are axis id string,
* use this to update multiple axes in one call
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was already a bit out of control, do we really want to add another signature?

Can we perhaps break it into 2 separate functions, one for signatures 1 & 2 and a second for 3, 4, 5? That's how it functions anyway - 3,4,5 just map into a bunch of calls to signature 2 (ignoring skipTitle) and then return.

Copy link
Contributor Author

@etpinard etpinard May 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure that would be nice. But since doTicks is one of our most referenced function in existing issues, I'm a little reluctant to do so.

But if you insist, than I'm thinking of making Axes.doTicks the multi-cartesian-axis version (that accepts '', 'redraw' and arrays of axis ids), and adding Axes.doTicksSingle (that accepts one axis id string or an axis object).

On master, we have:

image

*
* N.B signatures 3, 4 and 5 reset:
* N.B doTicks updates:
* - ax._r (stored range for use by zoom/pan)
* - ax._rl (stored linearized range for use by zoom/pan)
*/
axes.doTicks = function(gd, arg, skipTitle) {
var fullLayout = gd._fullLayout;

if(arg === 'redraw') {
fullLayout._paper.selectAll('g.subplot').each(function(subplot) {
var plotinfo = fullLayout._plots[subplot];
var xa = plotinfo.xaxis;
var ya = plotinfo.yaxis;

plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick').remove();
plotinfo.yaxislayer.selectAll('.' + ya._id + 'tick').remove();
if(plotinfo.gridlayer) plotinfo.gridlayer.selectAll('path').remove();
if(plotinfo.zerolinelayer) plotinfo.zerolinelayer.selectAll('path').remove();
fullLayout._infolayer.select('.g-' + xa._id + 'title').remove();
fullLayout._infolayer.select('.g-' + ya._id + 'title').remove();
});
}

var axList = (!arg || arg === 'redraw') ? axes.listIds(gd) : arg;

Lib.syncOrAsync(axList.map(function(axid) {
return function() {
if(!axid) return;

var axDone = axes.doTicksSingle(gd, axid, skipTitle);

var ax = axes.getFromId(gd, axid);
ax._r = ax.range.slice();
ax._rl = Lib.simpleMap(ax._r, ax.r2l);

return axDone;
};
}));
};

/** Per axis drawing routine!
*
* This routine draws axis ticks and much more (... grids, labels, title etc.)
* Supports multiple argument signatures.
* N.B. this thing is async in general (because of MathJax rendering)
*
* @param {DOM element} gd : graph div
* @param {string or array of strings} arg : polymorphic argument
* @param {boolean} skipTitle : optional flag to skip axis title draw/update
* @return {promise}
*
* Signature 1: Axes.doTicks(gd, ax)
* where ax is an axis object as in fullLayout
*
* Signature 2: Axes.doTicks(gd, axId)
* where axId is a axis id string
*/
axes.doTicksSingle = function(gd, arg, skipTitle) {
var fullLayout = gd._fullLayout;
var independent = false;
var ax;

if(Lib.isPlainObject(arg)) {
ax = arg;
independent = true;
} else if(!arg || arg === 'redraw' || Array.isArray(arg)) {
var axList = (!arg || arg === 'redraw') ? axes.listIds(gd) : arg;

if(arg === 'redraw') {
fullLayout._paper.selectAll('g.subplot').each(function(subplot) {
var plotinfo = fullLayout._plots[subplot];
var xa = plotinfo.xaxis;
var ya = plotinfo.yaxis;

plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick').remove();
plotinfo.yaxislayer.selectAll('.' + ya._id + 'tick').remove();
if(plotinfo.gridlayer) plotinfo.gridlayer.selectAll('path').remove();
if(plotinfo.zerolinelayer) plotinfo.zerolinelayer.selectAll('path').remove();
fullLayout._infolayer.select('.g-' + xa._id + 'title').remove();
fullLayout._infolayer.select('.g-' + ya._id + 'title').remove();
});
}

return Lib.syncOrAsync(axList.map(function(a) {
return function() {
if(!a) return;
var axDone = axes.doTicks(gd, a);
var ax = axes.getFromId(gd, a);
ax._r = ax.range.slice();
ax._rl = Lib.simpleMap(ax._r, ax.r2l);
return axDone;
};
}));
} else {
ax = axes.getFromId(gd, arg);
}
Expand Down
4 changes: 2 additions & 2 deletions src/plots/cartesian/dragbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var FROM_TL = require('../../constants/alignment').FROM_TL;

var Plots = require('../plots');

var doTicks = require('./axes').doTicks;
var doTicksSingle = require('./axes').doTicksSingle;
var getFromId = require('./axis_ids').getFromId;
var prepSelect = require('./select').prepSelect;
var clearSelect = require('./select').clearSelect;
Expand Down Expand Up @@ -585,7 +585,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
updates = {};
for(i = 0; i < activeAxIds.length; i++) {
var axId = activeAxIds[i];
doTicks(gd, axId, true);
doTicksSingle(gd, axId, true);
var ax = getFromId(gd, axId);
updates[ax._name + '.range[0]'] = ax.range[0];
updates[ax._name + '.range[1]'] = ax.range[1];
Expand Down
2 changes: 1 addition & 1 deletion src/plots/cartesian/transition_axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ module.exports = function transitionAxes(gd, newLayout, transitionOpts, makeOnCo
activeAxIds = [xa._id, ya._id];

for(i = 0; i < activeAxIds.length; i++) {
Axes.doTicks(gd, activeAxIds[i], true);
Axes.doTicksSingle(gd, activeAxIds[i], true);
}

function redrawObjs(objArray, method, shortCircuit) {
Expand Down
18 changes: 9 additions & 9 deletions src/plots/polar/polar.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function Polar(gd, id) {
.attr('class', id);

// unfortunately, we have to keep track of some axis tick settings
// so that we don't have to call Axes.doTicks with its special redraw flag
// so that we don't have to call Axes.doTicksSingle with its special redraw flag
this.radialTickLayout = null;
this.angularTickLayout = null;
}
Expand Down Expand Up @@ -286,7 +286,7 @@ proto.updateRadialAxis = function(fullLayout, polarLayout) {
anchor: 'free',
position: 0,

// dummy truthy value to make Axes.doTicks draw the grid
// dummy truthy value to make Axes.doTicksSingle draw the grid
_counteraxis: true,

// don't use automargins routine for labels
Expand All @@ -302,7 +302,7 @@ proto.updateRadialAxis = function(fullLayout, polarLayout) {
// rotate auto tick labels by 180 if in quadrant II and III to make them
// readable from left-to-right
//
// TODO try moving deeper in doTicks for better results?
// TODO try moving deeper in doTicksSingle for better results?
if(ax.tickangle === 'auto' && (a0 > 90 && a0 <= 270)) {
ax.tickangle = 180;
}
Expand All @@ -324,7 +324,7 @@ proto.updateRadialAxis = function(fullLayout, polarLayout) {
_this.radialTickLayout = newTickLayout;
}

Axes.doTicks(gd, ax, true);
Axes.doTicksSingle(gd, ax, true);

updateElement(layers['radial-axis'], radialLayout.showticklabels || radialLayout.ticks, {
transform: strTranslate(cx, cy) + strRotate(-radialLayout.angle)
Expand Down Expand Up @@ -413,7 +413,7 @@ proto.updateAngularAxis = function(fullLayout, polarLayout) {
anchor: 'free',
position: 0,

// dummy truthy value to make Axes.doTicks draw the grid
// dummy truthy value to make Axes.doTicksSingle draw the grid
_counteraxis: true,

// don't use automargins routine for labels
Expand Down Expand Up @@ -460,7 +460,7 @@ proto.updateAngularAxis = function(fullLayout, polarLayout) {
setScale(ax, angularLayout, fullLayout);

// wrapper around c2rad from setConvertAngular
// note that linear ranges are always set in degrees for Axes.doTicks
// note that linear ranges are always set in degrees for Axes.doTicksSingle
function c2rad(d) {
return ax.c2rad(d.x, 'degrees');
}
Expand Down Expand Up @@ -530,7 +530,7 @@ proto.updateAngularAxis = function(fullLayout, polarLayout) {
_this.angularTickLayout = newTickLayout;
}

Axes.doTicks(gd, ax, true);
Axes.doTicksSingle(gd, ax, true);

updateElement(layers['angular-line'].select('path'), angularLayout.showline, {
d: pathSectorClosed(radius, sector),
Expand Down Expand Up @@ -833,7 +833,7 @@ proto.updateRadialDrag = function(fullLayout, polarLayout) {
if((drange > 0) !== (rprime > range0[0])) return;
rng1 = radialAxis.range[1] = rprime;

Axes.doTicks(gd, _this.radialAxis, true);
Axes.doTicksSingle(gd, _this.radialAxis, true);
layers['radial-grid']
.attr('transform', strTranslate(cx, cy))
.selectAll('path').attr('transform', null);
Expand Down Expand Up @@ -961,7 +961,7 @@ proto.updateAngularDrag = function(fullLayout, polarLayout) {
}

setConvertAngular(angularAxis);
Axes.doTicks(gd, angularAxis, true);
Axes.doTicksSingle(gd, angularAxis, true);

if(_this._hasClipOnAxisFalse && !isFullCircle(sector)) {
// mutate sector to trick isPtWithinSector
Expand Down
6 changes: 3 additions & 3 deletions src/plots/ternary/ternary.js
Original file line number Diff line number Diff line change
Expand Up @@ -376,9 +376,9 @@ proto.drawAxes = function(doTitles) {
caxis = _this.caxis;
// 3rd arg true below skips titles, so we can configure them
// correctly later on.
Axes.doTicks(gd, aaxis, true);
Axes.doTicks(gd, baxis, true);
Axes.doTicks(gd, caxis, true);
Axes.doTicksSingle(gd, aaxis, true);
Axes.doTicksSingle(gd, baxis, true);
Axes.doTicksSingle(gd, caxis, true);

if(doTitles) {
var apad = Math.max(aaxis.showticklabels ? aaxis.tickfont.size / 2 : 0,
Expand Down