mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Finished Moser Project
This commit is contained in:
parent
1f93da1a06
commit
b5fbf7edea
8 changed files with 544 additions and 42 deletions
43
animation.py
43
animation.py
|
@ -263,20 +263,21 @@ class ScaleInPlace(Transform):
|
||||||
Transform.__init__(self, mobject, target, *args, **kwargs)
|
Transform.__init__(self, mobject, target, *args, **kwargs)
|
||||||
|
|
||||||
class ApplyMethod(Transform):
|
class ApplyMethod(Transform):
|
||||||
def __init__(self, method, mobject, *args, **kwargs):
|
def __init__(self, method, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Method is a method of Mobject
|
Method is a method of Mobject. *args is for the method,
|
||||||
|
**kwargs is for the transform itself.
|
||||||
|
|
||||||
|
Relies on the fact that mobject methods return the mobject
|
||||||
"""
|
"""
|
||||||
method_args = ()
|
if not inspect.ismethod(method) or \
|
||||||
if isinstance(method, tuple):
|
not isinstance(method.im_self, Mobject):
|
||||||
method, method_args = method[0], method[1:]
|
|
||||||
if not inspect.ismethod(method):
|
|
||||||
raise "Not a valid Mobject method"
|
raise "Not a valid Mobject method"
|
||||||
Transform.__init__(
|
Transform.__init__(
|
||||||
self,
|
self,
|
||||||
mobject,
|
method.im_self,
|
||||||
method(copy.deepcopy(mobject), *method_args),
|
copy.deepcopy(method)(*args),
|
||||||
*args, **kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
class ApplyFunction(Transform):
|
class ApplyFunction(Transform):
|
||||||
|
@ -382,8 +383,28 @@ class Flash(Animation):
|
||||||
alpha
|
alpha
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#Fuck this is cool!
|
||||||
|
class TransformAnimations(Transform):
|
||||||
|
def __init__(self, start_anim, end_anim,
|
||||||
|
alpha_func = squish_alpha_func(high_inflection_0_to_1),
|
||||||
|
**kwargs):
|
||||||
|
self.start_anim, self.end_anim = start_anim, end_anim
|
||||||
|
Transform.__init__(
|
||||||
|
self,
|
||||||
|
start_anim.mobject,
|
||||||
|
end_anim.mobject,
|
||||||
|
run_time = max(start_anim.run_time, end_anim.run_time),
|
||||||
|
alpha_func = alpha_func,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
#Rewire starting and ending mobjects
|
||||||
|
start_anim.mobject = self.starting_mobject
|
||||||
|
end_anim.mobject = self.ending_mobject
|
||||||
|
|
||||||
|
def update(self, alpha):
|
||||||
|
self.start_anim.update(alpha)
|
||||||
|
self.end_anim.update(alpha)
|
||||||
|
Transform.update(self, alpha)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,10 +36,12 @@ def paint_mobject(mobject, image_array = None):
|
||||||
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
|
||||||
|
|
||||||
#TODO, Let z add a depth componenet?
|
|
||||||
points = np.array(mobject.points[:, :2])
|
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)
|
rgbs = np.array(mobject.rgbs)
|
||||||
#Flips y-axis
|
#Flips y-axis
|
||||||
points[:,1] *= -1
|
points[:,1] *= -1
|
||||||
|
|
10
helpers.py
10
helpers.py
|
@ -82,6 +82,16 @@ def not_quite_there(t, proportion = 0.7):
|
||||||
def wiggle(t, wiggles = 2):
|
def wiggle(t, wiggles = 2):
|
||||||
return there_and_back(t) * np.sin(wiggles*np.pi*t)
|
return there_and_back(t) * np.sin(wiggles*np.pi*t)
|
||||||
|
|
||||||
|
def squish_alpha_func(func, a = 0.4, b = 0.6):
|
||||||
|
def result(t):
|
||||||
|
if t < a:
|
||||||
|
return 0
|
||||||
|
elif t > b:
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return func((t-a)/(b-a))
|
||||||
|
return result
|
||||||
|
|
||||||
### Functional Functions ###
|
### Functional Functions ###
|
||||||
|
|
||||||
def composition(func_list):
|
def composition(func_list):
|
||||||
|
|
|
@ -89,13 +89,13 @@ class VideoIcon(ImageMobject):
|
||||||
def text_mobject(text, size = r"\Large"):
|
def text_mobject(text, size = r"\Large"):
|
||||||
image = tex_to_image(text, size, TEMPLATE_TEXT_FILE)
|
image = tex_to_image(text, size, TEMPLATE_TEXT_FILE)
|
||||||
assert(not isinstance(image, list))
|
assert(not isinstance(image, list))
|
||||||
return ImageMobject(image)
|
return ImageMobject(image).center()
|
||||||
|
|
||||||
#Purely redundant function to make singulars and plurals sensible
|
#Purely redundant function to make singulars and plurals sensible
|
||||||
def tex_mobject(expression, size = r"\Large"):
|
def tex_mobject(expression, size = r"\Huge"):
|
||||||
return tex_mobjects(expression, size)
|
return tex_mobjects(expression, size)
|
||||||
|
|
||||||
def tex_mobjects(expression, size = r"\Large"):
|
def tex_mobjects(expression, size = r"\Huge"):
|
||||||
images = tex_to_image(expression, size)
|
images = tex_to_image(expression, size)
|
||||||
if isinstance(images, list):
|
if isinstance(images, list):
|
||||||
#TODO, is checking listiness really the best here?
|
#TODO, is checking listiness really the best here?
|
||||||
|
|
43
mobject.py
43
mobject.py
|
@ -376,6 +376,49 @@ class Cube(Mobject1D):
|
||||||
])
|
])
|
||||||
self.pose_at_angle()
|
self.pose_at_angle()
|
||||||
|
|
||||||
|
class Octohedron(Mobject1D):
|
||||||
|
DEFAULT_COLOR = "pink"
|
||||||
|
def generate_points(self):
|
||||||
|
x = np.array([1, 0, 0])
|
||||||
|
y = np.array([0, 1, 0])
|
||||||
|
z = np.array([0, 0, 1])
|
||||||
|
vertex_pairs = [(x+y, x-y), (x+y,-x+y), (-x-y,-x+y), (-x-y,x-y)]
|
||||||
|
vertex_pairs += [
|
||||||
|
(b[0]*x+b[1]*y, b[2]*np.sqrt(2)*z)
|
||||||
|
for b in it.product(*[(-1, 1)]*3)
|
||||||
|
]
|
||||||
|
for pair in vertex_pairs:
|
||||||
|
self.add_points(
|
||||||
|
Line(pair[0], pair[1], density = 1/self.epsilon).points
|
||||||
|
)
|
||||||
|
self.pose_at_angle()
|
||||||
|
|
||||||
|
class Dodecahedron(Mobject1D):
|
||||||
|
DEFAULT_COLOR = "limegreen"
|
||||||
|
def generate_points(self):
|
||||||
|
phi = (1 + np.sqrt(5)) / 2
|
||||||
|
x = np.array([1, 0, 0])
|
||||||
|
y = np.array([0, 1, 0])
|
||||||
|
z = np.array([0, 0, 1])
|
||||||
|
v1, v2 = (phi, 1/phi, 0), (phi, -1/phi, 0)
|
||||||
|
vertex_pairs = [
|
||||||
|
(v1, v2),
|
||||||
|
(x+y+z, v1),
|
||||||
|
(x+y-z, v1),
|
||||||
|
(x-y+z, v2),
|
||||||
|
(x-y-z, v2),
|
||||||
|
]
|
||||||
|
five_lines_points = CompoundMobject(*[
|
||||||
|
Line(pair[0], pair[1], density = 1.0/self.epsilon)
|
||||||
|
for pair in vertex_pairs
|
||||||
|
]).points
|
||||||
|
#Rotate those 5 edges into all 30.
|
||||||
|
for i in range(3):
|
||||||
|
perm = map(lambda j : j%3, range(i, i+3))
|
||||||
|
for b in [-1, 1]:
|
||||||
|
matrix = b*np.array([x[perm], y[perm], z[perm]])
|
||||||
|
self.add_points(np.dot(five_lines_points, matrix))
|
||||||
|
self.pose_at_angle()
|
||||||
|
|
||||||
class Sphere(Mobject2D):
|
class Sphere(Mobject2D):
|
||||||
def generate_points(self):
|
def generate_points(self):
|
||||||
|
|
82
moser/images.py
Normal file
82
moser/images.py
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import itertools as it
|
||||||
|
import operator as op
|
||||||
|
from copy import deepcopy
|
||||||
|
from random import random, randint
|
||||||
|
import sys
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
|
||||||
|
from animation import *
|
||||||
|
from mobject import *
|
||||||
|
from image_mobject import *
|
||||||
|
from constants import *
|
||||||
|
from region import *
|
||||||
|
from scene import Scene
|
||||||
|
from script_wrapper import command_line_create_scene
|
||||||
|
|
||||||
|
from moser_helpers import *
|
||||||
|
from graphs import *
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
prefix = "moser_images/"
|
||||||
|
# cs_outer = CircleScene(RADIANS[:6])
|
||||||
|
# cs_outer.highlight_region(
|
||||||
|
# Region(lambda x, y : x**2 + y**2 > RADIUS**2)
|
||||||
|
# )
|
||||||
|
|
||||||
|
# cs_graph = CircleScene(RADIANS)
|
||||||
|
# cs_graph.generate_intersection_dots()
|
||||||
|
# cs_graph.add(*cs_graph.intersection_dots)
|
||||||
|
# cs_graph.chop_lines_at_intersection_points()
|
||||||
|
# cs_graph.chop_circle_at_points()
|
||||||
|
# for line in cs_graph.lines:
|
||||||
|
# line.scale_in_place(0.5)
|
||||||
|
# for piece in cs_graph.smaller_circle_pieces:
|
||||||
|
# piece.highlight("yellow")
|
||||||
|
# cs_graph.remove(*cs_graph.circle_pieces)
|
||||||
|
# cs_graph.add(*cs_graph.smaller_circle_pieces)
|
||||||
|
|
||||||
|
savable_things = [
|
||||||
|
# (Mobject(), "Blackness")
|
||||||
|
# (tex_mobject(r"V-E+F=2"), "EulersFormula"),
|
||||||
|
# (PascalsTriangleScene(N_PASCAL_ROWS), "PascalsTriangle"),
|
||||||
|
# (tex_mobject(r"1, 2, 4, 8, 16, 31, \dots"), "FalsePattern"),
|
||||||
|
# (
|
||||||
|
# tex_mobject(r"""
|
||||||
|
# \underbrace{1, 2, 4, 16, 31, 57, 99, 163, 256, 386, \dots}_{
|
||||||
|
# \text{What is this pattern?}
|
||||||
|
# }
|
||||||
|
# """),
|
||||||
|
# "WhatIsThisPattern"
|
||||||
|
# ),
|
||||||
|
# (tex_mobject(r"n \choose k"), "NChooseK"),
|
||||||
|
# (GraphScene(SAMPLE_GRAPH), "SampleGraph"),
|
||||||
|
# (text_mobject("You don't even want me to draw this..."), "DontWantToDraw"),
|
||||||
|
# (tex_mobject(r"{100 \choose 2} = \frac{100 \cdot 99}{2} = 4950"), "100Choose2"),
|
||||||
|
# (text_mobject("What? You actually want me to draw it? Okay..."), "ReallyDontWant"),
|
||||||
|
# (text_mobject(r"There! You happy? \\ It's just one big blue blob."), "YouHappy"),
|
||||||
|
# (
|
||||||
|
# tex_mobject(
|
||||||
|
# r"{100 \choose 4} = \frac{(100)(99)(98)(97)}{(1)(2)(3)(4)} = 3,921,225"
|
||||||
|
# ),
|
||||||
|
# "100Choose4"
|
||||||
|
# ),
|
||||||
|
# (text_mobject("Euler's Characteristic Formula"), "EF_Words"),
|
||||||
|
# (cs_outer, "OuterRegion"),
|
||||||
|
# (text_mobject("Pause and see if you can remember on your own!"), "Recap")
|
||||||
|
# (CircleScene([2*np.pi*random() for x in range(100)]), "CircleScene100")
|
||||||
|
# (text_mobject(r"""
|
||||||
|
# \textbf{Eul$\cdot$er's} (\text{oil}\textschwa\text{rz}), \emph{adj}:
|
||||||
|
# \begin{enumerate}
|
||||||
|
# \item Beautiful
|
||||||
|
# \item Demonstrating an unexpected logical aesthetic, especially in the context of mathematics.
|
||||||
|
# \end{enumerate}
|
||||||
|
# """), "EulersDefinition"),
|
||||||
|
# (cs_graph, "SuitableGraph"),
|
||||||
|
]
|
||||||
|
for thing, name in savable_things:
|
||||||
|
thing.save_image(prefix + name)
|
||||||
|
|
383
moser/main.py
383
moser/main.py
|
@ -168,8 +168,6 @@ class HardProblemsSimplerQuestions(Scene):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CountLines(CircleScene):
|
class CountLines(CircleScene):
|
||||||
def __init__(self, radians, *args, **kwargs):
|
def __init__(self, radians, *args, **kwargs):
|
||||||
CircleScene.__init__(self, radians, *args, **kwargs)
|
CircleScene.__init__(self, radians, *args, **kwargs)
|
||||||
|
@ -281,6 +279,62 @@ class NonGeneralPosition(CircleScene):
|
||||||
for mob1, mob2 in zip(self.mobjects, new_self.mobjects)
|
for mob1, mob2 in zip(self.mobjects, new_self.mobjects)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
class GeneralPositionRule(Scene):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
Scene.__init__(self, *args, **kwargs)
|
||||||
|
tuples = [
|
||||||
|
(
|
||||||
|
np.arange(0, 2*np.pi, np.pi/3),
|
||||||
|
"Not okay",
|
||||||
|
zip(range(3), range(3, 6))
|
||||||
|
),
|
||||||
|
(
|
||||||
|
RADIANS,
|
||||||
|
"Okay",
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
np.arange(0, 2*np.pi, np.pi/4),
|
||||||
|
"Not okay",
|
||||||
|
zip(range(4), range(4, 8))
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[2*np.pi*random() for x in range(5)],
|
||||||
|
"Okay",
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
first_time = True
|
||||||
|
for radians, words, pairs in tuples:
|
||||||
|
cs = CircleScene(radians)
|
||||||
|
self.add(*cs.mobjects)
|
||||||
|
words_mob = text_mobject(words).scale(2).shift((5, 3, 0))
|
||||||
|
if not first_time:
|
||||||
|
self.add(words_mob)
|
||||||
|
if words == "Okay":
|
||||||
|
words_mob.highlight("green")
|
||||||
|
self.dither(2)
|
||||||
|
else:
|
||||||
|
words_mob.highlight()
|
||||||
|
intersecting_lines = [
|
||||||
|
line.scale_in_place(0.3).highlight()
|
||||||
|
for i, j in pairs
|
||||||
|
for line in [Line(cs.points[i], cs.points[j])]
|
||||||
|
]
|
||||||
|
self.animate(*[
|
||||||
|
ShowCreation(line, run_time = 1.0)
|
||||||
|
for line in intersecting_lines
|
||||||
|
])
|
||||||
|
if first_time:
|
||||||
|
self.animate(Transform(
|
||||||
|
CompoundMobject(*intersecting_lines),
|
||||||
|
words_mob
|
||||||
|
))
|
||||||
|
first_time = False
|
||||||
|
self.dither()
|
||||||
|
self.remove(*self.mobjects)
|
||||||
|
|
||||||
|
|
||||||
class LineCorrespondsWithPair(CircleScene):
|
class LineCorrespondsWithPair(CircleScene):
|
||||||
args_list = [
|
args_list = [
|
||||||
(RADIANS, 2, 5),
|
(RADIANS, 2, 5),
|
||||||
|
@ -303,7 +357,7 @@ class LineCorrespondsWithPair(CircleScene):
|
||||||
self.dots.remove(dot1)
|
self.dots.remove(dot1)
|
||||||
self.dither()
|
self.dither()
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
ApplyMethod((Mobject.fade, 0.2), mob)
|
ApplyMethod(mob.fade, 0.2)
|
||||||
for mob in self.lines + self.dots
|
for mob in self.lines + self.dots
|
||||||
])
|
])
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
|
@ -334,9 +388,9 @@ class IllustrateNChooseK(Scene):
|
||||||
[
|
[
|
||||||
(r'\\&' if c%(20//k) == 0 else r'\;\;') + str(p)
|
(r'\\&' if c%(20//k) == 0 else r'\;\;') + str(p)
|
||||||
for p, c in zip(tuples, it.count())
|
for p, c in zip(tuples, it.count())
|
||||||
],
|
],
|
||||||
size = r"\small"
|
size = r"\small",
|
||||||
)
|
)#TODO, scale these up!
|
||||||
tuple_terms = {
|
tuple_terms = {
|
||||||
2 : "pairs",
|
2 : "pairs",
|
||||||
3 : "triplets",
|
3 : "triplets",
|
||||||
|
@ -357,9 +411,9 @@ class IllustrateNChooseK(Scene):
|
||||||
"%d"%choose(n, k),
|
"%d"%choose(n, k),
|
||||||
r" \text{ total %s}"%tuple_term
|
r" \text{ total %s}"%tuple_term
|
||||||
])
|
])
|
||||||
# pronunciation = text_mobject(
|
pronunciation = text_mobject(
|
||||||
# "(pronounced ``%d choose %d\'\')"%(n, k)
|
"(pronounced ``%d choose %d\'\')"%(n, k)
|
||||||
# )
|
)
|
||||||
for mob in nrange_mobs:
|
for mob in nrange_mobs:
|
||||||
mob.shift((0, 2, 0))
|
mob.shift((0, 2, 0))
|
||||||
for mob in form1, count, form2:
|
for mob in form1, count, form2:
|
||||||
|
@ -367,7 +421,9 @@ class IllustrateNChooseK(Scene):
|
||||||
count_center = count.get_center()
|
count_center = count.get_center()
|
||||||
for mob in tuple_mobs:
|
for mob in tuple_mobs:
|
||||||
mob.scale(0.6)
|
mob.scale(0.6)
|
||||||
# pronunciation.shift(form1.get_center() + (-1, 1, 0))
|
pronunciation.shift(
|
||||||
|
form1.get_center() + (0, 1, 0)
|
||||||
|
)
|
||||||
|
|
||||||
self.add(*nrange_mobs)
|
self.add(*nrange_mobs)
|
||||||
self.dither()
|
self.dither()
|
||||||
|
@ -385,7 +441,7 @@ class IllustrateNChooseK(Scene):
|
||||||
self.remove(count_mob)
|
self.remove(count_mob)
|
||||||
self.remove(tuple_copy)
|
self.remove(tuple_copy)
|
||||||
self.add(count_mob)
|
self.add(count_mob)
|
||||||
self.animate(FadeIn(CompoundMobject(form1, form2)))
|
self.animate(FadeIn(CompoundMobject(form1, form2, pronunciation)))
|
||||||
|
|
||||||
class IntersectionPointCorrespondances(CircleScene):
|
class IntersectionPointCorrespondances(CircleScene):
|
||||||
args_list = [
|
args_list = [
|
||||||
|
@ -825,12 +881,18 @@ class HowIntersectionChopsLine(CircleScene):
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
class ApplyEulerToMoser(Scene):
|
class ApplyEulerToMoser(CircleScene):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
#Boy is this an ugly implementation..., maybe you should
|
radius = 2
|
||||||
#make a generic formula manipuating module
|
CircleScene.__init__(self, *args, radius = radius, **kwargs)
|
||||||
Scene.__init__(self, *args, **kwargs)
|
self.generate_intersection_dots()
|
||||||
|
self.chop_lines_at_intersection_points()
|
||||||
|
self.chop_circle_at_points()
|
||||||
|
self.generate_regions()
|
||||||
|
for dot in self.dots + self.intersection_dots:
|
||||||
|
dot.scale_in_place(radius / RADIUS)
|
||||||
|
self.remove(*self.mobjects)
|
||||||
|
|
||||||
V = {}
|
V = {}
|
||||||
minus = {}
|
minus = {}
|
||||||
minus1 = {}
|
minus1 = {}
|
||||||
|
@ -864,10 +926,13 @@ class ApplyEulerToMoser(Scene):
|
||||||
tex_mobjects(["F", "=", r"{n \choose 2}", "+", r"{n \choose 4}", "+", "2"])
|
tex_mobjects(["F", "=", r"{n \choose 2}", "+", r"{n \choose 4}", "+", "2"])
|
||||||
F[7], equals[7], two[7], plus[7], nc2[7], plus1[7], nc4[7] = \
|
F[7], equals[7], two[7], plus[7], nc2[7], plus1[7], nc4[7] = \
|
||||||
tex_mobjects(["F", "=", "2", "+", r"{n \choose 2}", "+", r"{n \choose 4}"])
|
tex_mobjects(["F", "=", "2", "+", r"{n \choose 2}", "+", r"{n \choose 4}"])
|
||||||
|
shift_val = (0, 3, 0)
|
||||||
for d in dicts:
|
for d in dicts:
|
||||||
if not d:
|
if not d:
|
||||||
continue
|
continue
|
||||||
main_key = d.keys()[0]
|
main_key = d.keys()[0]
|
||||||
|
for key in d.keys():
|
||||||
|
d[key].shift(shift_val)
|
||||||
main_center = d[main_key].get_center()
|
main_center = d[main_key].get_center()
|
||||||
for key in d.keys():
|
for key in d.keys():
|
||||||
d[key] = deepcopy(d[main_key]).shift(
|
d[key] = deepcopy(d[main_key]).shift(
|
||||||
|
@ -879,9 +944,59 @@ class ApplyEulerToMoser(Scene):
|
||||||
for d in [V, minus, E, plus, F, equals, two]
|
for d in [V, minus, E, plus, F, equals, two]
|
||||||
])
|
])
|
||||||
self.dither()
|
self.dither()
|
||||||
self.remove(*self.mobjects)
|
F[1].highlight()
|
||||||
|
self.add(*self.lines + self.circle_pieces)
|
||||||
|
for region in self.regions:
|
||||||
|
self.highlight_region(region)
|
||||||
|
self.highlight_region(self.exterior, "blue")
|
||||||
|
self.dither()
|
||||||
|
self.reset_background()
|
||||||
|
F[1].highlight("white")
|
||||||
|
E[1].highlight()
|
||||||
|
self.remove(*self.lines + self.circle_pieces)
|
||||||
|
self.animate(*[
|
||||||
|
Transform(
|
||||||
|
deepcopy(line),
|
||||||
|
deepcopy(line).scale_in_place(0.5),
|
||||||
|
run_time = 2.0,
|
||||||
|
)
|
||||||
|
for line in self.lines
|
||||||
|
] + [
|
||||||
|
Transform(
|
||||||
|
deepcopy(cp), scp,
|
||||||
|
run_time = 2.0
|
||||||
|
)
|
||||||
|
for cp, scp in zip(self.circle_pieces, self.smaller_circle_pieces)
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
E[1].highlight("white")
|
||||||
|
V[1].highlight()
|
||||||
|
self.add(*self.dots + self.intersection_dots)
|
||||||
|
self.remove(*self.lines + self.circle_pieces)
|
||||||
|
self.animate(*[
|
||||||
|
Transform(
|
||||||
|
deepcopy(dot),
|
||||||
|
deepcopy(dot).scale_in_place(1.4).highlight("yellow")
|
||||||
|
)
|
||||||
|
for dot in self.dots + self.intersection_dots
|
||||||
|
] + [
|
||||||
|
Transform(
|
||||||
|
deepcopy(line),
|
||||||
|
deepcopy(line).fade(0.4)
|
||||||
|
)
|
||||||
|
for line in self.lines + self.circle_pieces
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
all_mobs = [mob for mob in self.mobjects]
|
||||||
|
self.remove(*all_mobs)
|
||||||
|
self.add(*[d[1] for d in [V, minus, E, plus, F, equals, two]])
|
||||||
|
V[1].highlight("white")
|
||||||
|
two[1].highlight()
|
||||||
|
self.dither()
|
||||||
|
self.add(*all_mobs)
|
||||||
|
self.remove(*[d[1] for d in [V, minus, E, plus, F, equals, two]])
|
||||||
self.animate(
|
self.animate(
|
||||||
Transform(V[2], CompoundMobject(n[3], minus1[3], nc4[3])),
|
Transform(V[2].repeat(2), CompoundMobject(n[3], minus1[3], nc4[3])),
|
||||||
*[
|
*[
|
||||||
Transform(d[2], d[3])
|
Transform(d[2], d[3])
|
||||||
for d in [F, equals, E, minus, plus, two]
|
for d in [F, equals, E, minus, plus, two]
|
||||||
|
@ -896,7 +1011,17 @@ class ApplyEulerToMoser(Scene):
|
||||||
*[
|
*[
|
||||||
Transform(d[3], d[4])
|
Transform(d[3], d[4])
|
||||||
for d in [F, equals, minus, n, minus1, nc4, plus, two]
|
for d in [F, equals, minus, n, minus1, nc4, plus, two]
|
||||||
]
|
] + [
|
||||||
|
Transform(
|
||||||
|
deepcopy(line),
|
||||||
|
deepcopy(line).scale_in_place(0.5),
|
||||||
|
)
|
||||||
|
for line in self.lines
|
||||||
|
] + [
|
||||||
|
Transform(deepcopy(cp), scp)
|
||||||
|
for cp, scp in zip(self.circle_pieces, self.smaller_circle_pieces)
|
||||||
|
],
|
||||||
|
run_time = 2.0
|
||||||
)
|
)
|
||||||
self.dither()
|
self.dither()
|
||||||
self.remove(*self.mobjects)
|
self.remove(*self.mobjects)
|
||||||
|
@ -932,12 +1057,18 @@ class ApplyEulerToMoser(Scene):
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
self.dither()
|
self.dither()
|
||||||
|
self.add(*self.lines + self.circle_pieces)
|
||||||
|
for region in self.regions:
|
||||||
|
self.highlight_region(region)
|
||||||
|
self.dither()
|
||||||
|
self.highlight_region(self.exterior, "blue")
|
||||||
|
self.dither()
|
||||||
|
self.highlight_region(self.exterior, "black")
|
||||||
self.remove(two[6])
|
self.remove(two[6])
|
||||||
two = two[7]
|
two = two[7]
|
||||||
one = tex_mobject("1").shift(two.get_center())
|
one = tex_mobject("1").shift(two.get_center())
|
||||||
two.highlight("red")
|
two.highlight("red")
|
||||||
self.add(two)
|
self.add(two)
|
||||||
self.dither()
|
|
||||||
self.animate(SemiCircleTransform(two, one))
|
self.animate(SemiCircleTransform(two, one))
|
||||||
|
|
||||||
class FormulaRelatesToPowersOfTwo(Scene):
|
class FormulaRelatesToPowersOfTwo(Scene):
|
||||||
|
@ -1063,7 +1194,7 @@ class PascalsTriangleNChooseKExample(PascalsTriangleScene):
|
||||||
[
|
[
|
||||||
ShowCreation(mob) for mob in triangle_terms
|
ShowCreation(mob) for mob in triangle_terms
|
||||||
]+[
|
]+[
|
||||||
ApplyMethod((Mobject.shift, formula_center), mob)
|
ApplyMethod(mob.shift, formula_center)
|
||||||
for mob in formula_terms
|
for mob in formula_terms
|
||||||
],
|
],
|
||||||
run_time = 1.0
|
run_time = 1.0
|
||||||
|
@ -1094,7 +1225,7 @@ class PascalsTriangleNChooseKExample(PascalsTriangleScene):
|
||||||
self.coords_to_mobs[n][b].highlight("green")
|
self.coords_to_mobs[n][b].highlight("green")
|
||||||
self.remove(b_mob)
|
self.remove(b_mob)
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
ApplyMethod((Mobject.fade, 0.2), mob)
|
ApplyMethod(mob.fade, 0.2)
|
||||||
for mob in triangle_terms
|
for mob in triangle_terms
|
||||||
if mob != self.coords_to_mobs[n][k]
|
if mob != self.coords_to_mobs[n][k]
|
||||||
])
|
])
|
||||||
|
@ -1244,6 +1375,214 @@ class MoserSolutionInPascal(PascalsTriangleScene):
|
||||||
terms_sum.highlight(term_color)
|
terms_sum.highlight(term_color)
|
||||||
self.animate(Transform(CompoundMobject(*terms), terms_sum))
|
self.animate(Transform(CompoundMobject(*terms), terms_sum))
|
||||||
|
|
||||||
|
class RotatingPolyhedra(Scene):
|
||||||
|
args_list = [
|
||||||
|
([Cube, Dodecahedron],)
|
||||||
|
]
|
||||||
|
@staticmethod
|
||||||
|
def args_to_string(class_list):
|
||||||
|
return "".join([c.__name__ for c in class_list])
|
||||||
|
|
||||||
|
def __init__(self, polyhedra_classes, *args, **kwargs):
|
||||||
|
Scene.__init__(self, *args, **kwargs)
|
||||||
|
rot_kwargs = {
|
||||||
|
"radians" : np.pi / 2,
|
||||||
|
"run_time" : 5.0,
|
||||||
|
"axis" : [0, 1, 0]
|
||||||
|
}
|
||||||
|
polyhedra = [
|
||||||
|
Class().scale(1.5).shift((1, 0, 0))
|
||||||
|
for Class in polyhedra_classes
|
||||||
|
]
|
||||||
|
curr_mob = polyhedra.pop()
|
||||||
|
for mob in polyhedra:
|
||||||
|
self.animate(TransformAnimations(
|
||||||
|
Rotating(curr_mob, **rot_kwargs),
|
||||||
|
Rotating(mob, **rot_kwargs)
|
||||||
|
))
|
||||||
|
for m in polyhedra:
|
||||||
|
m.rotate(rot_kwargs["radians"], rot_kwargs["axis"])
|
||||||
|
self.animate(Rotating(curr_mob, **rot_kwargs))
|
||||||
|
|
||||||
|
class ExplainNChoose2Formula(Scene):
|
||||||
|
args_list = [(7,2,6)]
|
||||||
|
@staticmethod
|
||||||
|
def args_to_string(n, a, b):
|
||||||
|
return "n=%d_a=%d_b=%d"%(n, a, b)
|
||||||
|
|
||||||
|
def __init__(self, n, a, b, *args, **kwargs):
|
||||||
|
Scene.__init__(self, *args, **kwargs)
|
||||||
|
r_paren, a_mob, comma, b_mob, l_paren = tex_mobjects(
|
||||||
|
("( %d , %d )"%(a, b)).split(" ")
|
||||||
|
)
|
||||||
|
parens = CompoundMobject(r_paren, comma, l_paren)
|
||||||
|
nums = [tex_mobject(str(k)) for k in range(1, n+1)]
|
||||||
|
height = 1.5*nums[0].get_height()
|
||||||
|
for x in range(n):
|
||||||
|
nums[x].shift((0, x*height, 0))
|
||||||
|
nums_compound = CompoundMobject(*nums)
|
||||||
|
nums_compound.shift(a_mob.get_center() - nums[0].get_center())
|
||||||
|
n_mob, n_minus_1, over_2 = tex_mobjects([
|
||||||
|
str(n), "(%d-1)"%n, r"\over{2}"
|
||||||
|
])
|
||||||
|
for part in n_mob, n_minus_1, over_2:
|
||||||
|
part.shift((SPACE_WIDTH-1.5, SPACE_HEIGHT-1, 0))
|
||||||
|
|
||||||
|
self.add(parens, n_mob)
|
||||||
|
up_unit = np.array((0, height, 0))
|
||||||
|
self.animate(ApplyMethod(nums_compound.shift, -(n-1)*up_unit))
|
||||||
|
self.animate(ApplyMethod(nums_compound.shift, (n-a)*up_unit))
|
||||||
|
self.remove(nums_compound)
|
||||||
|
nums = nums_compound.split()
|
||||||
|
a_mob = nums.pop(a-1)
|
||||||
|
nums_compound = CompoundMobject(*nums)
|
||||||
|
self.add(a_mob, nums_compound)
|
||||||
|
self.dither()
|
||||||
|
right_shift = b_mob.get_center() - a_mob.get_center()
|
||||||
|
right_shift[1] = 0
|
||||||
|
self.animate(
|
||||||
|
ApplyMethod(nums_compound.shift, right_shift),
|
||||||
|
FadeIn(n_minus_1)
|
||||||
|
)
|
||||||
|
self.animate(ApplyMethod(nums_compound.shift, (a-b)*up_unit))
|
||||||
|
self.remove(nums_compound)
|
||||||
|
nums = nums_compound.split()
|
||||||
|
b_mob = nums.pop(b-2 if a < b else b-1)
|
||||||
|
self.add(b_mob)
|
||||||
|
self.animate(*[
|
||||||
|
SemiCircleTransform(
|
||||||
|
mob,
|
||||||
|
Point(mob.get_center()).highlight("black")
|
||||||
|
)
|
||||||
|
for mob in nums
|
||||||
|
])
|
||||||
|
self.animate(*[
|
||||||
|
ApplyMethod(mob.shift, (0, 1, 0))
|
||||||
|
for mob in parens, a_mob, b_mob
|
||||||
|
])
|
||||||
|
parens_copy = deepcopy(parens).shift((0, -2, 0))
|
||||||
|
a_center = a_mob.get_center()
|
||||||
|
b_center = b_mob.get_center()
|
||||||
|
a_copy = deepcopy(a_mob).center().shift(b_center - (0, 2, 0))
|
||||||
|
b_copy = deepcopy(b_mob).center().shift(a_center - (0, 2, 0))
|
||||||
|
self.add(over_2, deepcopy(a_mob), deepcopy(b_mob))
|
||||||
|
self.animate(
|
||||||
|
SemiCircleTransform(a_mob, a_copy),
|
||||||
|
SemiCircleTransform(b_mob, b_copy),
|
||||||
|
FadeIn(parens_copy),
|
||||||
|
FadeIn(text_mobject("is considered the same as"))
|
||||||
|
)
|
||||||
|
|
||||||
|
class ExplainNChoose4Formula(Scene):
|
||||||
|
args_list = [(7,)]
|
||||||
|
@staticmethod
|
||||||
|
def args_to_string(n):
|
||||||
|
return "n=%d"%n
|
||||||
|
|
||||||
|
def __init__(self, n, *args, **kwargs):
|
||||||
|
Scene.__init__(self, *args, **kwargs)
|
||||||
|
# quad = list(it.combinations(range(1,n+1), 4))[randint(0, choose(n, 4)-1)]
|
||||||
|
quad = (4, 2, 5, 1)
|
||||||
|
tuple_mobs = tex_mobjects(
|
||||||
|
("( %d , %d , %d , %d )"%quad).split(" ")
|
||||||
|
)
|
||||||
|
quad_mobs = tuple_mobs[1::2]
|
||||||
|
parens = CompoundMobject(*tuple_mobs[0::2])
|
||||||
|
form_mobs = tex_mobjects([
|
||||||
|
str(n), "(%d-1)"%n, "(%d-2)"%n,"(%d-3)"%n,
|
||||||
|
r"\over {4 \cdot 3 \cdot 2 \cdot 1}"
|
||||||
|
])
|
||||||
|
form_mobs = CompoundMobject(*form_mobs).scale(0.7).shift((4, 3, 0)).split()
|
||||||
|
nums = [tex_mobject(str(k)) for k in range(1, n+1)]
|
||||||
|
height = 1.5*nums[0].get_height()
|
||||||
|
for x in range(n):
|
||||||
|
nums[x].shift((0, x*height, 0))
|
||||||
|
nums_compound = CompoundMobject(*nums)
|
||||||
|
nums_compound.shift(quad_mobs[0].get_center() - nums[0].get_center())
|
||||||
|
curr_num = 1
|
||||||
|
self.add(parens)
|
||||||
|
up_unit = np.array((0, height, 0))
|
||||||
|
for i in range(4):
|
||||||
|
self.add(form_mobs[i])
|
||||||
|
self.animate(ApplyMethod(
|
||||||
|
nums_compound.shift, (curr_num-quad[i])*up_unit))
|
||||||
|
self.remove(nums_compound)
|
||||||
|
nums = nums_compound.split()
|
||||||
|
chosen = nums[quad[i]-1]
|
||||||
|
nums[quad[i]-1] = Point(chosen.get_center()).highlight("black")
|
||||||
|
nums_compound = CompoundMobject(*nums)
|
||||||
|
self.add(chosen)
|
||||||
|
if i < 3:
|
||||||
|
right_shift = quad_mobs[i+1].get_center() - chosen.get_center()
|
||||||
|
right_shift[1] = 0
|
||||||
|
self.animate(
|
||||||
|
ApplyMethod(nums_compound.shift, right_shift)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.animate(*[
|
||||||
|
SemiCircleTransform(
|
||||||
|
mob,
|
||||||
|
Point(mob.get_center()).highlight("black")
|
||||||
|
)
|
||||||
|
for mob in nums
|
||||||
|
])
|
||||||
|
curr_num = quad[i]
|
||||||
|
self.remove(*self.mobjects)
|
||||||
|
num_perms_explain = text_mobject(
|
||||||
|
r"There are $(4 \cdot 3 \cdot 2 \cdot 1)$ total permutations"
|
||||||
|
).shift((0, -2, 0))
|
||||||
|
self.add(parens, num_perms_explain, *form_mobs)
|
||||||
|
perms = list(it.permutations(range(4)))
|
||||||
|
for count in range(6):
|
||||||
|
perm = perms[randint(0, 23)]
|
||||||
|
new_quad_mobs = [
|
||||||
|
deepcopy(quad_mobs[i]).shift(
|
||||||
|
quad_mobs[perm[i]].get_center() - \
|
||||||
|
quad_mobs[i].get_center()
|
||||||
|
)
|
||||||
|
for i in range(4)
|
||||||
|
]
|
||||||
|
compound_quad = CompoundMobject(*quad_mobs)
|
||||||
|
self.animate(SemiCircleTransform(
|
||||||
|
compound_quad,
|
||||||
|
CompoundMobject(*new_quad_mobs)
|
||||||
|
))
|
||||||
|
self.remove(compound_quad)
|
||||||
|
quad_mobs = new_quad_mobs
|
||||||
|
|
||||||
|
class IntersectionChoppingExamples(Scene):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
Scene.__init__(self, *args, **kwargs)
|
||||||
|
pairs1 = [
|
||||||
|
((-1,-1, 0), (-1, 0, 0)),
|
||||||
|
((-1, 0, 0), (-1, 1, 0)),
|
||||||
|
((-2, 0, 0), (-1, 0, 0)),
|
||||||
|
((-1, 0, 0), ( 1, 0, 0)),
|
||||||
|
(( 1, 0, 0), ( 2, 0, 0)),
|
||||||
|
(( 1,-1, 0), ( 1, 0, 0)),
|
||||||
|
(( 1, 0, 0), ( 1, 1, 0)),
|
||||||
|
]
|
||||||
|
pairs2 = pairs1 + [
|
||||||
|
(( 1, 1, 0), ( 1, 2, 0)),
|
||||||
|
(( 0, 1, 0), ( 1, 1, 0)),
|
||||||
|
(( 1, 1, 0), ( 2, 1, 0)),
|
||||||
|
]
|
||||||
|
for pairs, exp in [(pairs1, "3 + 2(2) = 7"),
|
||||||
|
(pairs2, "4 + 2(3) = 10")]:
|
||||||
|
lines = [Line(*pair).scale(2) for pair in pairs]
|
||||||
|
self.add(tex_mobject(exp).shift((0, SPACE_HEIGHT-1, 0)))
|
||||||
|
self.add(*lines)
|
||||||
|
self.dither()
|
||||||
|
self.animate(*[
|
||||||
|
Transform(line, deepcopy(line).scale(1.2).scale_in_place(1/1.2))
|
||||||
|
for line in lines
|
||||||
|
])
|
||||||
|
self.count(lines, run_time = 3.0, num_offset = ORIGIN)
|
||||||
|
self.dither()
|
||||||
|
self.remove(*self.mobjects)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##################################################
|
##################################################
|
||||||
|
|
|
@ -28,9 +28,9 @@ class CircleScene(Scene):
|
||||||
def args_to_string(*args):
|
def args_to_string(*args):
|
||||||
return str(len(args[0])) #Length of radians
|
return str(len(args[0])) #Length of radians
|
||||||
|
|
||||||
def __init__(self, radians, *args, **kwargs):
|
def __init__(self, radians, radius = RADIUS, *args, **kwargs):
|
||||||
Scene.__init__(self, *args, **kwargs)
|
Scene.__init__(self, *args, **kwargs)
|
||||||
self.radius = RADIUS
|
self.radius = radius
|
||||||
self.circle = Circle(density = CIRCLE_DENSITY).scale(self.radius)
|
self.circle = Circle(density = CIRCLE_DENSITY).scale(self.radius)
|
||||||
self.points = [
|
self.points = [
|
||||||
(self.radius * np.cos(angle), self.radius * np.sin(angle), 0)
|
(self.radius * np.cos(angle), self.radius * np.sin(angle), 0)
|
||||||
|
@ -39,8 +39,7 @@ class CircleScene(Scene):
|
||||||
self.dots = [Dot(point) for point in self.points]
|
self.dots = [Dot(point) for point in self.points]
|
||||||
self.lines = [Line(p1, p2) for p1, p2 in it.combinations(self.points, 2)]
|
self.lines = [Line(p1, p2) for p1, p2 in it.combinations(self.points, 2)]
|
||||||
self.n_equals = tex_mobject(
|
self.n_equals = tex_mobject(
|
||||||
"n=%d"%len(radians),
|
"n=%d"%len(radians),
|
||||||
size = r"\small"
|
|
||||||
).shift((-SPACE_WIDTH+1, SPACE_HEIGHT-1.5, 0))
|
).shift((-SPACE_WIDTH+1, SPACE_HEIGHT-1.5, 0))
|
||||||
self.add(self.circle, self.n_equals, *self.dots + self.lines)
|
self.add(self.circle, self.n_equals, *self.dots + self.lines)
|
||||||
|
|
||||||
|
@ -110,6 +109,12 @@ class CircleScene(Scene):
|
||||||
self.smaller_circle_pieces.append(smaller_circle)
|
self.smaller_circle_pieces.append(smaller_circle)
|
||||||
self.add(*self.circle_pieces)
|
self.add(*self.circle_pieces)
|
||||||
|
|
||||||
|
def generate_regions(self):
|
||||||
|
self.regions = plane_partition_from_points(*self.points)
|
||||||
|
interior = Region(lambda x, y : x**2 + y**2 < self.radius**2)
|
||||||
|
map(lambda r : r.intersect(interior), self.regions)
|
||||||
|
self.exterior = interior.complement()
|
||||||
|
|
||||||
class GraphScene(Scene):
|
class GraphScene(Scene):
|
||||||
args_list = [
|
args_list = [
|
||||||
(CUBE_GRAPH,),
|
(CUBE_GRAPH,),
|
||||||
|
|
Loading…
Add table
Reference in a new issue