block by fil 3f52274945a2be33a3f6bfd585ca949b

Stressed Cells 1

Full Screen

Cells adapt their shape to their environment. When pressed by surrounding cells, they get stressed and become more opaque.

Drag a cell to play.

Made by Philippe Rivière with blockbuilder.org.

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <style>
        body {
            margin: 0;
            position: fixed;
            top: 0;
            right: 0;
            bottom: 0;
            left: 0;
            background: #333;
        }
        
        .active path {
            stroke-width: 3px;
        }
    </style>
</head>

<body>
<script>
    var svg = d3.select("body").append("svg")
        .attr("width", 960)
        .attr("height", 500)
        .append('g')
        .attr('transform', 'translate(480,250)');

    var color = d3.scaleOrdinal(d3.schemeCategory10);

  color = d3.scaleOrdinal(['#999', '#966', '#696']);
  color = function(i) { return d3.cubehelix((i%100)*3.60, 1.2, 0.6); }
  
    // my data is a set of initial positions and a radius
    var data = d3.range(500)
        .map(function (i) {
            var a = 2 * Math.PI * Math.random(),
                d = Math.sqrt(Math.random());
            return {
                id: i,
                r: 4 * (1 + 5 * Math.random() * Math.random()),
                x: 300 * Math.cos(a) * d,
                y: 200 * Math.sin(a) * d,
                color: color(i),
            };
        });

    // add n feelers
    data = data.map(function (d) {
        var n = Math.floor(4 + d.r / 3),
            start = 2 * Math.PI * Math.random();
        d.children = d3.range(n)
            .map(function (i) {
                var angle = i * Math.PI * 2 / n + start,
                    t = {
                        length: 1,
                        angle: angle,
                        sin: Math.sin(angle),
                        cos: Math.cos(angle),
                        parent: d,
                    };
                return t;
            });
        return d;
    });

    a = 0;

    var simulation = d3.forceSimulation(data)
        .alphaTarget(0.02)
        .force("surface", function (alpha) {
            cells.each(function (d) {
                d.surface = Math.sqrt(
                    d.children.map(function (t) {
                        return t.length * t.length;
                    })
                    .reduce(function (a, b) {
                        return a + b;
                    }, 0) / d.children.length);
            })
        })
        .force("collidecell", function (alpha) {
            cells.each(function (d) {
                var e = d3.extent(d.children.map(function (t) {
                        return t.length;
                    })),
                    p1 = d.r + 3;
                d.polygon = d.children.map(function (t) {
                    return [d.x + p1 * t.length * t.sin, d.y - p1 * t.length * t.cos];
                });
            });

            var quadtree = d3.quadtree(data,
                function (d) {
                    return d.x;
                },
                function (d) {
                    return d.y;
                });

            var collisions = 0;
            cells.each(function (d, i) {
                // simulation.findMany(…)
                quadtree.visit(function (node, x0, y0, x1, y1) {
                    if (x1 < d.x - 2 * d.r || x0 > d.x + 2 * d.r || y1 < d.y - 2 * d.r || y0 > d.y + 2 * d.r) {
                        return true;
                    }
                    var p = node.data;
                    if (!p) return;
                    if (p.id == d.id) return;
                    var dx = p.x - d.x,
                        dy = p.y - d.y,
                        dist2 = dx * dx + dy * dy;
                    if (dist2 > 4 * (d.r + p.r) * (d.r + p.r)) return;
                    var stress = 0;
                    d.children.forEach(function (t) {
                        var txy = [d.x + d.r * t.length * t.sin, d.y - d.r * t.length * t.cos];
                        if (d3.polygonContains(p.polygon, txy)) {
                            collisions++;
                            stress++;
                            t.length /= 1.05;
                            var tens = d.surface / p.surface,
                                f = 0.1 * (stress > 2 ? 6 : 1);
                            d.vx += f * Math.atan((d.x - p.x) * tens);
                            d.vy += f * Math.atan((d.y - p.y) * tens);
                            p.vx -= f * Math.atan((d.x - p.x) / tens);
                            p.vy -= f * Math.atan((d.y - p.y) / tens);
                        }
                    });

                });
            });
            //console.log('collisions', collisions);
        })
        .force("tension", function (alpha) {
            cells.each(function (d) {
                var l = d.children.length;
                d.children.forEach(function (t, i) {
                    var m = d.children[(l - 1) % l].length + d.children[(l + 1) % l].length;
                    var f = 1 / 10; // spiky-ness
                    t.length = (1 - f) * t.length + f * m / 2;
                });
            })
        })
        .force("expand", function (alpha) {
            cells.each(function (d) {
                var u = 1 / Math.sqrt(d.surface);
                d.children.forEach(function (t) {
                    t.length *= u;
                    t.length = Math.min(t.length, 2);
                })
            })
        })
        .force("internal", function (alpha) {
            var f = 1 / 10;
            cells.each(function (d) {
                d.vx += f * d3.sum(d.children, function (t) {
                    return t.length * t.sin;
                });
                d.vy += f * d3.sum(d.children, function (t) {
                    return -t.length * t.cos;
                });
            })
        })
        .force("x", d3.forceX(function(d) { return d.x; }).strength(0.01))
        .force("y", d3.forceY(function(d) { return d.y; }).strength(0.01))
        .force("move", function(){
          cells.each(function (d) {
                d.vx += Math.random()-0.5;
                d.vy += Math.random()-0.5;
            });
        })
        .on("tick", ticked);


    // cells
    var cells = svg.selectAll('g.cell')
        .data(data)
        .enter()
        .append('g')
        .classed('cell', true)
        .attr('transform', function (d) {
            return 'translate(' + [d.x, d.y] + ')'
        });

    if (false) cells.append('circle')
        .attr('r', 1)
        .attr('fill', function (d) {
            return d.color;
        });

    // segments
    var paths = cells
        .append('path')
        .attr('stroke', function (d, i) {
            return color(i);
        })
        .attr('fill', function (d, i) {
            return color(i);
        })
        .attr('fill-opacity', 0.3);



    function ticked() {
        cells
            .attr('transform', function (d) {
                return 'translate(' + [d.x, d.y] + ')'
            });

        paths
            .attr('d', function (d) {
                var arc = 2 * Math.PI / d.children.length,
                    data = d.children
                    .map(function (t, i) {
                        return [i * arc, d.r * t.length];
                    });
                return line(data) + 'Z';
            })
        .transition()
            .attr('fill-opacity', function (d) {
                return 0.05 - 8*Math.log(d.surface);
            });
    }


    var line = d3.radialArea()
        .curve(d3.curveCatmullRomClosed)
        .innerRadius(function (d) {
            return 0.4 * line.outerRadius()(d);
        });
    var line = d3.radialLine()
        .curve(d3.curveCatmullRomClosed);
    var ease = function (t) {
        return t > 1 ? 1 : t < 0 ? 0 : d3.easeExpIn(t);
    }

    cells
        .call(d3.drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended));

    function dragstarted(d) {
        d3.select(this).raise().classed("active", true);
    }

    function dragged(d) {
        d3.select(this)
            .attr('transform', function (d) {
                return 'translate(' + [d.x = d3.event.x, d.y = d3.event.y] + ')';
            });
    }

    function dragended(d) {
        d3.select(this).classed("active", false);
        simulation.alpha(1).restart();
    }
</script>
</body>
</html>