d3.forceSimulation
bounding box collision detection. This is useful for label adjustment or rectangular nodes. Each node receives a bounding box array of a top right and bottom left corner of that node relative to its x position. In the case of this dataset, that size is based on the length of the word in the source dataset.
A function for calculating this array based off the data isis passed into the forceRectCollide function, which is later passed as a "collide" constraint in your force settings.
var collide = d3.forceRectCollide(function (d,i) {
var hw = halfWidth(d)
if (i%3 === 0) {
return [[-hw / 2, -hw],[hw / 2, hw]]
}
return [[-hw, -hw / 2],[hw,hw / 2]]
})
.strength(1)
.iterations(2)
The code above creates a vertical or horizontal rectangle. Make sure you make your corresponding on-screen rectangle match the size of your constraint.
xxxxxxxxxx
<html>
<head>
<title>Bounding Box Collision</title>
<meta charset="utf-8" />
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="d3-force.js"></script>
</head>
<style>
svg {
height: 1000px;
width: 1000px;
border: 1px solid gray;
}
</style>
<body>
<div id="viz">
<svg class="main">
</svg>
</div>
</body>
<footer>
<script>
d3.csv("words.csv",function(error,data) {createWordcloud(data)});
function createWordcloud(data) {
var networkCenter = d3.forceCenter().x(500).y(500);
var textScale = d3.scaleLinear().domain([10,70]).range([2,30])
var forceX = d3.forceX(function (d) {return 500})
.strength(function (d) {return textScale(d.frequency) * 0.001})
var forceY = d3.forceY(function (d) {return 500})
.strength(function (d) {return textScale(d.frequency) * 0.001})
function halfWidth(d) {
return d.text.length * textScale(d.frequency)
}
var collide = d3.forceRectCollide(function (d,i) {
var hw = halfWidth(d)
if (i%3 === 0) {
return [[-hw / 2, -hw],[hw / 2, hw]]
}
return [[-hw, -hw / 2],[hw,hw / 2]]
})
.strength(1)
.iterations(2)
var color = d3.scaleOrdinal(d3.schemeCategory10)
var force = d3.forceSimulation(data)
.velocityDecay(0.6)
.force("center", networkCenter)
.force("x", forceX)
.force("y", forceY)
.force("collide", collide)
.on("tick", updateNetwork);
var nodeEnter = d3.select("svg.main")
.append("g")
.selectAll("g.node")
.data(data)
.enter()
.append("g")
.attr("class", "node")
nodeEnter.append("rect")
.attr("class", "base")
.style("stroke-width", 1)
.style("stroke", function (d, i) {return d3.color(color(i)).brighter(2)})
.each(function (d,i) {
var hw = halfWidth(d)
if (i%3 === 0) {
d3.select(this)
.style("fill", color(i))
.attr("x", -hw / 2)
.attr("y", -hw)
.attr("height", hw * 2)
.attr("width", hw)
}
else {
d3.select(this)
.style("fill", d3.color(color(i)).darker(2))
.attr("y", -hw / 2)
.attr("x", -hw)
.attr("width", hw * 2)
.attr("height", hw)
}
})
function updateNetwork() {
d3.select("svg.main").selectAll("g.node")
.attr("transform", function (d) {return "translate(" + d.x + "," + d.y + ")"})
}
}
</script>
</footer>
</html>
https://d3js.org/d3.v4.min.js