block by fil 75f14da692354b49a88b9c015324eaae

Brownian Motion Urquhart

Full Screen

A mixed bag of forces:

Displayed as an Urquhart graph just because.

See this rectangular variant.

forked from Fil‘s block: Urquhart Force Mesh

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v4.js"></script>
<script>

var width = 500,
    height = 500,
    τ = 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,
                d2 = dx * dx + dy * dy;
            if (d2 > 0.16) {
                var angle = Math.atan2(dy, dx),
                    angle1 = Math.atan2(node.vy, node.vx),
                    angle2 = Math.PI - angle1 + 2 * angle,
                    norm = Math.sqrt(node.vx*node.vx + node.vy*node.vy);
                node.vx = norm * Math.cos(angle2);
                node.vy = norm * Math.sin(angle2);
            }

        }
    })
    .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.arc(width/2, height/2, width * 0.4 + 2, 0, τ);
    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>