Skip to content

Commit

Permalink
first cut at static ax.matches behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
etpinard committed Feb 1, 2019
1 parent 6813865 commit 6f2bb78
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 22 deletions.
29 changes: 24 additions & 5 deletions src/plot_api/plot_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -1948,6 +1948,10 @@ function axRangeSupplyDefaultsByPass(gd, flags, specs) {
axOut.range = axIn.range.slice();
axOut.cleanRange();
}

// no need to consider matching axes here,
// if we keep block in doAutoRangeAndConstraints

return true;
}

Expand All @@ -1957,14 +1961,29 @@ function addAxRangeSequence(seq, rangesAltered) {
// executed after drawData
var drawAxes = rangesAltered ?
function(gd) {
var opts = {skipTitle: true};
var axIds = [];
var skipTitle = true;
var matchGroups = gd._fullLayout._axisMatchGroups || [];

for(var id in rangesAltered) {
if(Axes.getFromId(gd, id).automargin) {
opts = {};
break;
var ax = Axes.getFromId(gd, id);
axIds.push(id);

for(var i = 0; i < matchGroups.length; i++) {
var group = matchGroups[i];
if(group[id]) {
for(var id2 in group) {
if(!rangesAltered[id2]) {
axIds.push(id2);
}
}
}
}

if(ax.automargin) skipTitle = false;
}
return Axes.draw(gd, Object.keys(rangesAltered), opts);

return Axes.draw(gd, axIds, {skipTitle: skipTitle});
} :
function(gd) {
return Axes.draw(gd, 'redraw');
Expand Down
38 changes: 35 additions & 3 deletions src/plot_api/subroutines.js
Original file line number Diff line number Diff line change
Expand Up @@ -697,16 +697,48 @@ exports.redrawReglTraces = function(gd) {
};

exports.doAutoRangeAndConstraints = function(gd) {
var fullLayout = gd._fullLayout;
var axList = Axes.list(gd, '', true);
var matchGroups = fullLayout._axisMatchGroups || [];
var ax;

for(var i = 0; i < axList.length; i++) {
var ax = axList[i];
ax = axList[i];
cleanAxisConstraints(gd, ax);
// in case margins changed, update scale
ax.setScale();
doAutoRange(gd, ax);
}

// TODO bypass this when matching axes aren't autoranged?
for(var j = 0; j < matchGroups.length; j++) {
var group = matchGroups[j];
var rng = null;
var id;

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

if(rng) {
if(rng[0] < rng[1]) {
rng[0] = Math.min(rng[0], ax.range[0]);
rng[1] = Math.max(rng[1], ax.range[1]);
} else {
rng[0] = Math.max(rng[0], ax.range[0]);
rng[1] = Math.min(rng[1], ax.range[1]);
}
} else {
rng = ax.range;
}
}

for(id in group) {
ax = Axes.getFromId(gd, id);
ax.range = rng.slice();
ax._input.range = rng.slice();
ax.setScale(0);
}
}

// TODO before or after matching axes?
enforceAxisConstraints(gd);
};

Expand Down
2 changes: 2 additions & 0 deletions src/plots/cartesian/autorange.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@ function concatExtremes(gd, ax) {
}

function doAutoRange(gd, ax) {
ax.setScale();

if(ax.autorange) {
ax.range = getAutoRange(gd, ax);

Expand Down
31 changes: 25 additions & 6 deletions src/plots/cartesian/constraints.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var FROM_BL = require('../../constants/alignment').FROM_BL;

exports.handleConstraintDefaults = function(containerIn, containerOut, coerce, allAxisIds, layoutOut) {
var constraintGroups = layoutOut._axisConstraintGroups;
var matchGroups = layoutOut._axisMatchGroups;
var thisID = containerOut._id;
var letter = thisID.charAt(0);

Expand All @@ -35,30 +36,48 @@ exports.handleConstraintDefaults = function(containerIn, containerOut, coerce, a
}
}, 'constraintoward');

if(!containerIn.scaleanchor) return;
if(!containerIn.scaleanchor && !containerIn.matches) return;

var constraintOpts = getConstraintOpts(constraintGroups, thisID, allAxisIds, layoutOut);
var opts = getConstraintOpts(constraintGroups, thisID, allAxisIds, layoutOut);

var scaleanchor = Lib.coerce(containerIn, containerOut, {
scaleanchor: {
valType: 'enumerated',
values: constraintOpts.linkableAxes
values: opts.linkableAxes
}
}, 'scaleanchor');

var matches = Lib.coerce(containerIn, containerOut, {
matches: {
valType: 'enumerated',
values: opts.linkableAxes
}
}, 'matches');

var found = false;

if(scaleanchor) {
var scaleratio = coerce('scaleratio');

// TODO: I suppose I could do attribute.min: Number.MIN_VALUE to avoid zero,
// but that seems hacky. Better way to say "must be a positive number"?
// Of course if you use several super-tiny values you could eventually
// force a product of these to zero and all hell would break loose...
// Likewise with super-huge values.
if(!scaleratio) scaleratio = containerOut.scaleratio = 1;

updateConstraintGroups(constraintGroups, constraintOpts.thisGroup,
thisID, scaleanchor, scaleratio);
updateConstraintGroups(constraintGroups, opts.thisGroup, thisID, scaleanchor, scaleratio);
found = true;
}
else if(allAxisIds.indexOf(containerIn.scaleanchor) !== -1) {

// TODO what happens when both scaleanchor and matches are present???

if(matches) {
updateConstraintGroups(matchGroups, opts.thisGroup, thisID, matches, 1);
found = true;
}

if(!found && allAxisIds.indexOf(containerIn.scaleanchor) !== -1) {
Lib.warn('ignored ' + containerOut._name + '.scaleanchor: "' +
containerIn.scaleanchor + '" to avoid either an infinite loop ' +
'and possibly inconsistent scaleratios, or because the target' +
Expand Down
17 changes: 17 additions & 0 deletions src/plots/cartesian/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,23 @@ module.exports = {
'and *right* for x axes, and *top*, *middle* (default), and *bottom* for y axes.'
].join(' ')
},
matches: {
valType: 'enumerated',
values: [
constants.idRegex.x.toString(),
constants.idRegex.y.toString()
],
role: 'info',
editType: 'calc',
description: [
'If set to another axis id (e.g. `x2`, `y`), the range of this axis',
'will match the range of the corresponding axis in data-coordinates space.',
'Moreover, matching axes share auto-range values, category lists and',
'histogram auto-bins.',
'Note that setting `matches` and `scaleratio` under a *range* `constrain`',
'to the same axis id is forbidden.'
].join(' ')
},
// ticks
tickmode: {
valType: 'enumerated',
Expand Down
49 changes: 41 additions & 8 deletions src/plots/cartesian/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
}

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;
Expand Down Expand Up @@ -199,14 +200,12 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
delete axLayoutOut.spikesnap;
}

var positioningOptions = {
handlePositionDefaults(axLayoutIn, axLayoutOut, coerce, {
letter: axLetter,
counterAxes: counterAxes[axLetter],
overlayableAxes: overlayableAxes,
grid: layoutOut.grid
};

handlePositionDefaults(axLayoutIn, axLayoutOut, coerce, positioningOptions);
});

axLayoutOut._input = axLayoutIn;
}
Expand Down Expand Up @@ -247,22 +246,56 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
coerce('fixedrange', fixedRangeDflt);
}

// Finally, handle scale constraints. We need to do this after all axes have
// coerced both `type` (so we link only axes of the same type) and
// Finally, handle scale constraints and matching axes.
//
// We need to do this after all axes have coerced both `type`
// (so we link only axes of the same type) and
// `fixedrange` (so we can avoid linking from OR TO a fixed axis).

// sets of axes linked by `scaleanchor` along with the scaleratios compounded
// together, populated in handleConstraintDefaults
layoutOut._axisConstraintGroups = [];
var allAxisIds = counterAxes.x.concat(counterAxes.y);
// similar to _axisConstraintGroups, but for matching axes
layoutOut._axisMatchGroups = [];

for(i = 0; i < axNames.length; i++) {
axName = axNames[i];
axLetter = axName.charAt(0);

axLayoutIn = layoutIn[axName];
axLayoutOut = layoutOut[axName];

handleConstraintDefaults(axLayoutIn, axLayoutOut, coerce, allAxisIds, layoutOut);
}

for(i = 0; i < layoutOut._axisMatchGroups.length; i++) {
var group = layoutOut._axisMatchGroups[i];
var rng = null;
var autorange = null;
var axId;

for(axId in group) {
axLayoutOut = layoutOut[id2name(axId)];
if(!axLayoutOut.matches) {
rng = axLayoutOut.range;
autorange = axLayoutOut.autorange;
}
}

if(rng === null || autorange === null) {
for(axId in group) {
axLayoutOut = layoutOut[id2name(axId)];
rng = axLayoutOut.range;
autorange = axLayoutOut.autorange;
break;
}
}

for(axId in group) {
axLayoutOut = layoutOut[id2name(axId)];
if(axLayoutOut.matches) {
axLayoutOut.range = rng.slice();
axLayoutOut.autorange = autorange;
}
}
}
};
Loading

0 comments on commit 6f2bb78

Please sign in to comment.