diff --git a/force_labels/README.md b/force_labels/README.md index 519605f..4ef4c16 100644 --- a/force_labels/README.md +++ b/force_labels/README.md @@ -1,8 +1,15 @@ -# D3 Force Labels v 0.1 +# D3 Force Labels -Demo: +Demo: -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: @@ -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 - \ No newline at end of file +- +- diff --git a/force_labels/force_labels.js b/force_labels/force_labels.js index cfa4454..ba86a43 100644 --- a/force_labels/force_labels.js +++ b/force_labels/force_labels.js @@ -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 + } +})()