Skip to content

Commit

Permalink
Merge pull request #4313 from plotly/image-constrain
Browse files Browse the repository at this point in the history
image: constrain axes to domain, apply defaults whenever an image is present
  • Loading branch information
antoinerg authored Oct 29, 2019
2 parents 2b0d3c3 + 29cf98f commit 4233619
Show file tree
Hide file tree
Showing 13 changed files with 65 additions and 26 deletions.
3 changes: 2 additions & 1 deletion src/plots/cartesian/constraints.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ exports.handleConstraintDefaults = function(containerIn, containerOut, coerce, o
var allAxisIds = opts.allAxisIds;
var layoutOut = opts.layoutOut;
var scaleanchorDflt = opts.scaleanchorDflt;
var constrainDflt = opts.constrainDflt;
var constraintGroups = layoutOut._axisConstraintGroups;
var matchGroups = layoutOut._axisMatchGroups;
var axId = containerOut._id;
Expand All @@ -31,7 +32,7 @@ exports.handleConstraintDefaults = function(containerIn, containerOut, coerce, o

// coerce the constraint mechanics even if this axis has no scaleanchor
// because it may be the anchor of another axis.
var constrain = coerce('constrain');
var constrain = coerce('constrain', constrainDflt);
Lib.coerce(containerIn, containerOut, {
constraintoward: {
valType: 'enumerated',
Expand Down
34 changes: 19 additions & 15 deletions src/plots/cartesian/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
var yaMustDisplay = {};
var yaMustNotReverse = {};
var yaMayReverse = {};
var yaMustNotScaleanchor = {};
var yaMayScaleanchor = {};
var axHasImage = {};
var outerTicks = {};
var noGrids = {};
var i, j;
Expand Down Expand Up @@ -80,17 +79,13 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
} else {
if(yaName) yaMayHide[yaName] = true;
}
yaMustNotScaleanchor[yaName] = true;
} else if(trace.type === 'image') {
if(yaName) {
yaMayReverse[yaName] = true;
yaMayScaleanchor[yaName] = true;
}
if(yaName) axHasImage[yaName] = true;
if(xaName) axHasImage[xaName] = true;
} else {
if(yaName) {
yaMustDisplay[yaName] = true;
yaMustNotReverse[yaName] = true;
yaMustNotScaleanchor[yaName] = true;
}

if(!traceIs(trace, 'carpet') || (trace.type === 'carpet' && !trace._cheater)) {
Expand Down Expand Up @@ -201,7 +196,11 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
(axLetter === 'y' && !yaMustDisplay[axName] && yaMayHide[axName]);

var reverseDflt =
(axLetter === 'y' && !yaMustNotReverse[axName] && yaMayReverse[axName]);
(axLetter === 'y' &&
(
(!yaMustNotReverse[axName] && yaMayReverse[axName]) ||
axHasImage[axName]
));

var defaultOptions = {
letter: axLetter,
Expand Down Expand Up @@ -303,16 +302,21 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
axLayoutIn = layoutIn[axName];
axLayoutOut = layoutOut[axName];

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

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

handleConstraintDefaults(axLayoutIn, axLayoutOut, coerce, {
allAxisIds: allAxisIds,
layoutOut: layoutOut,
scaleanchorDflt: scaleanchorDflt
scaleanchorDflt: scaleanchorDflt,
constrainDflt: constrainDflt
});
}

Expand Down
7 changes: 4 additions & 3 deletions src/traces/image/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ module.exports = {
meta: {
description: [
'Display an image, i.e. data on a 2D regular raster.',
'If only images are displayed in a subplot,',
'the y axis will be reversed (ie. `autorange: \'reversed\'`)',
'and it will have the same scale as the x axis (ie. `scaleanchor: \'x\,`)',
'By default, when an image is displayed in a subplot,',
'its y axis will be reversed (ie. `autorange: \'reversed\'`),',
'constrained to the domain (ie. `constrain: \'domain\'`)',
'and it will have the same scale as its x axis (ie. `scaleanchor: \'x\,`)',
'in order for pixels to be rendered as squares.'
].join(' ')
}
Expand Down
Binary file modified test/image/baselines/image_axis_reverse.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/image_axis_type.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/image_cat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/image_colormodel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/image_opacity.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/image_with_gaps.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/image_with_heatmap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/image_zmin_zmax.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion test/image/mocks/image_adventurer.json

Large diffs are not rendered by default.

44 changes: 38 additions & 6 deletions test/jasmine/tests/image_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,19 @@ describe('image smart layout defaults', function() {
expect(gd._fullLayout.yaxis.autorange).toBe('reversed');
});

it('should NOT reverse yaxis if another trace is present', function() {
it('should reverse yaxis even if another trace is present', function() {
gd = {};
gd.data = [{type: 'image', z: [[[255, 0, 0]]]}, {type: 'scatter', y: [5, 3, 2]}];
supplyAllDefaults(gd);
expect(gd._fullLayout.yaxis.autorange).not.toBe('reversed');
expect(gd._fullLayout.yaxis.autorange).toBe('reversed');
});

it('should NOT reverse yaxis if it\'s already defined', function() {
gd = {};
gd.data = [{type: 'image', z: [[[255, 0, 0]]]}];
gd.layout = {yaxis: {autorange: false}};
supplyAllDefaults(gd);
expect(gd._fullLayout.yaxis.autorange).toBe(false);
});

it('should set scaleanchor to make square pixels if only images are present', function() {
Expand All @@ -130,19 +138,43 @@ describe('image smart layout defaults', function() {
expect(gd._fullLayout.yaxis.scaleanchor).toBe('x');
});

it('should NOT set scaleanchor if another trace is present', function() {
it('should set scaleanchor even if another trace is present', function() {
gd = {};
gd.data = [{type: 'image', z: [[[255, 0, 0]]]}, {type: 'scatter', y: [5, 3, 2]}];
supplyAllDefaults(gd);
expect(gd._fullLayout.yaxis.scaleanchor).toBe(undefined);
expect(gd._fullLayout.yaxis.scaleanchor).toBe('x');
});

it('should NOT set scaleanchor if it\'s already defined', function() {
gd.data = [{type: 'image', z: [[[255, 0, 0]]]}, {type: 'scatter', y: [5, 3, 2]}];
gd.data = [{type: 'image', z: [[[255, 0, 0]]]}];
gd.layout = {yaxis: {scaleanchor: 'x3'}};
supplyAllDefaults(gd);
expect(gd._fullLayout.yaxis.scaleanchor).toBe(undefined);
});

it('should constrain axes to domain if only images are present', function() {
gd = {};
gd.data = [{type: 'image', z: [[[255, 0, 0]]]}];
supplyAllDefaults(gd);
expect(gd._fullLayout.xaxis.constrain).toBe('domain');
expect(gd._fullLayout.yaxis.constrain).toBe('domain');
});

it('should constrain axes to domain even if another trace is present', function() {
gd = {};
gd.data = [{type: 'image', z: [[[255, 0, 0]]]}, {type: 'scatter', y: [5, 3, 2]}];
supplyAllDefaults(gd);
expect(gd._fullLayout.xaxis.constrain).toBe('domain');
expect(gd._fullLayout.yaxis.constrain).toBe('domain');
});

it('should NOT constrain axes to domain if it\'s already defined', function() {
gd.data = [{type: 'image', z: [[[255, 0, 0]]]}];
gd.layout = {yaxis: {constrain: false}, xaxis: {constrain: false}};
supplyAllDefaults(gd);
expect(gd._fullLayout.xaxis.constrain).toBe('range');
expect(gd._fullLayout.yaxis.constrain).toBe('range');
});
});

describe('image plot', function() {
Expand Down Expand Up @@ -489,7 +521,7 @@ describe('image hover:', function() {
zmax: [1, 1, 1],
text: [['A', 'B', 'C'], ['D', 'E', 'F']],
hovertemplate: '%{text}<extra></extra>'
}], layout: {width: 400, height: 400}};
}], layout: {width: 400, height: 400, yaxis: {constrain: 'range'}}};

Plotly.newPlot(gd, mockCopy)
.then(function() {_hover(140, 180);})
Expand Down

0 comments on commit 4233619

Please sign in to comment.