mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
Disection of brachistochrone project
This commit is contained in:
parent
1652621e51
commit
0053c70f01
4 changed files with 342 additions and 47 deletions
|
@ -1,33 +1,26 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import numpy as np
|
||||
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 mobject.tex_mobject import TexMobject, TextMobject
|
||||
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
|
||||
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
|
||||
Arc, FilledRectangle
|
||||
from topics.characters import Randolph, Mathematician
|
||||
from topics.functions import ParametricFunction, FunctionGraph
|
||||
from topics.number_line import NumberPlane
|
||||
|
@ -36,28 +29,7 @@ from scene import Scene
|
|||
|
||||
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):
|
||||
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):
|
||||
DEFAULT_CONFIG = {
|
||||
"gravity" : 3,
|
||||
|
@ -273,22 +323,38 @@ class RollingRandolph(PathSlidingScene):
|
|||
|
||||
|
||||
|
||||
class SimplePhoton(Scene):
|
||||
class OceanScene(Scene):
|
||||
def construct(self):
|
||||
photon = wavify(Cycloid())
|
||||
photon.highlight(YELLOW)
|
||||
shaddow = photon.copy().highlight(BLACK)
|
||||
self.rolling_waves()
|
||||
|
||||
self.play(
|
||||
ShowCreation(photon, rate_func = None),
|
||||
ShowCreation(
|
||||
shaddow,
|
||||
rate_func = lambda t : max(0, t-0.1)
|
||||
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
|
||||
)
|
||||
)
|
||||
self.dither()
|
||||
|
||||
|
||||
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()
|
||||
|
||||
|
||||
|
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()
|
Loading…
Add table
Reference in a new issue