mirror of
https://github.com/3b1b/manim.git
synced 2025-07-30 05:24:22 +08:00
Much pythagoras, plus better point thickness and display implementations
This commit is contained in:
@ -48,8 +48,8 @@ class Transform(Animation):
|
|||||||
|
|
||||||
Animation.__init__(self, mobject, **kwargs)
|
Animation.__init__(self, mobject, **kwargs)
|
||||||
self.name += "To" + str(ending_mobject)
|
self.name += "To" + str(ending_mobject)
|
||||||
self.mobject.should_buffer_points = \
|
self.mobject.point_thickness = ending_mobject.point_thickness
|
||||||
mobject.should_buffer_points and ending_mobject.should_buffer_points
|
|
||||||
|
|
||||||
def black_out_extra_points(self, count1, count2):
|
def black_out_extra_points(self, count1, count2):
|
||||||
#Ensure redundant pixels fade to black
|
#Ensure redundant pixels fade to black
|
||||||
|
@ -46,6 +46,11 @@ LEFT = np.array((-1, 0, 0))
|
|||||||
IN = np.array(( 0, 0,-1))
|
IN = np.array(( 0, 0,-1))
|
||||||
OUT = 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__))
|
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||||
FILE_DIR = os.path.join(THIS_DIR, "files")
|
FILE_DIR = os.path.join(THIS_DIR, "files")
|
||||||
IMAGE_DIR = os.path.join(FILE_DIR, "images")
|
IMAGE_DIR = os.path.join(FILE_DIR, "images")
|
||||||
|
111
displayer.py
111
displayer.py
@ -33,59 +33,80 @@ def paint_region(region, image_array = None, color = None):
|
|||||||
return pixels
|
return pixels
|
||||||
|
|
||||||
def paint_mobject(mobject, image_array = None):
|
def paint_mobject(mobject, image_array = None):
|
||||||
pixels = get_pixels(image_array)
|
return paint_mobjects([mobject], image_array)
|
||||||
if mobject.get_num_points() == 0:
|
|
||||||
return pixels
|
def paint_mobjects(mobjects, image_array = None):
|
||||||
|
pixels = get_pixels(image_array)
|
||||||
height = pixels.shape[0]
|
height = pixels.shape[0]
|
||||||
width = pixels.shape[1]
|
width = pixels.shape[1]
|
||||||
space_height = SPACE_HEIGHT
|
space_height = SPACE_HEIGHT
|
||||||
space_width = SPACE_HEIGHT * width / 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')
|
pixels = pixels.reshape((height, width, 3)).astype('uint8')
|
||||||
return pixels
|
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):
|
def write_to_gif(scene, name):
|
||||||
#TODO, find better means of compression
|
#TODO, find better means of compression
|
||||||
if not name.endswith(".gif"):
|
if not name.endswith(".gif"):
|
||||||
|
@ -238,8 +238,11 @@ def angle_between(v1, v2):
|
|||||||
v2 / np.linalg.norm(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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ class ImageMobject(Mobject):
|
|||||||
"filter_color" : "black",
|
"filter_color" : "black",
|
||||||
"invert" : True,
|
"invert" : True,
|
||||||
"use_cache" : True,
|
"use_cache" : True,
|
||||||
"should_buffer_points" : False,
|
"point_thickness" : 1,
|
||||||
"scale_value" : 1.0,
|
"scale_value" : 1.0,
|
||||||
"should_center" : True
|
"should_center" : True
|
||||||
}
|
}
|
||||||
@ -134,7 +134,7 @@ def tex_mobject(expression,
|
|||||||
#Todo, make this more sophisticated.
|
#Todo, make this more sophisticated.
|
||||||
image_files = tex_to_image(expression, size, template_tex_file)
|
image_files = tex_to_image(expression, size, template_tex_file)
|
||||||
config = {
|
config = {
|
||||||
"should_buffer_points" : True,
|
"point_thickness" : 1,
|
||||||
"should_center" : False,
|
"should_center" : False,
|
||||||
}
|
}
|
||||||
if isinstance(image_files, list):
|
if isinstance(image_files, list):
|
||||||
@ -148,6 +148,10 @@ def tex_mobject(expression,
|
|||||||
return result.center().highlight("white")
|
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
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class Mobject(object):
|
|||||||
#Number of numbers used to describe a point (3 for pos, 3 for normal vector)
|
#Number of numbers used to describe a point (3 for pos, 3 for normal vector)
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
"color" : "white",
|
"color" : "white",
|
||||||
"should_buffer_points" : GENERALLY_BUFFER_POINTS,
|
"point_thickness" : 2,
|
||||||
"name" : None,
|
"name" : None,
|
||||||
}
|
}
|
||||||
DIM = 3
|
DIM = 3
|
||||||
@ -156,9 +156,12 @@ class Mobject(object):
|
|||||||
if aligned_edge is not None:
|
if aligned_edge is not None:
|
||||||
anchor_point = self.get_corner(aligned_edge-direction)
|
anchor_point = self.get_corner(aligned_edge-direction)
|
||||||
target_point = mobject.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)
|
anchor_point = self.get_edge_center(-direction)
|
||||||
target_point = mobject.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)
|
self.shift(target_point - anchor_point + buff*direction)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@ -353,13 +356,13 @@ class Mobject1D(Mobject):
|
|||||||
self.epsilon = 1.0 / self.density
|
self.epsilon = 1.0 / self.density
|
||||||
Mobject.__init__(self, **kwargs)
|
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)
|
length = np.linalg.norm(end - start)
|
||||||
epsilon = self.epsilon / max(length, min_density)
|
epsilon = self.epsilon / max(length, min_density)
|
||||||
self.add_points([
|
self.add_points([
|
||||||
interpolate(start, end, t)
|
interpolate(start, end, t)
|
||||||
for t in np.arange(0, 1, epsilon)
|
for t in np.arange(0, 1, epsilon)
|
||||||
])
|
], color = color)
|
||||||
|
|
||||||
class Mobject2D(Mobject):
|
class Mobject2D(Mobject):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
@ -377,11 +380,10 @@ class CompoundMobject(Mobject):
|
|||||||
for mobject in mobjects:
|
for mobject in mobjects:
|
||||||
self.original_mobs_num_points.append(mobject.points.shape[0])
|
self.original_mobs_num_points.append(mobject.points.shape[0])
|
||||||
self.add_points(mobject.points, mobject.rgbs)
|
self.add_points(mobject.points, mobject.rgbs)
|
||||||
self.should_buffer_points = reduce(
|
self.point_thickness = max([
|
||||||
op.and_,
|
m.point_thickness
|
||||||
[m.should_buffer_points for m in mobjects],
|
for m in mobjects
|
||||||
GENERALLY_BUFFER_POINTS
|
])
|
||||||
)
|
|
||||||
|
|
||||||
def split(self):
|
def split(self):
|
||||||
result = []
|
result = []
|
||||||
|
@ -163,17 +163,29 @@ class Circle(Mobject1D):
|
|||||||
class Polygon(Mobject1D):
|
class Polygon(Mobject1D):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
"color" : "limegreen",
|
"color" : "limegreen",
|
||||||
|
"edge_colors" : None
|
||||||
}
|
}
|
||||||
def __init__(self, *points, **kwargs):
|
def __init__(self, *points, **kwargs):
|
||||||
assert len(points) > 1
|
assert len(points) > 1
|
||||||
digest_config(self, Polygon, kwargs)
|
digest_config(self, Polygon, kwargs)
|
||||||
self.vertices = points
|
self.original_points = points
|
||||||
Mobject1D.__init__(self, **kwargs)
|
Mobject1D.__init__(self, **kwargs)
|
||||||
|
|
||||||
def generate_points(self):
|
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:]):
|
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):
|
class Rectangle(Mobject1D):
|
||||||
@ -197,11 +209,12 @@ class Rectangle(Mobject1D):
|
|||||||
|
|
||||||
class Square(Rectangle):
|
class Square(Rectangle):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
"height" : 2.0,
|
"side_length" : 2.0,
|
||||||
"width" : 2.0,
|
|
||||||
}
|
}
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
digest_config(self, Square, kwargs)
|
digest_config(self, Square, kwargs)
|
||||||
|
for arg in ["height", "width"]:
|
||||||
|
kwargs[arg] = self.side_length
|
||||||
Rectangle.__init__(self, **kwargs)
|
Rectangle.__init__(self, **kwargs)
|
||||||
|
|
||||||
class Bubble(Mobject):
|
class Bubble(Mobject):
|
||||||
|
@ -8,7 +8,7 @@ from helpers import *
|
|||||||
|
|
||||||
class Stars(Mobject):
|
class Stars(Mobject):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
"should_buffer_points" : False,
|
"point_thickness" : 1,
|
||||||
"radius" : SPACE_WIDTH,
|
"radius" : SPACE_WIDTH,
|
||||||
"num_points" : 1000,
|
"num_points" : 1000,
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,12 @@ def region_from_line_boundary(*lines, **kwargs):
|
|||||||
reg.intersect(HalfPlane(line, **kwargs))
|
reg.intersect(HalfPlane(line, **kwargs))
|
||||||
return reg
|
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):
|
def plane_partition(*lines, **kwargs):
|
||||||
"""
|
"""
|
||||||
A 'line' is a pair of points [(x0, y0,...), (x1, y1,...)]
|
A 'line' is a pair of points [(x0, y0,...), (x1, y1,...)]
|
||||||
|
@ -16,8 +16,8 @@ from script_wrapper import command_line_create_scene
|
|||||||
|
|
||||||
class SampleScene(Scene):
|
class SampleScene(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
plane = NumberPlane(density = 400)
|
plane = NumberPlane()
|
||||||
arrow1 = Arrow(ORIGIN, UP, color = "green")
|
arrow1 = Arrow(ORIGIN, UP, color = "green", point_thickness = 5)
|
||||||
arrow2 = Arrow(ORIGIN, LEFT, color = "Red")
|
arrow2 = Arrow(ORIGIN, LEFT, color = "Red")
|
||||||
self.add(plane, arrow1, arrow2)
|
self.add(plane, arrow1, arrow2)
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import time
|
|||||||
import os
|
import os
|
||||||
import copy
|
import copy
|
||||||
import progressbar
|
import progressbar
|
||||||
|
import inspect
|
||||||
|
|
||||||
from helpers import *
|
from helpers import *
|
||||||
from mobject import *
|
from mobject import *
|
||||||
@ -63,6 +64,16 @@ class Scene(object):
|
|||||||
self.mobjects.append(mobject)
|
self.mobjects.append(mobject)
|
||||||
return self
|
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):
|
def remove(self, *mobjects):
|
||||||
for mobject in mobjects:
|
for mobject in mobjects:
|
||||||
if not isinstance(mobject, Mobject):
|
if not isinstance(mobject, Mobject):
|
||||||
@ -106,9 +117,7 @@ class Scene(object):
|
|||||||
#This way current mobjects don't have to be redrawn with
|
#This way current mobjects don't have to be redrawn with
|
||||||
#every change, and one can later call "apply" without worrying
|
#every change, and one can later call "apply" without worrying
|
||||||
#about it applying to these mobjects
|
#about it applying to these mobjects
|
||||||
self.background = disp.paint_mobject(
|
self.background = disp.paint_mobjects(mobjects, self.background)
|
||||||
CompoundMobject(*mobjects), self.background
|
|
||||||
)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def play(self, *animations, **kwargs):
|
def play(self, *animations, **kwargs):
|
||||||
@ -130,9 +139,7 @@ class Scene(object):
|
|||||||
progress_bar.update(t)
|
progress_bar.update(t)
|
||||||
for animation in animations:
|
for animation in animations:
|
||||||
animation.update(t / animation.run_time)
|
animation.update(t / animation.run_time)
|
||||||
new_frame = disp.paint_mobject(
|
new_frame = disp.paint_mobjects(moving_mobjects, background)
|
||||||
CompoundMobject(*moving_mobjects), background
|
|
||||||
)
|
|
||||||
self.frames.append(new_frame)
|
self.frames.append(new_frame)
|
||||||
for animation in animations:
|
for animation in animations:
|
||||||
animation.clean_up()
|
animation.clean_up()
|
||||||
@ -172,9 +179,7 @@ class Scene(object):
|
|||||||
])
|
])
|
||||||
|
|
||||||
def get_frame(self):
|
def get_frame(self):
|
||||||
return disp.paint_mobject(
|
return disp.paint_mobjects(self.mobjects, self.background)
|
||||||
CompoundMobject(*self.mobjects), self.background
|
|
||||||
)
|
|
||||||
|
|
||||||
def dither(self, duration = DEFAULT_DITHER_TIME):
|
def dither(self, duration = DEFAULT_DITHER_TIME):
|
||||||
self.frames += [self.get_frame()]*int(duration / self.frame_duration)
|
self.frames += [self.get_frame()]*int(duration / self.frame_duration)
|
||||||
|
2168
scripts/inventing_math.py
Normal file
2168
scripts/inventing_math.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -94,7 +94,7 @@ class OpenInterval(CompoundMobject):
|
|||||||
|
|
||||||
class Piano(ImageMobject):
|
class Piano(ImageMobject):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
"should_buffer_points" : False,
|
"point_thickness" : 1,
|
||||||
"invert" : False,
|
"invert" : False,
|
||||||
"scale_value" : 0.5
|
"scale_value" : 0.5
|
||||||
}
|
}
|
||||||
@ -112,7 +112,7 @@ class Piano(ImageMobject):
|
|||||||
for count in range(14):
|
for count in range(14):
|
||||||
key = Mobject(
|
key = Mobject(
|
||||||
color = "white",
|
color = "white",
|
||||||
should_buffer_points = False
|
point_thickness = 1
|
||||||
)
|
)
|
||||||
x0 = left + count*self.ivory_jump
|
x0 = left + count*self.ivory_jump
|
||||||
x1 = x0 + self.ivory_jump
|
x1 = x0 + self.ivory_jump
|
||||||
@ -663,7 +663,7 @@ class ConstructPiano(Scene):
|
|||||||
askew = deepcopy(keys[-1])
|
askew = deepcopy(keys[-1])
|
||||||
keys[-1].rotate_in_place(np.pi/5)
|
keys[-1].rotate_in_place(np.pi/5)
|
||||||
for key in keys:
|
for key in keys:
|
||||||
key.should_buffer_points = False
|
key.point_thickness = 1
|
||||||
key_copy = deepcopy(key).to_corner(DOWN+LEFT)
|
key_copy = deepcopy(key).to_corner(DOWN+LEFT)
|
||||||
key_copy.scale_in_place(0.25)
|
key_copy.scale_in_place(0.25)
|
||||||
key_copy.shift(1.8*random.random()*SPACE_WIDTH*RIGHT)
|
key_copy.shift(1.8*random.random()*SPACE_WIDTH*RIGHT)
|
||||||
|
523
scripts/pythagorean_proof.py
Normal file
523
scripts/pythagorean_proof.py
Normal 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)
|
Reference in New Issue
Block a user