A mixed bag of forces:
-
Brownian motion
-
Elastic Bounce on a container’s edge
-
Collide
Displayed as an Urquhart graph just because.
forked from Fil's block: Urquhart Force Mesh
forked from Fil's block: Brownian Motion Urquhart
| license: gpl-3.0 | |
| border: no | |
| scrolling: no | |
| height: 500 |
A mixed bag of forces:
Brownian motion
Elastic Bounce on a container’s edge
Collide
Displayed as an Urquhart graph just because.
forked from Fil's block: Urquhart Force Mesh
forked from Fil's block: Brownian Motion Urquhart
| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <body> | |
| <script src="//d3js.org/d3.v4.js"></script> | |
| <script> | |
| var width = 400 * 1.618, | |
| height = 400, | |
| τ = 2 * Math.PI; | |
| var nodes = d3.range(200).map(function () { | |
| return { | |
| x: width/2 + (-0.5 + Math.random())*150, | |
| y: height/2 + (-0.5 + Math.random())*150 | |
| }; | |
| }); | |
| var force = d3.forceSimulation() | |
| .nodes(nodes.slice()) | |
| .force("collide", d3.forceCollide(4)) | |
| .force("brownian", function () { | |
| for (var i = 0, n = nodes.length, node, k = 0.1; i < n; ++i) { | |
| if (Math.random() > 1 - k) { | |
| var angle = Math.random() * τ; | |
| node = nodes[i]; | |
| node.vx += 0.2 * Math.cos(angle); | |
| node.vy += 0.2 * Math.sin(angle); | |
| } | |
| } | |
| }) | |
| .force("bounce-on-container", function () { | |
| for (var i = 0, n = nodes.length, node; i < n; ++i) { | |
| node = nodes[i]; | |
| var dx = node.x / width - 1 / 2, | |
| dy = node.y / height - 1 / 2; | |
| if (dx*dx > 0.16) { | |
| node.vx *= -1; | |
| } | |
| if (dy*dy > 0.16) { | |
| node.vy *= -1; | |
| } | |
| } | |
| }) | |
| .on("tick", ticked) | |
| .alphaDecay(0) | |
| .velocityDecay(0.001); | |
| var voronoi = d3.voronoi() | |
| .x(function (d) { | |
| return d.x; | |
| }) | |
| .y(function (d) { | |
| return d.y; | |
| }); | |
| var canvas = d3.select("body").append("canvas") | |
| .attr("width", width) | |
| .attr("height", height); | |
| var context = canvas.node().getContext("2d"); | |
| function moved() { | |
| var p1 = d3.mouse(this); | |
| nodes[0].fx = p1[0]; | |
| nodes[0].fy = p1[1]; | |
| force.alpha(0.1).restart(); | |
| } | |
| function urquhart(diagram) { | |
| var urquhart = d3.map(); | |
| diagram.links() | |
| .forEach(function (link) { | |
| var v = d3.extent([link.source.index, link.target.index]); | |
| urquhart.set(v, link); | |
| }); | |
| urquhart._remove = []; | |
| diagram.triangles() | |
| .forEach(function (t) { | |
| var l = 0, | |
| length = 0, | |
| i = "bleh", | |
| v; | |
| for (var j = 0; j < 3; j++) { | |
| var a = t[j], | |
| b = t[(j + 1) % 3]; | |
| v = d3.extent([a.index, b.index]); | |
| length = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); | |
| if (length > l) { | |
| l = length; | |
| i = v; | |
| } | |
| } | |
| urquhart._remove.push(i); | |
| }); | |
| urquhart._remove.forEach(function (i) { | |
| if (urquhart.has(i)) urquhart.remove(i); | |
| }); | |
| return urquhart.values(); | |
| } | |
| function ticked() { | |
| var diagram = voronoi(nodes); | |
| //var links = diagram.links(); | |
| var links = urquhart(diagram); | |
| //var links = []; | |
| context.clearRect(0, 0, width, height); | |
| context.beginPath(); | |
| for (var i = 0, n = links.length; i < n; ++i) { | |
| var link = links[i], | |
| dx = link.source.x - link.target.x, | |
| dy = link.source.y - link.target.y; | |
| context.moveTo(link.source.x, link.source.y); | |
| context.lineTo(link.target.x, link.target.y); | |
| } | |
| context.strokeStyle = "#eee"; | |
| context.lineWidth = 8; | |
| context.stroke(); | |
| context.beginPath(); | |
| for (var i = 0, n = links.length; i < n; ++i) { | |
| var link = links[i], | |
| dx = link.source.x - link.target.x, | |
| dy = link.source.y - link.target.y; | |
| context.moveTo(link.source.x, link.source.y); | |
| context.lineTo(link.target.x, link.target.y); | |
| } | |
| context.strokeStyle = "#000"; | |
| context.lineWidth = 0.5; | |
| context.stroke(); | |
| context.strokeStyle = "#fcc"; | |
| context.beginPath(); | |
| context.moveTo(width * 0.1, height * 0.1); | |
| context.lineTo(width * 0.1, height * 0.9); | |
| context.lineTo(width * 0.9, height * 0.9); | |
| context.lineTo(width * 0.9, height * 0.1); | |
| context.lineTo(width * 0.1, height * 0.1); | |
| context.lineWidth = 6; | |
| context.stroke() | |
| context.beginPath(); | |
| for (var i = 0, n = nodes.length; i < n; ++i) { | |
| var node = nodes[i]; | |
| context.moveTo(node.x, node.y); | |
| context.arc(node.x, node.y, 1, 0, τ); | |
| } | |
| context.lineWidth = 1; | |
| context.strokeStyle = "#fff"; | |
| context.stroke(); | |
| context.fillStyle = "yellow"; | |
| context.fill(); | |
| } | |
| </script> |