Make link paths wavy for randomly-generated network.
wave
function creates a wavy-like path between source and target link, using coordinate transforamtions.
license: gpl-3.0 |
Make link paths wavy for randomly-generated network.
wave
function creates a wavy-like path between source and target link, using coordinate transforamtions.
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
.links path { | |
fill: none; | |
stroke: #999; | |
stroke-opacity: 0.6; | |
} | |
.nodes circle { | |
stroke: #fff; | |
stroke-width: 1.5px; | |
} | |
</style> | |
<svg width="900" height="600"></svg> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script> | |
var nodes = d3.range(100).map(function(i) { | |
return { index: i } | |
}) | |
var links = d3.range(nodes.length - 1).map(function(i) { | |
return { source: Math.floor(Math.sqrt(i)), target: i + 1} | |
}) | |
var graph = {nodes: nodes, links: links} | |
var svg = d3.select("svg"), | |
width = +svg.attr("width"), | |
height = +svg.attr("height"); | |
var color = d3.scaleOrdinal(d3.schemeCategory20); | |
var simulation = d3.forceSimulation() | |
.force("link", d3.forceLink().distance(50).strength(1)) | |
.force("charge", d3.forceManyBody()) | |
.force("center", d3.forceCenter(width / 2, height / 2)); | |
var link = svg.append("g") | |
.attr("class", "links") | |
.selectAll("path") | |
.data(graph.links) | |
.enter().append("path") | |
var node = svg.append("g") | |
.attr("class", "nodes") | |
.selectAll("circle") | |
.data(graph.nodes) | |
.enter().append("circle") | |
.attr("r", function(d) { return 4 + 6*Math.random(); }) | |
.attr("fill", function(d) { return color(Math.floor(Math.sqrt(d.index))); }) | |
.call(d3.drag() | |
.on("start", dragstarted) | |
.on("drag", dragged) | |
.on("end", dragended)); | |
simulation | |
.nodes(graph.nodes) | |
.on("tick", ticked); | |
simulation.force("link") | |
.links(graph.links); | |
function ticked() { | |
link.attr("d", wave); | |
node | |
.attr("cx", function(d) { return d.x; }) | |
.attr("cy", function(d) { return d.y; }); | |
} | |
// Create wavy path between source and target | |
function wave(d) { | |
var p1 = d.source, p2 = d.target | |
var step = 0.7, amp = 3, cpi = step*Math.PI | |
var dist = Math.hypot(p2.y - p1.y, p2.x - p1.x) | |
var angle = Math.atan2(p2.y - p1.y, p2.x - p1.x) | |
var ca = Math.cos(angle), sa = Math.sin(angle) | |
var arr = [] | |
for (x=0; x<=dist; x+=step){ | |
var y = amp * Math.sin(x/cpi) | |
var x2 = p1.x + x*ca - y*sa | |
var y2 = p1.y + x*sa + y*ca | |
arr.push({x:x2, y:y2}) | |
} | |
var line = d3.line().curve(d3.curveBasis) | |
.x(function(d) { return d.x; }) | |
.y(function(d) { return d.y; }) | |
return line(arr) | |
} | |
function dragstarted(d) { | |
if (!d3.event.active) simulation.alphaTarget(0.3).restart(); | |
d.fx = d.x; | |
d.fy = d.y; | |
} | |
function dragged(d) { | |
d.fx = d3.event.x; | |
d.fy = d3.event.y; | |
} | |
function dragended(d) { | |
if (!d3.event.active) simulation.alphaTarget(0); | |
d.fx = null; | |
d.fy = null; | |
} | |
</script> |