EiffelFly / EiffelFly/Hololink-Graph-Overview

How to wrap long text in force diagram?

Open
#2 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
HTML
Stars
2
Forks
0
PR merge metrics
No merged PRs in 30d

Description

First of all, there is only a method provide by mbostock on internet, all solution surround that.
[https://bl.ocks.org/mbostock/7555321](https://bl.ocks.org/mbostock/7555321)

```javascript
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
```

To speed the process of coding I try this solution too, and discover various problems.

1. Only support English.
2. Base on every English letter, which is not accurate.
3. Need to be adjust when use force diagram

But still, thanks mbostock for providing this elegant code.

---

When I first implement this function this is what I got, Which definitely have some problem. And I discovered when you want to wrap chinese letter you have to customize it.

![螢幕快照 2020-08-25 下午5 05 10](https://user-images.githubusercontent.com/57251712/91155355-4f4c5500-e6f5-11ea-9e37-4f59facb718a.png)

and I begin working on chinese version. First, I replace this line `words = text.text().split(/\s+/).reverse(), ` with `text.text().split('').reverse(), ` to fit chinese characteristic. Then the diagram become:

![wrong-diagram_2](https://user-images.githubusercontent.com/57251712/91175984-330be080-e714-11ea-9d83-c5a0ab6764e6.png)

In the console I discovered that I didn't pass x and y variable correctly. And after dig inside the code I recognized that I misunderstood the whole logic of d3 force. For regular diagram such as bar chart, I believe the x and y parameter of every nodes are computed after you use `data().enter()` binding the data. But force diagram is different, the computing process begin when you call simulation on and ticking which is totally logical.

If you want to access the computed x, y and store them inside every nodes as a attribute, you need to initiate simulation first then selectAll and modify it.

From then on, I want to make sure I get the computed x, y parameter.

```javascript

var nodeElements = g.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter()
.append("circle")
.attr("r", getNodeSize)
.attr("fill", getNodeColor)
.style("stroke", getLinkColor)
.attr("x", function(d){ return d.x; })
.attr("y", function(d){ return d.y; })
.on('mouseover.fade', fade(0.2))
.on('mouseout.fade', fade(1));

var textElements = g.append("g")
.attr("class", "text")
.selectAll("text")
.data(graph.nodes)
.enter()
.filter(function(d){
return d.connection >= 2 || d.level == 'article'
})
.append('text')
.text(node => node.id)
.attr('text-anchor', 'middle')
.attr('font-family', "'Noto Sans TC', sans-serif")
.attr('font-weight', getTextFontWeight)
.attr('font-size', getTextFontSize)
.attr("x", function(d){ return d.x; })
.attr("y", function(d){ return d.y; })
.style('fill', 'black')
.each(wrap);

function wrap(d) {
if (d.connection >= 2 || d.level == 'article'){
var text = d3.select(this),
width = getNodeSize(d),
words = text.text().split("").reverse(),
word,
x = d.x
y = d.y
line = [],
lineNumber = 0,
lineHeight = 1.1,
tspan = text.text(null).append("tspan").attr("x", x).attr("y", y);
console.log(d, d.x, text.attr('x')) // <= this output interesting data

while (word = words.pop()) {
line.push(word);
tspan.text(line.join(""));
//console.log(word, tspan.node().getComputedTextLength(), x,y)

if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(""));
line = [word];
tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + "em").text(word);
}
};
}

};
```

![螢幕快照 2020-08-26 下午3 28 24](https://user-images.githubusercontent.com/57251712/91274140-d6123800-e7b0-11ea-81a3-36dc4cb3ce4d.png)

d is node's array and is including the computed x, y for sure, but when you try to access d.x it become another data which is node attribute we put into it previously. What exactly is that and how we get the actual computed x,y.

What I had tried:

1. Learned the data format you can load into function when use `selectAll() & data()`

```javascript
function (d, i, nodes){
d = node
nodes = arrays contain all nodes
i = index
nodes[i] = d
}
```
![螢幕快照 2020-08-27 下午4 04 30](https://user-images.githubusercontent.com/57251712/91414377-0de7b100-e87f-11ea-8e93-68c94ac3e482.png)

2. The process of force diagram

a. simulation: if node is empty then create an empty array.
b. simulation.node(): this method will arrange the position
c. so in theory I should get the x after sinulation.

3. I should put it before selectAll to get x is wrong. The reason is simple, at that moment there is no force, just some nodes. So the nodes will arrange on a line, that is the reason I get a singular x in node array.

4. some data format thought different process

```javascript

var nodeElements = g.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter()
.append("circle")
.attr('test', testing)

function testing(d, i, nodes){
node = d3.select(this)
node_data = d3.select(this).datum()
console.log(this, node, node_data ,d)
}
```
=> 1. ...with node data
=> 2. d3 selection format of node data
=> 3. node array
=> 4. node array

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.