block by armollica 635bdcac377f2ac721854ccb5b66e257

Missing square puzzle

Full Screen

From the Nautilus magazine article The Impossible Mathematics of the Real World:

Then there’s the missing-square puzzle. In this one (above), a right triangle is cut up into four pieces. When the pieces are rearranged, a gap appears. Where’d it come from? It’s a near miss. Neither “triangle” is really a triangle. The hypotenuse is not a straight line, but has a little bend where the slope changes from 0.4 in the blue triangle to 0.375 in the red triangle. The defect is almost imperceptible, which is why the illusion is so striking.

Also see the Wikipedia page: Missing square puzzle.

index.html

<html>
<head>
<style>

svg { display: block; margin: 40px auto; }

.triangle--0 { fill: #66c2a5; stroke: rgb(71, 136, 115); }
.triangle--1 { fill: #fc8d62; stroke: rgb(176, 99, 69); }
.triangle--2 { fill: #8da0cb; stroke: rgb(99, 112, 142); }
.triangle--3 { fill: #e78ac3; stroke: rgb(162, 97, 137); }

.axis .domain { display: none; }
.axis .tick line { stroke: #ddd; }

</style>
</head>
<body>    
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>

var n = 15,
    m = 7,
    cellSize = 40,
    width = n * cellSize,
    height = m * cellSize;

var triangleData = [
    {
        offset: { a: [1, 1], b: [6, 3] },
        vertices: [[0, 0], [8, 3], [8, 0]]
    },
    {
        offset: { a: [9, 4], b: [1, 1] },
        vertices: [[0, 0], [5, 2], [5, 0]]
    },
    {
        offset: { a: [9, 2], b: [6, 1] },
        vertices: [[0, 0], [0, 2], [5, 2], [5, 1], [2, 1], [2, 0]]
    },
    {
        offset: { a: [9, 1], b: [9, 1] },
        vertices: [[0, 0], [0, 1], [2, 1], [2, 2], [5, 2], [5, 0]]
    }
];

var xScale = d3.scaleLinear().domain([0, n]).range([0, width]),
    yScale = d3.scaleLinear().domain([0, m]).range([height, 0]);

var xAxis = d3.axisTop(xScale).tickValues(d3.range(0, n + 1)),
    yAxis = d3.axisLeft(yScale).tickValues(d3.range(0, m + 1));

var line = d3.line()
    .x(function(d) { return xScale(d[0]); })
    .y(function(d) { return yScale(d[1]); });

var svg = d3.select('body').append('svg')
    .attr('width', width + 1)
    .attr('height', height + 1);

function translate(key) {
    return function(d) {
        var offset = d.offset[key],
            tx = xScale(offset[0]),
            ty = yScale(offset[1]);
        return 'translate(' + tx + ',' + ty + ')';
    };
}

function translateX(key) {
    return function(d) {
        var offsetX = d.offset[key],
            offsetY = d.offset[key === 'a' ? 'b' : 'a'],
            tx = xScale(offsetX[0]),
            ty = yScale(offsetY[1]);
        return 'translate(' + tx + ',' + ty + ')';
    };
}

var key = 'a';

var triangle = svg.append('g').attr('class', 'triangles')
        .attr('transform', 'translate(0,' + (-height) + ')')
        .selectAll('.triangle').data(triangleData)
    .enter().append('path')
        .attr('class', function(d, i) { return 'triangle triangle--' + i; })
        .attr('d', function(d) { return line(d.vertices); })
        .attr('transform', translate(key));

svg.append('g').attr('class', 'axis axis--x')
    .call(xAxis)
    .selectAll('.tick line')
    .attr('y2', height);

svg.append('g').attr('class', 'axis axis--y')
    .call(yAxis)
    .selectAll('.tick line')
    .attr('x2', width);

function update() {
    triangle
        .transition()
            .duration(500)
            .delay(function(d, i) { return i * 750; })
            .attr('transform', translateX(key))
        .transition()
            .duration(500)
            .attr('transform', translate(key));
}

setTimeout(function() {
    key = key === 'a' ? 'b' : 'a';
    update();
    d3.interval(function(d) {
        key = key === 'a' ? 'b' : 'a';
        update();
    }, 5000);
}, 1000);

</script>
</body>
</html>