Skip to content

Commit

Permalink
Merge pull request #1952 from plotly/ternary-axis-layer
Browse files Browse the repository at this point in the history
Ternary axis layer
  • Loading branch information
etpinard authored Aug 16, 2017
2 parents 4ae95dd + 9efb321 commit 885c545
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 38 deletions.
1 change: 1 addition & 0 deletions src/plots/ternary/layout/axis_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ module.exports = {
showgrid: extendFlat({}, axesAttrs.showgrid, {dflt: true}),
gridcolor: axesAttrs.gridcolor,
gridwidth: axesAttrs.gridwidth,
layer: axesAttrs.layer,
// range
min: {
valType: 'number',
Expand Down
2 changes: 2 additions & 0 deletions src/plots/ternary/layout/axis_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,6 @@ module.exports = function supplyLayoutDefaults(containerIn, containerOut, option
coerce('gridcolor', colorMix(dfltColor, options.bgColor, 60).toRgbString());
coerce('gridwidth');
}

coerce('layer');
};
101 changes: 64 additions & 37 deletions src/plots/ternary/ternary.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function Ternary(options, fullLayout) {
this.id = options.id;
this.graphDiv = options.graphDiv;
this.init(fullLayout);
this.makeFramework();
this.makeFramework(fullLayout);
}

module.exports = Ternary;
Expand All @@ -43,6 +43,7 @@ proto.init = function(fullLayout) {
this.defs = fullLayout._defs;
this.layoutId = fullLayout._uid;
this.traceHash = {};
this.layers = {};
};

proto.plot = function(ternaryCalcData, fullLayout) {
Expand All @@ -60,15 +61,15 @@ proto.plot = function(ternaryCalcData, fullLayout) {
}
}

_this.updateLayers(ternaryLayout);
_this.adjustLayout(ternaryLayout, graphSize);

Plots.generalUpdatePerTraceModule(_this, ternaryCalcData, ternaryLayout);

_this.layers.plotbg.select('path').call(Color.fill, ternaryLayout.bgcolor);
};

