Tutorial: Pythagoras by Rearrangement

A proof you can watch: four copies of a right triangle sit inside a big square, leaving a square hole of side c. Slide the same four triangles into new places and the hole becomes two squares, of sides a and b. The triangles didn't change and neither did the big square, so the holes have the same area: a² + b² = c².

This one uses the scene form, with one scene per step, so each step is a section you can scrub to.

1. Corners helper

The big square has side a + b. A small function turns "x along, y up from the big square's bottom-left corner" into a point in the picture, so the triangles can be written in plain numbers.

from pymations import *

a, b = 1.5, 2.5
s = a + b
x0, y0 = -4.0, -2.0   # the big square's bottom-left corner


def P(x, y):
    return [x0 + x, y0 + y, 0]


def frame():
    return Square(side_length=s, color=WHITE).move_to(P(s / 2, s / 2))

2. Two ways to place four triangles

def tilted():
    # One triangle in each corner: the hole in the middle is a tilted square of side c.
    return VGroup(
        Polygon(P(0, 0), P(a, 0), P(0, b)),
        Polygon(P(s, 0), P(s, a), P(a, 0)),
        Polygon(P(s, s), P(b, s), P(s, a)),
        Polygon(P(0, s), P(0, b), P(b, s)),
    ).set_fill(GREY_B, opacity=0.85).set_stroke(WHITE, width=2)


def paired():
    # The same four triangles as two rectangles: the holes are squares of side a and b.
    return VGroup(
        Polygon(P(a, 0), P(s, 0), P(s, a)),
        Polygon(P(a, a), P(a, 0), P(s, a)),
        Polygon(P(0, a), P(a, a), P(0, s)),
        Polygon(P(a, s), P(0, s), P(a, a)),
    ).set_fill(GREY_B, opacity=0.85).set_stroke(WHITE, width=2)

3. The scenes

class Tilted(Scene):
    def construct(self):
        tris = tilted()
        self.play(Create(frame()))
        self.play(LaggedStart(*[DrawBorderThenFill(t) for t in tris], lag_ratio=0.25), run_time=2.5)
        hole = Polygon(P(a, 0), P(s, a), P(b, s), P(0, b), stroke_width=0, fill_color=YELLOW, fill_opacity=0.6)
        c2 = MathTex("c^2", color=YELLOW, font_size=60).move_to(P(s / 2, s / 2))
        self.play(FadeIn(hole), Write(c2))
        self.wait(2)


class Rearranged(Scene):
    def construct(self):
        tris = tilted()
        self.add(frame(), tris)
        self.wait(0.5)
        self.play(*[Transform(t, u) for t, u in zip(tris, paired())], run_time=3)

        sq_a = Square(side_length=a, stroke_width=0, fill_color=BLUE, fill_opacity=0.6).move_to(P(a / 2, a / 2))
        sq_b = Square(side_length=b, stroke_width=0, fill_color=GREEN, fill_opacity=0.6).move_to(P(a + b / 2, a + b / 2))
        self.play(FadeIn(sq_a), FadeIn(sq_b),
                  Write(MathTex("a^2").move_to(sq_a)), Write(MathTex("b^2", font_size=60).move_to(sq_b)))
        self.wait(2)


class Conclusion(Scene):
    def construct(self):
        result = MathTex("a^2", "+", "b^2", "=", "c^2", font_size=96)
        result[0].set_color(BLUE)
        result[2].set_color(GREEN)
        result[4].set_color(YELLOW)
        self.play(Write(result))
        self.play(Create(SurroundingRectangle(result, buff=0.3)))
        self.wait(2)

Put all three parts in one file and press Run. The three scenes play one after the other; the ticks on the timeline mark where each one starts.

Transform(t, u) for each pair moves every triangle from its first place to its second at the same time. zip pairs the two groups up, first with first.

Try this

  • Change a and b: the proof works for any right triangle.
  • Add a first scene with one triangle and its sides labelled a, b and c (MathTex("a").next_to(triangle, LEFT)).
  • Zoom in on the result at the end: make Conclusion a MovingCameraScene and play self.camera.frame.animate.scale(0.7).move_to(result). See The camera.
  • Record it at 1080p and you have a short explainer video.