Skip to content

Commit ff0c00a

Browse files
authored
Merge pull request #5531 from plotly/sankey-text-and-shadow
Fixup sankey text and shadow
2 parents ed52205 + 2f38a9d commit ff0c00a

22 files changed

+97
-122
lines changed

src/lib/svg_text_utils.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -755,10 +755,12 @@ function alignHTMLWith(_base, container, options) {
755755
};
756756
}
757757

758-
exports.makeTextShadow = function(offset, color) {
759-
var x = offset + 'px ';
760-
var y = offset + 'px ';
761-
var b = '1px ';
758+
var onePx = '1px ';
759+
760+
exports.makeTextShadow = function(color) {
761+
var x = onePx;
762+
var y = onePx;
763+
var b = onePx;
762764
return x + y + b + color + ', ' +
763765
'-' + x + '-' + y + b + color + ', ' +
764766
x + '-' + y + b + color + ', ' +

src/traces/parcats/parcats.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ function performPlot(parcatsModels, graphDiv, layout, svg) {
238238
})
239239
.attr('alignment-baseline', 'middle')
240240

241-
.style('text-shadow', svgTextUtils.makeTextShadow(1, paperColor))
241+
.style('text-shadow', svgTextUtils.makeTextShadow(paperColor))
242242
.style('fill', 'rgb(0, 0, 0)')
243243
.attr('x',
244244
function(d) {

src/traces/parcoords/parcoords.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,7 @@ module.exports = function parcoords(gd, cdModule, layout, callbacks) {
649649
.attr('stroke-width', '1px');
650650

651651
axis.selectAll('text')
652-
.style('text-shadow', svgTextUtils.makeTextShadow(1, paperColor))
652+
.style('text-shadow', svgTextUtils.makeTextShadow(paperColor))
653653
.style('cursor', 'default');
654654

655655
var axisHeading = axisOverlays.selectAll('.' + c.cn.axisHeading)

src/traces/sankey/constants.js

+1-5
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@ module.exports = {
1616
sankeyNodeSet: 'sankey-node-set',
1717
sankeyNode: 'sankey-node',
1818
nodeRect: 'node-rect',
19-
nodeCapture: 'node-capture',
20-
nodeCentered: 'node-entered',
21-
nodeLabelGuide: 'node-label-guide',
22-
nodeLabel: 'node-label',
23-
nodeLabelTextPath: 'node-label-text-path'
19+
nodeLabel: 'node-label'
2420
}
2521
};

src/traces/sankey/render.js

+51-93
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
11
'use strict';
22

3-
var c = require('./constants');
3+
var d3Force = require('d3-force');
4+
var interpolateNumber = require('d3-interpolate').interpolateNumber;
45
var d3 = require('@plotly/d3');
6+
var d3Sankey = require('@plotly/d3-sankey');
7+
var d3SankeyCircular = require('@plotly/d3-sankey-circular');
8+
9+
var c = require('./constants');
510
var tinycolor = require('tinycolor2');
611
var Color = require('../../components/color');
712
var Drawing = require('../../components/drawing');
8-
var d3Sankey = require('@plotly/d3-sankey');
9-
var d3SankeyCircular = require('@plotly/d3-sankey-circular');
10-
var d3Force = require('d3-force');
1113
var Lib = require('../../lib');
1214
var strTranslate = Lib.strTranslate;
15+
var strRotate = Lib.strRotate;
1316
var gup = require('../../lib/gup');
1417
var keyFun = gup.keyFun;
1518
var repeat = gup.repeat;
1619
var unwrap = gup.unwrap;
17-
var interpolateNumber = require('d3-interpolate').interpolateNumber;
1820
var svgTextUtils = require('../../lib/svg_text_utils');
1921

2022
var Registry = require('../../registry');
2123

24+
var alignmentConstants = require('../../constants/alignment');
25+
var CAP_SHIFT = alignmentConstants.CAP_SHIFT;
26+
var LINE_SPACING = alignmentConstants.LINE_SPACING;
27+
var TEXTPAD = 3;
28+
2229
// view models
2330

2431
function sankeyModel(layout, d, traceIndex) {
@@ -547,22 +554,6 @@ function sankeyTransform(d) {
547554
return offset + (d.horizontal ? 'matrix(1 0 0 1 0 0)' : 'matrix(0 1 1 0 0 0)');
548555
}
549556

550-
function nodeCentering(d) {
551-
return strTranslate(d.horizontal ? 0 : d.labelY, d.horizontal ? d.labelY : 0);
552-
}
553-
554-
function textGuidePath(d) {
555-
return d3.svg.line()([
556-
[d.horizontal ? (d.left ? -d.sizeAcross : d.visibleWidth + c.nodeTextOffsetHorizontal) : c.nodeTextOffsetHorizontal, 0],
557-
[d.horizontal ? (d.left ? - c.nodeTextOffsetHorizontal : d.sizeAcross) : d.visibleHeight - c.nodeTextOffsetHorizontal, 0]
558-
]);
559-
}
560-
561-
function sankeyInverseTransform(d) {return d.horizontal ? 'matrix(1 0 0 1 0 0)' : 'matrix(0 1 1 0 0 0)';}
562-
function textFlip(d) {return d.horizontal ? 'scale(1 1)' : 'scale(-1 1)';}
563-
function nodeTextColor(d) {return d.darkBackground && !d.horizontal ? 'rgb(255,255,255)' : 'rgb(0,0,0)';}
564-
function nodeTextOffset(d) {return d.horizontal && d.left ? '100%' : '0%';}
565-
566557
// event handling
567558

568559
function attachPointerEvents(selection, sankey, eventSet) {
@@ -970,88 +961,55 @@ module.exports = function(gd, svg, calcData, layout, callbacks) {
970961
.ease(c.ease).duration(c.duration)
971962
.call(sizeNode);
972963

973-
var nodeCapture = sankeyNode.selectAll('.' + c.cn.nodeCapture)
974-
.data(repeat);
975-
976-
nodeCapture.enter()
977-
.append('rect')
978-
.classed(c.cn.nodeCapture, true)
979-
.style('fill-opacity', 0);
980-
981-
nodeCapture
982-
.attr('x', function(d) {return d.zoneX;})
983-
.attr('y', function(d) {return d.zoneY;})
984-
.attr('width', function(d) {return d.zoneWidth;})
985-
.attr('height', function(d) {return d.zoneHeight;});
986-
987-
var nodeCentered = sankeyNode.selectAll('.' + c.cn.nodeCentered)
988-
.data(repeat);
989-
990-
nodeCentered.enter()
991-
.append('g')
992-
.classed(c.cn.nodeCentered, true)
993-
.attr('transform', nodeCentering);
994-
995-
nodeCentered
996-
.transition()
997-
.ease(c.ease).duration(c.duration)
998-
.attr('transform', nodeCentering);
999-
1000-
var nodeLabelGuide = nodeCentered.selectAll('.' + c.cn.nodeLabelGuide)
1001-
.data(repeat);
1002-
1003-
nodeLabelGuide.enter()
1004-
.append('path')
1005-
.classed(c.cn.nodeLabelGuide, true)
1006-
.attr('id', function(d) {return d.uniqueNodeLabelPathId;})
1007-
.attr('d', textGuidePath)
1008-
.attr('transform', sankeyInverseTransform);
1009-
1010-
nodeLabelGuide
1011-
.transition()
1012-
.ease(c.ease).duration(c.duration)
1013-
.attr('d', textGuidePath)
1014-
.attr('transform', sankeyInverseTransform);
1015-
1016-
var nodeLabel = nodeCentered.selectAll('.' + c.cn.nodeLabel)
964+
var nodeLabel = sankeyNode.selectAll('.' + c.cn.nodeLabel)
1017965
.data(repeat);
1018966

1019967
nodeLabel.enter()
1020968
.append('text')
1021969
.classed(c.cn.nodeLabel, true)
1022-
.attr('transform', textFlip)
1023-
.style('cursor', 'default')
1024-
.style('fill', 'black');
970+
.style('cursor', 'default');
1025971

1026972
nodeLabel
1027-
.style('text-shadow', function(d) {
1028-
return d.horizontal ? svgTextUtils.makeTextShadow(1, '#fff') : 'none';
973+
.attr('data-notex', 1) // prohibit tex interpretation until we can handle tex and regular text together
974+
.text(function(d) { return d.node.label; })
975+
.each(function(d) {
976+
var e = d3.select(this);
977+
Drawing.font(e, d.textFont);
978+
svgTextUtils.convertToTspans(e, gd);
1029979
})
1030-
.each(function(d) {Drawing.font(nodeLabel, d.textFont);});
1031-
1032-
nodeLabel
1033-
.transition()
1034-
.ease(c.ease).duration(c.duration)
1035-
.attr('transform', textFlip);
1036-
1037-
var nodeLabelTextPath = nodeLabel.selectAll('.' + c.cn.nodeLabelTextPath)
1038-
.data(repeat);
980+
.style('text-shadow', svgTextUtils.makeTextShadow(gd._fullLayout.paper_bgcolor))
981+
.attr('text-anchor', function(d) {
982+
return (d.horizontal && d.left) ? 'end' : 'start';
983+
})
984+
.attr('transform', function(d) {
985+
var e = d3.select(this);
986+
// how much to shift a multi-line label to center it vertically.
987+
var nLines = svgTextUtils.lineCount(e);
988+
var blockHeight = d.textFont.size * (
989+
(nLines - 1) * LINE_SPACING - CAP_SHIFT
990+
);
991+
992+
var posX = d.nodeLineWidth / 2 + TEXTPAD;
993+
var posY = ((d.horizontal ? d.visibleHeight : d.visibleWidth) - blockHeight) / 2;
994+
if(d.horizontal) {
995+
if(d.left) {
996+
posX = -posX;
997+
} else {
998+
posX += d.visibleWidth;
999+
}
1000+
}
10391001

1040-
nodeLabelTextPath.enter()
1041-
.append('textPath')
1042-
.classed(c.cn.nodeLabelTextPath, true)
1043-
.attr('alignment-baseline', 'middle')
1044-
.attr('xlink:href', function(d) {return '#' + d.uniqueNodeLabelPathId;})
1045-
.attr('startOffset', nodeTextOffset)
1046-
.style('fill', nodeTextColor);
1002+
var flipText = d.horizontal ? '' : (
1003+
'scale(-1,1)' + strRotate(90)
1004+
);
10471005

1048-
nodeLabelTextPath
1049-
.text(function(d) {return d.horizontal || d.node.dy > 5 ? d.node.label : '';})
1050-
.attr('text-anchor', function(d) {return d.horizontal && d.left ? 'end' : 'start';});
1006+
return strTranslate(
1007+
d.horizontal ? posX : posY,
1008+
d.horizontal ? posY : posX
1009+
) + flipText;
1010+
});
10511011

1052-
nodeLabelTextPath
1012+
nodeLabel
10531013
.transition()
1054-
.ease(c.ease).duration(c.duration)
1055-
.attr('startOffset', nodeTextOffset)
1056-
.style('fill', nodeTextColor);
1014+
.ease(c.ease).duration(c.duration);
10571015
};
-6 Bytes
Loading
-241 Bytes
Loading
-158 Bytes
Loading
-139 Bytes
Loading
-108 Bytes
Loading
-3.24 KB
Loading
43.3 KB
Loading
-2 Bytes
Loading
-348 Bytes
Loading
-100 Bytes
Loading

test/image/baselines/sankey_messy.png

-65 Bytes
Loading
24.8 KB
Loading
Loading

test/image/baselines/sankey_x_y.png

34 Bytes
Loading

test/image/mocks/sankey_energy_dark.json

+1
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@
366366
"height": 1000,
367367
"paper_bgcolor": "rgba(0,0,0,1)",
368368
"font": {
369+
"color": "white",
369370
"size": 10
370371
},
371372
"updatemenus": [

test/image/mocks/sankey_subplots.json

+25-7
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,57 @@
11
{
22
"data": [{
33
"domain": {
4-
"x": [0, 0.45]
4+
"y": [0, 0.45]
55
},
66
"type": "sankey",
77
"orientation": "h",
88
"node": {
99
"line": {
10+
"width": 4,
1011
"color": "black"
1112
},
12-
"label": ["el1", "el2", "el3"]
13+
"label": [
14+
"<i>Rien ne se perd,<br>rien ne se crée,<br>tout se <b>transforme</b>.</i><sub>",
15+
"H<sub>2</sub>O",
16+
"<b>e<sup>iπ</sup> = cos i + i sin π</b>"
17+
]
1318
},
1419
"link": {
1520
"source": [0, 2],
1621
"target": [1, 1],
1722
"value": [120, 50],
1823
"label": ["stram1", "stream2"]
24+
},
25+
"textfont": {
26+
"color": "darkblue"
1927
}
2028
},
2129
{
2230
"domain": {
23-
"x": [0.55, 1]
31+
"y": [0.55, 1]
2432
},
2533
"type": "sankey",
26-
"orientation": "h",
34+
"orientation": "v",
2735
"node": {
36+
"thickness": 50,
2837
"line": {
38+
"width": 4,
2939
"color": "black"
3040
},
31-
"label": ["el4", "el5", "el6"]
41+
"label": [
42+
"<i>Rien ne se perd,<br>rien ne se crée,<br>tout se <b>transforme</b>.</i><sub>",
43+
"H<sub>2</sub>O",
44+
"<b>e<sup>iπ</sup> = cos i + i sin π</b>"
45+
]
3246
},
3347
"link": {
3448
"source": [0, 2],
3549
"target": [1, 1],
3650
"value": [120, 50],
37-
"label": ["stram4", "stream5"]
51+
"label": ["stram1", "stream2"]
52+
},
53+
"textfont": {
54+
"color": "darkblue"
3855
}
3956
},
4057
{
@@ -44,7 +61,8 @@
4461
],
4562
"layout": {
4663
"title": {"text": "Multiple Sankey plots"},
47-
"autosize": true,
64+
"width": 600,
65+
"height": 600,
4866
"showlegend": false
4967
}
5068
}

0 commit comments

Comments
 (0)