Skip to content
This repository was archived by the owner on Nov 7, 2018. It is now read-only.

Update force_labels plugin for d3 v4 #161

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 32 additions & 14 deletions force_labels/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
# D3 Force Labels v 0.1
# D3 Force Labels

Demo: <http://bl.ocks.org/1691430/>
Demo: <https://bl.ocks.org/TylerRick/c5df6e777a9de71777b4c4b449abfae8>

Generates an automatic and dynamic positioning for labels, using the d3 force layout. Once a```force_labels``` object has been created, simply call the bound function ```update``` with a selection of the objects you want to attach labels to as an argument. The force_labels object is a d3.force object which allows full control over the charge, gravity, theta etc.
Generates an automatic and dynamic positioning for labels, using the d3 force layout. Once a ```forceLabels``` object has been created, simply call the bound function ```update``` with a selection of the objects you want to attach labels to as an argument.

`forceLabels()` returns a [`d3.forceSimulation()`](https://github.com/d3/d3-force/blob/master/README.md#forceSimulation) object which allows full control over the charge strength, etc.

For example, to change the charge strength, you could do something like this:
```
labelSim.force('charge').strength(-60);
```

At each tick the following occurs:

Expand All @@ -11,19 +18,30 @@ At each tick the following occurs:

Both the ```anchorPos``` and ```labelPos``` are inserted in the ```__data__``` variable of the object being labeled. This allows easy access when drawing the labels and connectors.

In the demo the label and link are created as svg objects on the same data selection as the anchors. As the position information is embedded in ```__data__``` the redraw function is simply:
In the demo the label and link are created as svg objects on the same data selection as the anchors. As the position information is embedded in ```__data__```, the redraw function is simply:

```js
function redrawLabels() {
labelBox
.attr("transform",function(d) { return "translate("+d.labelPos.x+" "+d.labelPos.y+")"});

links
.attr("x1",function(d) { return d.anchorPos.x})
.attr("y1",function(d) { return d.anchorPos.y})
.attr("x2",function(d) { return d.labelPos.x})
.attr("y2",function(d) { return d.labelPos.y});
labelBox
.attr("transform", function(d) {
return "translate(" + d.labelPos.x + " " + d.labelPos.y + ")"
})

links
.attr("x1", function(d) { return d.anchorPos.x })
.attr("y1", function(d) { return d.anchorPos.y })
.attr("x2", function(d) { return d.labelPos.x })
.attr("y2", function(d) { return d.labelPos.y })
}
```
```

which is attached like this:
```js
labelSim = d3.forceLabels()
.on("tick", redrawLabels)
```

## Authors

<https://github.com/ZJONSSON>
- <https://github.com/ZJONSSON>
- <https://github.com/TylerRick>
97 changes: 52 additions & 45 deletions force_labels/force_labels.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,64 @@
(function() {
d3.force_labels = function force_labels() {
var labels = d3.layout.force();
d3.forceLabels = function forceLabels() {
var labelSim = d3.forceSimulation()

// Update the position of the anchor based on the center of bounding box
function updateAnchor() {
if (!labels.selection) return;
labels.selection.each(function(d) {
if (!labelSim.selection) return;

labelSim.selection.each(function(d) {
var bbox = this.getBBox(),
x = bbox.x + bbox.width / 2,
y = bbox.y + bbox.height / 2;
x = bbox.x + bbox.width/2,
y = bbox.y + bbox.height/2

d.anchorPos.x = x
d.anchorPos.y = y

d.anchorPos.x = x;
d.anchorPos.y = y;

// If a label position does not exist, set it to be the anchor position
// If a label position does not exist, set it to be the anchor position
if (d.labelPos.x == null) {
d.labelPos.x = x;
d.labelPos.y = y;
d.labelPos.x = x
d.labelPos.y = y
}
});
})
}
//The anchor position should be updated on each tick
labels.on("tick.labels", updateAnchor);

// The anchor position should be updated on each tick
labelSim.on("tick.labels", updateAnchor)

// This updates all nodes/links - retaining any previous labelPos on updated nodes
labels.update = function(selection) {
labels.selection = selection;
labelSim.update = function(selection) {
labelSim.selection = selection
var nodes = [], links = [];
selection[0].forEach(function(d) {
if(d && d.__data__) {
var data = d.__data__;
if (!d.labelPos) d.labelPos = {fixed: false};
if (!d.anchorPos) d.anchorPos = {fixed: true};
// Place position objects in __data__ to make them available through
selection.nodes().forEach(function(d) {
if (d && d.__data__) {
var data = d.__data__

if (!d.labelPos) d.labelPos = {fixed:false}
if (!d.anchorPos) d.anchorPos = {fixed:true}

// Place position objects in __data__ to make them available through
// d.labelPos/d.anchorPos for different elements
data.labelPos = d.labelPos;
data.anchorPos = d.anchorPos;
links.push({target: d.anchorPos, source: d.labelPos});
nodes.push(d.anchorPos);
nodes.push(d.labelPos);
data.labelPos = d.labelPos
data.anchorPos = d.anchorPos

links.push({target:d.anchorPos, source:d.labelPos})
nodes.push(d.anchorPos)
nodes.push(d.labelPos)
}
});
labels
.stop()
.nodes(nodes)
.links(links);
updateAnchor();
labels.start();
};
return labels;
};
})();
})

// Initialize simulation with nodes and some default forces (can be
// changed later by user)
labelSim
.nodes(nodes)
.force("charge", d3.forceManyBody())
.force("link", d3.forceLink()
.links(links)
.id(function id(d) { return d.id; })
.distance(function id(link, i) { return 10; })
)
updateAnchor()
}
return labelSim
}
})()