Much pythagoras, plus better point thickness and display implementations

This commit is contained in:
Grant Sanderson
2015-10-09 19:53:38 -07:00
parent b8fba1f3ec
commit e2fc29851e
14 changed files with 2831 additions and 81 deletions

View File

@ -48,8 +48,8 @@ class Transform(Animation):
Animation.__init__(self, mobject, **kwargs)
self.name += "To" + str(ending_mobject)
self.mobject.should_buffer_points = \
mobject.should_buffer_points and ending_mobject.should_buffer_points
self.mobject.point_thickness = ending_mobject.point_thickness
def black_out_extra_points(self, count1, count2):
#Ensure redundant pixels fade to black

View File

@ -46,6 +46,11 @@ LEFT = np.array((-1, 0, 0))
IN = np.array(( 0, 0,-1))
OUT = np.array(( 0, 0, 1))
TOP = SPACE_HEIGHT*UP
BOTTOM = SPACE_HEIGHT*DOWN
LEFT_SIDE = SPACE_WIDTH*LEFT
RIGHT_SIDE = SPACE_WIDTH*RIGHT
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
FILE_DIR = os.path.join(THIS_DIR, "files")
IMAGE_DIR = os.path.join(FILE_DIR, "images")

View File

@ -33,59 +33,80 @@ def paint_region(region, image_array = None, color = None):
return pixels
def paint_mobject(mobject, image_array = None):
pixels = get_pixels(image_array)
if mobject.get_num_points() == 0:
return pixels
return paint_mobjects([mobject], image_array)
def paint_mobjects(mobjects, image_array = None):
pixels = get_pixels(image_array)
height = pixels.shape[0]
width = pixels.shape[1]
space_height = SPACE_HEIGHT
space_width = SPACE_HEIGHT * width / height
# camera_distance = 10
pixels = pixels.reshape((pixels.size/3, 3))
for mobject in mobjects:
if mobject.get_num_points() == 0:
continue
points, rgbs = place_on_screen(mobject.points, mobject.rgbs,
space_width, space_height)
#Map points to pixel space, which requires rescaling and shifting
#Remember, 2*space_height -> height
points[:,0] = points[:,0]*width/space_width/2 + width/2
#Flip on y-axis as you go
points[:,1] = -1*points[:,1]*height/space_height/2 + height/2
points, rgbs = add_thickness(
points.astype('int'), rgbs,
mobject.point_thickness,
width, height
)
flattener = np.array([[1], [width]], dtype = 'int')
indices = np.dot(points, flattener)
pixels[indices[:,0]] = (255*rgbs).astype(int)
points = np.array(mobject.points[:, :2])
# for i in range(2):
# points[:,i] *= camera_distance/(camera_distance-mobject.points[:,2])
rgbs = np.array(mobject.rgbs)
#Flips y-axis
points[:,1] *= -1
#Removes points out of space
to_remove = (abs(points[:,0]) < SPACE_WIDTH) & \
(abs(points[:,1]) < SPACE_HEIGHT)
points = points[to_remove]
rgbs = rgbs[to_remove]
#Map points to pixel space, then create pixel array first in terms
#of its flattened version
try:
points += np.array(
[space_width, space_height]*points.shape[0]
).reshape(points.shape)
except:
print points.shape, mobject.points.shape
points *= np.array(
[width / (2.0 * space_width), height / (2.0 * space_height)]*\
points.shape[0]
).reshape(points.shape)
points = points.astype('int')
flattener = np.array([1, width], dtype = 'int').reshape((2, 1))
indices = np.dot(points, flattener)
indices = indices.reshape(indices.size)
if mobject.should_buffer_points:#Is this alright?
for tweak in [
indices + 1,
indices + width,
indices + width + 1
]:
indices = np.append(indices, tweak)
rgbs = np.array(list(rgbs) * 4)
admissibles = (indices < height * width) * (indices > 0)
indices = indices[admissibles]
rgbs = rgbs[admissibles]
rgbs = (rgbs * 255).astype(int)
pixels = pixels.reshape((height * width, 3))
pixels[indices] = rgbs.reshape((rgbs.size/3), 3)#WHY?
pixels = pixels.reshape((height, width, 3)).astype('uint8')
return pixels
def add_thickness(pixel_indices, rgbs, thickness, width, height):
"""
Imagine dragging each pixel around like a paintbrush in
a square of pixels tickness x thickness big surrounding it
"""
nudge_values = range(-thickness/2+1, thickness/2+1)
original = np.array(pixel_indices)
original_rgbs = np.array(rgbs)
for x, y in it.product(nudge_values, nudge_values):
if x == 0 and y == 0:
continue
pixel_indices = np.append(
pixel_indices,
original+[x, y],
axis = 0
)
rgbs = np.append(rgbs, original_rgbs, axis = 0)
admissibles = (pixel_indices[:,0] >= 0) & \
(pixel_indices[:,0] < width) & \
(pixel_indices[:,1] >= 0) & \
(pixel_indices[:,1] < height)
return pixel_indices[admissibles], rgbs[admissibles]
def place_on_screen(points, rgbs, space_width, space_height):
"""
Projects points to 2d space and remove those outside a
the space constraints
"""
# camera_distance = 10
points = np.array(points[:, :2])
# for i in range(2):
# points[:,i] *= camera_distance/(camera_distance-mobject.points[:,2])
rgbs = np.array(rgbs)
#Removes points out of space
to_keep = (abs(points[:,0]) < space_width) & \
(abs(points[:,1]) < space_height)
return points[to_keep], rgbs[to_keep]
def write_to_gif(scene, name):
#TODO, find better means of compression
if not name.endswith(".gif"):