proto.makeFramework = function() {
proto.makeFramework = function(fullLayout) {
var _this = this;
var ternaryLayout = fullLayout[_this.id];

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

_this.layers = {};
_this.updateLayers(ternaryLayout);

Drawing.setClipUrl(_this.layers.backplot, clipId);
Drawing.setClipUrl(_this.layers.grids, clipId);
};

proto.updateLayers = function(ternaryLayout) {
var _this = this;
var layers = _this.layers;

// inside that container, we have one container for the data, and
// one each for the three axes around it.
var plotLayers = [
'draglayer',
'plotbg',
'backplot',
'grids',
'frontplot',
'aaxis', 'baxis', 'caxis', 'axlines'
];

var plotLayers = ['draglayer', 'plotbg', 'backplot', 'grids'];

if(ternaryLayout.aaxis.layer === 'below traces') {
plotLayers.push('aaxis', 'aline');
}
if(ternaryLayout.baxis.layer === 'below traces') {
plotLayers.push('baxis', 'bline');
}
if(ternaryLayout.caxis.layer === 'below traces') {
plotLayers.push('caxis', 'cline');
}

plotLayers.push('frontplot');

if(ternaryLayout.aaxis.layer === 'above traces') {
plotLayers.push('aaxis', 'aline');
}
if(ternaryLayout.baxis.layer === 'above traces') {
plotLayers.push('baxis', 'bline');
}
if(ternaryLayout.caxis.layer === 'above traces') {
plotLayers.push('caxis', 'cline');
}

var toplevel = _this.plotContainer.selectAll('g.toplevel')
.data(plotLayers);
.data(plotLayers, String);

var grids = ['agrid', 'bgrid', 'cgrid'];

toplevel.enter().append('g')
.attr('class', function(d) { return 'toplevel ' + d; })
.each(function(d) {
var s = d3.select(this);
_this.layers[d] = s;
layers[d] = s;

// containers for different trace types.
// NOTE - this is different from cartesian, where all traces
// are in front of grids. Here I'm putting maps behind the grids
// so the grids will always be visible if they're requested.
// Perhaps we want that for cartesian too?
if(d === 'frontplot') s.append('g').classed('scatterlayer', true);
else if(d === 'backplot') s.append('g').classed('maplayer', true);
else if(d === 'plotbg') s.append('path').attr('d', 'M0,0Z');
else if(d === 'axlines') {
s.selectAll('path').data(['aline', 'bline', 'cline'])
.enter().append('path').each(function(d) {
d3.select(this).classed(d, true);
});
if(d === 'frontplot') {
s.append('g').classed('scatterlayer', true);
} else if(d === 'backplot') {
s.append('g').classed('maplayer', true);
} else if(d === 'plotbg') {
s.append('path').attr('d', 'M0,0Z');
} else if(d === 'aline' || d === 'bline' || d === 'cline') {
s.append('path');
} else if(d === 'grids') {
grids.forEach(function(d) {
layers[d] = s.append('g').classed('grid ' + d, true);
});
}
});

var grids = _this.plotContainer.select('.grids').selectAll('g.grid')
.data(['agrid', 'bgrid', 'cgrid']);
grids.enter().append('g')
.attr('class', function(d) { return 'grid ' + d; })
.each(function(d) { _this.layers[d] = d3.select(this); });

_this.plotContainer.selectAll('.backplot,.grids')
.call(Drawing.setClipUrl, clipId);
toplevel.order();
};

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

var axlines = _this.layers.axlines;
axlines.select('.aline')
_this.layers.aline.select('path')
.attr('d', aaxis.showline ?
'M' + x0 + ',' + (y0 + h) + 'l' + (w / 2) + ',-' + h : 'M0,0')
.call(Color.stroke, aaxis.linecolor || '#000')
.style('stroke-width', (aaxis.linewidth || 0) + 'px');
axlines.select('.bline')
_this.layers.bline.select('path')
.attr('d', baxis.showline ?
'M' + x0 + ',' + (y0 + h) + 'h' + w : 'M0,0')
.call(Color.stroke, baxis.linecolor || '#000')
.style('stroke-width', (baxis.linewidth || 0) + 'px');
axlines.select('.cline')
_this.layers.cline.select('path')
.attr('d', caxis.showline ?
'M' + (x0 + w / 2) + ',' + y0 + 'l' + (w / 2) + ',' + h : 'M0,0')
.call(Color.stroke, caxis.linecolor || '#000')
Expand All @@ -336,8 +361,10 @@ proto.adjustLayout = function(ternaryLayout, graphSize) {
_this.initInteractions();
}

_this.plotContainer.select('.frontplot')
.call(Drawing.setClipUrl, _this._hasClipOnAxisFalse ? null : _this.clipId);
Drawing.setClipUrl(
_this.layers.frontplot,
_this._hasClipOnAxisFalse ? null : _this.clipId
);
};

proto.drawAxes = function(doTitles) {
Expand Down
Binary file added test/image/baselines/ternary_axis_layers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions test/image/mocks/ternary_axis_layers.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"data": [
{
"type": "scatterternary",
"mode": "markers",
"a": [70, 75, 5, 10],
"b": [10, 20, 60, 80],
"c": [20, 5, 35, 10],
"name": "not clipped",
"marker": {
"symbol": "square",
"size": 40,
"line": { "width": 4 }
},
"cliponaxis": false
},
{
"type": "scatterternary",
"mode": "markers",
"name": "clipped",
"a": [10, 20, 10, 15, 10, 20],
"b": [90, 70, 20, 5, 10, 10],
"c": [0, 10, 70, 80, 80, 70],
"marker": {
"symbol": "diamond",
"size": 30,
"line": { "width": 4 }
},
"cliponaxis": true
}
],
"layout": {
"ternary": {
"sum": 100,
"aaxis": {
"title": "",
"layer": "below traces",
"linewidth": 4,
"min": 4.5
},
"baxis": {
"title": "",
"layer": "below traces",
"linewidth": 4,
"min": 7.5
},
"caxis": {
"title": "",
"layer": "below traces",
"linewidth": 4,
"min": 7.5
}
},
"title": "ternary axes below traces",
"showlegend": false,
"height": 450,
"width": 500
}
}
74 changes: 73 additions & 1 deletion test/jasmine/tests/ternary_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ var supplyLayoutDefaults = require('@src/plots/ternary/layout/defaults');
var d3 = require('d3');
var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
var fail = require('../assets/fail_test');
var mouseEvent = require('../assets/mouse_event');
var click = require('../assets/click');
var doubleClick = require('../assets/double_click');
var customMatchers = require('../assets/custom_matchers');
var getClientPosition = require('../assets/get_client_position');


describe('ternary plots', function() {
'use strict';

Expand Down Expand Up @@ -244,6 +244,78 @@ describe('ternary plots', function() {
});
});

it('should be able to reorder axis layers when relayout\'ing *layer*', function(done) {
var gd = createGraphDiv();
var fig = Lib.extendDeep({}, require('@mocks/ternary_simple.json'));
var dflt = [
'draglayer', 'plotbg', 'backplot', 'grids',
'frontplot',
'aaxis', 'aline', 'baxis', 'bline', 'caxis', 'cline'
];

function _assert(layers) {
var toplevel = d3.selectAll('g.ternary > .toplevel');

expect(toplevel.size()).toBe(layers.length, '# of layer');

toplevel.each(function(d, i) {
var className = d3.select(this)
.attr('class')
.split('toplevel ')[1];

expect(className).toBe(layers[i], 'layer ' + i);
});
}

Plotly.plot(gd, fig).then(function() {
_assert(dflt);
return Plotly.relayout(gd, 'ternary.aaxis.layer', 'below traces');
})
.then(function() {
_assert([
'draglayer', 'plotbg', 'backplot', 'grids',
'aaxis', 'aline',
'frontplot',
'baxis', 'bline', 'caxis', 'cline'
]);
return Plotly.relayout(gd, 'ternary.caxis.layer', 'below traces');
})
.then(function() {
_assert([
'draglayer', 'plotbg', 'backplot', 'grids',
'aaxis', 'aline', 'caxis', 'cline',
'frontplot',
'baxis', 'bline'
]);
return Plotly.relayout(gd, 'ternary.baxis.layer', 'below traces');
})
.then(function() {
_assert([
'draglayer', 'plotbg', 'backplot', 'grids',
'aaxis', 'aline', 'baxis', 'bline', 'caxis', 'cline',
'frontplot'
]);
return Plotly.relayout(gd, 'ternary.aaxis.layer', null);
})
.then(function() {
_assert([
'draglayer', 'plotbg', 'backplot', 'grids',
'baxis', 'bline', 'caxis', 'cline',
'frontplot',
'aaxis', 'aline'
]);
return Plotly.relayout(gd, {
'ternary.baxis.layer': null,
'ternary.caxis.layer': null
});
})
.then(function() {
_assert(dflt);
})
.catch(fail)
.then(done);
});

function countTernarySubplot() {
return d3.selectAll('.ternary').size();
}
Expand Down

0 comments on commit 885c545

Please sign in to comment.