Skip to content
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

Handle 'missing' matching axes #4529

Merged
merged 11 commits into from
Jan 31, 2020
6 changes: 6 additions & 0 deletions src/plot_api/subroutines.js
Original file line number Diff line number Diff line change
Expand Up @@ -670,13 +670,15 @@ exports.doAutoRangeAndConstraints = function(gd) {
var fullLayout = gd._fullLayout;
var axList = Axes.list(gd, '', true);
var matchGroups = fullLayout._axisMatchGroups || [];
var axLookup = {};
var ax;
var axRng;

for(var i = 0; i < axList.length; i++) {
ax = axList[i];
cleanAxisConstraints(gd, ax);
doAutoRange(gd, ax);
axLookup[ax._id] = 1;
}

enforceAxisConstraints(gd);
Expand All @@ -689,6 +691,10 @@ exports.doAutoRangeAndConstraints = function(gd) {

for(id in group) {
ax = Axes.getFromId(gd, id);

// skip over 'missing' axes which do not pass through doAutoRange
if(!axLookup[ax._id]) continue;
// if one axis has autorange false, we're done
if(ax.autorange === false) continue groupLoop;

axRng = Lib.simpleMap(ax.range, ax.r2l);
Expand Down
8 changes: 6 additions & 2 deletions src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -1669,10 +1669,14 @@ axes.drawOne = function(gd, ax, opts) {
var axId = ax._id;
var axLetter = axId.charAt(0);
var counterLetter = axes.counterLetter(axId);
var mainLinePosition = ax._mainLinePosition;
var mainMirrorPosition = ax._mainMirrorPosition;
var mainPlotinfo = fullLayout._plots[ax._mainSubplot];

// this happens when updating matched group with 'missing' axes
if(!mainPlotinfo) return;

var mainAxLayer = mainPlotinfo[axLetter + 'axislayer'];
var mainLinePosition = ax._mainLinePosition;
var mainMirrorPosition = ax._mainMirrorPosition;

var vals = ax._vals = axes.calcTicks(ax);

Expand Down
3 changes: 1 addition & 2 deletions src/plots/cartesian/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@
*/

'use strict';
var counterRegex = require('../../lib/regex').counter;

var counterRegex = require('../../lib/regex').counter;

module.exports = {

idRegex: {
x: counterRegex('x'),
y: counterRegex('y')
Expand Down
132 changes: 105 additions & 27 deletions src/plots/cartesian/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ var axisIds = require('./axis_ids');
var id2name = axisIds.id2name;
var name2id = axisIds.name2id;

var AX_ID_PATTERN = require('./constants').AX_ID_PATTERN;

var Registry = require('../../registry');
var traceIs = Registry.traceIs;
var getComponentMethod = Registry.getComponentMethod;
Expand Down Expand Up @@ -133,7 +135,28 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {

var bgColor = Color.combine(plotBgColor, layoutOut.paper_bgcolor);

var axName, axLetter, axLayoutIn, axLayoutOut;
// name of single axis (e.g. 'xaxis', 'yaxis2')
var axName;
// id of single axis (e.g. 'y', 'x5')
var axId;
// 'x' or 'y'
var axLetter;
// input layout axis container
var axLayoutIn;
// full layout axis container
var axLayoutOut;

function newAxLayoutOut() {
var traces = ax2traces[axName] || [];
axLayoutOut._traceIndices = traces.map(function(t) { return t._expandedIndex; });
axLayoutOut._annIndices = [];
axLayoutOut._shapeIndices = [];
axLayoutOut._imgIndices = [];
axLayoutOut._subplotsWith = [];
axLayoutOut._counterAxes = [];
axLayoutOut._name = axLayoutOut._attr = axName;
axLayoutOut._id = axId;
}

function coerce(attr, dflt) {
return Lib.coerce(axLayoutIn, axLayoutOut, layoutAttributes, attr, dflt);
Expand All @@ -147,9 +170,6 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
return (axLetter === 'x') ? yIds : xIds;
}

var counterAxes = {x: getCounterAxes('x'), y: getCounterAxes('y')};
var allAxisIds = counterAxes.x.concat(counterAxes.y);

function getOverlayableAxes(axLetter, axName) {
var list = (axLetter === 'x') ? xNames : yNames;
var out = [];
Expand All @@ -165,9 +185,26 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
return out;
}

// list of available counter axis names
var counterAxes = {x: getCounterAxes('x'), y: getCounterAxes('y')};
// list of all x AND y axis ids
var allAxisIds = counterAxes.x.concat(counterAxes.y);
// list of axis ids that axes in axNames have a reference to,
// even though they are missing from allAxisIds
var missingMatchedAxisIds = [];

// fill in 'missing' axis list when an axis is set to match an axis
// not part of the allAxisIds list
function addMissingMatchedAxis(matchesIn) {
if(AX_ID_PATTERN.test(matchesIn) && allAxisIds.indexOf(matchesIn) === -1) {
Lib.pushUnique(missingMatchedAxisIds, matchesIn);
}
}

// first pass creates the containers, determines types, and handles most of the settings
for(i = 0; i < axNames.length; i++) {
axName = axNames[i];
axId = name2id(axName);
axLetter = axName.charAt(0);

if(!Lib.isPlainObject(layoutIn[axName])) {
Expand All @@ -176,20 +213,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {

axLayoutIn = layoutIn[axName];
axLayoutOut = Template.newContainer(layoutOut, axName, axLetter + 'axis');

var traces = ax2traces[axName] || [];
axLayoutOut._traceIndices = traces.map(function(t) { return t._expandedIndex; });
axLayoutOut._annIndices = [];
axLayoutOut._shapeIndices = [];
axLayoutOut._imgIndices = [];
axLayoutOut._subplotsWith = [];
axLayoutOut._counterAxes = [];

// set up some private properties
axLayoutOut._name = axLayoutOut._attr = axName;
var id = axLayoutOut._id = name2id(axName);

var overlayableAxes = getOverlayableAxes(axLetter, axName);
newAxLayoutOut();

var visibleDflt =
(axLetter === 'x' && !xaMustDisplay[axName] && xaMayHide[axName]) ||
Expand All @@ -207,13 +231,13 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
font: layoutOut.font,
outerTicks: outerTicks[axName],
showGrid: !noGrids[axName],
data: traces,
data: ax2traces[axName] || [],
bgColor: bgColor,
calendar: layoutOut.calendar,
automargin: true,
visibleDflt: visibleDflt,
reverseDflt: reverseDflt,
splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[id]
splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId]
};

coerce('uirevision', layoutOut.uirevision);
Expand All @@ -239,12 +263,60 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
handlePositionDefaults(axLayoutIn, axLayoutOut, coerce, {
letter: axLetter,
counterAxes: counterAxes[axLetter],
overlayableAxes: overlayableAxes,
overlayableAxes: getOverlayableAxes(axLetter, axName),
grid: layoutOut.grid
});

coerce('title.standoff');

addMissingMatchedAxis(axLayoutIn.matches);

axLayoutOut._input = axLayoutIn;
}

// coerce the 'missing' axes
i = 0;
while(i < missingMatchedAxisIds.length) {
archmoj marked this conversation as resolved.
Show resolved Hide resolved
axId = missingMatchedAxisIds[i++];
axName = id2name(axId);
axLetter = axName.charAt(0);

if(!Lib.isPlainObject(layoutIn[axName])) {
layoutIn[axName] = {};
}

axLayoutIn = layoutIn[axName];
axLayoutOut = Template.newContainer(layoutOut, axName, axLetter + 'axis');
newAxLayoutOut();

var defaultOptions2 = {
letter: axLetter,
font: layoutOut.font,
outerTicks: outerTicks[axName],
showGrid: !noGrids[axName],
data: [],
bgColor: bgColor,
calendar: layoutOut.calendar,
automargin: true,
visibleDflt: false,
reverseDflt: false,
splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId]
};

coerce('uirevision', layoutOut.uirevision);

handleTypeDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions2);
handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions2, layoutOut);

handlePositionDefaults(axLayoutIn, axLayoutOut, coerce, {
letter: axLetter,
counterAxes: counterAxes[axLetter],
overlayableAxes: getOverlayableAxes(axLetter, axName),
grid: layoutOut.grid
});

addMissingMatchedAxis(axLayoutIn.matches);

axLayoutOut._input = axLayoutIn;
}

Expand Down Expand Up @@ -295,25 +367,32 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
var constraintGroups = layoutOut._axisConstraintGroups = [];
// similar to _axisConstraintGroups, but for matching axes
var matchGroups = layoutOut._axisMatchGroups = [];
// make sure to include 'missing' axes here
var allAxisIdsIncludingMissing = allAxisIds.concat(missingMatchedAxisIds);
var axNamesIncludingMissing = axNames.concat(Lib.simpleMap(missingMatchedAxisIds, id2name));

for(i = 0; i < axNames.length; i++) {
axName = axNames[i];
for(i = 0; i < axNamesIncludingMissing.length; i++) {
axName = axNamesIncludingMissing[i];
axLetter = axName.charAt(0);
axLayoutIn = layoutIn[axName];
axLayoutOut = layoutOut[axName];

var scaleanchorDflt;
if(axLetter === 'y' && !axLayoutIn.hasOwnProperty('scaleanchor') && axHasImage[axName]) {
scaleanchorDflt = axLayoutOut.anchor;
} else {scaleanchorDflt = undefined;}
} else {
scaleanchorDflt = undefined;
}

var constrainDflt;
if(!axLayoutIn.hasOwnProperty('constrain') && axHasImage[axName]) {
constrainDflt = 'domain';
} else {constrainDflt = undefined;}
} else {
constrainDflt = undefined;
}

handleConstraintDefaults(axLayoutIn, axLayoutOut, coerce, {
allAxisIds: allAxisIds,
allAxisIds: allAxisIdsIncludingMissing,
layoutOut: layoutOut,
scaleanchorDflt: scaleanchorDflt,
constrainDflt: constrainDflt
Expand All @@ -324,7 +403,6 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
var group = matchGroups[i];
var rng = null;
var autorange = null;
var axId;

// find 'matching' range attrs
for(axId in group) {
Expand Down
11 changes: 7 additions & 4 deletions src/plots/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -1272,10 +1272,13 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac
var subplots = layout._subplots;
var subplotId = '';

// TODO - currently if we draw an empty gl2d subplot, it draws
// nothing then gets stuck and you can't get it back without newPlot
// sort this out in the regl refactor? but for now just drop empty gl2d subplots
if(basePlotModule.name !== 'gl2d' || visible) {
if(
visible ||
basePlotModule.name !== 'gl2d' // for now just drop empty gl2d subplots
// TODO - currently if we draw an empty gl2d subplot, it draws
// nothing then gets stuck and you can't get it back without newPlot
// sort this out in the regl refactor?
) {
if(Array.isArray(subplotAttr)) {
for(i = 0; i < subplotAttr.length; i++) {
var attri = subplotAttr[i];
Expand Down
Loading