Skip to content

Commit 5c42db8

Browse files
committed
image: layout defaults: add scaleanchor, fix logic for reverse axis
1 parent c8a9f8a commit 5c42db8

12 files changed

+99
-18
lines changed

src/plots/cartesian/constraints.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ var concatExtremes = require('./autorange').concatExtremes;
1717
var ALMOST_EQUAL = require('../../constants/numerical').ALMOST_EQUAL;
1818
var FROM_BL = require('../../constants/alignment').FROM_BL;
1919

20-
exports.handleConstraintDefaults = function(containerIn, containerOut, coerce, allAxisIds, layoutOut) {
20+
exports.handleConstraintDefaults = function(containerIn, containerOut, coerce, opts) {
21+
var allAxisIds = opts.allAxisIds;
22+
var layoutOut = opts.layoutOut;
2123
var constraintGroups = layoutOut._axisConstraintGroups;
2224
var matchGroups = layoutOut._axisMatchGroups;
2325
var axId = containerOut._id;
@@ -53,14 +55,14 @@ exports.handleConstraintDefaults = function(containerIn, containerOut, coerce, a
5355
// 'matches' wins over 'scaleanchor' (for now)
5456
var scaleanchor, scaleOpts;
5557

56-
if(!matches && containerIn.scaleanchor && !(containerOut.fixedrange && constrain !== 'domain')) {
58+
if(!matches && (containerIn.scaleanchor || opts.scaleanchorDflt) && !(containerOut.fixedrange && constrain !== 'domain')) {
5759
scaleOpts = getConstraintOpts(constraintGroups, thisID, allAxisIds, layoutOut, constrain);
5860
scaleanchor = Lib.coerce(containerIn, containerOut, {
5961
scaleanchor: {
6062
valType: 'enumerated',
6163
values: scaleOpts.linkableAxes || []
6264
}
63-
}, 'scaleanchor');
65+
}, 'scaleanchor', opts.scaleanchorDflt);
6466
}
6567

6668
if(matches) {

src/plots/cartesian/layout_defaults.js

+21-11
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
3939
var yaMayHide = {};
4040
var xaMustDisplay = {};
4141
var yaMustDisplay = {};
42-
var yaMustForward = {};
43-
var yaMayBackward = {};
42+
var yaMustNotReverse = {};
43+
var yaMayReverse = {};
44+
var yaMustNotScaleanchor = {};
45+
var yaMayScaleanchor = {};
4446
var outerTicks = {};
4547
var noGrids = {};
4648
var i, j;
@@ -74,24 +76,24 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
7476
if(trace.type === 'funnel') {
7577
if(trace.orientation === 'h') {
7678
if(xaName) xaMayHide[xaName] = true;
77-
if(yaName) yaMayBackward[yaName] = true;
79+
if(yaName) yaMayReverse[yaName] = true;
7880
} else {
7981
if(yaName) yaMayHide[yaName] = true;
8082
}
83+
yaMustNotScaleanchor[yaName] = true;
84+
} else if(trace.type === 'image') {
85+
if(yaName) yaMayReverse[yaName] = true;
86+
if(yaName) yaMayScaleanchor[yaName] = true;
8187
} else {
8288
if(yaName) {
8389
yaMustDisplay[yaName] = true;
84-
yaMustForward[yaName] = true;
90+
yaMustNotReverse[yaName] = true;
91+
yaMustNotScaleanchor[yaName] = true;
8592
}
8693

8794
if(!traceIs(trace, 'carpet') || (trace.type === 'carpet' && !trace._cheater)) {
8895
if(xaName) xaMustDisplay[xaName] = true;
8996
}
90-
91-
if(trace.type === 'image') {
92-
if(yaName) yaMustForward[yaName] = false;
93-
if(yaName) yaMayBackward[yaName] = true;
94-
}
9597
}
9698

9799
// Two things trigger axis visibility:
@@ -197,7 +199,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
197199
(axLetter === 'y' && !yaMustDisplay[axName] && yaMayHide[axName]);
198200

199201
var reverseDflt =
200-
(axLetter === 'y' && !yaMustForward[axName] && yaMayBackward[axName]);
202+
(axLetter === 'y' && !yaMustNotReverse[axName] && yaMayReverse[axName]);
201203

202204
var defaultOptions = {
203205
letter: axLetter,
@@ -299,7 +301,15 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
299301
axLayoutIn = layoutIn[axName];
300302
axLayoutOut = layoutOut[axName];
301303

302-
handleConstraintDefaults(axLayoutIn, axLayoutOut, coerce, allAxisIds, layoutOut);
304+
var scaleanchorDflt = null;
305+
if(axLetter === 'y' && !axLayoutIn.hasOwnProperty('scaleanchor') && !yaMustNotScaleanchor[axName] && yaMayScaleanchor[axName]) {
306+
scaleanchorDflt = axLayoutOut.anchor;
307+
}
308+
handleConstraintDefaults(axLayoutIn, axLayoutOut, coerce, {
309+
allAxisIds: allAxisIds,
310+
layoutOut: layoutOut,
311+
scaleanchorDflt: scaleanchorDflt
312+
});
303313
}
304314

305315
for(i = 0; i < matchGroups.length; i++) {
4.91 KB
Loading
-310 Bytes
Loading

test/image/mocks/image_adventurer.json

+1-1
Large diffs are not rendered by default.

test/image/mocks/image_axis_type.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"width": 400, "height": 600, "title": {"text": "Image on categorical and log axes"},
2929
"grid": {"rows": 2, "columns": 1, "pattern": "independent"},
3030
"xaxis2": {"type": "log"},
31-
"yaxis2": {"type": "log"}
31+
"yaxis": {"scaleanchor": null},
32+
"yaxis2": {"type": "log", "scaleanchor": null}
3233
}
3334
}

test/image/mocks/image_cat.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"layout": {
3-
"width": 400, "height": 400, "yaxis": {"scaleanchor": "x"}, "margin": {"t":25, "b": 25, "r": 25, "l": 40}
3+
"width": 400, "height": 400, "yaxis": {"scaleanchor": "x", "autorange": "reversed"}, "margin": {"t":25, "b": 25, "r": 25, "l": 40}
44
},
55
"data": [
66
{"x":[50, 150, 350, 50], "y":[350, 23, 100, 350]},

test/image/mocks/image_colormodel.json

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
}],
2020
"layout": {
2121
"grid": {"rows": 2, "columns": 1, "pattern": "independent"},
22+
"yaxis": {"scaleanchor": null},
23+
"yaxis2": {"scaleanchor": null},
2224
"width": 600,
2325
"height": 400
2426
}

test/image/mocks/image_opacity.json

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"z": [[[255, 0, 0], [0, 255, 0], [0, 0, 255]]]
66
}],
77
"layout": {
8+
"yaxis": {"scaleanchor": null},
89
"width": 400, "height": 400,
910
"title": {
1011
"text": "image with opacity 0.1"

test/image/mocks/image_with_gaps.json

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"zmax": [1, 1, 1]
1010
}],
1111
"layout": {
12+
"yaxis": {"scaleanchor": null},
1213
"width": 400, "height": 400, "title": {"text": "Image with missing pixels"}
1314
}
1415
}