View File

@ -238,8 +238,11 @@ def angle_between(v1, v2):
v2 / np.linalg.norm(v2)
))
def angle_of_vector(vector):
"""
Returns polar coordinate theta when vector is project on xy plane
"""
return np.log(complex(*vector[:2])).imag

View File

@ -15,7 +15,7 @@ class ImageMobject(Mobject):
"filter_color" : "black",
"invert" : True,
"use_cache" : True,
"should_buffer_points" : False,
"point_thickness" : 1,
"scale_value" : 1.0,
"should_center" : True
}
@ -134,7 +134,7 @@ def tex_mobject(expression,
#Todo, make this more sophisticated.
image_files = tex_to_image(expression, size, template_tex_file)
config = {
"should_buffer_points" : True,
"point_thickness" : 1,
"should_center" : False,
}
if isinstance(image_files, list):
@ -148,6 +148,10 @@ def tex_mobject(expression,
return result.center().highlight("white")
def underbrace(left, right):
result = tex_mobject("\\underbrace{%s}"%(14*"\\quad"))
result.stretch_to_fit_width(right[0]-left[0])
result.shift(left - result.points[0])
return result

View File

@ -21,7 +21,7 @@ class Mobject(object):
#Number of numbers used to describe a point (3 for pos, 3 for normal vector)
DEFAULT_CONFIG = {
"color" : "white",
"should_buffer_points" : GENERALLY_BUFFER_POINTS,
"point_thickness" : 2,
"name" : None,
}
DIM = 3
@ -156,9 +156,12 @@ class Mobject(object):
if aligned_edge is not None:
anchor_point = self.get_corner(aligned_edge-direction)
target_point = mobject.get_corner(aligned_edge+direction)
else:
elif list(direction) in map(list, [LEFT, RIGHT, UP, DOWN]):
anchor_point = self.get_edge_center(-direction)
target_point = mobject.get_edge_center(direction)
else:
anchor_point = self.get_boundary_point(-direction)
target_point = mobject.get_boundary_point(direction)
self.shift(target_point - anchor_point + buff*direction)
return self
@ -353,13 +356,13 @@ class Mobject1D(Mobject):
self.epsilon = 1.0 / self.density
Mobject.__init__(self, **kwargs)
def add_line(self, start, end, min_density = 0.1):
def add_line(self, start, end, min_density = 0.1, color = None):
length = np.linalg.norm(end - start)
epsilon = self.epsilon / max(length, min_density)
self.add_points([
interpolate(start, end, t)
for t in np.arange(0, 1, epsilon)
])
], color = color)
class Mobject2D(Mobject):
DEFAULT_CONFIG = {
@ -377,11 +380,10 @@ class CompoundMobject(Mobject):
for mobject in mobjects:
self.original_mobs_num_points.append(mobject.points.shape[0])
self.add_points(mobject.points, mobject.rgbs)
self.should_buffer_points = reduce(
op.and_,
[m.should_buffer_points for m in mobjects],
GENERALLY_BUFFER_POINTS
)
self.point_thickness = max([
m.point_thickness
for m in mobjects
])
def split(self):
result = []

View File

@ -163,17 +163,29 @@ class Circle(Mobject1D):
class Polygon(Mobject1D):
DEFAULT_CONFIG = {
"color" : "limegreen",
"edge_colors" : None
}
def __init__(self, *points, **kwargs):
assert len(points) > 1
digest_config(self, Polygon, kwargs)
self.vertices = points
self.original_points = points
Mobject1D.__init__(self, **kwargs)
def generate_points(self):
points = list(self.vertices) + [self.vertices[0]]
if self.edge_colors:
colors = it.cycle(self.edge_colors)
else:
colors = it.cycle([self.color])
self.indices_of_vertices = []
points = list(self.original_points)
points.append(points[0])
for start, end in zip(points, points[1:]):
self.add_line(start, end)
self.indices_of_vertices.append(self.get_num_points())
self.add_line(start, end, color = colors.next())
def get_vertices(self):
return self.points[self.indices_of_vertices]
class Rectangle(Mobject1D):
@ -197,11 +209,12 @@ class Rectangle(Mobject1D):
class Square(Rectangle):
DEFAULT_CONFIG = {
"height" : 2.0,
"width" : 2.0,
"side_length" : 2.0,
}
def __init__(self, **kwargs):
digest_config(self, Square, kwargs)
for arg in ["height", "width"]:
kwargs[arg] = self.side_length
Rectangle.__init__(self, **kwargs)
class Bubble(Mobject):

View File

@ -8,7 +8,7 @@ from helpers import *
class Stars(Mobject):
DEFAULT_CONFIG = {
"should_buffer_points" : False,
"point_thickness" : 1,
"radius" : SPACE_WIDTH,
"num_points" : 1000,
}

View File

@ -84,6 +84,12 @@ def region_from_line_boundary(*lines, **kwargs):
reg.intersect(HalfPlane(line, **kwargs))
return reg
def region_from_polygon_vertices(*vertices, **kwargs):
points = list(vertices)
points.append(points[0])
return region_from_line_boundary(*zip(points, points[1:]), **kwargs)
def plane_partition(*lines, **kwargs):
"""
A 'line' is a pair of points [(x0, y0,...), (x1, y1,...)]

View File

@ -16,8 +16,8 @@ from script_wrapper import command_line_create_scene
class SampleScene(Scene):
def construct(self):
plane = NumberPlane(density = 400)
arrow1 = Arrow(ORIGIN, UP, color = "green")
plane = NumberPlane()
arrow1 = Arrow(ORIGIN, UP, color = "green", point_thickness = 5)
arrow2 = Arrow(ORIGIN, LEFT, color = "Red")
self.add(plane, arrow1, arrow2)

View File

@ -7,6 +7,7 @@ import time
import os
import copy
import progressbar
import inspect
from helpers import *
from mobject import *
@ -63,6 +64,16 @@ class Scene(object):
self.mobjects.append(mobject)
return self
def add_local_mobjects(self):
"""
So a scene can just add all mobjects it's defined up to that point
"""
caller_locals = inspect.currentframe().f_back.f_locals
self.add(*filter(
lambda m : isinstance(m, Mobject),
caller_locals.values()
))
def remove(self, *mobjects):
for mobject in mobjects:
if not isinstance(mobject, Mobject):
@ -106,9 +117,7 @@ class Scene(object):
#This way current mobjects don't have to be redrawn with
#every change, and one can later call "apply" without worrying
#about it applying to these mobjects
self.background = disp.paint_mobject(
CompoundMobject(*mobjects), self.background
)
self.background = disp.paint_mobjects(mobjects, self.background)
return self
def play(self, *animations, **kwargs):
@ -130,9 +139,7 @@ class Scene(object):
progress_bar.update(t)
for animation in animations:
animation.update(t / animation.run_time)
new_frame = disp.paint_mobject(
CompoundMobject(*moving_mobjects), background
)
new_frame = disp.paint_mobjects(moving_mobjects, background)
self.frames.append(new_frame)
for animation in animations:
animation.clean_up()
@ -172,9 +179,7 @@ class Scene(object):
])
def get_frame(self):
return disp.paint_mobject(
CompoundMobject(*self.mobjects), self.background
)
return disp.paint_mobjects(self.mobjects, self.background)
def dither(self, duration = DEFAULT_DITHER_TIME):
self.frames += [self.get_frame()]*int(duration / self.frame_duration)

2168
scripts/inventing_math.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -94,7 +94,7 @@ class OpenInterval(CompoundMobject):
class Piano(ImageMobject):
DEFAULT_CONFIG = {
"should_buffer_points" : False,
"point_thickness" : 1,
"invert" : False,
"scale_value" : 0.5
}
@ -112,7 +112,7 @@ class Piano(ImageMobject):
for count in range(14):
key = Mobject(
color = "white",
should_buffer_points = False
point_thickness = 1
)
x0 = left + count*self.ivory_jump
x1 = x0 + self.ivory_jump
@ -663,7 +663,7 @@ class ConstructPiano(Scene):
askew = deepcopy(keys[-1])
keys[-1].rotate_in_place(np.pi/5)
for key in keys:
key.should_buffer_points = False
key.point_thickness = 1
key_copy = deepcopy(key).to_corner(DOWN+LEFT)
key_copy.scale_in_place(0.25)
key_copy.shift(1.8*random.random()*SPACE_WIDTH*RIGHT)

View File

@ -0,0 +1,523 @@
#!/usr/bin/env python
import numpy as np
import itertools as it
from copy import deepcopy
import sys
from animation import *
from mobject import *
from constants import *
from region import *
from scene import Scene
from script_wrapper import command_line_create_scene
MOVIE_PREFIX = "pythagorean_proof"
A_COLOR = "skyblue"
B_COLOR = "pink"
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.2):
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 = center-points[2]
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 = "green"
)
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 = "green"
)
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 = "green")
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 = "green")
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)
if __name__ == "__main__":
command_line_create_scene(MOVIE_PREFIX)