Straight movement on circle

optical-illusion
Published

November 14, 2020

Each individual circle moves along a straight line, but collectively, they look like a circle rolling inside a larger circle:

Code
import { dom, time, math, svg as _svg } from "/src/cscheid/cscheid.js";

{
    let svg = d3.select("#main").append("svg")
        .attr("viewBox", "0 0 600 600")
        .attr("width", "100%")
        .attr("height", "100%");

    var scale = d3.scaleLinear().domain([-1, 1]).range([25, 575]);

    svg.append("circle")
    .attr("cx", scale(0))
    .attr("cy", scale(0))
    .attr("r", scale(1) - scale(0))
    .attr("stroke", "black")
    .attr("fill", "none")
    .attr("stroke-width", "3px");

    function makeMovingCircles(angles)
    {
        return svg.append("g")
        .selectAll("circle")
        .data(angles)
        .enter()
        .append("circle")
        .attr("r", Math.abs(scale(-0.95) - scale(-1)))
        .attr("cx", d => scale(Math.cos(math.radians(d/2)) * 0.95))
        .attr("cy", scale(0))
        .attr("fill", d => d3.hcl(d, 70, 70))
        .attr("transform", d => _svg.rotate(d/2, scale(0), scale(0)));
    }

    function makeSupportingLines(angles)
    {
        return svg.append("g")
        .selectAll("line")
        .data(angles)
        .enter()
        .append("line")
        .attr("x1", scale(-1))
        .attr("x2", scale( 1))
        .attr("y1", scale(0))
        .attr("y2", scale(0))
        .attr("stroke", "gray")
        .attr("fill", "none")
        .attr("stroke-width", "1px")
        .attr("transform", d => _svg.rotate(d/2, scale(0), scale(0)));
    }

    var angles = d3.range(16).map(d => d * (360 / 16));
    makeSupportingLines(angles);
    var sel = makeMovingCircles(angles);

    dom.animate(function() {
        let t = time.elapsed() * 90;
        sel.attr("cx", d => scale(
            Math.cos(math.radians(d/2 + t)) * 0.95));
    });
}

Acknowledgments

As usual, found on twitter. Apparently this is known as a Tusi couple.