test/image/mocks/image_zmin_zmax.json

+8
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@
6060
"layout": {
6161
"width": 400,
6262
"height": 800,
63+
"yaxis": {"scaleanchor": null},
64+
"yaxis2": {"scaleanchor": null},
65+
"yaxis3": {"scaleanchor": null},
66+
"yaxis4": {"scaleanchor": null},
67+
"yaxis5": {"scaleanchor": null},
68+
"yaxis6": {"scaleanchor": null},
69+
"yaxis7": {"scaleanchor": null},
70+
"yaxis8": {"scaleanchor": null},
6371
"grid": {
6472
"rows": 4,
6573
"columns": 2,

test/jasmine/tests/image_test.js

+57-1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,62 @@ describe('image supplyDefaults', function() {
100100
});
101101
});
102102

103+
describe('image smart layout defaults', function() {
104+
var gd;
105+
beforeEach(function() {
106+
gd = createGraphDiv();
107+
});
108+
109+
afterEach(destroyGraphDiv);
110+
111+
it('should reverse yaxis if only images are present', function(done) {
112+
Plotly.newPlot(gd, [{type: 'image', z: [[[255, 0, 0]]]}])
113+
.then(function(gd) {
114+
expect(gd._fullLayout.yaxis.range[0]).toBeGreaterThan(gd._fullLayout.yaxis.range[1]);
115+
})
116+
.catch(failTest)
117+
.then(done);
118+
});
119+
120+
it('should NOT reverse yaxis if another trace is present', function(done) {
121+
Plotly.newPlot(gd, [{type: 'image', z: [[[255, 0, 0]]]}, {type: 'scatter', y: [5, 3, 2]}])
122+
.then(function(gd) {
123+
expect(gd._fullLayout.yaxis.range[1]).toBeGreaterThan(gd._fullLayout.yaxis.range[0]);
124+
})
125+
.catch(failTest)
126+
.then(done);
127+
});
128+
129+
it('should set scaleanchor to make square pixels if only images are present', function(done) {
130+
Plotly.newPlot(gd, [{type: 'image', z: [[[255, 0, 0]]]}])
131+
.then(function(gd) {
132+
expect(gd._fullLayout.yaxis.scaleanchor).toBe('x');
133+
})
134+
.catch(failTest)
135+
.then(done);
136+
});
137+
138+
it('should NOT set scaleanchor if another trace is present', function(done) {
139+
Plotly.newPlot(gd, [{type: 'image', z: [[[255, 0, 0]]]}, {type: 'scatter', y: [5, 3, 2]}])
140+
.then(function(gd) {
141+
expect(gd._fullLayout.yaxis.scaleanchor).toBe(undefined);
142+
})
143+
.catch(failTest)
144+
.then(done);
145+
});
146+
147+
it('should NOT set scaleanchor if it\'s already defined', function(done) {
148+
Plotly.newPlot(gd, [
149+
{type: 'image', z: [[[255, 0, 0]]]}, {type: 'scatter', y: [5, 3, 2]}
150+
], {yaxis: {scaleanchor: 'x3'}})
151+
.then(function(gd) {
152+
expect(gd._fullLayout.yaxis.scaleanchor).toBe(undefined);
153+
})
154+
.catch(failTest)
155+
.then(done);
156+
});
157+
});
158+
103159
describe('image plot', function() {
104160
'use strict';
105161

@@ -444,7 +500,7 @@ describe('image hover:', function() {
444500
zmax: [1, 1, 1],
445501
text: [['A', 'B', 'C'], ['D', 'E', 'F']],
446502
hovertemplate: '%{text}<extra></extra>'
447-
}], layout: {width: 400, height: 400}};
503+
}], layout: {width: 400, height: 400, yaxis: {scaleanchor: null}}};
448504

449505
Plotly.newPlot(gd, mockCopy)
450506
.then(function() {_hover(140, 200);})

0 commit comments

Comments
 (0)