Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixup sankey text and shadow #5531

Merged
merged 11 commits into from
Mar 5, 2021
10 changes: 6 additions & 4 deletions src/lib/svg_text_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -755,10 +755,12 @@ function alignHTMLWith(_base, container, options) {
};
}

exports.makeTextShadow = function(offset, color) {
var x = offset + 'px ';
var y = offset + 'px ';
var b = '1px ';
var onePx = '1px ';

exports.makeTextShadow = function(color) {
var x = onePx;
var y = onePx;
var b = onePx;
return x + y + b + color + ', ' +
'-' + x + '-' + y + b + color + ', ' +
x + '-' + y + b + color + ', ' +
Expand Down
2 changes: 1 addition & 1 deletion src/traces/parcats/parcats.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ function performPlot(parcatsModels, graphDiv, layout, svg) {
})
.attr('alignment-baseline', 'middle')

.style('text-shadow', svgTextUtils.makeTextShadow(1, paperColor))
.style('text-shadow', svgTextUtils.makeTextShadow(paperColor))
.style('fill', 'rgb(0, 0, 0)')
.attr('x',
function(d) {
Expand Down
2 changes: 1 addition & 1 deletion src/traces/parcoords/parcoords.js
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ module.exports = function parcoords(gd, cdModule, layout, callbacks) {
.attr('stroke-width', '1px');

axis.selectAll('text')
.style('text-shadow', svgTextUtils.makeTextShadow(1, paperColor))
.style('text-shadow', svgTextUtils.makeTextShadow(paperColor))
.style('cursor', 'default');

var axisHeading = axisOverlays.selectAll('.' + c.cn.axisHeading)
Expand Down
6 changes: 1 addition & 5 deletions src/traces/sankey/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ module.exports = {
sankeyNodeSet: 'sankey-node-set',
sankeyNode: 'sankey-node',
nodeRect: 'node-rect',
nodeCapture: 'node-capture',
nodeCentered: 'node-entered',
nodeLabelGuide: 'node-label-guide',
nodeLabel: 'node-label',
nodeLabelTextPath: 'node-label-text-path'
nodeLabel: 'node-label'
}
};
144 changes: 51 additions & 93 deletions src/traces/sankey/render.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
'use strict';

var c = require('./constants');
var d3Force = require('d3-force');
var interpolateNumber = require('d3-interpolate').interpolateNumber;
var d3 = require('@plotly/d3');
var d3Sankey = require('@plotly/d3-sankey');
var d3SankeyCircular = require('@plotly/d3-sankey-circular');

var c = require('./constants');
var tinycolor = require('tinycolor2');
var Color = require('../../components/color');
var Drawing = require('../../components/drawing');
var d3Sankey = require('@plotly/d3-sankey');
var d3SankeyCircular = require('@plotly/d3-sankey-circular');
var d3Force = require('d3-force');
var Lib = require('../../lib');
var strTranslate = Lib.strTranslate;
var strRotate = Lib.strRotate;
var gup = require('../../lib/gup');
var keyFun = gup.keyFun;
var repeat = gup.repeat;
var unwrap = gup.unwrap;
var interpolateNumber = require('d3-interpolate').interpolateNumber;
var svgTextUtils = require('../../lib/svg_text_utils');

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

var alignmentConstants = require('../../constants/alignment');
var CAP_SHIFT = alignmentConstants.CAP_SHIFT;
var LINE_SPACING = alignmentConstants.LINE_SPACING;
var TEXTPAD = 3;

// view models

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

function nodeCentering(d) {
return strTranslate(d.horizontal ? 0 : d.labelY, d.horizontal ? d.labelY : 0);
}

function textGuidePath(d) {
return d3.svg.line()([
[d.horizontal ? (d.left ? -d.sizeAcross : d.visibleWidth + c.nodeTextOffsetHorizontal) : c.nodeTextOffsetHorizontal, 0],
[d.horizontal ? (d.left ? - c.nodeTextOffsetHorizontal : d.sizeAcross) : d.visibleHeight - c.nodeTextOffsetHorizontal, 0]
]);
}

function sankeyInverseTransform(d) {return d.horizontal ? 'matrix(1 0 0 1 0 0)' : 'matrix(0 1 1 0 0 0)';}
function textFlip(d) {return d.horizontal ? 'scale(1 1)' : 'scale(-1 1)';}
function nodeTextColor(d) {return d.darkBackground && !d.horizontal ? 'rgb(255,255,255)' : 'rgb(0,0,0)';}
function nodeTextOffset(d) {return d.horizontal && d.left ? '100%' : '0%';}

// event handling

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

var nodeCapture = sankeyNode.selectAll('.' + c.cn.nodeCapture)
.data(repeat);

nodeCapture.enter()
.append('rect')
.classed(c.cn.nodeCapture, true)
.style('fill-opacity', 0);

nodeCapture
.attr('x', function(d) {return d.zoneX;})
.attr('y', function(d) {return d.zoneY;})
.attr('width', function(d) {return d.zoneWidth;})
.attr('height', function(d) {return d.zoneHeight;});

var nodeCentered = sankeyNode.selectAll('.' + c.cn.nodeCentered)
.data(repeat);

nodeCentered.enter()
.append('g')
.classed(c.cn.nodeCentered, true)
.attr('transform', nodeCentering);

nodeCentered
.transition()
.ease(c.ease).duration(c.duration)
.attr('transform', nodeCentering);

var nodeLabelGuide = nodeCentered.selectAll('.' + c.cn.nodeLabelGuide)
.data(repeat);

nodeLabelGuide.enter()
.append('path')
.classed(c.cn.nodeLabelGuide, true)
.attr('id', function(d) {return d.uniqueNodeLabelPathId;})
.attr('d', textGuidePath)
.attr('transform', sankeyInverseTransform);

nodeLabelGuide
.transition()
.ease(c.ease).duration(c.duration)
.attr('d', textGuidePath)
.attr('transform', sankeyInverseTransform);

var nodeLabel = nodeCentered.selectAll('.' + c.cn.nodeLabel)
var nodeLabel = sankeyNode.selectAll('.' + c.cn.nodeLabel)
.data(repeat);

nodeLabel.enter()
.append('text')
.classed(c.cn.nodeLabel, true)
.attr('transform', textFlip)
.style('cursor', 'default')
.style('fill', 'black');
.style('cursor', 'default');

nodeLabel
.style('text-shadow', function(d) {
return d.horizontal ? svgTextUtils.makeTextShadow(1, '#fff') : 'none';
.attr('data-notex', 1) // prohibit tex interpretation until we can handle tex and regular text together
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nonblocking, but this isn't quite the rationale here, here we don't want tex because of shadows and positioning issues :)

.text(function(d) { return d.node.label; })
.each(function(d) {
var e = d3.select(this);
Drawing.font(e, d.textFont);
svgTextUtils.convertToTspans(e, gd);
})
.each(function(d) {Drawing.font(nodeLabel, d.textFont);});

nodeLabel
.transition()
.ease(c.ease).duration(c.duration)
.attr('transform', textFlip);

var nodeLabelTextPath = nodeLabel.selectAll('.' + c.cn.nodeLabelTextPath)
.data(repeat);
.style('text-shadow', svgTextUtils.makeTextShadow(gd._fullLayout.paper_bgcolor))
.attr('text-anchor', function(d) {
return (d.horizontal && d.left) ? 'end' : 'start';
})
.attr('transform', function(d) {
var e = d3.select(this);
// how much to shift a multi-line label to center it vertically.
var nLines = svgTextUtils.lineCount(e);
var blockHeight = d.textFont.size * (
(nLines - 1) * LINE_SPACING - CAP_SHIFT
);

var posX = d.nodeLineWidth / 2 + TEXTPAD;
var posY = ((d.horizontal ? d.visibleHeight : d.visibleWidth) - blockHeight) / 2;
if(d.horizontal) {
if(d.left) {
posX = -posX;
} else {
posX += d.visibleWidth;
}
}

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

nodeLabelTextPath
.text(function(d) {return d.horizontal || d.node.dy > 5 ? d.node.label : '';})
.attr('text-anchor', function(d) {return d.horizontal && d.left ? 'end' : 'start';});
return strTranslate(
d.horizontal ? posX : posY,
d.horizontal ? posY : posX
) + flipText;
});

nodeLabelTextPath
nodeLabel
.transition()
.ease(c.ease).duration(c.duration)
.attr('startOffset', nodeTextOffset)
.style('fill', nodeTextColor);
.ease(c.ease).duration(c.duration);
};
Binary file modified test/image/baselines/sankey_circular.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/sankey_circular_large.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/sankey_circular_process.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/sankey_circular_simple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/sankey_circular_simple2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/sankey_energy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/sankey_energy_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/sankey_groups.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/sankey_large_padding.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/sankey_link_concentration.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/sankey_messy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/sankey_subplots.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/sankey_subplots_circular.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/sankey_x_y.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/image/mocks/sankey_energy_dark.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@
"height": 1000,
"paper_bgcolor": "rgba(0,0,0,1)",
"font": {
"color": "white",
"size": 10
},
"updatemenus": [
Expand Down
32 changes: 25 additions & 7 deletions test/image/mocks/sankey_subplots.json
Original file line number Diff line number Diff line change
@@ -1,40 +1,57 @@
{
"data": [{
"domain": {
"x": [0, 0.45]
"y": [0, 0.45]
},
"type": "sankey",
"orientation": "h",
"node": {
"line": {
"width": 4,
"color": "black"
},
"label": ["el1", "el2", "el3"]
"label": [
"<i>Rien ne se perd,<br>rien ne se crée,<br>tout se <b>transforme</b>.</i><sub>",
"H<sub>2</sub>O",
"<b>e<sup>iπ</sup> = cos i + i sin π</b>"
]
},
"link": {
"source": [0, 2],
"target": [1, 1],
"value": [120, 50],
"label": ["stram1", "stream2"]
},
"textfont": {
"color": "darkblue"
}
},
{
"domain": {
"x": [0.55, 1]
"y": [0.55, 1]
},
"type": "sankey",
"orientation": "h",
"orientation": "v",
"node": {
"thickness": 50,
"line": {
"width": 4,
"color": "black"
},
"label": ["el4", "el5", "el6"]
"label": [
"<i>Rien ne se perd,<br>rien ne se crée,<br>tout se <b>transforme</b>.</i><sub>",
"H<sub>2</sub>O",
"<b>e<sup>iπ</sup> = cos i + i sin π</b>"
]
},
"link": {
"source": [0, 2],
"target": [1, 1],
"value": [120, 50],
"label": ["stram4", "stream5"]
"label": ["stram1", "stream2"]
},
"textfont": {
"color": "darkblue"
}
},
{
Expand All @@ -44,7 +61,8 @@
],
"layout": {
"title": {"text": "Multiple Sankey plots"},
"autosize": true,
"width": 600,
"height": 600,
"showlegend": false
}
}
Loading