Skip to content
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
139 changes: 112 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,30 @@ 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);
// lookup and list of axis ids that axes in axNames have a reference to,
// even though they are missing from allAxisIds
var missingMatchedAxisIdsLookup = {};
var missingMatchedAxisIds = [];

// fill in 'missing' axis lookup when an axis is set to match an axis
// not part of the allAxisIds list, save axis type so that we can propagate
// it to the missing axes
function addMissingMatchedAxis() {
var matchesIn = axLayoutIn.matches;
if(AX_ID_PATTERN.test(matchesIn) && allAxisIds.indexOf(matchesIn) === -1) {
missingMatchedAxisIdsLookup[matchesIn] = axLayoutIn.type;
missingMatchedAxisIds = Object.keys(missingMatchedAxisIdsLookup);
}
}

// 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 +217,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 +235,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 +267,63 @@ 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();

axLayoutOut._input = axLayoutIn;
}

// coerce the 'missing' axes
i = 0;
while(i < missingMatchedAxisIds.length) {
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);

axLayoutOut.type = missingMatchedAxisIdsLookup[axId] || 'linear';

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

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

coerce('fixedrange');

addMissingMatchedAxis();

axLayoutOut._input = axLayoutIn;
}

Expand Down Expand Up @@ -295,25 +374,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 +410,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
39 changes: 30 additions & 9 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 Expand Up @@ -2934,15 +2937,15 @@ plots.doCalcdata = function(gd, traces) {
calcdata[i] = cd;
}

setupAxisCategories(axList, fullData);
setupAxisCategories(axList, fullData, fullLayout);

// 'transform' loop - must calc container traces first
// so that if their dependent traces can get transform properly
for(i = 0; i < fullData.length; i++) calci(i, true);
for(i = 0; i < fullData.length; i++) transformCalci(i);

// clear stuff that should recomputed in 'regular' loop
if(hasCalcTransform) setupAxisCategories(axList, fullData);
if(hasCalcTransform) setupAxisCategories(axList, fullData, fullLayout);

// 'regular' loop - make sure container traces (eg carpet) calc before
// contained traces (eg contourcarpet)
Expand Down Expand Up @@ -3147,13 +3150,31 @@ function sortAxisCategoriesByValue(axList, gd) {
return affectedTraces;
}

function setupAxisCategories(axList, fullData) {
for(var i = 0; i < axList.length; i++) {
var ax = axList[i];
function setupAxisCategories(axList, fullData, fullLayout) {
var axLookup = {};
var i, ax, axId;

for(i = 0; i < axList.length; i++) {
ax = axList[i];
axId = ax._id;

ax.clearCalc();
if(ax.type === 'multicategory') {
ax.setupMultiCategory(fullData);
}

axLookup[ax._id] = 1;
}

// look into match groups for 'missing' axes
var matchGroups = fullLayout._axisMatchGroups || [];
for(i = 0; i < matchGroups.length; i++) {
for(axId in matchGroups[i]) {
if(!axLookup[axId]) {
ax = fullLayout[axisIDs.id2name(axId)];
ax.clearCalc();
}
}
}
}

Expand Down
Binary file added test/image/baselines/matching-missing-axes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/image/compare_pixels_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ var FLAKY_LIST = [
'treemap_coffee',
'treemap_textposition',
'treemap_with-without_values_template',
'treemap_with-without_values',
'trace_metatext',
'gl3d_directions-streamtube1'
];
Expand Down
Loading