Skip to content

Commit c2f7f71

Browse files
committed
ternary + scatterternary basic functionality
1 parent abdce19 commit c2f7f71

File tree

16 files changed

+548
-55
lines changed

16 files changed

+548
-55
lines changed

lib/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ Core.register([
2929
require('./mesh3d'),
3030
require('./scattergeo'),
3131
require('./choropleth'),
32-
require('./scattergl')
32+
require('./scattergl'),
33+
require('./scatterternary')
3334
]);
3435

3536
module.exports = Core;

lib/scatterternary.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Copyright 2012-2016, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
module.exports = require('../src/traces/scatterternary');

src/components/titles/index.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ Titles.draw = function(gd, title) {
9797
}
9898
else if(axletter === 'x') {
9999
xa = cont;
100-
ya = (xa.anchor === 'free') ?
100+
ya = xa._counteraxis || ((xa.anchor === 'free') ?
101101
{_offset: gs.t + (1 - (xa.position || 0)) * gs.h, _length: 0} :
102-
axisIds.getFromId(gd, xa.anchor);
102+
axisIds.getFromId(gd, xa.anchor));
103103

104104
x = xa._offset + xa._length / 2;
105105
y = ya._offset + ((xa.side === 'top') ?
@@ -112,9 +112,9 @@ Titles.draw = function(gd, title) {
112112
}
113113
else if(axletter === 'y') {
114114
ya = cont;
115-
xa = (ya.anchor === 'free') ?
115+
xa = ya._counteraxis || ((ya.anchor === 'free') ?
116116
{_offset: gs.l + (ya.position || 0) * gs.w, _length: 0} :
117-
axisIds.getFromId(gd, ya.anchor);
117+
axisIds.getFromId(gd, ya.anchor));
118118

119119
y = ya._offset + ya._length / 2;
120120
x = xa._offset + ((ya.side === 'right') ?

src/plots/cartesian/axes.js

Lines changed: 58 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,12 +1373,19 @@ axes.doTicks = function(td, axid, skipTitle) {
13731373
pad = (ax.linewidth||1) / 2,
13741374
labelStandoff =
13751375
(ax.ticks==='outside' ? ax.ticklen : 1) + (ax.linewidth||0),
1376+
labelShift = 0,
13761377
gridWidth = Plotly.Drawing.crispRound(td, ax.gridwidth, 1),
13771378
zeroLineWidth = Plotly.Drawing.crispRound(td, ax.zerolinewidth, gridWidth),
13781379
tickWidth = Plotly.Drawing.crispRound(td, ax.tickwidth, 1),
1379-
sides, transfn, tickprefix, tickmid,
1380+
sides, transfn, tickpathfn,
13801381
i;
13811382

1383+
if(ax._counterangle && ax.ticks==='outside') {
1384+
var caRad = ax._counterangle * Math.PI / 180;
1385+
labelStandoff = ax.ticklen * Math.cos(caRad) + (ax.linewidth || 0);
1386+
labelShift = ax.ticklen * Math.sin(caRad);
1387+
}
1388+
13821389
// positioning arguments for x vs y axes
13831390
if(axletter==='x') {
13841391
sides = ['bottom', 'top'];
@@ -1387,16 +1394,26 @@ axes.doTicks = function(td, axid, skipTitle) {
13871394
};
13881395
// dumb templating with string concat
13891396
// would be better to use an actual template
1390-
tickprefix = 'M0,';
1391-
tickmid = 'v';
1397+
tickpathfn = function(shift, len) {
1398+
if(ax._counterangle) {
1399+
var caRad = ax._counterangle * Math.PI / 180;
1400+
return 'M0,' + shift + 'l' + (Math.sin(caRad) * len) + ',' + (Math.cos(caRad) * len);
1401+
}
1402+
else return 'M0,' + shift + 'v' + len;
1403+
};
13921404
}
13931405
else if(axletter==='y') {
13941406
sides = ['left', 'right'];
13951407
transfn = function(d) {
13961408
return 'translate(0,'+ax.l2p(d.x)+')';
13971409
};
1398-
tickprefix = 'M';
1399-
tickmid = ',0h';
1410+
tickpathfn = function(shift, len) {
1411+
if(ax._counterangle) {
1412+
var caRad = ax._counterangle * Math.PI / 180;
1413+
return 'M' + shift + ',0l' + (Math.cos(caRad) * len) + ',' + (-Math.sin(caRad) * len);
1414+
}
1415+
else return 'M' + shift + ',0h' + len;
1416+
};
14001417
}
14011418
else {
14021419
console.log('unrecognized doTicks axis', axid);
@@ -1444,10 +1461,10 @@ axes.doTicks = function(td, axid, skipTitle) {
14441461
return;
14451462
}
14461463

1447-
var labelx, labely, labelanchor, labelpos0;
1464+
var labelx, labely, labelanchor, labelpos0, flipit;
14481465
if(axletter==='x') {
1449-
var flipit = axside==='bottom' ? 1 : -1;
1450-
labelx = function(d) { return d.dx; };
1466+
flipit = axside==='bottom' ? 1 : -1;
1467+
labelx = function(d) { return d.dx + labelShift * flipit; };
14511468
labelpos0 = position + (labelStandoff+pad)*flipit;
14521469
labely = function(d) {
14531470
return d.dy+labelpos0+d.fontSize *
@@ -1461,11 +1478,11 @@ axes.doTicks = function(td, axid, skipTitle) {
14611478
};
14621479
}
14631480
else {
1464-
labely = function(d) { return d.dy+d.fontSize/2; };
1481+
flipit = axside==='right' ? 1 : -1;
1482+
labely = function(d) { return d.dy+d.fontSize/2 - labelShift * flipit; };
14651483
labelx = function(d) {
14661484
return d.dx + position + (labelStandoff + pad +
1467-
(Math.abs(ax.tickangle)===90 ? d.fontSize/2 : 0)) *
1468-
(axside==='right' ? 1 : -1);
1485+
(Math.abs(ax.tickangle)===90 ? d.fontSize/2 : 0)) * flipit;
14691486
};
14701487
labelanchor = function(angle) {
14711488
if(isNumeric(angle) && Math.abs(angle)===90) {
@@ -1630,8 +1647,8 @@ axes.doTicks = function(td, axid, skipTitle) {
16301647
var gridcontainer = plotinfo.gridlayer,
16311648
zlcontainer = plotinfo.zerolinelayer,
16321649
gridvals = plotinfo['hidegrid'+axletter]?[]:valsClipped,
1633-
gridpath = 'M0,0'+((axletter==='x') ? 'v' : 'h') +
1634-
counteraxis._length,
1650+
gridpath = ax._gridpath ||
1651+
'M0,0'+((axletter==='x') ? 'v' : 'h') + counteraxis._length,
16351652
grid = gridcontainer.selectAll('path.'+gcls)
16361653
.data(ax.showgrid===false ? [] : gridvals, datafn);
16371654
grid.enter().append('path').classed(gcls, 1)
@@ -1649,31 +1666,38 @@ axes.doTicks = function(td, axid, skipTitle) {
16491666
grid.exit().remove();
16501667

16511668
// zero line
1652-
var hasBarsOrFill = false;
1653-
for(var i = 0; i < td._fullData.length; i++) {
1654-
if(traceHasBarsOrFill(td._fullData[i], subplot)) {
1655-
hasBarsOrFill = true;
1656-
break;
1669+
if(zlcontainer) {
1670+
var hasBarsOrFill = false;
1671+
for(var i = 0; i < td._fullData.length; i++) {
1672+
if(traceHasBarsOrFill(td._fullData[i], subplot)) {
1673+
hasBarsOrFill = true;
1674+
break;
1675+
}
16571676
}
1677+
var showZl = (ax.range[0]*ax.range[1]<=0) && ax.zeroline &&
1678+
(ax.type==='linear' || ax.type==='-') && gridvals.length &&
1679+
(hasBarsOrFill || clipEnds({x: 0}) || !ax.showline);
1680+
var zl = zlcontainer.selectAll('path.'+zcls)
1681+
.data(showZl ? [{x: 0}] : []);
1682+
zl.enter().append('path').classed(zcls,1).classed('zl',1)
1683+
.classed('crisp',1)
1684+
.attr('d',gridpath);
1685+
zl.attr('transform',transfn)
1686+
.call(Plotly.Color.stroke, ax.zerolinecolor || Plotly.Color.defaultLine)
1687+
.style('stroke-width', zeroLineWidth+'px');
1688+
zl.exit().remove();
16581689
}
1659-
var showZl = (ax.range[0]*ax.range[1]<=0) && ax.zeroline &&
1660-
(ax.type==='linear' || ax.type==='-') && gridvals.length &&
1661-
(hasBarsOrFill || clipEnds({x: 0}) || !ax.showline);
1662-
1663-
var zl = zlcontainer.selectAll('path.'+zcls)
1664-
.data(showZl ? [{x: 0}] : []);
1665-
zl.enter().append('path').classed(zcls,1).classed('zl',1)
1666-
.classed('crisp',1)
1667-
.attr('d',gridpath);
1668-
zl.attr('transform',transfn)
1669-
.call(Plotly.Color.stroke, ax.zerolinecolor || Plotly.Color.defaultLine)
1670-
.style('stroke-width', zeroLineWidth+'px');
1671-
zl.exit().remove();
16721690
}
16731691

16741692
if(independent) {
1675-
drawTicks(ax._axislayer, tickprefix + (ax._pos+pad*ticksign[2]) +
1676-
tickmid + (ticksign[2]*ax.ticklen));
1693+
drawTicks(ax._axislayer, tickpathfn(ax._pos+pad*ticksign[2], ticksign[2]*ax.ticklen));
1694+
if(ax._counteraxis) {
1695+
var fictionalPlotinfo = {
1696+
gridlayer: ax._gridlayer,
1697+
zerolinelayer: ax._zerolinelayer
1698+
};
1699+
drawGrid(fictionalPlotinfo, ax._counteraxis);
1700+
}
16771701
return drawLabels(ax._axislayer,ax._pos);
16781702
}
16791703
else {
@@ -1713,8 +1737,7 @@ axes.doTicks = function(td, axid, skipTitle) {
17131737
var pos = linepositions[sidei],
17141738
tsign = ticksign[sidei];
17151739
if(showside && isNumeric(pos)) {
1716-
tickpath += tickprefix + (pos+pad*tsign) +
1717-
tickmid + (tsign*ax.ticklen);
1740+
tickpath += tickpathfn(pos+pad*tsign, tsign*ax.ticklen);
17181741
}
17191742
});
17201743

src/plots/plots.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,7 @@ plots.supplyDataDefaults = function(traceIn, i, layout) {
635635
// differently for 3D cases.
636636
coerceSubplotAttr('gl3d', 'scene');
637637
coerceSubplotAttr('geo', 'geo');
638+
coerceSubplotAttr('ternary', 'subplot');
638639

639640
// module-specific attributes --- note: we need to send a trace into
640641
// the 3D modules to have it removed from the webgl context.

src/plots/ternary/index.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ var Plots = require('../../plots/plots');
1616

1717
exports.name = 'ternary';
1818

19-
exports.attr = 'ternary';
19+
exports.attr = 'subplot';
2020

2121
exports.idRoot = 'ternary';
2222

@@ -45,7 +45,7 @@ exports.plot = function plotTernary(gd) {
4545
ternary = new Ternary({
4646
id: ternaryId,
4747
graphDiv: gd,
48-
container: fullLayout._ternarycontainer.node()
48+
container: fullLayout._ternarylayer.node()
4949
},
5050
fullLayout
5151
);
@@ -65,7 +65,8 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout)
6565
var oldTernary = oldFullLayout[oldTernaryKey]._ternary;
6666

6767
if(!newFullLayout[oldTernaryKey] && !!oldTernary) {
68-
oldTernary.ternaryDiv.remove();
68+
oldTernary.plotContainer.remove();
69+
oldFullLayout.clipDef.remove();
6970
}
7071
}
7172
};

src/plots/ternary/layout/axis_attributes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module.exports = {
1818
titlefont: axesAttrs.titlefont,
1919
color: axesAttrs.color,
2020
// ticks
21-
nticks: axesAttrs.nticks,
21+
nticks: extendFlat({}, axesAttrs.nticks, {dflt: 6, min: 1}),
2222
ticks: axesAttrs.ticks,
2323
ticklen: axesAttrs.ticklen,
2424
tickwidth: axesAttrs.tickwidth,

src/plots/ternary/layout/axis_defaults.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
'use strict';
1111

1212
var Lib = require('../../../lib');
13-
var lightColor = require('../../components/color').lightColor;
13+
var lightColor = require('../../../components/color').lightColor;
1414

1515
var layoutAttributes = require('./axis_attributes');
1616
var handleTickMarkDefaults = require('../../cartesian/tick_mark_defaults');
@@ -71,7 +71,9 @@ module.exports = function supplyLayoutDefaults(containerIn, containerOut, option
7171
delete containerOut.linewidth;
7272
}
7373

74-
var gridColor = coerce2('gridcolor', lightColor(dfltColor, options.bgColor)),
74+
// default grid color is darker here (backFraction 0.6, vs default 0.909)
75+
// because the grid is not square so the eye needs heavier cues to follow
76+
var gridColor = coerce2('gridcolor', lightColor(dfltColor, options.bgColor, 0.6)),
7577
gridWidth = coerce2('gridwidth'),
7678
showGridLines = coerce('showgrid', !!gridColor || !!gridWidth);
7779

src/plots/ternary/layout/defaults.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
'use strict';
1111

12-
var Color = require('../../components/color');
12+
var Color = require('../../../components/color');
1313

1414
var handleSubplotDefaults = require('../../subplot_defaults');
1515
var layoutAttributes = require('./layout_attributes');
@@ -31,15 +31,33 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
3131

3232
function handleTernaryDefaults(ternaryLayoutIn, ternaryLayoutOut, coerce, options) {
3333
var bgColor = coerce('bgcolor');
34-
coerce('sum');
34+
var sum = coerce('sum');
3535
options.bgColor = Color.combine(bgColor, options.paper_bgcolor);
3636
var axName, containerIn, containerOut;
3737

38+
// TODO: allow most (if not all) axis attributes to be set
39+
// in the outer container and used as defaults in the individual axes?
40+
3841
for(var j = 0; j < axesNames.length; j++) {
3942
axName = axesNames[j];
4043
containerIn = ternaryLayoutIn[axName] || {};
41-
containerOut = {_name: axName};
44+
containerOut = ternaryLayoutOut[axName] = {_name: axName};
4245

4346
handleAxisDefaults(containerIn, containerOut, options);
4447
}
48+
49+
// if the min values contradict each other, set them all to default (0)
50+
// and delete *all* the inputs so the user doesn't get confused later by
51+
// changing one and having them all change.
52+
var aaxis = ternaryLayoutOut.aaxis,
53+
baxis = ternaryLayoutOut.baxis,
54+
caxis = ternaryLayoutOut.caxis;
55+
if(aaxis.min + baxis.min + caxis.min >= sum) {
56+
aaxis.min = 0;
57+
baxis.min = 0;
58+
caxis.min = 0;
59+
if(ternaryLayoutIn.aaxis) delete ternaryLayoutIn.aaxis.min;
60+
if(ternaryLayoutIn.baxis) delete ternaryLayoutIn.baxis.min;
61+
if(ternaryLayoutIn.caxis) delete ternaryLayoutIn.caxis.min;
62+
}
4563
}

0 commit comments

Comments
 (0)