mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Incremental progress on WindingNumber
This commit is contained in:
parent
9bd1628bf4
commit
00ee9c3497
1 changed files with 118 additions and 100 deletions
|
@ -32,64 +32,8 @@ from topics.graph_scene import *
|
||||||
# TODO/WARNING: There's a lot of refactoring and cleanup to be done in this code,
|
# TODO/WARNING: There's a lot of refactoring and cleanup to be done in this code,
|
||||||
# (and it will be done, but first I'll figure out what I'm doing with all this...)
|
# (and it will be done, but first I'll figure out what I'm doing with all this...)
|
||||||
# -SR
|
# -SR
|
||||||
|
|
||||||
class DualScene(Scene):
|
|
||||||
CONFIG = {
|
|
||||||
"num_needed_anchor_points" : 10
|
|
||||||
}
|
|
||||||
|
|
||||||
def setup(self):
|
|
||||||
split_line = DashedLine(SPACE_HEIGHT * UP, SPACE_HEIGHT * DOWN)
|
|
||||||
self.num_plane = NumberPlane(x_radius = SPACE_WIDTH/2)
|
|
||||||
self.num_plane.to_edge(LEFT, buff = 0)
|
|
||||||
self.num_plane.prepare_for_nonlinear_transform()
|
|
||||||
self.add(self.num_plane, split_line)
|
|
||||||
|
|
||||||
def apply_function(self, func, run_time = 3):
|
|
||||||
self.func = func
|
|
||||||
right_plane = self.num_plane.copy()
|
|
||||||
right_plane.center()
|
|
||||||
right_plane.prepare_for_nonlinear_transform()
|
|
||||||
right_plane.apply_function(func)
|
|
||||||
right_plane.shift(SPACE_WIDTH/2 * RIGHT)
|
|
||||||
self.right_plane = right_plane
|
|
||||||
crappy_cropper = FullScreenFadeRectangle(fill_opacity = 1)
|
|
||||||
crappy_cropper.stretch_to_fit_width(SPACE_WIDTH)
|
|
||||||
crappy_cropper.to_edge(LEFT, buff = 0)
|
|
||||||
self.play(
|
|
||||||
ReplacementTransform(self.num_plane.copy(), right_plane),
|
|
||||||
FadeIn(crappy_cropper),
|
|
||||||
Animation(self.num_plane),
|
|
||||||
run_time = run_time
|
|
||||||
)
|
|
||||||
|
|
||||||
def squash_onto_left(self, object):
|
|
||||||
object.shift(SPACE_WIDTH/2 * LEFT)
|
|
||||||
|
|
||||||
def squash_onto_right(self, object):
|
|
||||||
object.shift(SPACE_WIDTH/2 * RIGHT)
|
|
||||||
|
|
||||||
def path_draw(self, input_object, run_time = 3):
|
|
||||||
output_object = input_object.copy()
|
|
||||||
if input_object.get_num_anchor_points() < self.num_needed_anchor_points:
|
|
||||||
input_object.insert_n_anchor_points(self.num_needed_anchor_points)
|
|
||||||
output_object.apply_function(self.func)
|
|
||||||
self.squash_onto_left(input_object)
|
|
||||||
self.squash_onto_right(output_object)
|
|
||||||
self.play(
|
|
||||||
ShowCreation(input_object),
|
|
||||||
ShowCreation(output_object),
|
|
||||||
run_time = run_time
|
|
||||||
)
|
|
||||||
|
|
||||||
class TestDual(DualScene):
|
|
||||||
def construct(self):
|
|
||||||
self.force_skipping()
|
|
||||||
self.apply_function(lambda (x, y, z) : complex_to_R3(complex(x,y)**2))
|
|
||||||
self.revert_to_original_skipping_status()
|
|
||||||
self.path_draw(Line(LEFT + DOWN, RIGHT + DOWN))
|
|
||||||
|
|
||||||
class EquationSolver1d(GraphScene, ZoomedScene, ReconfigurableScene):
|
class EquationSolver1d(GraphScene, ZoomedScene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"func" : lambda x : x,
|
"func" : lambda x : x,
|
||||||
"targetX" : 0,
|
"targetX" : 0,
|
||||||
|
@ -302,7 +246,7 @@ class ArrowCircleTest(Scene):
|
||||||
base_arrow = Arrow(circle_radius * 0.7 * RIGHT, circle_radius * 1.3 * RIGHT)
|
base_arrow = Arrow(circle_radius * 0.7 * RIGHT, circle_radius * 1.3 * RIGHT)
|
||||||
|
|
||||||
def rev_rotate(x, revs):
|
def rev_rotate(x, revs):
|
||||||
x.rotate(revs * TAU)
|
x.rotate(revs * TAU, about_point = ORIGIN)
|
||||||
x.set_color(color_func(revs))
|
x.set_color(color_func(revs))
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
@ -560,8 +504,11 @@ class PiWalker(Scene):
|
||||||
rev_func = rev_func,
|
rev_func = rev_func,
|
||||||
remover = (i < len(walk_coords) - 1)
|
remover = (i < len(walk_coords) - 1)
|
||||||
),
|
),
|
||||||
ShowCreation(Line(start_point, end_point)),
|
ShowCreation(Line(start_point, end_point), rate_func = None),
|
||||||
run_time = self.step_run_time)
|
run_time = self.step_run_time)
|
||||||
|
|
||||||
|
# TODO: Allow smooth paths instead of brekaing them up into lines, and
|
||||||
|
# use point_from_proportion to get points along the way
|
||||||
|
|
||||||
|
|
||||||
self.wait()
|
self.wait()
|
||||||
|
@ -626,10 +573,10 @@ class EquationSolver2d(Scene):
|
||||||
alpha_winder = make_alpha_winder(rev_func, start, end, self.num_checkpoints)
|
alpha_winder = make_alpha_winder(rev_func, start, end, self.num_checkpoints)
|
||||||
a0 = alpha_winder(0)
|
a0 = alpha_winder(0)
|
||||||
rebased_winder = lambda alpha: alpha_winder(alpha) - a0 + start_wind
|
rebased_winder = lambda alpha: alpha_winder(alpha) - a0 + start_wind
|
||||||
line = Line(num_plane.coords_to_point(*start), num_plane.coords_to_point(*end),
|
flashing_line = Line(num_plane.coords_to_point(*start), num_plane.coords_to_point(*end),
|
||||||
stroke_width = 5,
|
stroke_width = 5,
|
||||||
color = RED)
|
color = RED)
|
||||||
thin_line = line.copy()
|
thin_line = flashing_line.copy()
|
||||||
thin_line.set_stroke(width = 1)
|
thin_line.set_stroke(width = 1)
|
||||||
walker_anim = LinearWalker(
|
walker_anim = LinearWalker(
|
||||||
start_coords = start,
|
start_coords = start,
|
||||||
|
@ -638,12 +585,12 @@ class EquationSolver2d(Scene):
|
||||||
rev_func = rev_func,
|
rev_func = rev_func,
|
||||||
remover = True
|
remover = True
|
||||||
)
|
)
|
||||||
line_draw_anim = AnimationGroup(ShowCreation(line, rate_func = None), walker_anim,
|
line_draw_anim = AnimationGroup(
|
||||||
run_time = 2)
|
ShowCreation(thin_line),
|
||||||
anim = Succession(
|
#ShowPassingFlash(flashing_line),
|
||||||
line_draw_anim,
|
walker_anim,
|
||||||
Transform, line, thin_line
|
rate_func = None)
|
||||||
)
|
anim = line_draw_anim
|
||||||
return (anim, rebased_winder(1))
|
return (anim, rebased_winder(1))
|
||||||
|
|
||||||
wind_so_far = 0
|
wind_so_far = 0
|
||||||
|
@ -736,6 +683,29 @@ class FirstSqrtScene(EquationSolver1d):
|
||||||
"show_target_line" : True,
|
"show_target_line" : True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SecondSqrtScene(FirstSqrtScene, ReconfigurableScene):
|
||||||
|
# TODO: Don't bother with ReconfigurableScene; just use new config from start
|
||||||
|
# (But can also use this as written, and just cut into middle in Premiere)
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
FirstSqrtScene.setup(self)
|
||||||
|
ReconfigurableScene.setup(self)
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
shiftVal = self.targetY
|
||||||
|
|
||||||
|
self.drawGraph()
|
||||||
|
newOrigin = self.coords_to_point(0, shiftVal)
|
||||||
|
self.transition_to_alt_config(
|
||||||
|
func = lambda x : x**2 - shiftVal,
|
||||||
|
targetY = 0,
|
||||||
|
graph_label = "y = x^2 - " + str(shiftVal),
|
||||||
|
y_min = self.y_min - shiftVal,
|
||||||
|
y_max = self.y_max - shiftVal,
|
||||||
|
show_target_line = False,
|
||||||
|
graph_origin = newOrigin)
|
||||||
|
self.solveEquation()
|
||||||
|
|
||||||
# TODO: Pi creatures intrigued
|
# TODO: Pi creatures intrigued
|
||||||
|
|
||||||
class ComplexPlaneIs2d(Scene):
|
class ComplexPlaneIs2d(Scene):
|
||||||
|
@ -758,15 +728,22 @@ class NumberLineScene(Scene):
|
||||||
|
|
||||||
left_point = num_line.number_to_point(-1)
|
left_point = num_line.number_to_point(-1)
|
||||||
right_point = num_line.number_to_point(1)
|
right_point = num_line.number_to_point(1)
|
||||||
|
# TODO: Make this line a thin rectangle
|
||||||
interval_1d = Line(left_point, right_point,
|
interval_1d = Line(left_point, right_point,
|
||||||
stroke_color = inner_color, stroke_width = stroke_width)
|
stroke_color = inner_color, stroke_width = stroke_width)
|
||||||
|
rect_1d = Rectangle(stroke_width = 0, fill_opacity = 1, fill_color = inner_color)
|
||||||
|
rect_1d.replace(interval_1d)
|
||||||
|
rect_1d.stretch_to_fit_height(SMALL_BUFF)
|
||||||
left_dot = Dot(left_point, stroke_width = stroke_width, color = border_color)
|
left_dot = Dot(left_point, stroke_width = stroke_width, color = border_color)
|
||||||
right_dot = Dot(right_point, stroke_width = stroke_width, color = border_color)
|
right_dot = Dot(right_point, stroke_width = stroke_width, color = border_color)
|
||||||
endpoints_1d = VGroup(left_dot, right_dot)
|
endpoints_1d = VGroup(left_dot, right_dot)
|
||||||
full_1d = VGroup(interval_1d, endpoints_1d)
|
full_1d = VGroup(rect_1d, endpoints_1d)
|
||||||
self.play(ShowCreation(full_1d))
|
self.play(ShowCreation(full_1d))
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
|
# TODO: Can polish the morphing above; have dots become left and right sides, and
|
||||||
|
# only then fill in the top and bottom
|
||||||
|
|
||||||
num_plane = NumberPlane()
|
num_plane = NumberPlane()
|
||||||
|
|
||||||
random_points = [UP + LEFT, UP + RIGHT, DOWN + RIGHT, DOWN + LEFT]
|
random_points = [UP + LEFT, UP + RIGHT, DOWN + RIGHT, DOWN + LEFT]
|
||||||
|
@ -790,20 +767,78 @@ class NumberLineScene(Scene):
|
||||||
|
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
class Initial2dFuncScene(Scene):
|
initial_2d_func = point_func_from_complex_func(lambda c : np.exp(c))
|
||||||
|
|
||||||
|
class Initial2dFuncSceneMorphing(Scene):
|
||||||
|
CONFIG = {
|
||||||
|
"num_needed_anchor_points" : 10,
|
||||||
|
"func" : initial_2d_func,
|
||||||
|
}
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
split_line = DashedLine(SPACE_HEIGHT * UP, SPACE_HEIGHT * DOWN)
|
||||||
|
self.num_plane = NumberPlane(x_radius = SPACE_WIDTH/2)
|
||||||
|
self.num_plane.to_edge(LEFT, buff = 0)
|
||||||
|
self.num_plane.prepare_for_nonlinear_transform()
|
||||||
|
self.add(self.num_plane, split_line)
|
||||||
|
|
||||||
|
def squash_onto_left(self, object):
|
||||||
|
object.shift(SPACE_WIDTH/2 * LEFT)
|
||||||
|
|
||||||
|
def squash_onto_right(self, object):
|
||||||
|
object.shift(SPACE_WIDTH/2 * RIGHT)
|
||||||
|
|
||||||
|
def obj_draw(self, input_object):
|
||||||
|
output_object = input_object.copy()
|
||||||
|
if input_object.get_num_anchor_points() < self.num_needed_anchor_points:
|
||||||
|
input_object.insert_n_anchor_points(self.num_needed_anchor_points)
|
||||||
|
output_object.apply_function(self.func)
|
||||||
|
self.squash_onto_left(input_object)
|
||||||
|
self.squash_onto_right(output_object)
|
||||||
|
self.play(
|
||||||
|
ShowCreation(input_object),
|
||||||
|
ShowCreation(output_object)
|
||||||
|
)
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
right_plane = self.num_plane.copy()
|
||||||
|
right_plane.center()
|
||||||
|
right_plane.prepare_for_nonlinear_transform()
|
||||||
|
right_plane.apply_function(self.func)
|
||||||
|
right_plane.shift(SPACE_WIDTH/2 * RIGHT)
|
||||||
|
self.right_plane = right_plane
|
||||||
|
crappy_cropper = FullScreenFadeRectangle(fill_opacity = 1)
|
||||||
|
crappy_cropper.stretch_to_fit_width(SPACE_WIDTH)
|
||||||
|
crappy_cropper.to_edge(LEFT, buff = 0)
|
||||||
|
self.play(
|
||||||
|
ReplacementTransform(self.num_plane.copy(), right_plane),
|
||||||
|
FadeIn(crappy_cropper),
|
||||||
|
Animation(self.num_plane),
|
||||||
|
run_time = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
points = [LEFT + DOWN, RIGHT + DOWN, LEFT + UP, RIGHT + UP]
|
||||||
|
for i in range(len(points) - 1):
|
||||||
|
line = Line(points[i], points[i + 1], color = RED)
|
||||||
|
self.obj_draw(line)
|
||||||
|
|
||||||
|
# Alternative to the above, using MappingCameras, but no morphing animation
|
||||||
|
class Initial2dFuncSceneWithoutMorphing(Scene):
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
left_camera = Camera(**self.camera_config)
|
left_camera = Camera(**self.camera_config)
|
||||||
right_camera = MappingCamera(
|
right_camera = MappingCamera(
|
||||||
mapping_func = point_func_from_complex_func(lambda c : np.exp(c)),
|
mapping_func = initial_2d_func,
|
||||||
**self.camera_config)
|
**self.camera_config)
|
||||||
split_screen_camera = SplitScreenCamera(left_camera, right_camera, **self.camera_config)
|
split_screen_camera = SplitScreenCamera(left_camera, right_camera, **self.camera_config)
|
||||||
self.camera = split_screen_camera
|
self.camera = split_screen_camera
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
num_plane = NumberPlane()
|
num_plane = NumberPlane()
|
||||||
num_plane.fade()
|
num_plane.prepare_for_nonlinear_transform()
|
||||||
|
#num_plane.fade()
|
||||||
self.add(num_plane)
|
self.add(num_plane)
|
||||||
|
|
||||||
points = [LEFT + DOWN, RIGHT + DOWN, LEFT + UP, RIGHT + UP]
|
points = [LEFT + DOWN, RIGHT + DOWN, LEFT + UP, RIGHT + UP]
|
||||||
for i in range(len(points) - 1):
|
for i in range(len(points) - 1):
|
||||||
line = Line(points[i], points[i + 1], color = RED)
|
line = Line(points[i], points[i + 1], color = RED)
|
||||||
|
@ -814,28 +849,8 @@ class Initial2dFuncScene(Scene):
|
||||||
# TODO: Bunch of Pi walker scenes
|
# TODO: Bunch of Pi walker scenes
|
||||||
|
|
||||||
# TODO: An odometer scene when introducing winding numbers
|
# TODO: An odometer scene when introducing winding numbers
|
||||||
|
# (Just set up an OdometerScene with function matching the walking of the Pi
|
||||||
class SecondSqrtScene(FirstSqrtScene, ReconfigurableScene):
|
# creature from previous scene, then place it as a simultaneous inset with Premiere)
|
||||||
# TODO: Don't bother with ReconfigurableScene; just use new config from start
|
|
||||||
|
|
||||||
def setup(self):
|
|
||||||
FirstSqrtScene.setup(self)
|
|
||||||
ReconfigurableScene.setup(self)
|
|
||||||
|
|
||||||
def construct(self):
|
|
||||||
shiftVal = self.targetY
|
|
||||||
|
|
||||||
self.drawGraph()
|
|
||||||
newOrigin = self.coords_to_point(0, shiftVal)
|
|
||||||
self.transition_to_alt_config(
|
|
||||||
func = lambda x : x**2 - shiftVal,
|
|
||||||
targetY = 0,
|
|
||||||
graph_label = "y = x^2 - " + str(shiftVal),
|
|
||||||
y_min = self.y_min - shiftVal,
|
|
||||||
y_max = self.y_max - shiftVal,
|
|
||||||
show_target_line = False,
|
|
||||||
graph_origin = newOrigin)
|
|
||||||
self.solveEquation()
|
|
||||||
|
|
||||||
class LoopSplitScene(Scene):
|
class LoopSplitScene(Scene):
|
||||||
|
|
||||||
|
@ -963,12 +978,12 @@ class LoopSplitSceneMapped(LoopSplitScene):
|
||||||
# to illustrate relation between degree and large-scale winding number
|
# to illustrate relation between degree and large-scale winding number
|
||||||
class FundThmAlg(EquationSolver2d):
|
class FundThmAlg(EquationSolver2d):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"func" : plane_poly_with_roots((1, 2), (-1, 3), (-1, 3)),
|
"func" : plane_poly_with_roots((1, 2), (-1, 2.5), (-1, 2.5)),
|
||||||
"num_iterations" : 1,
|
"num_iterations" : 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO: Borsuk-Ulam visuals
|
# TODO: Borsuk-Ulam visuals
|
||||||
# Note: May want to do an ordinary square scene, then mapping func it into a circle
|
# Note: May want to do an ordinary square scene, then MappingCamera it into a circle
|
||||||
# class BorsukUlamScene(PiWalker):
|
# class BorsukUlamScene(PiWalker):
|
||||||
|
|
||||||
# 3-way scene of "Good enough"-illustrating odometers; to be composed in Premiere
|
# 3-way scene of "Good enough"-illustrating odometers; to be composed in Premiere
|
||||||
|
@ -1002,15 +1017,16 @@ class DiffOdometer(OdometerScene):
|
||||||
|
|
||||||
# TODOs, from easiest to hardest:
|
# TODOs, from easiest to hardest:
|
||||||
|
|
||||||
# Minor fiddling with little things in each animation; placements, colors, timing
|
# Minor fiddling with little things in each animation; placements, colors, timing, text
|
||||||
|
|
||||||
# Odometer/swinging arrows stuff
|
# Initial odometer scene (simple once previous Pi walker scene is decided upon)
|
||||||
|
|
||||||
# Writing new Pi creature walker scenes off of general template
|
# Writing new Pi walker scenes by parametrizing general template
|
||||||
|
|
||||||
# Split screen illustration of 2d function (before domain coloring)
|
# Generalizing Pi walker stuff to make bullets on pulsing lines change colors dynamically according to
|
||||||
|
# function traced out
|
||||||
|
|
||||||
# Generalizing Pi color walker stuff/making bullets on pulsing lines change colors dynamically according to function traced out
|
# Debugging Pi walker stuff added to EquationSolver2d
|
||||||
|
|
||||||
# ----
|
# ----
|
||||||
|
|
||||||
|
@ -1022,4 +1038,6 @@ class DiffOdometer(OdometerScene):
|
||||||
|
|
||||||
# Domain coloring
|
# Domain coloring
|
||||||
|
|
||||||
|
# TODO: Ask about tracked mobject, which is probably very useful for our animations
|
||||||
|
|
||||||
# FIN
|
# FIN
|
||||||
|
|
Loading…
Add table
Reference in a new issue