Skip to content

Commit 885c545

Browse files
authored
Merge pull request #1952 from plotly/ternary-axis-layer
Ternary axis layer
2 parents 4ae95dd + 9efb321 commit 885c545

File tree

6 files changed

+199
-38
lines changed

6 files changed

+199
-38
lines changed

src/plots/ternary/layout/axis_attributes.js

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ module.exports = {
4747
showgrid: extendFlat({}, axesAttrs.showgrid, {dflt: true}),
4848
gridcolor: axesAttrs.gridcolor,
4949
gridwidth: axesAttrs.gridwidth,
50+
layer: axesAttrs.layer,
5051
// range
5152
min: {
5253
valType: 'number',

src/plots/ternary/layout/axis_defaults.js

+2
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,6 @@ module.exports = function supplyLayoutDefaults(containerIn, containerOut, option
7979
coerce('gridcolor', colorMix(dfltColor, options.bgColor, 60).toRgbString());
8080
coerce('gridwidth');
8181
}
82+
83+
coerce('layer');
8284
};

src/plots/ternary/ternary.js

+64-37
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function Ternary(options, fullLayout) {
3131
this.id = options.id;
3232
this.graphDiv = options.graphDiv;
3333
this.init(fullLayout);
34-
this.makeFramework();
34+
this.makeFramework(fullLayout);
3535
}
3636

3737
module.exports = Ternary;
@@ -43,6 +43,7 @@ proto.init = function(fullLayout) {
4343
this.defs = fullLayout._defs;
4444
this.layoutId = fullLayout._uid;
4545
this.traceHash = {};
46+
this.layers = {};
4647
};
4748

4849
proto.plot = function(ternaryCalcData, fullLayout) {
@@ -60,15 +61,15 @@ proto.plot = function(ternaryCalcData, fullLayout) {
6061
}
6162
}
6263

64+
_this.updateLayers(ternaryLayout);
6365
_this.adjustLayout(ternaryLayout, graphSize);
64-
6566
Plots.generalUpdatePerTraceModule(_this, ternaryCalcData, ternaryLayout);
66-
6767
_this.layers.plotbg.select('path').call(Color.fill, ternaryLayout.bgcolor);
6868
};
6969

