mirror of
https://github.com/3b1b/manim.git
synced 2025-08-01 17:29:06 +08:00
Disection of brachistochrone project
This commit is contained in:
@ -1,33 +1,26 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import itertools as it
|
import itertools as it
|
||||||
import operator as op
|
|
||||||
import sys
|
|
||||||
import inspect
|
|
||||||
from PIL import Image
|
|
||||||
import cv2
|
|
||||||
import random
|
|
||||||
from scipy.spatial.distance import cdist
|
|
||||||
from scipy import ndimage
|
|
||||||
|
|
||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
from mobject.tex_mobject import TexMobject, TextMobject
|
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
||||||
from mobject import Mobject
|
from mobject import Mobject
|
||||||
from mobject.image_mobject import \
|
from mobject.image_mobject import \
|
||||||
MobjectFromRegion, ImageMobject, MobjectFromPixelArray
|
MobjectFromRegion, ImageMobject, MobjectFromPixelArray
|
||||||
|
from topics.three_dimensions import Stars
|
||||||
|
|
||||||
|
from animation import Animation
|
||||||
from animation.transform import \
|
from animation.transform import \
|
||||||
Transform, CounterclockwiseTransform, ApplyPointwiseFunction,\
|
Transform, CounterclockwiseTransform, ApplyPointwiseFunction,\
|
||||||
FadeIn, FadeOut, GrowFromCenter, ApplyFunction, ApplyMethod, \
|
FadeIn, FadeOut, GrowFromCenter, ApplyFunction, ApplyMethod, \
|
||||||
ShimmerIn
|
ShimmerIn
|
||||||
from animation.simple_animations import \
|
from animation.simple_animations import \
|
||||||
ShowCreation, Homotopy, PhaseFlow, ApplyToCenters, DelayByOrder
|
ShowCreation, Homotopy, PhaseFlow, ApplyToCenters, DelayByOrder, \
|
||||||
|
ShowPassingFlash
|
||||||
from animation.playground import TurnInsideOut, Vibrate
|
from animation.playground import TurnInsideOut, Vibrate
|
||||||
from topics.geometry import \
|
from topics.geometry import \
|
||||||
Line, Circle, Square, Grid, Rectangle, Arrow, Dot, Point, \
|
Line, Circle, Square, Grid, Rectangle, Arrow, Dot, Point, \
|
||||||
Arc
|
Arc, FilledRectangle
|
||||||
from topics.characters import Randolph, Mathematician
|
from topics.characters import Randolph, Mathematician
|
||||||
from topics.functions import ParametricFunction, FunctionGraph
|
from topics.functions import ParametricFunction, FunctionGraph
|
||||||
from topics.number_line import NumberPlane
|
from topics.number_line import NumberPlane
|
||||||
@ -36,28 +29,7 @@ from scene import Scene
|
|||||||
|
|
||||||
RANDY_SCALE_VAL = 0.3
|
RANDY_SCALE_VAL = 0.3
|
||||||
|
|
||||||
###########
|
|
||||||
|
|
||||||
def wavify(mobject):
|
|
||||||
tangent_vectors = mobject.points[1:]-mobject.points[:-1]
|
|
||||||
lengths = np.apply_along_axis(
|
|
||||||
np.linalg.norm, 1, tangent_vectors
|
|
||||||
)
|
|
||||||
thick_lengths = lengths.repeat(3).reshape((len(lengths), 3))
|
|
||||||
unit_tangent_vectors = tangent_vectors/thick_lengths
|
|
||||||
rot_matrix = np.transpose(rotation_matrix(np.pi/2, OUT))
|
|
||||||
normal_vectors = np.dot(unit_tangent_vectors, rot_matrix)
|
|
||||||
# total_length = np.sum(lengths)
|
|
||||||
times = np.cumsum(lengths)
|
|
||||||
nudge_sizes = 0.1*np.sin(2*np.pi*times)
|
|
||||||
thick_nudge_sizes = nudge_sizes.repeat(3).reshape((len(nudge_sizes), 3))
|
|
||||||
nudges = thick_nudge_sizes*normal_vectors
|
|
||||||
result = mobject.copy()
|
|
||||||
result.points[1:] += nudges
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
###########
|
|
||||||
|
|
||||||
class Cycloid(ParametricFunction):
|
class Cycloid(ParametricFunction):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
@ -98,6 +70,84 @@ class LoopTheLoop(ParametricFunction):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SlideWordDownCycloid(Animation):
|
||||||
|
DEFAULT_CONFIG = {
|
||||||
|
"rate_func" : None,
|
||||||
|
"run_time" : 8
|
||||||
|
}
|
||||||
|
def __init__(self, word, **kwargs):
|
||||||
|
self.path = Cycloid(end_theta = np.pi)
|
||||||
|
word_mob = TextMobject(list(word))
|
||||||
|
end_word = word_mob.copy()
|
||||||
|
end_word.shift(-end_word.get_bottom())
|
||||||
|
end_word.shift(self.path.get_corner(DOWN+RIGHT))
|
||||||
|
end_word.shift(3*RIGHT)
|
||||||
|
self.end_letters = end_word.split()
|
||||||
|
for letter in word_mob.split():
|
||||||
|
letter.center()
|
||||||
|
letter.angle = 0
|
||||||
|
unit_interval = np.arange(0, 1, 1./len(word))
|
||||||
|
self.start_times = 0.5*(1-(unit_interval))
|
||||||
|
Animation.__init__(self, word_mob, **kwargs)
|
||||||
|
|
||||||
|
def update_mobject(self, alpha):
|
||||||
|
virtual_times = 2*(alpha - self.start_times)
|
||||||
|
cut_offs = [
|
||||||
|
0.1,
|
||||||
|
0.3,
|
||||||
|
0.7,
|
||||||
|
]
|
||||||
|
for letter, time, end_letter in zip(
|
||||||
|
self.mobject.split(), virtual_times, self.end_letters
|
||||||
|
):
|
||||||
|
time = max(time, 0)
|
||||||
|
time = min(time, 1)
|
||||||
|
if time < cut_offs[0]:
|
||||||
|
brightness = time/cut_offs[0]
|
||||||
|
letter.rgbs = brightness*np.ones(letter.rgbs.shape)
|
||||||
|
position = self.path.points[0]
|
||||||
|
angle = 0
|
||||||
|
elif time < cut_offs[1]:
|
||||||
|
alpha = (time-cut_offs[0])/(cut_offs[1]-cut_offs[0])
|
||||||
|
angle = -rush_into(alpha)*np.pi/2
|
||||||
|
position = self.path.points[0]
|
||||||
|
elif time < cut_offs[2]:
|
||||||
|
alpha = (time-cut_offs[1])/(cut_offs[2]-cut_offs[1])
|
||||||
|
index = int(alpha*self.path.get_num_points())
|
||||||
|
position = self.path.points[index]
|
||||||
|
try:
|
||||||
|
angle = angle_of_vector(
|
||||||
|
self.path.points[index+1] - \
|
||||||
|
self.path.points[index]
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
angle = letter.angle
|
||||||
|
else:
|
||||||
|
alpha = (time-cut_offs[2])/(1-cut_offs[2])
|
||||||
|
start = self.path.points[-1]
|
||||||
|
end = end_letter.get_bottom()
|
||||||
|
position = interpolate(start, end, rush_from(alpha))
|
||||||
|
angle = 0
|
||||||
|
|
||||||
|
letter.shift(position-letter.get_bottom())
|
||||||
|
letter.rotate_in_place(angle-letter.angle)
|
||||||
|
letter.angle = angle
|
||||||
|
|
||||||
|
|
||||||
|
class BrachistochroneWordSliding(Scene):
|
||||||
|
def construct(self):
|
||||||
|
anim = SlideWordDownCycloid("Brachistochrone")
|
||||||
|
anim.path.gradient_highlight(WHITE, BLUE_E)
|
||||||
|
self.play(ShowCreation(anim.path))
|
||||||
|
self.play(anim)
|
||||||
|
self.dither()
|
||||||
|
self.play(
|
||||||
|
FadeOut(anim.path),
|
||||||
|
ApplyMethod(anim.mobject.center)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PathSlidingScene(Scene):
|
class PathSlidingScene(Scene):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
"gravity" : 3,
|
"gravity" : 3,
|
||||||
@ -273,22 +323,38 @@ class RollingRandolph(PathSlidingScene):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SimplePhoton(Scene):
|
class OceanScene(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
photon = wavify(Cycloid())
|
self.rolling_waves()
|
||||||
photon.highlight(YELLOW)
|
|
||||||
shaddow = photon.copy().highlight(BLACK)
|
|
||||||
|
|
||||||
self.play(
|
def rolling_waves(self):
|
||||||
ShowCreation(photon, rate_func = None),
|
if not hasattr(self, "ocean"):
|
||||||
ShowCreation(
|
self.setup_ocean()
|
||||||
shaddow,
|
for state in self.ocean_states:
|
||||||
rate_func = lambda t : max(0, t-0.1)
|
self.play(Transform(self.ocean, state))
|
||||||
|
|
||||||
|
|
||||||
|
def setup_ocean(self):
|
||||||
|
def func(points):
|
||||||
|
result = np.zeros(points.shape)
|
||||||
|
result[:,1] = 0.25 * np.sin(points[:,0]) * np.sin(points[:,1])
|
||||||
|
return result
|
||||||
|
|
||||||
|
self.ocean_states = []
|
||||||
|
for unit in -1, 1:
|
||||||
|
ocean = FilledRectangle(
|
||||||
|
color = BLUE_D,
|
||||||
|
density = 25
|
||||||
)
|
)
|
||||||
)
|
nudges = unit*func(ocean.points)
|
||||||
self.dither()
|
ocean.points += nudges
|
||||||
|
alphas = nudges[:,1]
|
||||||
|
alphas -= np.min(alphas)
|
||||||
|
whites = np.ones(ocean.rgbs.shape)
|
||||||
|
thick_alphas = alphas.repeat(3).reshape((len(alphas), 3))
|
||||||
|
ocean.rgbs = interpolate(ocean.rgbs, whites, thick_alphas)
|
||||||
|
self.ocean_states.append(ocean)
|
||||||
|
self.ocean = self.ocean_states[1].copy()
|
||||||
|
|
||||||
|
|
||||||
|
|
65
brachistochrone/light.py
Normal file
65
brachistochrone/light.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import numpy as np
|
||||||
|
import itertools as it
|
||||||
|
|
||||||
|
from helpers import *
|
||||||
|
|
||||||
|
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
||||||
|
from mobject import Mobject
|
||||||
|
from mobject.image_mobject import \
|
||||||
|
MobjectFromRegion, ImageMobject, MobjectFromPixelArray
|
||||||
|
from topics.three_dimensions import Stars
|
||||||
|
|
||||||
|
from animation import Animation
|
||||||
|
from animation.transform import \
|
||||||
|
Transform, CounterclockwiseTransform, ApplyPointwiseFunction,\
|
||||||
|
FadeIn, FadeOut, GrowFromCenter, ApplyFunction, ApplyMethod, \
|
||||||
|
ShimmerIn
|
||||||
|
from animation.simple_animations import \
|
||||||
|
ShowCreation, Homotopy, PhaseFlow, ApplyToCenters, DelayByOrder, \
|
||||||
|
ShowPassingFlash
|
||||||
|
from animation.playground import TurnInsideOut, Vibrate
|
||||||
|
from topics.geometry import \
|
||||||
|
Line, Circle, Square, Grid, Rectangle, Arrow, Dot, Point, \
|
||||||
|
Arc, FilledRectangle
|
||||||
|
from topics.characters import Randolph, Mathematician
|
||||||
|
from topics.functions import ParametricFunction, FunctionGraph
|
||||||
|
from topics.number_line import NumberPlane
|
||||||
|
from region import Region, region_from_polygon_vertices
|
||||||
|
from scene import Scene
|
||||||
|
|
||||||
|
class PhotonScene(Scene):
|
||||||
|
def wavify(self, mobject):
|
||||||
|
tangent_vectors = mobject.points[1:]-mobject.points[:-1]
|
||||||
|
lengths = np.apply_along_axis(
|
||||||
|
np.linalg.norm, 1, tangent_vectors
|
||||||
|
)
|
||||||
|
thick_lengths = lengths.repeat(3).reshape((len(lengths), 3))
|
||||||
|
unit_tangent_vectors = tangent_vectors/thick_lengths
|
||||||
|
rot_matrix = np.transpose(rotation_matrix(np.pi/2, OUT))
|
||||||
|
normal_vectors = np.dot(unit_tangent_vectors, rot_matrix)
|
||||||
|
# total_length = np.sum(lengths)
|
||||||
|
times = np.cumsum(lengths)
|
||||||
|
nudge_sizes = 0.1*np.sin(2*np.pi*times)
|
||||||
|
thick_nudge_sizes = nudge_sizes.repeat(3).reshape((len(nudge_sizes), 3))
|
||||||
|
nudges = thick_nudge_sizes*normal_vectors
|
||||||
|
result = mobject.copy()
|
||||||
|
result.points[1:] += nudges
|
||||||
|
return result
|
||||||
|
|
||||||
|
def photon_along_path(self, path, color = YELLOW, run_time = 1):
|
||||||
|
photon = self.wavify(path)
|
||||||
|
photon.highlight(color)
|
||||||
|
self.play(ShowPassingFlash(
|
||||||
|
photon,
|
||||||
|
run_time = run_time,
|
||||||
|
rate_func = None
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
class SimplePhoton(PhotonScene):
|
||||||
|
def construct(self):
|
||||||
|
text = TextMobject("Light")
|
||||||
|
text.to_edge(UP)
|
||||||
|
self.play(ShimmerIn(text))
|
||||||
|
self.photon_along_path(Cycloid())
|
||||||
|
self.dither()
|
62
brachistochrone/metaphors.py
Normal file
62
brachistochrone/metaphors.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import numpy as np
|
||||||
|
import itertools as it
|
||||||
|
|
||||||
|
from helpers import *
|
||||||
|
|
||||||
|
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
||||||
|
from mobject import Mobject
|
||||||
|
from mobject.image_mobject import \
|
||||||
|
MobjectFromRegion, ImageMobject, MobjectFromPixelArray
|
||||||
|
from topics.three_dimensions import Stars
|
||||||
|
|
||||||
|
from animation import Animation
|
||||||
|
from animation.transform import \
|
||||||
|
Transform, CounterclockwiseTransform, ApplyPointwiseFunction,\
|
||||||
|
FadeIn, FadeOut, GrowFromCenter, ApplyFunction, ApplyMethod, \
|
||||||
|
ShimmerIn
|
||||||
|
from animation.simple_animations import \
|
||||||
|
ShowCreation, Homotopy, PhaseFlow, ApplyToCenters, DelayByOrder, \
|
||||||
|
ShowPassingFlash
|
||||||
|
from animation.playground import TurnInsideOut, Vibrate
|
||||||
|
from topics.geometry import \
|
||||||
|
Line, Circle, Square, Grid, Rectangle, Arrow, Dot, Point, \
|
||||||
|
Arc, FilledRectangle
|
||||||
|
from topics.characters import Randolph, Mathematician
|
||||||
|
from topics.functions import ParametricFunction, FunctionGraph
|
||||||
|
from topics.number_line import NumberPlane
|
||||||
|
from region import Region, region_from_polygon_vertices
|
||||||
|
from scene import Scene
|
||||||
|
|
||||||
|
|
||||||
|
class OceanScene(Scene):
|
||||||
|
def construct(self):
|
||||||
|
self.rolling_waves()
|
||||||
|
|
||||||
|
def rolling_waves(self):
|
||||||
|
if not hasattr(self, "ocean"):
|
||||||
|
self.setup_ocean()
|
||||||
|
for state in self.ocean_states:
|
||||||
|
self.play(Transform(self.ocean, state))
|
||||||
|
|
||||||
|
|
||||||
|
def setup_ocean(self):
|
||||||
|
def func(points):
|
||||||
|
result = np.zeros(points.shape)
|
||||||
|
result[:,1] = 0.25 * np.sin(points[:,0]) * np.sin(points[:,1])
|
||||||
|
return result
|
||||||
|
|
||||||
|
self.ocean_states = []
|
||||||
|
for unit in -1, 1:
|
||||||
|
ocean = FilledRectangle(
|
||||||
|
color = BLUE_D,
|
||||||
|
density = 25
|
||||||
|
)
|
||||||
|
nudges = unit*func(ocean.points)
|
||||||
|
ocean.points += nudges
|
||||||
|
alphas = nudges[:,1]
|
||||||
|
alphas -= np.min(alphas)
|
||||||
|
whites = np.ones(ocean.rgbs.shape)
|
||||||
|
thick_alphas = alphas.repeat(3).reshape((len(alphas), 3))
|
||||||
|
ocean.rgbs = interpolate(ocean.rgbs, whites, thick_alphas)
|
||||||
|
self.ocean_states.append(ocean)
|
||||||
|
self.ocean = self.ocean_states[1].copy()
|
102
brachistochrone/wordplay.py
Normal file
102
brachistochrone/wordplay.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import numpy as np
|
||||||
|
import itertools as it
|
||||||
|
|
||||||
|
from helpers import *
|
||||||
|
|
||||||
|
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
||||||
|
from mobject import Mobject
|
||||||
|
from mobject.image_mobject import \
|
||||||
|
MobjectFromRegion, ImageMobject, MobjectFromPixelArray
|
||||||
|
from topics.three_dimensions import Stars
|
||||||
|
|
||||||
|
from animation import Animation
|
||||||
|
from animation.transform import \
|
||||||
|
Transform, CounterclockwiseTransform, ApplyPointwiseFunction,\
|
||||||
|
FadeIn, FadeOut, GrowFromCenter, ApplyFunction, ApplyMethod, \
|
||||||
|
ShimmerIn
|
||||||
|
from animation.simple_animations import \
|
||||||
|
ShowCreation, Homotopy, PhaseFlow, ApplyToCenters, DelayByOrder, \
|
||||||
|
ShowPassingFlash
|
||||||
|
from animation.playground import TurnInsideOut, Vibrate
|
||||||
|
from topics.geometry import \
|
||||||
|
Line, Circle, Square, Grid, Rectangle, Arrow, Dot, Point, \
|
||||||
|
Arc, FilledRectangle
|
||||||
|
from topics.characters import Randolph, Mathematician
|
||||||
|
from topics.functions import ParametricFunction, FunctionGraph
|
||||||
|
from topics.number_line import NumberPlane
|
||||||
|
from region import Region, region_from_polygon_vertices
|
||||||
|
from scene import Scene
|
||||||
|
|
||||||
|
|
||||||
|
class DisectBrachistochroneWord(Scene):
|
||||||
|
def construct(self):
|
||||||
|
word = TextMobject(
|
||||||
|
["Bra", "chis", "to", "chrone"]
|
||||||
|
)
|
||||||
|
original_word = word.copy()
|
||||||
|
dots = []
|
||||||
|
for part in word.split():
|
||||||
|
if dots:
|
||||||
|
part.next_to(dots[-1], buff = 0.06)
|
||||||
|
dot = TexMobject("\\cdot")
|
||||||
|
dot.next_to(part, buff = 0.06)
|
||||||
|
dots.append(dot)
|
||||||
|
dots = Mobject(*dots[:-1])
|
||||||
|
dots.shift(0.1*DOWN)
|
||||||
|
Mobject(word, dots).center()
|
||||||
|
overbrace1 = Brace(Mobject(*word.split()[:-1]), UP)
|
||||||
|
overbrace2 = Brace(word.split()[-1], UP)
|
||||||
|
shortest = TextMobject("Shortest")
|
||||||
|
shortest.next_to(overbrace1, UP)
|
||||||
|
shortest.highlight(YELLOW)
|
||||||
|
time = TextMobject("Time")
|
||||||
|
time.next_to(overbrace2, UP)
|
||||||
|
time.highlight(YELLOW)
|
||||||
|
chrono_example = TextMobject("As in ``Chronological''")
|
||||||
|
chrono_example.scale(0.5)
|
||||||
|
chrono_example.to_edge(RIGHT)
|
||||||
|
chrono_example.shift(2*UP)
|
||||||
|
chrono_example.highlight(BLUE_D)
|
||||||
|
chrono_arrow = Arrow(word.split()[-1], chrono_example)
|
||||||
|
chrono_arrow.highlight(BLUE_D)
|
||||||
|
|
||||||
|
pronunciation = TextMobject(["/br", "e", "kist","e","kr$\\bar{o}$n/"])
|
||||||
|
pronunciation.split()[1].rotate_in_place(np.pi)
|
||||||
|
pronunciation.split()[3].rotate_in_place(np.pi)
|
||||||
|
pronunciation.scale(0.7)
|
||||||
|
pronunciation.shift(DOWN)
|
||||||
|
|
||||||
|
latin = TextMobject(list("Latin"))
|
||||||
|
greek = TextMobject(list("Greek"))
|
||||||
|
for mob in latin, greek:
|
||||||
|
mob.to_edge(LEFT)
|
||||||
|
question_mark = TextMobject("?").next_to(greek, buff = 0.1)
|
||||||
|
stars = Stars().highlight(BLACK)
|
||||||
|
stars.scale(0.5).shift(question_mark.get_center())
|
||||||
|
|
||||||
|
self.play(Transform(original_word, word), ShowCreation(dots))
|
||||||
|
self.play(ShimmerIn(pronunciation))
|
||||||
|
self.dither()
|
||||||
|
self.play(
|
||||||
|
GrowFromCenter(overbrace1),
|
||||||
|
GrowFromCenter(overbrace2)
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
self.play(ShimmerIn(latin))
|
||||||
|
self.play(FadeIn(question_mark))
|
||||||
|
self.play(Transform(
|
||||||
|
latin, greek,
|
||||||
|
path_func = counterclockwise_path()
|
||||||
|
))
|
||||||
|
self.dither()
|
||||||
|
self.play(Transform(question_mark, stars))
|
||||||
|
self.remove(stars)
|
||||||
|
self.dither()
|
||||||
|
self.play(ShimmerIn(shortest))
|
||||||
|
self.play(ShimmerIn(time))
|
||||||
|
self.dither()
|
||||||
|
self.play(
|
||||||
|
ShowCreation(chrono_arrow),
|
||||||
|
ShimmerIn(chrono_example)
|
||||||
|
)
|
||||||
|
self.dither()
|
Reference in New Issue
Block a user