mirror of
https://github.com/3b1b/manim.git
synced 2025-08-02 02:35:22 +08:00
Moved complex transformation out of zeta
This commit is contained in:
@ -1,11 +1,174 @@
|
||||
from helpers import *
|
||||
|
||||
|
||||
from mobject import VGroup
|
||||
from mobject.tex_mobject import TexMobject, TextMobject
|
||||
from number_line import NumberPlane
|
||||
from animation import Animation
|
||||
from animation.transform import ApplyPointwiseFunction
|
||||
from animation.simple_animations import Homotopy
|
||||
from scene import Scene
|
||||
|
||||
|
||||
class ComplexTransformationScene(Scene):
|
||||
CONFIG = {
|
||||
"plane_config" : {
|
||||
"x_line_frequency" : 1,
|
||||
"y_line_frequency" : 1,
|
||||
"secondary_line_ratio" : 1,
|
||||
},
|
||||
"background_fade_factor" : 0.5,
|
||||
"x_min" : -int(SPACE_WIDTH),
|
||||
"x_max" : int(SPACE_WIDTH),
|
||||
"y_min" : -SPACE_HEIGHT,
|
||||
"y_max" : SPACE_HEIGHT,
|
||||
"use_multicolored_plane" : False,
|
||||
"vert_start_color" : MAROON_B,
|
||||
"vert_end_color" : RED,
|
||||
"horiz_start_color" : GREEN_B,
|
||||
"horiz_end_color" : YELLOW,
|
||||
"num_anchors_to_add_per_line" : 50,
|
||||
"post_transformation_stroke_width" : None,
|
||||
"default_apply_complex_function_kwargs" : {
|
||||
"run_time" : 5,
|
||||
},
|
||||
"background_label_scale_val" : 0.5,
|
||||
}
|
||||
def setup(self):
|
||||
self.foreground_mobjects = []
|
||||
self.transformable_mobjects = []
|
||||
self.add_background_plane()
|
||||
|
||||
def add_foreground_mobject(self, mobject):
|
||||
self.add_foreground_mobjects(mobject)
|
||||
|
||||
def add_transformable_mobjects(self, *mobjects):
|
||||
self.transformable_mobjects += list(mobjects)
|
||||
self.add(*mobjects)
|
||||
|
||||
def add_foreground_mobjects(self, *mobjects):
|
||||
self.foreground_mobjects += list(mobjects)
|
||||
Scene.add(self, *mobjects)
|
||||
|
||||
def add(self, *mobjects):
|
||||
Scene.add(self, *list(mobjects)+self.foreground_mobjects)
|
||||
|
||||
def play(self, *animations, **kwargs):
|
||||
Scene.play(
|
||||
self,
|
||||
*list(animations)+map(Animation, self.foreground_mobjects),
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def add_background_plane(self):
|
||||
background = NumberPlane(**self.plane_config).fade(
|
||||
self.background_fade_factor
|
||||
)
|
||||
real_labels = VGroup(*[
|
||||
TexMobject(str(x)).shift(
|
||||
background.num_pair_to_point((x, 0))
|
||||
)
|
||||
for x in range(-int(self.x_max), int(self.x_max))
|
||||
])
|
||||
imag_labels = VGroup(*[
|
||||
TexMobject("%di"%y).shift(
|
||||
background.num_pair_to_point((0, y))
|
||||
)
|
||||
for y in range(-int(self.y_max), int(self.y_max))
|
||||
if y != 0
|
||||
])
|
||||
for labels in real_labels, imag_labels:
|
||||
for label in labels:
|
||||
label.scale_in_place(self.background_label_scale_val)
|
||||
label.next_to(label.get_center(), DOWN+LEFT, buff = SMALL_BUFF)
|
||||
label.add_background_rectangle()
|
||||
background.add(labels)
|
||||
self.real_labels = real_labels
|
||||
self.imag_labels = imag_labels
|
||||
self.add(background)
|
||||
self.background = background
|
||||
|
||||
def add_transformable_plane(self, animate = False):
|
||||
self.plane_config.update({
|
||||
"x_radius" : (self.x_max - self.x_min)/2.,
|
||||
"y_radius" : (self.y_max - self.y_min)/2.,
|
||||
})
|
||||
plane = NumberPlane(**self.plane_config)
|
||||
plane.shift(
|
||||
(self.x_max+self.x_min)*RIGHT/2.,
|
||||
(self.y_max+self.y_min)*UP/2.,
|
||||
)
|
||||
self.paint_plane(plane)
|
||||
if animate:
|
||||
self.play(ShowCreation(plane, run_time = 2))
|
||||
else:
|
||||
self.add(plane)
|
||||
self.plane = plane
|
||||
|
||||
def prepare_for_transformation(self, mob):
|
||||
if hasattr(mob, "prepare_for_nonlinear_transform"):
|
||||
mob.prepare_for_nonlinear_transform(
|
||||
self.num_anchors_to_add_per_line
|
||||
)
|
||||
#TODO...
|
||||
|
||||
def paint_plane(self, plane):
|
||||
if self.use_multicolored_plane:
|
||||
for lines in plane.main_lines, plane.secondary_lines:
|
||||
lines.gradient_highlight(
|
||||
self.vert_start_color,
|
||||
self.vert_end_color,
|
||||
self.horiz_start_color,
|
||||
self.horiz_end_color,
|
||||
)
|
||||
plane.axes.gradient_highlight(
|
||||
self.horiz_start_color,
|
||||
self.vert_start_color
|
||||
)
|
||||
|
||||
def z_to_point(self, z):
|
||||
return self.background.num_pair_to_point((z.real, z.imag))
|
||||
|
||||
def get_transformer(self, **kwargs):
|
||||
transform_kwargs = dict(self.default_apply_complex_function_kwargs)
|
||||
transform_kwargs.update(kwargs)
|
||||
plane = self.plane
|
||||
self.prepare_for_transformation(plane)
|
||||
transformer = VGroup(
|
||||
plane, *self.transformable_mobjects
|
||||
)
|
||||
return transformer, transform_kwargs
|
||||
|
||||
|
||||
def apply_complex_function(self, func, added_anims = [], **kwargs):
|
||||
transformer, transform_kwargs = self.get_transformer(**kwargs)
|
||||
transformer.generate_target()
|
||||
transformer.target.apply_complex_function(func)
|
||||
for mob in transformer.target[0].family_members_with_points():
|
||||
mob.make_smooth()
|
||||
if self.post_transformation_stroke_width is not None:
|
||||
transformer.target.set_stroke(width = self.post_transformation_stroke_width)
|
||||
self.play(
|
||||
MoveToTarget(transformer, **transform_kwargs),
|
||||
*added_anims
|
||||
)
|
||||
|
||||
def apply_complex_homotopy(self, complex_homotopy, added_anims = [], **kwargs):
|
||||
transformer, transform_kwargs = self.get_transformer(**kwargs)
|
||||
def homotopy(x, y, z, t):
|
||||
output = complex_homotopy(complex(x, y), t)
|
||||
return (output.real, output.imag, z)
|
||||
|
||||
self.play(
|
||||
SmoothedVectorizedHomotopy(
|
||||
homotopy, transformer,
|
||||
**transform_kwargs
|
||||
),
|
||||
*added_anims
|
||||
)
|
||||
|
||||
##### Unsure about what comes under here...
|
||||
|
||||
def complex_string(complex_num):
|
||||
return filter(lambda c : c not in "()", str(complex_num))
|
||||
|
||||
@ -99,107 +262,22 @@ class ComplexHomotopy(Homotopy):
|
||||
Homotopy.__init__(self, homotopy, mobject, *args, **kwargs)
|
||||
|
||||
|
||||
class ComplexMultiplication(Scene):
|
||||
@staticmethod
|
||||
def args_to_string(multiplier, mark_one = False):
|
||||
num_str = complex_string(multiplier)
|
||||
arrow_str = "MarkOne" if mark_one else ""
|
||||
return num_str + arrow_str
|
||||
|
||||
@staticmethod
|
||||
def string_to_args(arg_string):
|
||||
parts = arg_string.split()
|
||||
multiplier = complex(parts[0])
|
||||
mark_one = len(parts) > 1 and parts[1] == "MarkOne"
|
||||
return (multiplier, mark_one)
|
||||
|
||||
def construct(self, multiplier, mark_one = False, **plane_config):
|
||||
norm = np.linalg.norm(multiplier)
|
||||
arg = np.log(multiplier).imag
|
||||
plane_config["faded_line_frequency"] = 0
|
||||
plane_config.update(DEFAULT_PLANE_CONFIG)
|
||||
if norm > 1 and "density" not in plane_config:
|
||||
plane_config["density"] = norm*DEFAULT_POINT_DENSITY_1D
|
||||
if "radius" not in plane_config:
|
||||
radius = SPACE_WIDTH
|
||||
if norm > 0 and norm < 1:
|
||||
radius /= norm
|
||||
else:
|
||||
radius = plane_config["radius"]
|
||||
plane_config["x_radius"] = plane_config["y_radius"] = radius
|
||||
plane = ComplexPlane(**plane_config)
|
||||
self.plane = plane
|
||||
self.add(plane)
|
||||
# plane.add_spider_web()
|
||||
self.anim_config = {
|
||||
"run_time" : 2.0,
|
||||
"path_func" : path_along_arc(arg)
|
||||
}
|
||||
|
||||
plane_config["faded_line_frequency"] = 0.5
|
||||
background = ComplexPlane(color = "grey", **plane_config)
|
||||
# background.add_spider_web()
|
||||
labels = background.get_coordinate_labels()
|
||||
self.paint_into_background(background, *labels)
|
||||
self.mobjects_to_move_without_molding = []
|
||||
if mark_one:
|
||||
self.draw_dot("1", 1, True)
|
||||
self.draw_dot("z", multiplier)
|
||||
|
||||
|
||||
self.mobjects_to_multiply = [plane]
|
||||
|
||||
self.additional_animations = []
|
||||
self.multiplier = multiplier
|
||||
if self.__class__ == ComplexMultiplication:
|
||||
self.apply_multiplication()
|
||||
|
||||
def draw_dot(self, tex_string, value, move_dot = False):
|
||||
dot = Dot(
|
||||
self.plane.number_to_point(value),
|
||||
radius = 0.1*self.plane.unit_to_spatial_width,
|
||||
color = BLUE if value == 1 else YELLOW
|
||||
)
|
||||
label = TexMobject(tex_string)
|
||||
label.shift(dot.get_center()+1.5*UP+RIGHT)
|
||||
arrow = Arrow(label, dot)
|
||||
self.add(label)
|
||||
self.play(ShowCreation(arrow))
|
||||
self.play(ShowCreation(dot))
|
||||
self.dither()
|
||||
|
||||
self.remove(label, arrow)
|
||||
if move_dot:
|
||||
self.mobjects_to_move_without_molding.append(dot)
|
||||
return dot
|
||||
|
||||
|
||||
def apply_multiplication(self):
|
||||
def func((x, y, z)):
|
||||
complex_num = self.multiplier*complex(x, y)
|
||||
return (complex_num.real, complex_num.imag, z)
|
||||
mobjects = self.mobjects_to_multiply
|
||||
mobjects += self.mobjects_to_move_without_molding
|
||||
mobjects += [anim.mobject for anim in self.additional_animations]
|
||||
|
||||
|
||||
self.add(*mobjects)
|
||||
full_multiplications = [
|
||||
ApplyMethod(mobject.apply_function, func, **self.anim_config)
|
||||
for mobject in self.mobjects_to_multiply
|
||||
]
|
||||
movements_with_plane = [
|
||||
ApplyMethod(
|
||||
mobject.shift,
|
||||
func(mobject.get_center())-mobject.get_center(),
|
||||
**self.anim_config
|
||||
)
|
||||
for mobject in self.mobjects_to_move_without_molding
|
||||
]
|
||||
self.dither()
|
||||
self.play(*reduce(op.add, [
|
||||
full_multiplications,
|
||||
movements_with_plane,
|
||||
self.additional_animations
|
||||
]))
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user