|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<canvas width="960" height="500"></canvas> |
|
<script src="https://d3js.org/d3.v4.0.0-alpha.44.min.js"></script> |
|
<script> |
|
|
|
var canvas = document.querySelector("canvas"), |
|
context = canvas.getContext("2d"), |
|
width = canvas.width, |
|
height = canvas.height |
|
debug = false; |
|
|
|
var numPoints = 5 |
|
var radius = 200 |
|
var nodes = d3.range(numPoints).map(function(d) { |
|
return { |
|
id: d, |
|
x: width / 2 + radius * Math.cos(Math.PI * 2 / numPoints * d) + Math.random() * 30 - 15, |
|
y: height / 2 + radius * Math.sin(Math.PI * 2 / numPoints * d) + Math.random() * 30 - 15 |
|
}; |
|
}); |
|
|
|
var links = nodes.map(function(n) { |
|
return { |
|
source: n.id, |
|
target: n.id + 1 === nodes.length ? 0 : n.id + 1 |
|
} |
|
}); |
|
|
|
var drawShape = d3.line() |
|
//.x(function(d) { return d.x; }) |
|
//.y(function(d) { return d.y; }) |
|
.curve(d3.curveBasisClosed) |
|
.context(context); |
|
|
|
var simulation = d3.forceSimulation() |
|
.force("link", d3.forceLink().id(function(d) { return d.id; }).strength(0.1)) |
|
.force("charge", d3.forceManyBody().strength(-1000)) |
|
.force("x", d3.forceX(function(d) { return d.x; }).strength(0.05)) |
|
.force("y", d3.forceY(function(d) { return d.y; }).strength(0.05)); |
|
|
|
render({ |
|
nodes: nodes, |
|
links: links |
|
}); |
|
|
|
function render(graph) { |
|
simulation |
|
.nodes(graph.nodes) |
|
.on("tick", ticked); |
|
simulation.force("link") |
|
.links(graph.links); |
|
d3.select(canvas) |
|
.call(d3.drag() |
|
.container(canvas) |
|
.subject(dragsubject) |
|
.on("start", dragstarted) |
|
.on("drag", dragged) |
|
.on("end", dragended)); |
|
|
|
d3.select("body") |
|
.on("keydown", toggleDebug) |
|
.on("keyup", toggleDebug); |
|
|
|
function ticked() { |
|
var hull = d3.polygonHull(graph.nodes.map(function(d) { return [d.x, d.y]; })); |
|
|
|
context.clearRect(0, 0, width, height); |
|
|
|
context.beginPath(); |
|
// Draw the shape from all nodes |
|
drawShape(hull); |
|
context.fillStyle = "#f55"; |
|
context.fill(); |
|
|
|
if (debug) { |
|
context.beginPath(); |
|
graph.links.forEach(drawLink); |
|
context.strokeStyle = "#003B5C"; |
|
context.stroke(); |
|
|
|
context.beginPath(); |
|
hull.forEach(function(d) { drawNode({x: d[0], y: d[1]}); }); |
|
context.fillStyle = "#003B5C"; |
|
context.fill(); |
|
context.strokeStyle = "#fff"; |
|
context.stroke(); |
|
|
|
context.beginPath(); |
|
context.fillStyle = "#03b700"; |
|
context.fill(); |
|
context.strokeStyle = "#fff"; |
|
context.stroke(); |
|
} |
|
} |
|
function toggleDebug() { |
|
debug = d3.event.shiftKey; |
|
ticked(); |
|
} |
|
} |
|
function dragsubject() { |
|
return simulation.find(d3.event.x, d3.event.y); |
|
} |
|
function dragstarted() { |
|
if (!d3.event.active) simulation.alphaTarget(0.5).restart() |
|
simulation.fix(d3.event.subject); |
|
} |
|
function dragged() { |
|
simulation.fix(d3.event.subject, d3.event.x, d3.event.y); |
|
} |
|
function dragended() { |
|
if (!d3.event.active) simulation.alphaTarget(0); |
|
simulation.unfix(d3.event.subject); |
|
} |
|
function drawLink(d) { |
|
context.moveTo(d.source.x, d.source.y); |
|
context.lineTo(d.target.x, d.target.y); |
|
} |
|
function drawNode(d) { |
|
context.moveTo(d.x + 3, d.y); |
|
context.arc(d.x, d.y, 3, 0, 2 * Math.PI); |
|
} |
|
</script> |