3b1b-manim/old_projects/pythagorean_proof.py
2015-10-28 16:03:33 -07:00

509 lines
17 KiB
Python

import numpy as np
import itertools as it
from copy import deepcopy
import sys
from helpers import *
from scene import Scene
from geometry import Polygon
from region import region_from_polygon_vertices, region_from_line_boundary
A_COLOR = BLUE
B_COLOR = MAROON_D
C_COLOR = YELLOW
TEX_MOB_SCALE_VAL = 0.5
POINTS = np.array([
DOWN,
2*UP,
DOWN+RIGHT,
2*DOWN,
2*DOWN+RIGHT,
DOWN+3*LEFT,
2*UP+3*LEFT,
4*RIGHT,
3*UP+3*RIGHT,
])
class Triangle(Polygon):
def __init__(self, **kwargs):
kwargs["color"] = C_COLOR
Polygon.__init__(
self,
*POINTS[[0, 1, 2]],
edge_colors = [B_COLOR, C_COLOR, A_COLOR],
**kwargs
)
nudge = 0.2
target = POINTS[0]+nudge*(UP+RIGHT)
for direction in UP, RIGHT:
self.add_line(POINTS[0]+nudge*direction, target, color = WHITE)
def add_all_letters(self):
for char in "abc":
self.add_letter(char)
return self
def add_letter(self, char, nudge = 0.3):
mob = tex_mobject(char).scale(TEX_MOB_SCALE_VAL)
if char == "a":
points = self.get_vertices()[[0, 2, 1]]
elif char == "b":
points = self.get_vertices()[[1, 0, 2]]
elif char == "c":
points = self.get_vertices()[[2, 1, 0]]
center = 0.5*sum(points[:2]) #average of first two points
mob.shift(center)
normal_dir = rotate_vector(points[1] - points[0], np.pi/2, OUT)
if np.dot(normal_dir, points[2]-center) > 0:
normal_dir = -normal_dir
normal_dir /= np.linalg.norm(normal_dir)
mob.shift(nudge*normal_dir)
self.add(mob)
return self
def place_hypotenuse_on(self, point1, point2):
#self.vertices[1], self.vertices[2]
start1, start2 = self.get_vertices()[[1, 2]]
target_vect = np.array(point2)-np.array(point1)
curr_vect = start2-start1
self.scale(np.linalg.norm(target_vect)/np.linalg.norm(curr_vect))
self.rotate(angle_of_vector(target_vect)-angle_of_vector(curr_vect))
self.shift(point1-self.get_vertices()[1])
return self
def a_square(**kwargs):
return Polygon(*POINTS[[0, 2, 4, 3]], color = A_COLOR, **kwargs)
def b_square(**kwargs):
return Polygon(*POINTS[[1, 0, 5, 6]], color = B_COLOR, **kwargs)
def c_square(**kwargs):
return Polygon(*POINTS[[1, 2, 7, 8]], color = C_COLOR, **kwargs)
class DrawPointsReference(Scene):
def construct(self):
for point, count in zip(POINTS, it.count()):
mob = tex_mobject(str(count)).scale(TEX_MOB_SCALE_VAL)
mob.shift(POINTS[count])
self.add(mob)
class DrawTriangle(Scene):
def construct(self):
self.add(Triangle().add_all_letters())
class DrawAllThreeSquares(Scene):
def construct(self):
a = a_square()
b = b_square()
c = c_square()
self.add(Triangle(), a, b, c)
for letter, mob in zip("abc", [a, b, c]):
char_mob = tex_mobject(letter+"^2").scale(TEX_MOB_SCALE_VAL)
char_mob.shift(mob.get_center())
self.add(char_mob)
class AddParallelLines(DrawAllThreeSquares):
args_list = [
(1, False),
(2, False),
(3, False),
(3, True),
]
@staticmethod
def args_to_string(num, trim):
return str(num) + ("Trimmed" if trim else "")
def construct(self, num, trim):
DrawAllThreeSquares.construct(self)
shift_pairs = [
(4*RIGHT, 3*UP),
(ORIGIN, DOWN),
(3*LEFT, 2*DOWN)
]
for side_shift, vert_shift in shift_pairs[:num]:
line1 = Line(BOTTOM, TOP, color = WHITE)
line1.shift(side_shift)
line2 = Line(LEFT_SIDE, RIGHT_SIDE, color = WHITE)
line2.shift(vert_shift)
self.add(line1, line2)
if trim:
for mob in self.mobjects:
mob.filter_out(lambda p : p[0] > 4)
mob.filter_out(lambda p : p[0] < -3)
mob.filter_out(lambda p : p[1] > 3)
mob.filter_out(lambda p : p[1] < -2)
class HighlightEmergentTriangles(AddParallelLines):
args_list = [(3,True)]
def construct(self, *args):
AddParallelLines.construct(self, *args)
triplets = [
[(0, 2), (0, -1), (1, -1)],
[(1, -1), (4, -1), (4, 0)],
[(4, 0), (4, 3), (3, 3)],
[(3, 3), (0, 3), (0, 2)],
]
for triplet in triplets:
self.highlight_region(
region_from_polygon_vertices(*triplet),
color = "DARK_BLUE"
)
class IndicateTroublePointFromParallelLines(AddParallelLines):
args_list = [(3,True)]
def construct(self, *args):
AddParallelLines.construct(self, *args)
circle = Circle(radius = 0.25)
circle.shift(DOWN+RIGHT)
vect = DOWN+RIGHT
arrow = Arrow(circle.get_center()+2*vect, circle.get_boundary_point(vect))
arrow.highlight(circle.get_color())
self.add_local_mobjects()
class DrawAllThreeSquaresWithMoreTriangles(DrawAllThreeSquares):
args_list = [
(1, True),
(2, True),
(3, True),
(4, True),
(5, True),
(6, True),
(7, True),
(8, True),
(9, True),
(10, True),
(10, False)
]
@staticmethod
def args_to_string(num, fill):
fill_string = "" if fill else "HollowTriangles"
return str(num) + fill_string
def construct(self, num, fill):
DrawAllThreeSquares.construct(self)
pairs = [
((0, 2, 0), (1, -1, 0)),
((-3, -1, 0), (0, -2, 0)),
((4, -1, 0), (1, -2, 0)),
((0, -2, 0), (-3, -1, 0)),
((1, -2, 0), (4, -1, 0)),
((1, -1, 0), (4, 0, 0)),
((4, 0, 0), (3, 3, 0)),
((3, 3, 0), (0, 2, 0)),
((-3, 3, 0), (0, 2, 0)),
((0, 2, 0), (-3, 3, 0))
]
to_flip = [1, 3, 8, 9]
for n in range(num):
triangle = Triangle()
if n in to_flip:
triangle.rotate(np.pi, UP)
self.add(triangle.place_hypotenuse_on(*pairs[n]))
vertices = list(triangle.get_vertices())
if n not in to_flip:
vertices.reverse()
if fill:
self.highlight_region(
region_from_polygon_vertices(*vertices),
color = DARK_BLUE
)
class IndicateBigRectangleTroublePoint(DrawAllThreeSquaresWithMoreTriangles):
args_list = [(10, False)]
def construct(self, *args):
DrawAllThreeSquaresWithMoreTriangles.construct(self, *args)
circle = Circle(radius = 0.25, color = WHITE)
circle.shift(4*RIGHT)
vect = DOWN+RIGHT
arrow = Arrow(circle.get_center()+vect, circle.get_boundary_point(vect))
self.add_local_mobjects()
class ShowBigRectangleDimensions(DrawAllThreeSquaresWithMoreTriangles):
args_list = [(10, False)]
def construct(self, num, fill):
DrawAllThreeSquaresWithMoreTriangles.construct(self, num, fill)
u_brace = underbrace((-3, -2, 0), (4, -2, 0))
side_brace = underbrace((-3, -3, 0), (2, -3, 0))
for brace in u_brace, side_brace:
brace.shift(0.2*DOWN)
side_brace.rotate(-np.pi/2)
a_plus_2b = tex_mobject("a+2b").scale(TEX_MOB_SCALE_VAL)
b_plus_2a = tex_mobject("b+2a").scale(TEX_MOB_SCALE_VAL)
a_plus_2b.next_to(u_brace, DOWN)
b_plus_2a.next_to(side_brace, LEFT)
self.add_local_mobjects()
class FillInAreaOfBigRectangle(DrawAllThreeSquaresWithMoreTriangles):
args_list = [(10, False)]
def construct(self, *args):
DrawAllThreeSquaresWithMoreTriangles.construct(self, *args)
args_list = [(10, False)]
color = Color("yellow")
color.set_rgb(0.3*np.array(color.get_rgb()))
self.highlight_region(
region_from_polygon_vertices(
(-3, 3),
(-3, -2),
(4, -2),
(4, 3)
),
color = color
)
class DrawOnlyABSquares(Scene):
def construct(self):
a = a_square()
b = b_square()
for char, mob in zip("ab", [a, b]):
symobl = tex_mobject(char+"^2").scale(TEX_MOB_SCALE_VAL)
symobl.shift(mob.get_center())
self.add(symobl)
triangle = Triangle()
self.add_local_mobjects()
class AddTriangleCopyToABSquares(DrawOnlyABSquares):
def construct(self):
DrawOnlyABSquares.construct(self)
triangle = Triangle()
triangle.rotate(np.pi, UP)
triangle.place_hypotenuse_on(3*LEFT+DOWN, 2*DOWN)
self.add(triangle)
self.highlight_triangles()
def highlight_triangles(self):
for mob in self.mobjects:
if isinstance(mob, Triangle):
vertices = list(mob.get_vertices())
for x in range(2):
self.highlight_region(region_from_polygon_vertices(
*vertices
), color = DARK_BLUE)
vertices.reverse()#silly hack
class AddAllTrianglesToABSquares(AddTriangleCopyToABSquares):
def construct(self):
AddTriangleCopyToABSquares.construct(self)
self.add(Triangle().place_hypotenuse_on(RIGHT+DOWN, 2*UP))
triangle = Triangle()
triangle.rotate(np.pi, UP)
triangle.place_hypotenuse_on(2*DOWN, 3*LEFT+DOWN)
self.add(triangle)
self.highlight_triangles()
class DrawNakedCSqurae(Scene):
def construct(self):
c = c_square().center()
triangle = Triangle().place_hypotenuse_on(*c.get_vertices()[[0,1]])
triangle.add_all_letters()
self.add(triangle, c)
class DrawCSquareWithAllTraingles(Scene):
args_list = [
(False, False, False, False),
(False, True, False, True),
(True, True, False, False),
(False, True, True, False),
]
@staticmethod
def args_to_string(*toggle_vector):
return "".join(map(str, map(int, toggle_vector)))
def construct(self, *toggle_vector):
if len(toggle_vector) == 0:
toggle_vector = [False]*4
self.c_square = c_square().center()
vertices = it.cycle(self.c_square.get_vertices())
last_vertex = vertices.next()
have_letters = False
self.triangles = []
for vertex, should_flip in zip(vertices, toggle_vector):
triangle = Triangle()
pair = np.array([last_vertex, vertex])
if should_flip:
triangle.rotate(np.pi, UP)
pair = pair[[1, 0]]
triangle.place_hypotenuse_on(*pair)
if not have_letters:
triangle.add_all_letters()
have_letters = True
self.triangles.append(triangle)
self.add(triangle)
last_vertex = vertex
self.add(self.c_square)
class HighlightCSquareInBigSquare(DrawCSquareWithAllTraingles):
args_list = [tuple([False]*4)]
def construct(self, *args):
DrawCSquareWithAllTraingles.construct(self, *args)
self.highlight_region(region_from_polygon_vertices(
*c_square().center().get_vertices()
), color = YELLOW)
class IndicateCSquareTroublePoint(DrawCSquareWithAllTraingles):
def construct(self, *toggle_vector):
DrawCSquareWithAllTraingles.construct(self, *toggle_vector)
circle = Circle(color = WHITE)
circle.scale(0.25)
vertex = self.c_square.get_vertices()[1]
circle.shift(vertex)
vect = 2*RIGHT+DOWN
arrow = Arrow(vertex+vect, circle.get_boundary_point(vect))
self.add(circle, arrow)
class ZoomInOnTroublePoint(Scene):
args_list = list(it.product([True, False], [True, False]))
@staticmethod
def args_to_string(with_labels, rotate):
label_string = "WithLabels" if with_labels else "WithoutLabels"
rotate_string = "Rotated" if rotate else ""
return label_string + rotate_string
def construct(self, with_labels, rotate):
zoom_factor = 10
density = zoom_factor*DEFAULT_POINT_DENSITY_1D
c = c_square(density = density)
c.shift(-c.get_vertices()[1])
c.scale(zoom_factor)
vertices = c.get_vertices()
for index in 0, 1:
triangle = Triangle(density = density)
triangle.place_hypotenuse_on(vertices[index], vertices[index+1])
self.add(triangle)
circle = Circle(radius = 2.5, color = WHITE)
angle1_arc = Circle(color = WHITE)
angle2_arc = Circle(color = WHITE).scale(0.5)
angle1_arc.filter_out(lambda (x, y, z) : not (x > 0 and y > 0 and y < x/3))
angle2_arc.filter_out(lambda (x, y, z) : not (x < 0 and y > 0 and y < -3*x))
self.add_local_mobjects()
self.add_elbow()
if rotate:
for mob in self.mobjects:
mob.rotate(np.pi/2)
if with_labels:
alpha = tex_mobject("\\alpha").scale(TEX_MOB_SCALE_VAL)
beta = tex_mobject("90-\\alpha").scale(TEX_MOB_SCALE_VAL)
if rotate:
alpha.next_to(angle1_arc, UP+0.1*LEFT)
beta.next_to(angle2_arc, DOWN+0.5*LEFT)
else:
alpha.next_to(angle1_arc, RIGHT)
beta.next_to(angle2_arc, LEFT)
self.add(alpha, beta)
def add_elbow(self):
c = 0.1
p1 = c*LEFT + 3*c*UP
p2 = 3*c*RIGHT + c*UP
p3 = 2*c*RIGHT + 4*c*UP
self.add(Line(p1, p3, color = WHITE))
self.add(Line(p2, p3, color = WHITE))
class DrawTriangleWithAngles(Scene):
def construct(self):
triangle = Triangle(density = 2*DEFAULT_POINT_DENSITY_1D)
triangle.scale(2).center().add_all_letters()
vertices = triangle.get_vertices()
kwargs = {"color" : WHITE}
angle1_arc = Circle(radius = 0.4, **kwargs).filter_out(
lambda (x, y, z) : not(x > 0 and y < 0 and y < -3*x)
).shift(vertices[1])
angle2_arc = Circle(radius = 0.2, **kwargs).filter_out(
lambda (x, y, z) : not(x < 0 and y > 0 and y < -3*x)
).shift(vertices[2])
alpha = tex_mobject("\\alpha")
beta = tex_mobject("90-\\alpha")
alpha.shift(vertices[1]+3*RIGHT+DOWN)
beta.shift(vertices[2]+3*RIGHT+UP)
arrow1 = Arrow(alpha, angle1_arc)
arrow2 = Arrow(beta, angle2_arc)
self.add(triangle, angle1_arc, angle2_arc, alpha, beta, arrow1, arrow2)
class LabelLargeSquare(DrawCSquareWithAllTraingles):
args_list = []
def construct(self):
DrawCSquareWithAllTraingles.construct(self)
everything = CompoundMobject(*self.mobjects)
u_brace = underbrace(2*(DOWN+LEFT), 2*(DOWN+RIGHT))
u_brace.shift(0.2*DOWN)
side_brace = deepcopy(u_brace).rotate(np.pi/2)
upper_brace = deepcopy(u_brace).rotate(np.pi)
a_plus_b = tex_mobject("a+b").scale(TEX_MOB_SCALE_VAL)
upper_brace.add(a_plus_b.next_to(upper_brace, UP))
side_brace.add(a_plus_b.next_to(side_brace, RIGHT))
self.add(upper_brace, side_brace)
class CompletelyFillLargeSquare(LabelLargeSquare):
def construct(self):
LabelLargeSquare.construct(self)
vertices = [2*(DOWN+LEFT), 2*(DOWN+RIGHT), 2*(UP+RIGHT), 2*(UP+LEFT)]
vertices.append(vertices[0])
pairs = zip(vertices, vertices[1:])
self.highlight_region(region_from_line_boundary(*pairs), color = BLUE)
class FillComponentsOfLargeSquare(LabelLargeSquare):
def construct(self):
LabelLargeSquare.construct(self)
points = np.array([
2*UP+2*LEFT,
UP+2*LEFT,
2*DOWN+2*LEFT,
2*DOWN+LEFT,
2*DOWN+2*RIGHT,
DOWN+2*RIGHT,
2*UP+2*RIGHT,
RIGHT+2*UP
])
for triplet in [[0, 1, 7], [2, 3, 1], [4, 5, 3], [6, 7, 5]]:
triplet.append(triplet[0])
self.highlight_region(region_from_line_boundary(*[
[points[i], points[j]]
for i, j in zip(triplet, triplet[1:])
]), color = DARK_BLUE)
vertices = points[[1, 3, 5, 7, 1]]
self.highlight_region(region_from_line_boundary(*[
[p1, p2]
for p1, p2 in zip(vertices, vertices[1:])
]), color = YELLOW)
class ShowRearrangementInBigSquare(DrawCSquareWithAllTraingles):
args_list = []
def construct(self):
self.add(Square(side_length = 4, color = WHITE))
DrawCSquareWithAllTraingles.construct(self)
self.remove(self.c_square)
self.triangles[1].shift(LEFT)
for i, j in [(0, 2), (3, 1)]:
self.triangles[i].place_hypotenuse_on(
*self.triangles[j].get_vertices()[[2, 1]]
)
class ShowRearrangementInBigSquareWithRegions(ShowRearrangementInBigSquare):
def construct(self):
ShowRearrangementInBigSquare.construct(self)
self.highlight_region(region_from_polygon_vertices(
2*(LEFT+UP), 2*LEFT+DOWN, RIGHT+DOWN, RIGHT+2*UP
), color = B_COLOR)
self.highlight_region(region_from_polygon_vertices(
RIGHT+DOWN, RIGHT+2*DOWN, 2*RIGHT+2*DOWN, 2*RIGHT+DOWN
), color = A_COLOR)