8000 Implementing matching axes by etpinard · Pull Request #3506 · plotly/plotly.js · GitHub
[go: up one dir, main page]

Skip to content

Implementing matching axes #3506

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 23 commits into from
Feb 18, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6813865
mv handleConstraintDefaults into constraints.js file
etpinard Jan 24, 2019
6f2bb78
first cut at static `ax.matches` behavior
etpinard Jan 23, 2019
5ddbf2a
some linting & commenting in dragbox.js
etpinard Feb 1, 2019
c60424b
first cut at zoom/pan/scroll `ax.matches` behavior
etpinard Feb 1, 2019
13007e0
add doScroll wrapper for dragbox tests
etpinard Jan 31, 2019
e962e96
mv dragbox tests calling makePlot to own describe block
etpinard Jan 31, 2019
ca1de5d
DRY-up drag-start/assert/drag-end tests
etpinard Jan 31, 2019
3b314fe
add toBeWithinArray custom jasmine matcher
etpinard Feb 1, 2019
6621419
add mucho matching axes dragbox tests
etpinard Jan 29, 2019
9118505
align autobinning of histogram traces on matching axes
etpinard Jan 28, 2019
d9c2d4e
link ax._categories & ax._categoriesMap to same ref for matching axes
etpinard Jan 28, 2019
c290adf
add axis.matches boolean to splom dims
etpinard Feb 1, 2019
ef256db
generalize "update matched ax rng" logic
etpinard Feb 1, 2019
9f2fad1
disallow constraining AND matching range
etpinard Feb 1, 2019
8c09944
add "matches" + "scaleanchor" mock
etpinard Feb 1, 2019
c5f9e74
fix partial ax-range relayout calls for matching axes
etpinard Feb 1, 2019
d0581e2
fix typo (ax.setScale has no arg)
etpinard Feb 5, 2019
6b34ae3
use ax._matchGroup to improve matching axes relayout perf
etpinard Feb 5, 2019
99a9edb
allow fixedrange subplots to scaleanchor with `constrain:domain`
etpinard Feb 6, 2019
91431ec
drop *scaleanchor* constraints for axes under *matches* constraint
etpinard Feb 12, 2019
70b10ff
add safe-guard in dragbox.js
etpinard Feb 18, 2019
772efe5
add info about matching axis type in attr description
etpinard Feb 18, 2019
1713c83
fix typo
etpinard Feb 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
mv handleConstraintDefaults into constraints.js file
  • Loading branch information
etpinard committed Feb 1, 2019
commit 681386563a6d9f0056f9a38975d3279ed25b5328
152 changes: 0 additions & 152 deletions src/plots/cartesian/constraint_defaults.js

This file was deleted.

139 changes: 137 additions & 2 deletions src/plots/cartesian/constraints.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,153 @@
* LICENSE file in the root directory of this source tree.
*/


'use strict';

var Lib = require('../../lib');
var id2name = require('./axis_ids').id2name;
var scaleZoom = require('./scale_zoom');
var makePadFn = require('./autorange').makePadFn;
var concatExtremes = require('./autorange').concatExtremes;

var ALMOST_EQUAL = require('../../constants/numerical').ALMOST_EQUAL;

var FROM_BL = require('../../constants/alignment').FROM_BL;

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

if(containerOut.fixedrange) return;

// coerce the constraint mechanics even if this axis has no scaleanchor
// because it may be the anchor of another axis.
coerce('constrain');
Lib.coerce(containerIn, containerOut, {
constraintoward: {
valType: 'enumerated',
values: letter === 'x' ? ['left', 'center', 'right'] : ['bottom', 'middle', 'top'],
dflt: letter === 'x' ? 'center' : 'middle'
}
}, 'constraintoward');

if(!containerIn.scaleanchor) return;

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

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

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);
}
else if(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' +
'axis has fixed range.');
}
};

function getConstraintOpts(constraintGroups, thisID, allAxisIds, layoutOut) {
// If this axis is already part of a constraint group, we can't
// scaleanchor any other axis in that group, or we'd make a loop.
// Filter allAxisIds to enforce this, also matching axis types.

var thisType = layoutOut[id2name(thisID)].type;

var i, j, idj, axj;

var linkableAxes = [];
for(j = 0; j < allAxisIds.length; j++) {
idj = allAxisIds[j];
if(idj === thisID) continue;

axj = layoutOut[id2name(idj)];
if(axj.type === thisType && !axj.fixedrange) linkableAxes.push(idj);
}

for(i = 0; i < constraintGroups.length; i++) {
if(constraintGroups[i][thisID]) {
var thisGroup = constraintGroups[i];

var linkableAxesNoLoops = [];
for(j = 0; j < linkableAxes.length; j++) {
idj = linkableAxes[j];
if(!thisGroup[idj]) linkableAxesNoLoops.push(idj);
}
return {linkableAxes: linkableAxesNoLoops, thisGroup: thisGroup};
}
}

return {linkableAxes: linkableAxes, thisGroup: null};
}

/*
* Add this axis to the axis constraint groups, which is the collection
* of axes that are all constrained together on scale.
*
* constraintGroups: a list of objects. each object is
* {axis_id: scale_within_group}, where scale_within_group is
* only important relative to the rest of the group, and defines
* the relative scales between all axes in the group
*
* thisGroup: the group the current axis is already in
* thisID: the id if the current axis
* scaleanchor: the id of the axis to scale it with
* scaleratio: the ratio of this axis to the scaleanchor axis
*/
function updateConstraintGroups(constraintGroups, thisGroup, thisID, scaleanchor, scaleratio) {
var i, j, groupi, keyj, thisGroupIndex;

if(thisGroup === null) {
thisGroup = {};
thisGroup[thisID] = 1;
thisGroupIndex = constraintGroups.length;
constraintGroups.push(thisGroup);
}
else {
thisGroupIndex = constraintGroups.indexOf(thisGroup);
}

var thisGroupKeys = Object.keys(thisGroup);

// we know that this axis isn't in any other groups, but we don't know
// about the scaleanchor axis. If it is, we need to merge the groups.
for(i = 0; i < constraintGroups.length; i++) {
groupi = constraintGroups[i];
if(i !== thisGroupIndex && groupi[scaleanchor]) {
var baseScale = groupi[scaleanchor];
for(j = 0; j < thisGroupKeys.length; j++) {
keyj = thisGroupKeys[j];
groupi[keyj] = baseScale * scaleratio * thisGroup[keyj];
}
constraintGroups.splice(thisGroupIndex, 1);
return;
}
}

// otherwise, we insert the new scaleanchor axis as the base scale (1)
// in its group, and scale the rest of the group to it
if(scaleratio !== 1) {
for(j = 0; j < thisGroupKeys.length; j++) {
thisGroup[thisGroupKeys[j]] *= scaleratio;
}
}
thisGroup[scaleanchor] = 1;
}

exports.enforce = function enforceAxisConstraints(gd) {
var fullLayout = gd._fullLayout;
Expand Down
2 changes: 1 addition & 1 deletion src/plots/cartesian/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var basePlotLayoutAttributes = require('../layout_attributes');
var layoutAttributes = require('./layout_attributes');
var handleTypeDefaults = require('./type_defaults');
var handleAxisDefaults = require('./axis_defaults');
var handleConstraintDefaults = require('./constraint_defaults');
var handleConstraintDefaults = require('./constraints').handleConstraintDefaults;
var handlePositionDefaults = require('./position_defaults');

var axisIds = require('./axis_ids');
Expand Down
0