mirror of
https://github.com/3b1b/manim.git
synced 2025-07-29 21:12:35 +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)
|
||||
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
|
||||
|
@ -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")
|
||||
|
111
displayer.py
111
displayer.py
@ -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"):
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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 = []
|
||||
|
@ -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):
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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,...)]
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
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):
|
||||
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)
|
||||
|
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