70-
proto.makeFramework = function() {
70+
proto.makeFramework = function(fullLayout) {
7171
var _this = this;
72+
var ternaryLayout = fullLayout[_this.id];
7273

7374
var defGroup = _this.defs.selectAll('g.clips')
7475
.data([0]);
@@ -95,50 +96,75 @@ proto.makeFramework = function() {
9596
_this.plotContainer.enter().append('g')
9697
.classed(_this.id, true);
9798

98-
_this.layers = {};
99+
_this.updateLayers(ternaryLayout);
100+
101+
Drawing.setClipUrl(_this.layers.backplot, clipId);
102+
Drawing.setClipUrl(_this.layers.grids, clipId);
103+
};
104+
105+
proto.updateLayers = function(ternaryLayout) {
106+
var _this = this;
107+
var layers = _this.layers;
99108

100109
// inside that container, we have one container for the data, and
101110
// one each for the three axes around it.
102-
var plotLayers = [
103-
'draglayer',
104-
'plotbg',
105-
'backplot',
106-
'grids',
107-
'frontplot',
108-
'aaxis', 'baxis', 'caxis', 'axlines'
109-
];
111+
112+
var plotLayers = ['draglayer', 'plotbg', 'backplot', 'grids'];
113+
114+
if(ternaryLayout.aaxis.layer === 'below traces') {
115+
plotLayers.push('aaxis', 'aline');
116+
}
117+
if(ternaryLayout.baxis.layer === 'below traces') {
118+
plotLayers.push('baxis', 'bline');
119+
}
120+
if(ternaryLayout.caxis.layer === 'below traces') {
121+
plotLayers.push('caxis', 'cline');
122+
}
123+
124+
plotLayers.push('frontplot');
125+
126+
if(ternaryLayout.aaxis.layer === 'above traces') {
127+
plotLayers.push('aaxis', 'aline');
128+
}
129+
if(ternaryLayout.baxis.layer === 'above traces') {
130+
plotLayers.push('baxis', 'bline');
131+
}
132+
if(ternaryLayout.caxis.layer === 'above traces') {
133+
plotLayers.push('caxis', 'cline');
134+
}
135+
110136
var toplevel = _this.plotContainer.selectAll('g.toplevel')
111-
.data(plotLayers);
137+
.data(plotLayers, String);
138+
139+
var grids = ['agrid', 'bgrid', 'cgrid'];
140+
112141
toplevel.enter().append('g')
113142
.attr('class', function(d) { return 'toplevel ' + d; })
114143
.each(function(d) {
115144
var s = d3.select(this);
116-
_this.layers[d] = s;
145+
layers[d] = s;
117146

118147
// containers for different trace types.
119148
// NOTE - this is different from cartesian, where all traces
120149
// are in front of grids. Here I'm putting maps behind the grids
121150
// so the grids will always be visible if they're requested.
122151
// Perhaps we want that for cartesian too?
123-
if(d === 'frontplot') s.append('g').classed('scatterlayer', true);
124-
else if(d === 'backplot') s.append('g').classed('maplayer', true);
125-
else if(d === 'plotbg') s.append('path').attr('d', 'M0,0Z');
126-
else if(d === 'axlines') {
127-
s.selectAll('path').data(['aline', 'bline', 'cline'])
128-
.enter().append('path').each(function(d) {
129-
d3.select(this).classed(d, true);
130-
});
152+
if(d === 'frontplot') {
153+
s.append('g').classed('scatterlayer', true);
154+
} else if(d === 'backplot') {
155+
s.append('g').classed('maplayer', true);
156+
} else if(d === 'plotbg') {
157+
s.append('path').attr('d', 'M0,0Z');
158+
} else if(d === 'aline' || d === 'bline' || d === 'cline') {
159+
s.append('path');
160+
} else if(d === 'grids') {
161+
grids.forEach(function(d) {
162+
layers[d] = s.append('g').classed('grid ' + d, true);
163+
});
131164
}
132165
});
133166

134-
var grids = _this.plotContainer.select('.grids').selectAll('g.grid')
135-
.data(['agrid', 'bgrid', 'cgrid']);
136-
grids.enter().append('g')
137-
.attr('class', function(d) { return 'grid ' + d; })
138-
.each(function(d) { _this.layers[d] = d3.select(this); });
139-
140-
_this.plotContainer.selectAll('.backplot,.grids')
141-
.call(Drawing.setClipUrl, clipId);
167+
toplevel.order();
142168
};
143169

144170
var w_over_h = Math.sqrt(4 / 3);
@@ -315,18 +341,17 @@ proto.adjustLayout = function(ternaryLayout, graphSize) {
315341
// make these counterproductive.
316342
_this.plotContainer.selectAll('.crisp').classed('crisp', false);
317343

318-
var axlines = _this.layers.axlines;
319-
axlines.select('.aline')
344+
_this.layers.aline.select('path')
320345
.attr('d', aaxis.showline ?
321346
'M' + x0 + ',' + (y0 + h) + 'l' + (w / 2) + ',-' + h : 'M0,0')
322347
.call(Color.stroke, aaxis.linecolor || '#000')
323348
.style('stroke-width', (aaxis.linewidth || 0) + 'px');
324-
axlines.select('.bline')
349+
_this.layers.bline.select('path')
325350
.attr('d', baxis.showline ?
326351
'M' + x0 + ',' + (y0 + h) + 'h' + w : 'M0,0')
327352
.call(Color.stroke, baxis.linecolor || '#000')
328353
.style('stroke-width', (baxis.linewidth || 0) + 'px');
329-
axlines.select('.cline')
354+
_this.layers.cline.select('path')
330355
.attr('d', caxis.showline ?
331356
'M' + (x0 + w / 2) + ',' + y0 + 'l' + (w / 2) + ',' + h : 'M0,0')
332357
.call(Color.stroke, caxis.linecolor || '#000')
@@ -336,8 +361,10 @@ proto.adjustLayout = function(ternaryLayout, graphSize) {
336361
_this.initInteractions();
337362
}
338363

339-
_this.plotContainer.select('.frontplot')
340-
.call(Drawing.setClipUrl, _this._hasClipOnAxisFalse ? null : _this.clipId);
364+
Drawing.setClipUrl(
365+
_this.layers.frontplot,
366+
_this._hasClipOnAxisFalse ? null : _this.clipId
367+
);
341368
};
342369

343370
proto.drawAxes = function(doTitles) {
29 KB
Loading
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{
2+
"data": [
3+
{
4+
"type": "scatterternary",
5+
"mode": "markers",
6+
"a": [70, 75, 5, 10],
7+
"b": [10, 20, 60, 80],
8+
"c": [20, 5, 35, 10],
9+
"name": "not clipped",
10+
"marker": {
11+
"symbol": "square",
12+
"size": 40,
13+
"line": { "width": 4 }
14+
},
15+
"cliponaxis": false
16+
},
17+
{
18+
"type": "scatterternary",
19+
"mode": "markers",
20+
"name": "clipped",
21+
"a": [10, 20, 10, 15, 10, 20],
22+
"b": [90, 70, 20, 5, 10, 10],
23+
"c": [0, 10, 70, 80, 80, 70],
24+
"marker": {
25+
"symbol": "diamond",
26+
"size": 30,
27+
"line": { "width": 4 }
28+
},
29+
"cliponaxis": true
30+
}
31+
],
32+
"layout": {
33+
"ternary": {
34+
"sum": 100,
35+
"aaxis": {
36+
"title": "",
37+
"layer": "below traces",
38+
"linewidth": 4,
39+
"min": 4.5
40+
},
41+
"baxis": {
42+
"title": "",
43+
"layer": "below traces",
44+
"linewidth": 4,
45+
"min": 7.5
46+
},
47+
"caxis": {
48+
"title": "",
49+
"layer": "below traces",
50+
"linewidth": 4,
51+
"min": 7.5
52+
}
53+
},
54+
"title": "ternary axes below traces",
55+
"showlegend": false,
56+
"height": 450,
57+
"width": 500
58+
}
59+
}

test/jasmine/tests/ternary_test.js

+73-1
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ var supplyLayoutDefaults = require('@src/plots/ternary/layout/defaults');
66
var d3 = require('d3');
77
var createGraphDiv = require('../assets/create_graph_div');
88
var destroyGraphDiv = require('../assets/destroy_graph_div');
9+
var fail = require('../assets/fail_test');
910
var mouseEvent = require('../assets/mouse_event');
1011
var click = require('../assets/click');
1112
var doubleClick = require('../assets/double_click');
1213
var customMatchers = require('../assets/custom_matchers');
1314
var getClientPosition = require('../assets/get_client_position');
1415

15-
1616
describe('ternary plots', function() {
1717
'use strict';
1818

@@ -244,6 +244,78 @@ describe('ternary plots', function() {
244244
});
245245
});
246246

247+
it('should be able to reorder axis layers when relayout\'ing *layer*', function(done) {
248+
var gd = createGraphDiv();
249+
var fig = Lib.extendDeep({}, require('@mocks/ternary_simple.json'));
250+
var dflt = [
251+
'draglayer', 'plotbg', 'backplot', 'grids',
252+
'frontplot',
253+
'aaxis', 'aline', 'baxis', 'bline', 'caxis', 'cline'
254+
];
255+
256+
function _assert(layers) {
257+
var toplevel = d3.selectAll('g.ternary > .toplevel');
258+
259+
expect(toplevel.size()).toBe(layers.length, '# of layer');
260+
261+
toplevel.each(function(d, i) {
262+
var className = d3.select(this)
263+
.attr('class')
264+
.split('toplevel ')[1];
265+
266+
expect(className).toBe(layers[i], 'layer ' + i);
267+
});
268+
}
269+
270+
Plotly.plot(gd, fig).then(function() {
271+
_assert(dflt);
272+
return Plotly.relayout(gd, 'ternary.aaxis.layer', 'below traces');
273+
})
274+
.then(function() {
275+
_assert([
276+
'draglayer', 'plotbg', 'backplot', 'grids',
277+
'aaxis', 'aline',
278+
'frontplot',
279+
'baxis', 'bline', 'caxis', 'cline'
280+
]);
281+
return Plotly.relayout(gd, 'ternary.caxis.layer', 'below traces');
282+
})
283+
.then(function() {
284+
_assert([
285+
'draglayer', 'plotbg', 'backplot', 'grids',
286+
'aaxis', 'aline', 'caxis', 'cline',
287+
'frontplot',
288+
'baxis', 'bline'
289+
]);
290+
return Plotly.relayout(gd, 'ternary.baxis.layer', 'below traces');
291+
})
292+
.then(function() {
293+
_assert([
294+
'draglayer', 'plotbg', 'backplot', 'grids',
295+
'aaxis', 'aline', 'baxis', 'bline', 'caxis', 'cline',
296+
'frontplot'
297+
]);
298+
return Plotly.relayout(gd, 'ternary.aaxis.layer', null);
299+
})
300+
.then(function() {
301+
_assert([
302+
'draglayer', 'plotbg', 'backplot', 'grids',
303+
'baxis', 'bline', 'caxis', 'cline',
304+
'frontplot',
305+
'aaxis', 'aline'
306+
]);
307+
return Plotly.relayout(gd, {
308+
'ternary.baxis.layer': null,
309+
'ternary.caxis.layer': null
310+
});
311+
})
312+
.then(function() {
313+
_assert(dflt);
314+
})
315+
.catch(fail)
316+
.then(done);
317+
});
318+
247319
function countTernarySubplot() {
248320
return d3.selectAll('.ternary').size();
249321
}

0 commit comments

Comments
 (0)