Built with blockbuilder.org
forked from RobinL's block: force directed test
forked from RobinL's block: force directed text fit
forked from RobinL's block: force directed text fit done
| license: gpl-3.0 |
Built with blockbuilder.org
forked from RobinL's block: force directed test
forked from RobinL's block: force directed text fit
forked from RobinL's block: force directed text fit done
| name | parent | id | data | text | |
|---|---|---|---|---|---|
| a | 1 | 200 | total budget | ||
| p | 1 | 2 | 70 | operations | |
| q | 1 | 3 | 30 | finance | |
| p1 | 2 | 4 | 4 | text | |
| p2 | 2 | 5 | 10 | text | |
| p3 | 2 | 6 | 13 | text | |
| p4 | 2 | 7 | 1 | text | |
| p5 | 2 | 8 | 7 | text | |
| p6 | 2 | 9 | 7 | text | |
| p7 | 2 | 10 | 7 | text | |
| p8 | 2 | 11 | 7 | text | |
| p9 | 2 | 12 | 7 | text | |
| p10 | 2 | 13 | 7 | text | |
| q1 | 3 | 14 | 8 | text | |
| q2 | 3 | 15 | 7 | text | |
| q3 | 3 | 16 | 8 | text | |
| q4 | 3 | 17 | 7 | text | |
| r | 1 | 18 | 10 | other | |
| s | 1 | 19 | 90 | policy | |
| r1 | 18 | 20 | 3 | text | |
| r2 | 18 | 21 | 3 | text | |
| r3 | 18 | 22 | 2 | text | |
| r4 | 18 | 23 | 2 | text | |
| s1 | 19 | 24 | 30 | text | |
| s2 | 19 | 25 | 20 | text | |
| s3 | 19 | 26 | 10 | text | |
| s4 | 19 | 27 | 10 | text | |
| s5 | 19 | 28 | 10 | text | |
| s6 | 19 | 29 | 10 | text | |
| s7 | 19 | 30 | 30 | hello hello |
| <!DOCTYPE html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <script src="https://d3js.org/d3.v4.min.js"></script> | |
| <script src=" | |
| https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script> | |
| <style> | |
| body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
| line {stroke: black} | |
| text { | |
| font: 24px "Helvetica Neue", Helvetica, Arial, sans-serif; | |
| text-anchor: middle; | |
| pointer-events: none; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <script> | |
| var height = 500 | |
| var width = 500 | |
| var svg = d3.select("body").append("svg") | |
| .attr("width", width) | |
| .attr("height", height) | |
| .append("g") | |
| .attr("transform", "translate(" + 0 + "," + 0 + ")"); | |
| var circle_size = 4; | |
| var colour_scale = d3.scaleOrdinal(d3.schemeCategory10); | |
| function flatten(root) { | |
| var nodes = [], | |
| i = 0; | |
| function recurse(node) { | |
| if (node.children) node.children.forEach(recurse); | |
| if (!node.id) node.id = ++i; | |
| nodes.push(node); | |
| } | |
| recurse(root); | |
| return nodes; | |
| } | |
| function add_label_leaves(csv_data, root) { | |
| function recurse(tree) { | |
| if (!(tree.children)) { | |
| //add to csv data with correct parent id and uniform size. | |
| new_record = {} | |
| new_record["name"] = tree.data.name + "_label" | |
| new_record["parent"] = tree.data.id | |
| new_record["id"] = tree.data.id + "_label" | |
| new_record["data"] = 20 | |
| new_record["text"] = tree.data.text | |
| csv_data.push(new_record) | |
| } else { | |
| _.each(tree.children, function(this_tree) { | |
| recurse(this_tree) | |
| }) | |
| } | |
| } | |
| recurse(root) | |
| } | |
| d3.csv("data2.csv", function(csv_data) { | |
| var root_fn = d3.stratify() | |
| .id(function(d) { | |
| return d.id; | |
| }) | |
| .parentId(function(d) { | |
| return d.parent; | |
| }) | |
| var root = root_fn(csv_data); | |
| //Recurse into root's children, and when we find | |
| var links = root.links() | |
| var nodes = flatten(root) | |
| add_label_leaves(csv_data, root) | |
| var root = root_fn(csv_data) | |
| var links = root.links() | |
| var nodes = flatten(root) | |
| var simulation = d3.forceSimulation(nodes) | |
| .force("link", | |
| d3.forceLink(links) | |
| .distance(function(d) { | |
| //We want the distance to be equal so they are spaced in a circle around the parent | |
| return Math.pow(d.source.data.data, 0.5) * circle_size*1.5; | |
| }) | |
| .strength(function(d) { | |
| //Strength just needs to be enough so that length is uniform | |
| return 0.1 | |
| }) | |
| ) | |
| .force("charge", d3.forceManyBody() | |
| .strength(function(d) { | |
| var force = -Math.pow(d.data.data,0.5)*circle_size; | |
| return force}) | |
| .distanceMin(0) | |
| .distanceMax(200) | |
| ) | |
| .force("center", d3.forceCenter(width / 2, height / 2)) | |
| .force("collide", d3.forceCollide(function(d) {return Math.pow(d.data.data,0.5)*circle_size})) | |
| .velocityDecay(0.05) | |
| .alphaMin(0.0001) | |
| .alphaDecay(0.01) | |
| .on("tick", ticked) | |
| function ticked() { | |
| var selection = svg.selectAll(".my_links") | |
| .data(links) | |
| selection.enter() | |
| .append("line") | |
| .attr("class", "my_links") | |
| .merge(selection) | |
| .attr("x1", function(d) { | |
| return d.source.x; | |
| }) | |
| .attr("y1", function(d) { | |
| return d.source.y; | |
| }) | |
| .attr("x2", function(d) { | |
| return d.target.x; | |
| }) | |
| .attr("y2", function(d) { | |
| return d.target.y; | |
| }) | |
| .attr("stroke-width", function(d) { | |
| if (d.target.children) { | |
| return 1 | |
| } else { | |
| return 0 | |
| } | |
| }) | |
| selection.exit().remove(); | |
| // Update the nodes… | |
| selection = svg.selectAll(".my_nodes") | |
| .data(nodes, function(d) { | |
| return d.id; | |
| }) | |
| //Entering | |
| enterSelection = selection | |
| .enter() | |
| .append("g") | |
| .attr("class", "my_nodes") | |
| circles = enterSelection.append("circle") | |
| rectangles = enterSelection.append("text") | |
| .text(function(d) { return d.data.text; }) | |
| .style("font-size", function(d) { | |
| var r = Math.pow(d.data.data, 0.5)*circle_size | |
| return Math.min(2 * r, (2 * r - 8) / this.getComputedTextLength() * 24) + "px"; }) | |
| .attr("dy", ".35em") | |
| .style("fill", function(d) { | |
| if (d.children) { | |
| return "white" | |
| } else { | |
| return "black" | |
| } | |
| }) | |
| //Update | |
| enterSelection.merge(selection) | |
| .attr("transform", function(d) {return "translate(" + d.x + "," + d.y + ")" }); | |
| enterSelection.merge(selection).select("circle") | |
| .attr("r", function(d) { | |
| return Math.pow(d.data.data, 0.5)*circle_size; | |
| }) | |
| .attr("fill", function(d,i) { | |
| if (d.children) { | |
| return colour_scale(i) | |
| } else { | |
| return "white" | |
| } | |
| }) | |
| } | |
| }); | |
| </script> | |
| </body> |