javascript - Automated collision detection and rectification for a force directed wordcloud in D3.js -
i have been experimenting creating force directed layout using d3.js. in following code, populate series of objects later rendered text elements. wish avoid collision between words, , adjust representation on grid accordingly.
i utilised code written eric dobbs here http://bl.ocks.org/dobbs/1d353282475013f5c156 still not working me. objects end flying on screen. have spent many hours puzzling on , appreciate available.
here code
<!doctype html> <html> <head> <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script> </head> <body> <script> //arguments passed in when class modularised var metadataobject = {"type":"wordcloud","label":"data1","data":"data2","color":"color"}; var dataobject = {"data1":["apple","orange","pear","grapes","mango","papaya","kiwi","banana", "watermelon","strawberry","honeydew","dragonfruit","durian","pineapple","jackfruit","lychee","mangosteen","passionfruit","raspberry","blueberry","rockmelon","coconut","lemon","lime","pomelo","rambutan","aguave","longan","mandarin","calamansi","sugarcane","avocado","bittergourd","wintermelon","dates"],"data2":[100,50,150,40,70,60,30,35,95,120,60,70,80,15,30,140,100,170,200,40,90,20,180,99,66,55,130,20,50,55,100,120,30,20,90],"color":["#23af50"]}; //transform raw data var frequency_list = transformdata(metadataobject, dataobject); frequency_list.sort(function(a, b) { return b.size-a.size; }); //set cloud container variables var cloudwidth = 600; var cloudheight = 400; var cloudcontainer = d3.select("body").append("svg") .attr("width", cloudwidth) .attr("height", cloudheight); //set approximate scaling var largestqty = d3.max(dataobject[metadataobject.data]); var rangecap = cloudwidth*cloudheight/7500; var scale = d3.scale.linear() .domain([0, largestqty]) .range([5, rangecap]); var color = d3.scale.linear() .domain([0,1,2,3,4,5,6,10,15,20,30,largestqty]) .range(["#ddd", "#ccc", "#bbb", "#aaa", "#999", "#888", "#777", "#666", "#555", "#444", "#333", "#222"]); var words = createtext(frequency_list); var rendered = cloudcontainer.selectall("node") .data(words) .enter() .append("text") .attr("id", function(d, i) { return "t"+i; }) .text(function(d) { return d.text; }) .attr("font-family", "sans-serif") .attr("fill", function(d, i) { return color(i); }) .attr("font-size", function(d) { return scale(d.size)+"px"; }) .on("mouseover", handlemouseover) .on("mouseout", handlemouseout); var force = d3.layout.force() .nodes(words) .size([cloudwidth, cloudheight]) .charge(-50) .gravity(0.1) .on("tick", tick) .start(); rendered.call(force.drag); function tick(e) { var q = d3.geom.quadtree(words); for(var i=0; i<words.length; i++) { var word = words[i]; q.visit(collide(word)); } // console.log(d.x2); rendered .attr("x", function(d) { return d.x; }) .attr("y", function(d) { return d.y; }); } function collide(node) { var nx1, nx2, ny1, ny2, padding; padding = 32; nx1 = node.x - padding; nx2 = node.x2 + padding; ny1 = node.y - padding; ny2 = node.y2 + padding; return function(quad, x1, y1, x2, y2) { var dx, dy; console.log(node.x2); if (quad.point && (quad.point !== node)) { if (overlap(node, quad.point)) { dx = math.min(node.x2 - quad.point.x, quad.point.x2 - node.x) / 2; node.x -= dx; quad.point.x += dx; dy = math.min(node.y2 - quad.point.y, quad.point.y2 - node.y) / 2; node.y -= dy; quad.point.y += dy; } } return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; }; }; function overlap(a, b) { return !(a.x2 < b.x || a.x > b.x2 || a.y2 < b.y || a.y > b.y2); } function createtext(frequency_list) { var words = []; for(var wordindex=0; wordindex<frequency_list.length; wordindex++) { var word = { x: math.random() * (cloudheight - 40) +20, y: math.random() * (cloudwidth -40) +20, text: frequency_list[wordindex].text, size: frequency_list[wordindex].size, // x2: word.x + word.text.length * word.size /1.5, // y2: word.y + scale(word.size) * 1.1 }; word.x2 = word.x + word.text.length * word.size /1.5; word.y2 = word.y + scale(word.size) * 1.1; words.push(word); } return words; } function transformdata(metadataobject, dataobject) { //to transform data format: // var frequency_list = [{"text":"apple","size":100}, {"text":"orange","size":100}, {"text":"pear","size":25}, {"text":"grapes","size":301}, {"text":"mango","size":56}]; var frequency_list = []; var wordfieldname = metadataobject.label; var valuesfieldname = metadataobject.data; var wordlist = dataobject[wordfieldname]; var valueslist = dataobject[valuesfieldname]; for(var itemindex=0; itemindex<wordlist.length; itemindex++) { var item = { text: wordlist[itemindex].touppercase(), size: valueslist[itemindex] } frequency_list.push(item); } return frequency_list; } function handlemouseover(d, i) { d3.select(this).transition().attr({ fill: "black", "font-size": scale(d.size) + 5 + "px" }); cloudcontainer.append("text").attr({ id: "t" + d.text + "-" + d.size, x: 10, y:20 }) .text(function() { return ["weight: " + d.size]; // return [""+occupiedspaces[1].top]; }) } function handlemouseout(d, i) { d3.select(this).transition().attr({ fill: color(i), "font-size": scale(d.size) + "px" }); d3.select("#t" + d.text + "-" + d.size).remove(); } </script> </body> </html>
Comments
Post a Comment